📊 第4章 Pandas 进阶
📄 一句话概括

格式化改的是「显示」,不是「数值」。format 之后那一列变成字符串,不能再算。 百分比要先除成 0 到 1 的小数,再交给 {:.1%},否则会大一百倍。 要显示用 style.format,要计算用原列。两边混用是格式化最常翻的车。

📝 本章和前面几章的关系

前四篇算出来的数都是 82.69999999999 这种,直接给人看不行。这一篇专治显示。 三条路:map 加 format 造一列字符串、style 改渲染、导出时交给 Excel。

一、保留两位小数

df['语文'].map('{:.2f}'.format) 得到一列 88.00、92.00、75.00。 这一列已经是字符串了。它不再是数字,不能再求和。 试着 sum 一下,得到的是「88.0092.0075.00…」,一长串拼接。

这就是格式化和改数值的区别:看着像数,实际是字。 要算就用原列,要给人看才格式化。两张表并存是最稳的写法。

二、百分比

(df['语文'] / 100).map('{:.1%}'.format) 得到 88.0%、92.0%、75.0%。 {:.1%} 会自动乘 100 再加百分号,所以先要把 88 除成 0.88。 忘了先除 100,结果就是 8800.0%。大了一百倍,而且看起来还挺正常。

比例本来就是 0 到 1 之间的数。要显示成百分数,乘 100 这一步交给格式串做。

三、千位分隔符

原值 千分位 带两位小数
1280 1,280 1,280.00
45600 45,600 45,600.00
1234567 1,234,567 1,234,567.00

{:,.0f} 里逗号管千分位,点后面的 0 管小数位。 合起来 {:,.2f} 就是「千分位加两位小数」。 金额、销量这类数一定要加千分位。1,234,567 和 1234567 的易读性差得远。

四、不造新列的办法:style

df[ ['语文','数学'] ].style.format({'语文': '{:.1f}'}) 只改渲染。 它在终端里打不出效果——你用 .data 拿回来还是原值 88,不是 88.0。 导出 Excel 或者渲染成 HTML 才看得到效果,数据本身一个字节都没变。

这正是它的好处:显示变了,计算不受影响。 缺点是它只在支持渲染的地方有效。要落到文本里,还得走 map 加 format。

五、动手 Demo:数据格式化:小数位、百分比、千位分隔

三件事各演示一遍:两位小数、百分比、千分位。 中间故意对格式化后的列求一次和,看它怎么变成字符串拼接。

import pandas as pd

S = pd.Series([88, 92, 75, 85, 90])

TXT = S.map("{:.2f}".format)
print("两位小数 %s" % TXT.tolist())
print("这一列的类型 %s" % TXT.dtype)
print("对它求和:%s" % TXT.sum())

PCT = (S / 100.0).map("{:.1%}".format)
print("百分比 %s" % PCT.tolist())
print("直接对 88 用:%s" % "{:.1%}".format(88))

for v in [1280, 45600, 1234567]:
    print("%9d -> %12s -> %14s" % (v, "{:,.0f}".format(v), "{:,.2f}".format(v)))

实跑输出:

--------------------------------------------------------------------
数据格式化:小数位、百分比、千位分隔
--------------------------------------------------------------------

  ① 保留两位小数
  --------------------------------------------------------------------

| 行 | 姓名 | 语文 | 语文两位 |
|---|---:|---:|---:|
| 0 | 张伟 | 88 | 88.00 |
| 1 | 王芳 | 92 | 92.00 |
| 2 | 李娜 | 75 | 75.00 |

  格式化之后这一列变成字符串了——它已经不是数字,不能再求和。
  试着 sum 一下:88.0092.0075.0085.0090.0068.0095.0072.0083.0079.00。是拼接,不是加法。
  ! format 只改显示,不改数值。要算就用原列,要给人看才用格式化列。

  ② 百分比
  --------------------------------------------------------------------

| 行 | 姓名 | 语文 | 得分率 |
|---|---:|---:|---:|
| 0 | 张伟 | 88 | 88.0% |
| 1 | 王芳 | 92 | 92.0% |
| 2 | 李娜 | 75 | 75.0% |

  {:.1%} 会自动乘 100 再加百分号,所以要先除成 0 到 1 之间的小数。
  直接对 88 用 {:.1%},出来是 8800.0%。
  ! 百分比格式最容易错的一步:忘了先除 100,结果大了一百倍还看不出来。

  ③ 千位分隔符
  --------------------------------------------------------------------

| 原值 | 千分位 | 带两位小数 |
|---:|---:|---:|
| 1280 | 1,280 | 1,280.00 |
| 45600 | 45,600 | 45,600.00 |
| 1234567 | 1,234,567 | 1,234,567.00 |

  {:,.0f} 里的逗号就是千分位,点后面的 0 是小数位。
  合起来写 {:,.2f} 就是「千分位加两位小数」。
  ! 金额、销量这类数一定要加千分位——1234567 和 1,234,567 的易读性差远了。

  ④ 一次格式化整张表
  --------------------------------------------------------------------
  style.format 传一个字典,一次给多列配格式。它只影响渲染,终端里打不出效果:

| 行 | 语文 | 数学 | 英语 |
|---|---:|---:|---:|
| 0 | 88 | 92 | 85 |
| 1 | 92 | 95 | 90 |
| 2 | 75 | 68 | 80 |

  上面仍是原值 88,不是 88.0。要看到效果得导出 Excel 或渲染成 HTML。
  这正是它的好处:显示变了,数据没变,后面照常算。
  ! 要显示用 style,要计算用原列。把两者混起来是格式化最常翻的车。

TXT.dtype 打出来是 str——pandas 3 里字符串列的 dtype 就叫这个名字。 求和的输出是一长串 88.0092.00…,这就是「看着像数其实是字」的铁证。

30 秒实验

  1. 把 {:.2f} 改成 {:.0f},看小数位怎么变。
  2. 对 0.0523 用 {:.1%} 和 {:.2%},各打一遍。
  3. 想一想:如果先格式化再排序,排出来会是什么顺序?为什么?

常见误区

  1. 格式化后接着算:字符串相加,得到一长串拼接。
  2. 百分比忘了除 100:结果大一百倍,还挺像真的。
  3. 以为 style 改了数据:它只改渲染,取回来还是原值。
  4. 用 round 代替格式化:round 改的是数值,会真的丢精度。
自测三问(点开对答案) 1. format 和 round 有什么区别? —— format 出字符串,只改显示;round 出数字,改的是值本身,精度真丢了。 2. {:.1%} 对一个 88 会输出什么? —— 8800.0%。它自动乘 100,所以传进去的必须是 0 到 1 之间的比例。 3. 什么时候该用 style.format? —— 只为了给人看、并且下游要支持渲染(HTML、Excel、Jupyter)时。要落到纯文本里还是得 map。

单张表的统计到此为止。下一篇开始「分组算」——按班级、按科目分别算。

相关笔记