pandas学习笔记(五)

pandas学习笔记(五) —变形

长宽表的变形

什么是长表?什么是宽表?这个概念是对于某一个特征而言的。例如:一个表中把性别存储在某一个列中,那么它就是关于性别的长表;如果把性别作为列名,列中的元素是某一其他的相关特征数值,那么这个表是关于性别的宽表。下面的两张表就分别是关于性别的长表和宽表:

pd.DataFrame({'Gender':['F','F','M','M'],
              'Height':[163, 160, 175, 180]})

Out: 
  Gender  Height
0      F     163
1      F     160
2      M     175
3      M     180

pd.DataFrame({'Height: F':[163, 160],
              'Height: M':[175, 180]})

Out: 
   Height: F  Height: M
0        163        175
1        160        180

分析:这两张表从信息上是完全等价的,它们包含相同的身高统计数值,只是这些数值的呈现方式不同,而其呈现方式主要又与性别一列选择的布局模式有关,即到底是以long 的状态存储还是以wide的状态存储。

1.pivot

pivot是一种典型的长表变宽表的函数,首先来看一个例子:下表存储了张三和李四的语文和数学分数,现在想要把语文和数学分数作为列来展示。

df = pd.DataFrame({'Class':[1,1,2,2],
                   'Name':['San Zhang','San Zhang','Si Li','Si Li'],
                   'Subject':['Chinese','Math','Chinese','Math'],
                   'Grade':[80,75,90,85]})
df

out:
	Class	Name	Subject	Grade
0	1	San Zhang	Chinese	80
1	1	San Zhang	Math	75
2	2	Si Li	Chinese	90
3	2	Si Li	Math	85

对于一个基本的长变宽的操作而言,最重要的有三个要素,分别是变形后的行索引、需要转到列索引的列,以及这些列和行索引对应的数值,它们分别对应了pivot方法中的index, columns, values参数。新生成表的列索引是columns对应列的unique值,而新表的行索引是index对应列的unique值,而values对应了想要展示的数值列。

df.pivot(index='Name', columns='Subject', values='Grade')

out:
Subject	Chinese	Math
Name		
San Zhang	80	75
Si Li	90	85

变形过程:../_images/ch5_mulpivot.png

pandas1.1.0开始,pivot相关的三个参数允许被设置为列表,这也意味着会返回多级索引。这里构造一个相应的例子来说明如何使用:下表中六列分别为班级、姓名、测试类型(期中考试和期末考试)、科目、成绩、排名。

df = pd.DataFrame({'Class':[1, 1, 2, 2, 1, 1, 2, 2],
                   'Name':['San Zhang', 'San Zhang', 'Si Li', 'Si Li',
                              'San Zhang', 'San Zhang', 'Si Li', 'Si Li'],
                   'Examination': ['Mid', 'Final', 'Mid', 'Final',
                                    'Mid', 'Final', 'Mid', 'Final'],
                   'Subject':['Chinese', 'Chinese', 'Chinese', 'Chinese',
                                 'Math', 'Math', 'Math', 'Math'],
                   'Grade':[80, 75, 85, 65, 90, 85, 92, 88],
                   'rank':[10, 15, 21, 15, 20, 7, 6, 2]})
df

out:
   Class       Name Examination  Subject  Grade  rank
0      1  San Zhang         Mid  Chinese     80    10
1      1  San Zhang       Final  Chinese     75    15
2      2      Si Li         Mid  Chinese     85    21
3      2      Si Li       Final  Chinese     65    15
4      1  San Zhang         Mid     Math     90    20
5      1  San Zhang       Final     Math     85     7
6      2      Si Li         Mid     Math     92     6
7      2      Si Li       Final     Math     88     2

# 现在想要把测试类型和科目联合组成的四个类别(期中语文、期末语文、期中数学、期末数学)转到列索引,并且同时统计成绩和排名:
pivot_multi = df.pivot(index = ['Class', 'Name'],
                       columns = ['Subject','Examination'],
                       values = ['Grade','rank'])
pivot_multi

out:

Grade	rank
Subject	Chinese	Math	Chinese	Math
Examination	Mid	Final	Mid	Final	Mid	Final	Mid	Final
Class	Name								
1	San Zhang	80	75	90	85	10	15	20	7
2	Si Li	85	65	92	88	21	15	6	2
2.pivot_table

pivot的使用依赖于唯一性条件,那如果不满足唯一性条件,那么必须通过聚合操作使得相同行列组合对应的多个值变为一个值。

例如,张三和李四都参加了两次语文考试和数学考试,按照学院规定,最后的成绩是两次考试分数的平均值,此时就无法通过pivot函数来完成。

#  参数是聚合字符串
df.pivot_table(index = 'Name',
               columns = 'Subject',
               values = 'Grade',
               aggfunc = 'mean')
#  参数是自定义操作  
df.pivot_table(index = 'Name',
               columns = 'Subject',
               values = 'Grade',
               aggfunc = lambda x:x.mean())
# 结果都一样:
'''
Subject    Chinese  Math
Name                    
San Zhang       85    95
Si Li           75    90

'''

边际汇总功能:pivot_table具有边际汇总的功能,通过设置margins=True来实现,其中边际的聚合方式与 aggfunc中给出的聚合方法一致。

例如:分别统计语文均分和数学均分、张三均分和李四均分,以及总体所有分数的均分

df.pivot_table(index = 'Name',
               columns = 'Subject',
               values = 'Grade',
               aggfunc='mean',
               margins=True)
'''
Subject    Chinese  Math    All
Name                           
San Zhang       85  95.0  90.00
Si Li           75  90.0  82.50
All             80  92.5  86.25
'''                
3.melt

长宽表只是数据呈现方式的差异,但其包含的信息量是等价的,前面提到了利用pivot把长表转为宽表,那么就可以通过相应的逆操作把宽表转为长表,melt函数就起到了这样的作用。在下面的例子中,Subject以列索引的形式存储,现在想要将其压缩到一个列中。

df_melted = df.melt(id_vars = ['Class', 'Name'],
                    value_vars = ['Chinese', 'Math'],
                    var_name = 'Subject',
                    value_name = 'Grade')
df_melted

out:
	Class	Name	Subject	Grade
0	1	San Zhang	Chinese	80
1	2	Si Li	Chinese	90
2	1	San Zhang	Math	80
3	2	Si Li	Math	75

压缩过程:

../_images/ch5_melt.png

melt 和 pivot :通过 pivot操作把 df_melted转回df

 df_unmelted = df_melted.pivot(index = ['Class', 'Name'],
                               columns='Subject',
                               values='Grade')
 '''
Subject          Chinese  Math
Class Name                    
1     San Zhang       80    80
2     Si Li           90    75
'''    
# 下面需要恢复索引,并且重命名列索引名称
df_unmelted = df_unmelted.reset_index().rename_axis(
                             columns={'Subject':''})     
# True                                               

索引的变形

1.stack与unstack

在第二章中提到了利用swaplevel或者reorder_levels进行索引内部的层交换,下面就要讨论行行列索引之间的交换,由于这种交换带来了DataFrame维度上的变化,因此属于变形操作。在第一节中提到的4种变形函数与其不同之处在于,它们都属于某一列或几列元 素 元素和列索引之间的转换,而不是索引之间的转换。

unstack函数的作用是把行索引转为列索引,例如下面这个简单的例子:

# unstack 函数的作用是把行索引转为列索引
df = pd.DataFrame(np.ones((4,2)),
                  index = pd.Index([('A', 'cat', 'big'),
                                    ('A', 'dog', 'small'),
                                    ('B', 'cat', 'big'),
                                    ('B', 'dog', 'small')]),
                  columns=['col_1', 'col_2'])
df

out:
		col_1	col_2
A	cat	big	1.0	1.0
dog	small	1.0	1.0
B	cat	big	1.0	1.0
dog	small	1.0	1.0
df.unstack()

out:
col_1	col_2
big	small	big	small
A	cat	1.0	NaN	1.0	NaN
dog	NaN	1.0	NaN	1.0
B	cat	1.0	NaN	1.0	NaN
dog	NaN	1.0	NaN	1.0

df.unstack([0,2])

out:

col_1	col_2
A	B	A	B
big	small	big	small	big	small	big	small
cat	1.0	NaN	1.0	NaN	1.0	NaN	1.0	NaN
dog	NaN	1.0	NaN	1.0	NaN	1.0	NaN	1.0

stack与unstack相反,stack的作用就是把列索引的层压入行索引,其用法完全类似。

df = pd.DataFrame(np.ones((4,2)),
                  index = pd.Index([('A', 'cat', 'big'),
                                    ('A', 'dog', 'small'),
                                    ('B', 'cat', 'big'),
                                    ('B', 'dog', 'small')]),
                  columns=['index_1', 'index_2']).T
df

out:
A	B
cat	dog	cat	dog
big	small	big	small
index_1	1.0	1.0	1.0	1.0
index_2	1.0	1.0	1.0	1.0

df.stack()

out:

A	B
cat	dog	cat	dog
index_1	big	1.0	NaN	1.0	NaN
small	NaN	1.0	NaN	1.0
index_2	big	1.0	NaN	1.0	NaN
small	NaN	1.0	NaN	1.0

2.聚合与变形的关系

变形:除了带有聚合效果的 pivot_table 以外,所有的函数在变形前后并不会带来 values 个数的改变,只是这些值在呈现的形式上发生了变化

聚合:分组聚合操作,由于生成了新的行列索引,因此必然也属于某种特殊的变形操作,但由于聚合之后把原来的多个值变为了一个值,因此values的个数产生了变化

其他变形函数

1.crosstab

crosstab并不是一个值得推荐使用的函数,因为它能实现的所有功能pivot_table都能完成,并且速度更快。在默认状态下,crosstab可以统计元素组合出现的频数,即count操作。例如统计learn_pandas数据集中学校和转系情况对应的频数:

df = pd.read_csv('data/learn_pandas.csv')
pd.crosstab(index = df.School, columns = df.Transfer)
'''
Transfer                        N  Y
School                              
Fudan University               38  1
Peking University              28  2
Shanghai Jiao Tong University  53  0
Tsinghua University            62  4
'''
# 和上面的写法等价
pd.crosstab(index = df.School, columns = df.Transfer,
            values = [0]*df.shape[0], aggfunc = 'count')
'''
Transfer                          N    Y
School                                  
Fudan University               38.0  1.0
Peking University              28.0  2.0
Shanghai Jiao Tong University  53.0  NaN
Tsinghua University            62.0  4.0
'''            

pivot_table进行等价操作

df.pivot_table(index = 'School',
               columns = 'Transfer',
               values = 'Name',
               aggfunc = 'count')
'''
Transfer                          N    Y
School                                  
Fudan University               38.0  1.0
Peking University              28.0  2.0
Shanghai Jiao Tong University  53.0  NaN
Tsinghua University            62.0  4.0
'''               

crosstabpivot_table区别:crosstab的对应位置传入的是具体的序列,而 pivot_table 传入的是被调用表对应的名字,若传入序列对应的值则会报错。

2.explode

explode参数能够对某一列的元素进行纵向的展开,被展开的单元格必须存储list, tuple, Series, np.ndarray中的一种类型。

df_ex = pd.DataFrame({'A': [[1, 2],
                         'my_str',
                         {1, 2},
                         pd.Series([3, 4])],
                        'B': 1})
df_ex.explode('A')                         
'''
        A  B
0       1  1
0       2  1
1  my_str  1
2  {1, 2}  1
3       3  1
3       4  1

'''                         
3.get_dummies

get_dummies是用于特征构建的重要函数之一,其作用是把类别特征转为指示变量。

例如,对年级一列转为指示变量,属于某一个年级的对应列标记为1,否则为0:

pd.get_dummies(df.Grade).head()
'''
   Freshman  Junior  Senior  Sophomore
0         1       0       0          0
1         1       0       0          0
2         0       0       1          0
3         0       0       0          1
4         0       0       0          1

'''

posted @ 2020-12-27 22:50  AiGgBoY  阅读(75)  评论(0编辑  收藏  举报