Pandas基础(四):pivot、melt、stack、哑变量
DataWhale第十二期组队学习:Python Pandas
import numpy as np
import pandas as pd
df = pd.read_csv('data/table.csv')
df.head()
School | Class | ID | Gender | Address | Height | Weight | Math | Physics | |
---|---|---|---|---|---|---|---|---|---|
0 | S_1 | C_1 | 1101 | M | street_1 | 173 | 63 | 34.0 | A+ |
1 | S_1 | C_1 | 1102 | F | street_2 | 192 | 73 | 32.5 | B+ |
2 | S_1 | C_1 | 1103 | M | street_2 | 186 | 82 | 87.2 | B+ |
3 | S_1 | C_1 | 1104 | F | street_2 | 167 | 81 | 80.4 | B- |
4 | S_1 | C_1 | 1105 | F | street_4 | 159 | 64 | 84.8 | B+ |
透视表是一种可以对数据动态排布并且分类汇总的表格格式
通过index
,coloumn
,value
信息,pivot
函数可以对数据表进行重新重塑
其中行与列两个参数是必须要有的
例如:我想以学号作为索引,查看男女生的身高情况
df.pivot(index='ID',columns='Gender',values='Height').head()
这样就以透视表的方式重塑了表格,但不能以学校作为索引,因为pivot
不支持重复的索引
Gender | F | M |
---|---|---|
ID | ||
1101 | NaN | 173.0 |
1102 | 192.0 | NaN |
1103 | NaN | 186.0 |
1104 | 167.0 | NaN |
1105 | 159.0 | NaN |
另外pivot
不是很灵活,下面这个更加常用
pivot_table有四个最重要的参数index
、values
、columns
、aggfunc
其中columns
是一个可选的参数,同index
一样,它代表列的层次
以下例子会让四个值慢慢添加,注意比较区别
例如:以学号作为索引进行查看(index)
pd.pivot_table(df,index='ID').head()
会自动以数值型的列,形成列名和值
Height | Math | Weight | |
---|---|---|---|
ID | |||
1101 | 173 | 34.0 | 63 |
1102 | 192 | 32.5 | 73 |
1103 | 186 | 87.2 | 82 |
1104 | 167 | 80.4 | 81 |
1105 | 159 | 84.8 | 64 |
例如:以学校为一级索引,学号为二级索引进行查看(index)
pd.pivot_table(df,index=['School','ID']).head()
因为并不是分组,因而按照顺序显示,所以学校二的部分没有显示
Height | Math | Weight | ||
---|---|---|---|---|
School | ID | |||
S_1 | 1101 | 173 | 34.0 | 63 |
1102 | 192 | 32.5 | 73 | |
1103 | 186 | 87.2 | 82 | |
1104 | 167 | 80.4 | 81 | |
1105 | 159 | 84.8 | 64 |
例如:以学号为索引,我只想查看身高与体重的情况(index、values)
pd.pivot_table(df,index=['ID'],values=['Height','Weight']).head()
这样就指定了我们重塑的表的列的值
Height | Weight | |
---|---|---|
ID | ||
1101 | 173 | 63 |
1102 | 192 | 73 |
1103 | 186 | 82 |
1104 | 167 | 81 |
1105 | 159 | 6 |
例如:以学校为索引,我想看身高与体重的平均值(index、values、aggfunc)
pd.pivot_table(df,index='School',values='Height',aggfunc=['mean']).head()
mean | |
---|---|
Height | |
School | |
S_1 | 175.733333 |
S_2 | 172.950000 |
例如:以学校为索引,我想看男女生身高与体重的均值与和值(index、values、aggfunc、columns)
pd.pivot_table(df,index='School',columns='Gender',values='Height',aggfunc=['mean','sum']).head()
mean | sum | |||
---|---|---|---|---|
Gender | F | M | F | M |
School | ||||
S_1 | 173.125000 | 178.714286 | 1385 | 1251 |
S_2 | 173.727273 | 172.000000 | 1911 | 1548 |
例如:几个基本参数都介绍完了,可以再看一个更综合的例子
pd.pivot_table(df,index='School',columns='Gender',values='Height',
aggfunc=['mean','sum'],fill_value=0,margins=True).head()
新的两个参数,一个用于填充NaN(尽管这里没有),一个用于汇总(名字可以改)
mean | sum | |||||
---|---|---|---|---|---|---|
Gender | F | M | All | F | M | All |
School | ||||||
S_1 | 173.125000 | 178.714286 | 175.733333 | 1385 | 1251 | 2636 |
S_2 | 173.727273 | 172.000000 | 172.950000 | 1911 | 1548 | 3459 |
All | 173.473684 | 174.937500 | 174.142857 | 3296 | 2799 | 6095 |
交叉表示用于统计分组频率的特殊透视表
例如:我想统计相同住在同一个地方的男生或女生数量(街道和性别分组的频数)
pd.crosstab(index=df['Address'],columns=df['Gender']).head()
同上有一个margin
参数表示汇总
另外有normalize
参数可选all
,index
,columns
三种归一化方式
Gender | F | M |
---|---|---|
Address | ||
street_1 | 1 | 2 |
street_2 | 4 | 2 |
street_4 | 3 | 5 |
street_5 | 3 | 3 |
street_6 | 5 | 1 |
例如:在上面的基础上,我想知道每一个(街道、性别)组合里面身高最高的人
pd.crosstab(index=df['Address'],columns=df['Gender'],
values=df['Height'],aggfunc='max').head()
values
参数和aggfunc
两个参数需要同时指定
Gender | F | M |
---|---|---|
Address | ||
street_1 | 175 | 175 |
street_2 | 192 | 195 |
street_4 | 176 | 187 |
street_5 | 187 | 193 |
street_6 | 193 | 160 |
这个函数具体的功能其实是多样化的,之类举一个最简单的例子来表示
我们使用一个新的数据表,为了方便,将前后的表格放在一起对比,先看代码
# 这里我使用一个新的表来延时
df_m = df[['Height','Weight','Math','Physics']].head(2)
# 然后使用melt函数来操作
df_m.melt(id_vars=['Height','Weight'],value_vars=['Math','Physics'],
var_name='subject',value_name='score')
下面两个表分别表示变换前和变换后的表
注意下面后两列、后两行的变化,从“竖”变成了“横”
Height | Weight | Math | Physics | |
---|---|---|---|---|
0 | 173 | 63 | 34.0 | A+ |
1 | 192 | 73 | 32.5 | B+ |
Height | Weight | subject | score | |
---|---|---|---|---|
0 | 173 | 63 | Math | 34 |
1 | 192 | 73 | Math | 32.5 |
2 | 173 | 63 | Physics | A+ |
3 | 192 | 73 | Physics | B+ |
id_vars
表示需要保留的列,value_vars
表示需要转换的一组列,另外两个都是设置名字的
这个同样需要一个例子来理解一下,这里也还是临时弄一个新表来延时,先看代码
# 重新构造一个表
df_s = pd.pivot_table(df,index=['Class','ID'],values=['Height','Weight'])
df_s.head()
# stack后
df_s.stack().to_frame().head()
# unstack后
df_s.stack().to_frame().unstack().head()
这个是我新构造出来的一个用于演示的表的前几行
Height | Weight | ||
---|---|---|---|
Class | ID | ||
C_1 | 1101 | 173 | 63 |
1102 | 192 | 73 | |
1103 | 186 | 82 | |
1104 | 167 | 81 | |
1105 | 159 | 64 |
可以看见上表中的列变为了第三级的索引
Class | ID | ||
---|---|---|---|
C_1 | 1101 | Height | 173 |
Weight | 63 | ||
1102 | Height | 192 | |
Weight | 73 | ||
1103 | Height | 186 |
unstack
后又变为了原来的表
Height | Weight | ||
---|---|---|---|
Class | ID | ||
C_1 | 1101 | 173 | 63 |
1102 | 192 | 73 | |
1103 | 186 | 82 | |
1104 | 167 | 81 | |
1105 | 159 | 64 |
哑变量对应有一个get_dummies
函数,其主要功能是进行one-hot
编码
我们所使用的数据表如下所示
df_d = df[['Class','Gender','Weight']]
df_d.head()
Class | Gender | Weight | |
---|---|---|---|
0 | C_1 | M | 63 |
1 | C_1 | F | 73 |
2 | C_1 | M | 82 |
3 | C_1 | F | 81 |
4 | C_1 | F | 64 |
现在希望将上面的表格前两列转化为哑变量,并加入第三列Weight
数值
pd.get_dummies(df_d[['Class','Gender']]).join(df_d['Weight']).head()
#可选prefix参数添加前缀,prefix_sep添加分隔符
Class_C_1 | Class_C_2 | Class_C_3 | Class_C_4 | Gender_F | Gender_M | Weight | |
---|---|---|---|---|---|---|---|
0 | 1 | 0 | 0 | 0 | 0 | 1 | 63 |
1 | 1 | 0 | 0 | 0 | 1 | 0 | 73 |
2 | 1 | 0 | 0 | 0 | 0 | 1 | 82 |
3 | 1 | 0 | 0 | 0 | 1 | 0 | 81 |
4 | 1 | 0 | 0 | 0 | 1 | 0 | 64 |
将Series或类似的数据结构映射为一组数字
相同的值映射为相同的数字,适用于同样的几个值反复出现的列
它返回一个元祖,第一个元素是映射的数字,第二个是Index类型,就是原来的值去重
codes, uniques = pd.factorize(['b', None, 'a', 'c', 'b'],sort=True)
display(codes)
display(uniques)
array([ 1, -1, 0, 2, 1], dtype=int64)
array(['a', 'b', 'c'], dtype=object)