DataFrame 有三种造法:从字典(最常用)、从列表加列名、从 numpy 数组。 字典的键变列名,列表的行变行,数组的形状直接变成表形状。 手工造表多半是为了造测试数据,真正的工作数据都是从文件读进来的。
上一篇讲一条 Series,这一篇讲整张表怎么造。 看似基础,但造测试数据是每天都要做的事——能三行造出想要的表,调试效率差一倍。
一、从字典造:最常用
pd.DataFrame({'姓名': ['甲','乙'], '分数': [90, 85]})。 字典的键变成列名,值变成列。两个列表长度必须一样,否则报 ValueError。
| 行 | 姓名 | 分数 |
|---|---|---|
| 0 | 甲 | 90 |
| 1 | 乙 | 85 |
这是手工造小表最快的方式。写测试数据、举例子、验证一个想法,都用它。
二、从列表的列表造
pd.DataFrame([ [甲, 90], [乙, 85]], columns=['姓名','分数'])。 每一条内层列表是一行。不给 columns,列名会自动变成 0、1。 从数据库、CSV 读出来的原始二维数组,多半走这条路。
列名数量必须和每行的长度对上,否则报错。
三、从 numpy 数组造
np.arange(6).reshape(2, 3) 造一个 2 行 3 列的数组,套进 DataFrame 就是一张表。 数组没有列名,所以要自己给 columns。行标签会自动补 0、1。 这条路适合「数已经算出来了,只差一个表壳」的场景,比如矩阵运算的结果。
DataFrame 内部就是一列一列的 numpy 数组,所以数值运算快,也和 numpy 无缝。
四、三种造法的取舍
| 来源 | 造法 | 什么时候用 |
|---|---|---|
| 手写小数据 | 字典 | 列名清楚,写得快 |
| 二维列表 | 列表 + columns | 数据来自现成的行 |
| 计算结果 | numpy 数组 | 已经在算矩阵了 |
还有一种没列进来:从另一张表切出来(df.loc[条件, 列]),那是筛选不是造表。 记住字典那一种就够应付八成场景。剩下两种,见到能认出来就行。
五、造表时最容易错的两件事
第一,列名重复。{'a':[1], 'a':[2]} 这种字典直接不允许,但赋值能造出来,之后 df['a'] 会返回两列。 第二,长度不齐。{'a':[1,2], 'b':[1]} 报 ValueError: All arrays must be of the same length。 这两个错都在造表那一刻发生,比算到一半才发现好得多。
造完立刻打一下 shape,是最便宜的检查。
六、动手 Demo:DataFrame 的三种造法
分别用字典、列表加列名、numpy 数组造三张小表,各打一次形状和内容。 三种造法的行列标签来源不同,注意对比。
import numpy as np
import pandas as pd
d1 = pd.DataFrame({"姓名": ["甲", "乙"], "分数": [90, 85]})
print("字典 %s" % (d1.shape,))
print(d1.to_string(index=False))
rows = [ ["甲", 90], ["乙", 85]]
d2 = pd.DataFrame(rows, columns=["姓名", "分数"])
print("列表 %s" % (d2.shape,))
print(d2.to_string(index=False))
arr = np.arange(6).reshape(2, 3)
d3 = pd.DataFrame(arr, columns=["A", "B", "C"])
print("数组 %s" % (d3.shape,))
print(d3.to_string(index=False))
实跑输出:
--------------------------------------------------------------------
DataFrame 的三种造法
--------------------------------------------------------------------
① 从字典造:最常用
--------------------------------------------------------------------
| 行 | 姓名 | 分数 |
|---|---:|---:|
| 0 | 甲 | 90 |
| 1 | 乙 | 85 |
字典的键变成列名,值变成列。两个列表长度必须一样,否则报错。
! 这是手工造小表最快的方式,写测试数据天天用。
② 从列表的列表造:要自己给列名
--------------------------------------------------------------------
| 行 | 姓名 | 分数 |
|---|---:|---:|
| 0 | 甲 | 90 |
| 1 | 乙 | 85 |
不给 columns 的话,列名会自动变成 0、1。
! 从数据库或 CSV 读出来的原始二维数组,多半走这条路。
③ 从 numpy 数组造:适合算出来的数
--------------------------------------------------------------------
| 行 | A | B | C |
|---|---:|---:|---:|
| 0 | 0 | 1 | 2 |
| 1 | 3 | 4 | 5 |
数组是 2 行 3 列,转成 DataFrame 之后行列标签自动补上。
! DataFrame 内部就是一列一列的 numpy 数组,所以数值运算快。
④ 三种造法的取舍
--------------------------------------------------------------------
| 来源 | 造法 | 什么时候用 |
|---|---|---|
| 手写小数据 | 字典 | 列名清楚,写得快 |
| 二维列表 | 列表 + columns | 数据来自现成的行 |
| 计算结果 | numpy 数组 | 已经在算矩阵了 |
还有一种:从另一张表切出来(df.loc[条件, 列]),第 3.9 篇讲。
! 不必记全,记住字典那一种就够应付八成场景。
三种造法都打了一次 shape——这是最便宜的验收动作,养成习惯。
np.arange(6).reshape(2, 3) 这个组合很常用:先造一串数,再折成想要的形状。
reshape 只改看法,元素总数必须对得上。
30 秒实验
- 把字典那组两个列表改成不等长(3 个对 2 个),看报什么错。
- 造一个 10 行 5 列全 0 的表,用 numpy 那条路,要写几行?
- 想一想:为什么 Column 的顺序在字典里能保持?这和字典本身的有序性有关吗?
常见误区
- 忘记给 columns:列名变成 0、1、2,后面按名字取列全报错。
- 字典里长度不齐:ValueError,这个错很好认。
- 以为 DataFrame 会复制数据:从数组造的时候是共享内存,改一边可能影响另一边。
- 用手工造表处理真实数据:几十行还行,上千行就该从文件读了。
自测三问(点开对答案)
1. 三种造法里,哪一种最常用于造测试数据? —— 字典。列名和值写在一起,一眼能看懂,改起来也方便。 2. 从 numpy 数组造表时,行标签是怎么来的? —— 自动补 0 到 n-1 的 RangeIndex。要自定义就传 index 参数。 3. 为什么说 DataFrame 内部是 numpy 数组? —— 因为每一列底层就是一个 ndarray,所以数值运算走的是 numpy 的快速路径。表造出来了,怎么快速摸清它?下一篇列五个属性和两个函数——拿到任何新数据,先打这几个。
- ☐ (本篇完,下一篇 3.5 DataFrame 的常用属性与函数)