深入浅出Pandas读书笔记
C9 Pandas数据重塑与透视
9.1 数据透视
9.1.1 整理透视 df.pivot()
要实现基础的透视操作, 可以使用df.pivot()返回按给定的索引, 列值重新组织整理后的DataFrame. df.pivot()有3个参数, 这些参数传入的值是原数据的列名, 作用分别为
- index 作为新DataFrame的索引, 取分组去重的值, 如果不传入, 取现有索引
- columns 作为新DataFrame的列, 取去重的值, 当列和索引的组合有多个值的时候会报错(需要使用聚合函数, pivot只负责重塑, 没有聚合功能), 需要使用df.pivot_table()进行操作
- values 作为新DataFrame()的值, 如果指定多个, 会形成多层索引, 如果不指定, 会默认人为所有剩余的列
9.1.2 整理透视操作
df = pd.DataFrame({"A": ['a1', 'a1', 'a2', 'a3', 'a3', 'a3'],
'B': ['b1', 'b2', 'b3', 'b1', 'b2', 'b3'],
'C': ['c1', 'c2', 'c3', 'c4', 'c5', 'c6'],
'D': ['d1', 'd2', 'd3', 'd4', 'd5', 'd6'],
})
df.pivot(index='A', columns='B', values='C') # A与B没有重复值, 将A列去重作为索引, B列去重作为列, 取C的内容作为具体的数据值
'''
B b1 b2 b3
A
a1 c1 c2 NaN
a2 NaN NaN c3
a3 c4 c5 c6
'''
9.1.3 聚合透视
df.pivot()只是对原数据的结构, 显示做了变换, 在实现业务中, 往往还需要在数据透视过程中对值进行计算, 这时候就要用到pd.pivot_table()
pd.pivot_table()有以下几个关键参数
- data
- index
- values 要聚合的值
- columns 要聚合的列
- aggfunc 聚合函数
- fill_value
- margins 是否增加汇总行列
9.1.4 聚合透视操作
df = pd.DataFrame({"A": ['a1', 'a1', 'a1', 'a2', 'a2', 'a2'],
'B': ['b2', 'b2', 'b1', 'b1', 'b1', 'b1'],
'C': ['c1', 'c1', 'c2', 'c2', 'c1', 'c1'],
'D': [1, 2, 3, 4, 5, 6],
})
# 如果对以上数据进行以A为索引, 以B为列的透视df.pivot(), 会报错, 因为索引和列组合后有重复数据, 涉及到聚合操作, 需要使用pd.pivot_table()
df.pivot_table(index='A', columns='B', values='D') # 默认求mean
9.1.5 聚合透视高级操作
df.pivot_table(index=['A', 'B'], columns='C', values='D', aggfunc=np.sum, fill_value=0, margins=True)
# 使用多个计算方法
df.pivot_table(index=['A', 'B'], columns='C', values='D', aggfunc=[np.mean, np.sum])
# 为每一列指定不同的计算方法
df = pd.DataFrame({
'A': ['a1', 'a1', 'a1', 'a2', 'a2', 'a2'],
'B': ['b2', 'b2', 'b1', 'b1', 'b1', 'b1'],
'C': ['c1', 'c1', 'c2', 'c2', 'c1', 'c1'],
'D': [1, 2, 3, 4, 5, 6],
'E': [9, 8, 7, 6, 5, 4]
})
df.pivot_table(index=['A', 'B'], columns='C', aggfunc={'D': np.mean, 'E': np.sum})
df.pivot_table(index=['A', 'B'], columns='C', aggfunc={'D': [np.mean], 'E': np.sum}) # 在aggfunc内的函数中加入[], 在现实中会把函数名显示出
9.2 数据堆叠
9.2.1 理解堆叠
堆叠stack的过程表示将数据列的所有数据表全部旋转到列上
解堆unstack的过程表示将在行上的索引旋转到列上
- 堆叠: 透视某个级别的列标签, 返回带有索引的DataFrame, 该索引带有一个新的行标签, 这个新标签在原有索引的左右边
- 解堆: 将航索引的某个级别透视到列轴, 从而生成具有新的最里面的列标签级别的重构的DataFrame
堆叠过程将数据集的列转行, 解堆过程为行转列
9.2.2 堆叠操作 df.stack()
df = pd.DataFrame({
'A': ['a1', 'a1', 'a2', 'a2'],
'B': ['b1', 'b2', 'b1', 'b2'],
'C': [1, 2, 3, 4],
'D': [5, 6, 7, 8],
'E': [5, 6, 7, 8]
})
df.set_index(['A', 'B'], inplace=True)
df.stack()
'''
A B
a1 b1 C 1
D 5
E 5
b2 C 2
D 6
E 6
a2 b1 C 3
D 7
E 7
b2 C 4
D 8
E 8
dtype: int64
'''
type(df.stack()) # pandas.core.series.Series
9.2.3 解堆操作 df.unstack()
df.stack().unstack()
'''
C D E
A B
a1 b1 1 5 5
b2 2 6 6
a2 b1 3 7 7
b2 4 8 8
'''
9.3 交叉表 crosstab
交叉表就是将两列或多列中不重复的元素组成一个新的DataFrame, 新数据的行和列交叉部分的值为其组合在原数据中的数量
9.3.1 基本语法
pd.crosstab(index, columns, values=None, rownames=None, colnames=None, aggfunc=None, margins=False,
margins_name: str='All', dropna: bool=True, normalize=False
)
参数说明如下
- index
- columns
- values
- aggfunc
- rownames 新行名
- colnames 新列名
- margins
- normalize 布尔值, {‘all’, ‘index’ ‘columns’} 或 {0, 1}, 默认值为False, 通过将所有值除以值的总和进行归一化
9.3.2 生成交叉表
df = pd.DataFrame({
'A': ['a1', 'a1', 'a2', 'a2', 'a1'],
'B': ['b2', 'b1', 'b2', 'b2', 'b1'],
'C': [1, 2, 3, 4, 5],
})
pd.crosstab(df.A, df.B)
'''
B b1 b2
A
a1 2 1
a2 0 2
'''
9.3.3 归一化
# 整体中的占比
pd.crosstab(df.A, df.B, normalize=True)
# 在列中的占比
pd.crosstab(df.A, df.B, normalize='columns')
9.3.4 指定聚合方法
9.3.5 汇总
9.4 数据转置 df.T
9.4.1 数据转置
9.4.2 转置操作
9.4.3 类型变化
9.4.4 轴交换 df.swapaxes()
9.5 数据融合 df.melt()
9.5.1 基本语法
pd.melt(frame, id_vars=None, value_vars=None, var_name='variable', value_name='value', col_level=None)
)
参数说明
- id_vars: 需要作为表示的列
- value_vars: 需要堆起来的列, 如果不指定, 则使用未设置为id_vars的所有列
- var_name
- value_name
- col_level
9.5.2 融合操作
df = pd.DataFrame({
'A': ['a1', 'a2', 'a3', 'a4', 'a5'],
'B': ['b1', 'b2', 'b3', 'b4', 'b5'],
'C': [1, 2, 3, 4, 5]
})
df.melt()
'''
variable value
0 A a1
1 A a2
2 A a3
3 A a4
4 A a5
5 B b1
6 B b2
7 B b3
8 B b4
9 B b5
10 C 1
11 C 2
12 C 3
13 C 4
14 C 5
'''
9.5.3 标识和值
9.5.4 指定名称
df.melt(id_vars=['A'], value_vars=['B'], var_name='Blabel', value_name='B_value')
9.6 虚拟变量 pd.get_dummies() -> one-hot编
虚拟变量(Dummy Variable)又称虚设变量, 名义变量或哑变量, 是一个用来反映质的属性的人工变量, 是量化了的自变量, 通常取值为0或1, 常被用于one-hot特征提取
9.6.1 语法结构
pd.get_dummies(data, prefix=None, prefix_sep='_', dummy_na=False, columns=None, sparse=False, drop_first=False, dtype=None)
9.6.2 生成虚拟变量
df = pd.DataFrame({
'a': list('abcd'),
'b': list('fehg'),
'a1': range(4),
'b1': range(4, 8)})
# 对a进行one-hot操作
pd.get_dummies(df.a)
'''
a b c d
0 1 0 0 0
1 0 1 0 0
2 0 0 1 0
3 0 0 0 1
'''
9.6.3 列前缀
9.6.4 从DataFrame生成
# 保持其他列的情况下, 对a进行one-hot
pd.get_dummies(df, columns=['a']) # 只对a进行one-hot处理
'''
b a1 b1 a_a a_b a_c a_d
0 f 0 4 1 0 0 0
1 e 1 5 0 1 0 0
2 h 2 6 0 0 1 0
3 g 3 7 0 0 0 1
'''
9.7 因子化
因子化是指讲一个存在大量重复值的一维数据解析成枚举值的过程, 这样可以方便我们进行分辨. factorize
既可以用作顶层函数pd.factorize()
, 也可以用作Series.factorize()
和Index.factorize()
方法
9.7.1 基本方法
对数据因子化后返回两个值, 一个是因子化后的编码列表, 另一个是原数据的去重值列表
data = ['b', 'b', 'a', 'c', 'b']
pd.factorize(data) # 返回编码, 和去重置
'''
(array([0, 0, 1, 2, 0], dtype=int64), array(['b', 'a', 'c'], dtype=object))
'''
#
cat = pd.Series(['a', 'a', 'c'])
codes, uniques = pd.factorize(cat)
codes, uniques
'''
(array([0, 0, 1], dtype=int64), Index(['a', 'c'], dtype='object'))
'''
9.7.2 排序
codes, uniques = pd.factorize(['b', 'b', 'a', 'c', 'b'], sort=True)
codes, uniques
'''
(array([1, 1, 0, 2, 1], dtype=int64), array(['a', 'b', 'c'], dtype=object))
'''
9.7.3 缺失值
# 缺失值不会出现在唯一值列表中, 在编码中将为-1
codes, uniques = pd.factorize(['b', None, 'a', 'c', 'b'], sort=True)
codes, uniques
'''
(array([ 1, -1, 0, 2, 1], dtype=int64),
array(['a', 'b', 'c'], dtype=object))
'''
9.7.4 枚举类型
Pandas的枚举类型数据Categorical
也可以使用此方法
小结
因子化方法pd.factorize()
做了两件事, 一是对数据进行数字编码, 二是对数据进行去重, 在大序列数据中, 因子化能帮助我们抽取数据特征, 将数据变成类别数据再进行分析.
9.8 爆炸列表
将类似列表每个元素转换为一行, 索引值是相同的
9.8.1 基本功能
s = pd.Series([[1, 2, 3], 'foo', [], [3, 4]])
s.explode()
'''
0 1
0 2
0 3
1 foo
2 NaN
3 3
3 4
dtype: object
'''
# 每行列表中的元素都独占了一行, 而索引保持不变, 空列表变成了NaN
9.8.2 DataFrame的爆炸
df = pd.DataFrame({'A': [[1, 2, 3], 'foo', [], [3, 4]], 'B': range(4)})
df.explode('A')
'''
A B
0 1 0
0 2 0
0 3 0
1 foo 1
2 NaN 2
3 3 3
3 4 3
'''
9.8.3 非列表格式
df = pd.DataFrame([{'var1': 'a, b, c', 'var2': 1}, {'var1': 'd, e, f', 'var2': 2}])
'''
var1 var2
0 a, b, c 1
1 d, e, f 2
'''
df.assign(var1=df.var1.str.split(',')).explode('var1')
'''
var1 var2
0 a 1
0 b 1
0 c 1
1 d 2
1 e 2
1 f 2
'''