跳至内容

d3-format

您是否注意到,有时 JavaScript 显示的数字并非您预期的结果?例如,您尝试使用简单的循环打印十分之几

js
for (let i = 0; i < 10; ++i) {
  console.log(0.1 * i);
}

您得到了以下结果

0
0.1
0.2
0.30000000000000004
0.4
0.5
0.6000000000000001
0.7000000000000001
0.8
0.9

欢迎来到 二进制浮点数!ಠ_ಠ

然而,舍入误差并非自定义数字格式的唯一原因。数字表格应格式一致以进行比较;以上,0.0 比 0 更合适。较大的数字应使用分组数字(例如 42,000)或使用科学记数法或公制记数法(4.2e+4,42k)。货币应具有固定精度($3.50)。报告的数值结果应四舍五入到有效数字(4021 变为 4000)。数字格式应适合读者的区域设置(42.000,00 或 42,000.00)。等等。

d3-format 的作用是将数字格式化为适合人类阅读的形式,其设计理念借鉴了 Python 3 的 格式规范迷你语言 (PEP 3101)。让我们重温一下上面的示例

js
const f = d3.format(".1f");
for (let i = 0; i < 10; ++i) {
  console.log(f(0.1 * i));
}

现在您得到了以下结果

0.0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9

但 d3-format 的功能远不止 number.toFixed 的别名!以下是一些其他示例

js
d3.format(".0%")(0.123) // rounded percentage, "12%"
js
d3.format("($.2f")(-3.5) // localized fixed-point currency, "(£3.50)"
js
d3.format("+20")(42) // space-filled and signed, "                 +42"
js
d3.format(".^20")(42) // dot-filled and centered, ".........42........."
js
d3.format(".2s")(42e6) // SI-prefix with two significant digits, "42M"
js
d3.format("#x")(48879) // prefixed lowercase hexadecimal, "0xbeef"
js
d3.format(",.2r")(4223) // grouped thousands with two significant digits, "4,200"

请参见 locale.format 以获取详细规范,并尝试对上述格式运行 d3.formatSpecifier 以解码其含义。

另请参见 number.toLocaleString.

format(specifier)

js
const f = d3.format(".2f");

源代码 · 默认区域设置locale.format 的别名。

formatPrefix(specifier, value)

js
const f = d3.formatPrefix(",.0", 1e-6);

源代码 · 默认区域设置locale.formatPrefix 的别名。

formatLocale(definition)

js
const enUs = d3.formatLocale({
  thousands: ",",
  grouping: [3],
  currency: ["$", ""]
});

源代码 · 返回具有 locale.formatlocale.formatPrefix 方法的指定 definitionlocale 对象。definition 必须包含以下属性

  • decimal - 小数点(例如 ".")。
  • thousands - 分组分隔符(例如 ",")。
  • grouping - 分组大小数组(例如 [3]),按需循环。
  • currency - 货币前缀和后缀(例如 ["$", ""])。
  • numerals - 可选;一个包含十个字符串的数组,用于替换数字 0-9。
  • percent - 可选;百分号(默认为 "%")。
  • minus - 可选;减号(默认为 "−")。
  • nan - 可选;非数字值(默认为 "NaN")。

请注意,thousands 属性是一个误称,因为分组定义允许使用除千位以外的其他分组。

formatDefaultLocale(definition)

js
const enUs = d3.formatDefaultLocale({
  thousands: ",",
  grouping: [3],
  currency: ["$", ""]
});

源代码 · 等效于 d3.formatLocale,只是它还将 d3.formatd3.formatPrefix 重新定义为新区域设置的 locale.formatlocale.formatPrefix。如果您没有设置默认区域设置,它将默认为 美式英语

locale.format(specifier)

js
const f = d3.format(".2f");

源代码 · 返回给定字符串 specifier 的新格式函数。返回的函数将数字作为唯一参数,并返回表示格式化数字的字符串。规范的通用形式为

[​[fill]align][sign][symbol][0][width][,][.precision][~][type]

fill 可以是任何字符。填充字符的存在由其后的 align 字符指示,该字符必须是以下字符之一

  • > - 强制字段在可用空间内右对齐。(默认行为)。
  • < - 强制字段在可用空间内左对齐。
  • ^ - 强制字段在可用空间内居中。
  • = - 类似于 >,但符号和任何符号都位于任何填充的左侧。

sign 可以是

  • - - 零或正数不显示任何内容,负数显示减号。(默认行为)。
  • + - 零或正数显示加号,负数显示减号。
  • ( - 零或正数不显示任何内容,负数显示括号。
  •   (空格) - 零或正数显示空格,负数显示减号。

symbol 可以是

  • $ - 根据区域设置定义应用货币符号。
  • # - 对于二进制、八进制或十六进制记数法,分别以 0b0o0x 为前缀。

zero (0) 选项启用零填充;这隐式地将 fill 设置为 0,并将 align 设置为 =width 定义最小字段宽度;如果未指定,则宽度将由内容确定。comma (,) 选项启用分组分隔符的使用,例如千位分隔符。

根据 typeprecision 表示小数点后的位数(类型 f%),或有效数字的位数(类型 egrsp)。如果未指定精度,则除了 (无)以外的所有类型都默认为 6,而 (无)则默认为 12。整数格式(类型 bodxX)和字符数据(类型 c)会忽略精度。请参见 precisionFixedprecisionRound 以获取有关选择适当精度的帮助。

~ 选项会删除所有格式类型中不重要的尾随零。这在与类型 res% 结合使用时最常见。例如

js
d3.format("s")(1500) // "1.50000k"
js
d3.format("~s")(1500) // "1.5k"

可用的 type 值为

  • e - 指数记数法。
  • f - 固定点记数法。
  • g - 十进制或指数记数法,四舍五入到有效数字。
  • r - 十进制记数法,四舍五入到有效数字。
  • s - 带有 SI 前缀 的十进制记数法,四舍五入到有效数字。
  • % - 乘以 100,然后使用十进制记数法并添加百分号。
  • p - 乘以 100,四舍五入到有效数字,然后使用十进制记数法并添加百分号。
  • b - 二进制记数法,四舍五入到整数。
  • o - 八进制记数法,四舍五入到整数。
  • d - 十进制记数法,四舍五入到整数。
  • x - 十六进制记数法,使用小写字母,四舍五入到整数。
  • X - 十六进制记数法,使用大写字母,四舍五入到整数。
  • c - 字符数据,用于文本字符串。

类型 (无)也作为 ~g 的简写支持(默认精度为 12 而不是 6),类型 n 作为 ,g 的简写支持。对于 gn(无)类型,如果结果字符串的位数少于或等于 precision,则使用十进制记数法;否则,使用指数记数法。例如

js
d3.format(".2")(42) // "42"
js
d3.format(".2")(4.2) // "4.2"
js
d3.format(".1")(42) // "4e+1"
js
d3.format(".1")(4.2) // "4"

locale.formatPrefix(specifier, value)

js
const f = d3.formatPrefix(",.0", 1e-6);

源代码 · 等效于 locale.format,只是返回的函数会将值转换为指定数值引用 value 的相应 SI 前缀 的单位,然后使用固定点记数法进行格式化。支持以下前缀

  • y - 幺,10⁻²⁴
  • z - 仄,10⁻²¹
  • a - 阿,10⁻¹⁸
  • f - 飞,10⁻¹⁵
  • p - 皮,10⁻¹²
  • n - 纳,10⁻⁹
  • µ - 微,10⁻⁶
  • m - 毫,10⁻³
  • (无) - 10⁰
  • k - 千,10³
  • M - 兆,10⁶
  • G - 吉,10⁹
  • T - 太,10¹²
  • P - 拍,10¹⁵
  • E - 艾,10¹⁸
  • Z - 泽,10²¹
  • Y - 尧,10²⁴

与使用 `s` 格式类型的 locale.format 不同,此方法返回一个带有固定 SI 前缀的格式化器,而不是为每个数字动态计算前缀。此外,对于给定说明符精度表示小数点后的位数(与 `f` 定点表示法相同),而不是有效位数。例如

js
const f = d3.formatPrefix(",.0", 1e-6);
f(0.00042); // "420µ"
f(0.0042); // "4,200µ"

此方法在格式化相同单位的多个数字以方便比较时很有用。有关选择合适精度的帮助,请参阅 precisionPrefix

formatSpecifier(specifier)

js
d3.formatSpecifier(".1f")

源代码 · 解析指定的说明符,返回一个包含与 格式规范迷你语言 对应的公开字段的对象,以及一个用于重建说明符的 toString 方法。例如,formatSpecifier("s") 返回

js
FormatSpecifier {
  "fill": " ",
  "align": ">",
  "sign": "-",
  "symbol": "",
  "zero": false,
  "width": undefined,
  "comma": false,
  "precision": undefined,
  "trim": false,
  "type": "s"
}

此方法对于理解格式说明符的解析方式以及推导出新的说明符很有用。例如,您可以使用 precisionFixed 根据要格式化的数字计算适当的精度,然后创建一个新的格式

js
const s = d3.formatSpecifier("f");
s.precision = d3.precisionFixed(0.01);
const f = d3.format(s);
f(42); // "42.00";

new d3.FormatSpecifier(specifier)

js
new d3.FormatSpecifier({type: "f", precision: 1})

源代码 · 给定指定的说明符对象,返回一个包含与 格式规范迷你语言 对应的公开字段的对象,以及一个用于重建说明符的 toString 方法。例如,new FormatSpecifier({type: "s"}) 返回

js
FormatSpecifier {
  "fill": " ",
  "align": ">",
  "sign": "-",
  "symbol": "",
  "zero": false,
  "width": undefined,
  "comma": false,
  "precision": undefined,
  "trim": false,
  "type": "s"
}

precisionFixed(step)

js
d3.precisionFixed(0.01) // 2

源代码 · 给定指定的数字步长值,返回定点表示法的建议小数精度。步长表示将要格式化的值的最小绝对差值。(这假设要格式化的值也是步长的倍数。)例如,对于数字 1、1.5 和 2,步长应为 0.5,建议精度为 1

js
const p = d3.precisionFixed(0.5);
const f = d3.format("." + p + "f");
f(1);   // "1.0"
f(1.5); // "1.5"
f(2);   // "2.0"

而对于数字 1、2 和 3,步长应为 1,建议精度为 0

js
const p = d3.precisionFixed(1);
const f = d3.format("." + p + "f");
f(1); // "1"
f(2); // "2"
f(3); // "3"

注意:对于 `%` 格式类型,减去 2

js
const p = Math.max(0, d3.precisionFixed(0.05) - 2);
const f = d3.format("." + p + "%");
f(0.45); // "45%"
f(0.50); // "50%"
f(0.55); // "55%"

precisionPrefix(step, value)

js
d3.precisionPrefix(1e5, 1.3e6) // 1

源代码 · 给定指定的数字步长和参考,返回与 locale.formatPrefix 一起使用的建议小数精度。步长表示将要格式化的值的最小绝对差值,而决定将使用哪个 SI 前缀。(这假设要格式化的值也是步长的倍数。)例如,对于数字 1.1e6、1.2e6 和 1.3e6,步长应为 1e5,可以为 1.3e6,建议精度为 1

js
const p = d3.precisionPrefix(1e5, 1.3e6);
const f = d3.formatPrefix("." + p, 1.3e6);
f(1.1e6); // "1.1M"
f(1.2e6); // "1.2M"
f(1.3e6); // "1.3M"

precisionRound(step, max)

js
d3.precisionRound(0.01, 1.01) // 3

源代码 · 给定指定的数字步长最大值,返回对有效位数进行舍入的格式类型的建议小数精度。步长表示将要格式化的值的最小绝对差值,而最大值表示将要格式化的最大绝对值。(这假设要格式化的值也是步长的倍数。)例如,对于数字 0.99、1.0 和 1.01,步长应为 0.01,最大值应为 1.01,建议精度为 3

js
const p = d3.precisionRound(0.01, 1.01);
const f = d3.format("." + p + "r");
f(0.99); // "0.990"
f(1.0);  // "1.00"
f(1.01); // "1.01"

而对于数字 0.9、1.0 和 1.1,步长应为 0.1,最大值应为 1.1,建议精度为 2

js
const p = d3.precisionRound(0.1, 1.1);
const f = d3.format("." + p + "r");
f(0.9); // "0.90"
f(1.0); // "1.0"
f(1.1); // "1.1"

注意:对于 `e` 格式类型,减去 1

js
const p = Math.max(0, d3.precisionRound(0.01, 1.01) - 1);
const f = d3.format("." + p + "e");
f(0.01); // "1.00e-2"
f(1.01); // "1.01e+0"