一.pandas层次化索引
1.1 创建多层索引
1.1.1 隐式构造
最常见的方法是给DataFrame构造函数的index参数传递两个或更多的数组
columns = [['期中', '期中','期中','期末','期末','期末'],['语文','数学','英语','语文','数学','英语']]
data = np.random.randint(0,150,size=(10,6))
# index = [['一班', 1],['一班', 1],['一班', 1],['一班', 1],['一班', 1],['二班', 1],['二班', 1],['二班', 1],['二班', 1],['二班', 1]]
index = [['一班','一班','一班','一班','一班','二班','二班','二班','二班','二班'], np.arange(10)]
df = DataFrame(data=data,columns=columns, index=index)
df
输出:
期中 期末
语文 数学 英语 语文 数学 英语
一班 0 83 23 23 41 85 16
1 82 143 63 69 51 12
2 142 21 4 19 52 124
3 63 99 89 106 30 8
4 142 126 51 111 42 83
二班 5 89 47 139 131 133 115
6 76 34 30 67 15 38
7 116 93 81 45 74 76
8 83 21 64 83 40 1
9 99 79 83 48 42 68
Series也可以创建多层索引
index = [['一班','一班','一班','一班','一班','二班','二班','二班','二班','二班'], np.arange(10)]
s = Series(data=np.random.randint(150,size=10),index=index)
s
输出:
一班 0 127
1 129
2 15
3 148
4 112
二班 5 91
6 59
7 80
8 130
9 140
dtype: int32
1.1.2 显式构造MultiIndex
- 使用数组
columns = pd.MultiIndex.from_arrays([['期中', '期中','期中','期末','期末','期末'],['语文','数学','英语','语文','数学','英语']])
data = np.random.randint(0,150,size=(10,6))
# index = [['一班', 1],['一班', 1],['一班', 1],['一班', 1],['一班', 1],['二班', 1],['二班', 1],['二班', 1],['二班', 1],['二班', 1]]
index = pd.MultiIndex.from_arrays([['一班','一班','一班','一班','一班','二班','二班','二班','二班','二班'], np.arange(10)])
df = DataFrame(data=data,columns=columns, index=index)
df
输出:
期中 期末
语文 数学 英语 语文 数学 英语
一班 0 119 8 81 109 117 30
1 5 35 149 47 114 31
2 128 60 148 55 96 101
3 65 92 94 147 145 72
4 48 108 74 50 48 86
二班 5 81 85 93 92 57 10
6 52 67 83 76 104 59
7 125 23 57 103 27 22
8 7 39 79 114 12 90
9 30 140 20 71 0 42
- 使用tuple
columns = [['期中', '期中','期中','期末','期末','期末'],['语文','数学','英语','语文','数学','英语']]
data = np.random.randint(0,150,size=(10,6))
# index = [['一班', 1],['一班', 1],['一班', 1],['一班', 1],['一班', 1],['二班', 1],['二班', 1],['二班', 1],['二班', 1],['二班', 1]]
# index = pd.MultiIndex.from_arrays([['一班','一班','一班','一班','一班','二班','二班','二班','二班','二班'], np.arange(10)])
index = pd.MultiIndex.from_tuples([('一班', 1),('一班', 2),('一班', 3),('一班', 4),('一班', 5),('二班', 6),('二班', 7),('二班', 8),('二班', 9),('二班', 10)])
df = DataFrame(data=data,columns=columns, index=index)
df
输出:
期中 期末
语文 数学 英语 语文 数学 英语
一班 1 147 10 49 41 120 45
2 35 134 120 125 90 31
3 133 93 80 49 56 68
4 65 137 147 1 134 33
5 2 5 111 16 51 108
二班 6 89 90 81 90 66 79
7 87 125 124 88 32 49
8 120 15 124 47 46 6
9 45 12 35 103 108 87
10 95 21 92 14 72 46
- 使用product(最简单,推荐使用)
columns = pd.MultiIndex.from_product([['期中','期末'],['语文','数学','英语']])
data = np.random.randint(0,150,size=(10,6))
# index = [['一班', 1],['一班', 1],['一班', 1],['一班', 1],['一班', 1],['二班', 1],['二班', 1],['二班', 1],['二班', 1],['二班', 1]]
# index = pd.MultiIndex.from_arrays([['一班','一班','一班','一班','一班','二班','二班','二班','二班','二班'], np.arange(10)])
index = pd.MultiIndex.from_product([['一班','二班'],np.arange(5)])
df = DataFrame(data=data,columns=columns, index=index)
df
输出:
期中 期末
语文 数学 英语 语文 数学 英语
一班 0 92 46 64 56 130 38
1 62 137 48 107 2 111
2 96 0 117 2 83 16
3 109 21 28 135 67 111
4 86 123 118 0 108 105
二班 0 52 118 38 140 119 82
1 101 75 75 69 43 87
2 61 25 65 68 106 63
3 63 83 106 81 34 113
4 25 97 53 118 35 137
1.2 多层列索引
除了行索引index,列索引columns也能用同样的方法创建多层索引
a = np.random.randint(0,150,(2,8))
Mindex = pd.MultiIndex.from_product([['语文','数学','英语','理综'],['期中','期末']])
ddd = DataFrame(data = a,
index = ['张三','李四'],
columns = Mindex)
ddd
输出:
语文 数学 英语 理综
期中 期末 期中 期末 期中 期末 期中 期末
张三 57 110 65 3 127 66 146 32
李四 73 24 57 19 35 133 57 127
1.3 多层索引对象的索引与切片操作
1.3.1 Series的操作
【重要】对于Series来说,直接中括号[]与使用.loc()完全一样,推荐使用.loc中括号索引和切片。
- 索引
s
一班 0 127
1 129
2 15
3 148
4 112
二班 5 91
6 59
7 80
8 130
9 140
dtype: int32
s['一班']
输出:
0 127
1 129
2 15
3 148
4 112
dtype: int32
s.loc['一班']
输出:
0 127
1 129
2 15
3 148
4 112
dtype: int32
- 切片
s[2:6]
输出:
一班 2 15
3 148
4 112
二班 5 91
1.3.2 DataFrame的操作
- 可以直接使用列名称来进行列索引
df
输出:
期中 期末
语文 数学 英语 语文 数学 英语
一班 张三 133 82 139 86 83 112
李四 129 85 23 131 4 18
王五 9 15 96 131 72 77
二班 张三 4 142 104 48 81 108
李四 75 60 108 126 85 122
王五 24 31 53 72 49
行多级索引的索引和切片操作
# DataFrame中操作行要调用loc[]
df.loc['一班'].loc[['李四']]
输出:
期中 期末
语文 数学 英语 语文 数学 英语
李四 129 85 23 131 4 18
df.loc['一班': '二班']
输出:
期中 期末
语文 数学 英语 语文 数学 英语
一班 张三 133 82 139 86 83 112
李四 129 85 23 131 4 18
王五 9 15 96 131 72 77
二班 张三 4 142 104 48 81 108
李四 75 60 108 126 85 122
王五 24 31 53 72 49 46
# 和Series一样,直接切内层索引,会没有结果.不会报错.
df.loc['张三': '李四']
输出:
期中 期末
语文 数学 英语 语文 数学 英语
# 隐式索引不涉及外层索引
df.iloc[[0]]
输出:
期中 期末
语文 数学 英语 语文 数学 英语
一班 张三 133 82 139 86 83 112
列多级索引的索引和切片操作
df['期中'][['语文', '数学']]
输出:
语文 数学
一班 张三 133 82
李四 129 85
王五 9 15
二班 张三 4 142
李四 75 60
王五 24 31
# 列索引,直接切片,没有报错,但是没有结果.
df['期中': '期末']
输出:
期中 期末
语文 数学 英语 语文 数学 英语
- 使用行索引需要用loc()函数
【极其重要】推荐使用loc()函数
注意在对行索引的时候,若一级行索引还有多个,对二级行索引会遇到问题!也就是说,无法直接对二级索引进行索引,必须让二级索引变成一级索引后才能对其进行索引!
df.loc['一班', '张三'].loc['期中', '语文'] = 88
输出:
期中 期末
语文 数学 英语 语文 数学 英语
一班 张三 88.0 82.0 88.0 86.0 83.0 112.0
李四 129.0 85.0 23.0 131.0 4.0 18.0
王五 9.0 15.0 96.0 131.0 72.0 77.0
二班 张三 4.0 142.0 104.0 48.0 81.0 108.0
李四 75.0 60.0 108.0 126.0 85.0 122.0
王五 24.0 31.0 53.0 72.0 49.0 46.0
1.4 索引的堆
stack()
unstack()
# stack就是把列索引,变成最内层的行索引.从水平变成垂直.
【小技巧】使用stack()的时候,level等于哪一个,哪一个就消失,出现在行里。
# unstack把最内层的行索引变成列索引.
【小技巧】使用unstack()的时候,level等于哪一个,哪一个就消失,出现在列里。
df.stack()
1.5 聚合操作
【注意】
需要指定axis
【小技巧】和unstack()相反,聚合的时候,axis等于哪一个,哪一个就保留。
# 聚合操作的时候,默认算的是列的聚合.
df.sum()
输出:
期中 语文 329.0
数学 415.0
英语 472.0
期末 语文 594.0
数学 374.0
英语 483.0
dtype: float64
# 聚合操作的时候.axis=0表示列,=1表示行
df.sum(axis=0)
输出:
期中 语文 329.0
数学 415.0
英语 472.0
期末 语文 594.0
数学 374.0
英语 483.0
dtype: float64
# 聚合操作的时候,level等于哪一层,就保留哪一层.
df.sum(level=1)
输出:
期中 期末
语文 数学 英语 语文 数学 英语
张三 92.0 224.0 192.0 134.0 164.0 220.0
李四 204.0 145.0 131.0 257.0 89.0 140.0
王五 33.0 46.0 149.0 203.0 121.0 123.0
所谓的聚合操作:平均数,方差,最大值,最小值……
df.mean(level=1, axis=1)
输出:
语文 数学 英语
一班 张三 87.0 82.5 100.0
李四 130.0 44.5 20.5
王五 70.0 43.5 86.5
二班 张三 26.0 111.5 106.0
李四 100.5 72.5 115.0
王五 48.0 40.0 49.5
df.max(level=1, axis=1)
输出:
语文 数学 英语
一班 张三 88.0 83.0 112.0
李四 131.0 85.0 23.0
王五 131.0 72.0 96.0
二班 张三 48.0 142.0 108.0
李四 126.0 85.0 122.0
王五 72.0 49.0 53.0
二.pandas拼接操作
# 传入行索引和列索引,返回一个DataFrame
def mk_df(inds, cols):
data = {col: [col + str(i) for i in inds] for col in cols}
return DataFrame(data=data, index=inds, columns=cols)
mk_df([1,2,3,4], list('ABCD'))
输出:
A B C D
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3
4 A4 B4 C4 D4
2.1 级联:pd.concat, pd.append
1) 简单级联
和np.concatenate一样,优先增加行数(默认axis=0)
df1 = mk_df([1,2,3,4], list('ABCD'))
df2 = mk_df([1,2,3,4], list('ABCD'))
输出:
A B C D
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3
4 A4 B4 C4 D4
A B C D
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3
4 A4 B4 C4 D4
pd.concat((df1,df2))
输出:
A B C D
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3
4 A4 B4 C4 D4
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3
4 A4 B4 C4 D4
#忽略原索引,重新进行索引
pd.concat((df1,df2), ignore_index=True)
输出:
A B C D
0 A1 B1 C1 D1
1 A2 B2 C2 D2
2 A3 B3 C3 D3
3 A4 B4 C4 D4
4 A1 B1 C1 D1
5 A2 B2 C2 D2
6 A3 B3 C3 D3
7 A4 B4 C4 D4
#可以通过设置axis来改变级联方向
pd.concat((df1,df2), axis=1, ignore_index=True)
输出:
0 1 2 3 4 5 6 7
1 A1 B1 C1 D1 A1 B1 C1 D1
2 A2 B2 C2 D2 A2 B2 C2 D2
3 A3 B3 C3 D3 A3 B3 C3 D3
4 A4 B4 C4 D4 A4 B4 C4 D4
#或者使用多层索引 keys
#concat([x,y],keys=['x','y'])
# 通过keys可以给拼接起来的索引做一个说明.
pd.concat((df1,df2), keys=['df1', 'df2'])
输出:
A B C D
df1 1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3
4 A4 B4 C4 D4
df2 1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3
4 A4 B4 C4 D4
2) 不匹配级联
不匹配指的是级联的维度的索引不一致。例如纵向级联时列索引不一致,横向级联时行索引不一致
df3 = mk_df([3,4,5,6], list('CDEF'))
df3
输出:
C D E F
3 C3 D3 E3 F3
4 C4 D4 E4 F4
5 C5 D5 E5 F5
6 C6 D6 E6 F6
pd.concat((df1,df3), sort=True)
输出:
A B C D E F
1 A1 B1 C1 D1 NaN NaN
2 A2 B2 C2 D2 NaN NaN
3 A3 B3 C3 D3 NaN NaN
4 A4 B4 C4 D4 NaN NaN
3 NaN NaN C3 D3 E3 F3
4 NaN NaN C4 D4 E4 F4
5 NaN NaN C5 D5 E5 F5
6 NaN NaN C6 D6 E6 F6
pd.concat((df1,df3), sort=True, axis=1)
输出:
A B C D C D E F
1 A1 B1 C1 D1 NaN NaN NaN NaN
2 A2 B2 C2 D2 NaN NaN NaN NaN
3 A3 B3 C3 D3 C3 D3 E3 F3
4 A4 B4 C4 D4 C4 D4 E4 F4
5 NaN NaN NaN NaN C5 D5 E5 F5
6 NaN NaN NaN NaN C6 D6 E6 F6
有3种连接方式:
- 外连接:补NaN(默认模式)
- 内连接:只连接匹配的项
# 内连接,只有匹配的索引才显示
pd.concat((df1,df3), sort=True, axis=1, join='inner')
输出:
A B C D C D E F
3 A3 B3 C3 D3 C3 D3 E3 F3
4 A4 B4 C4 D4 C4 D4 E4 F4
- 连接指定轴 join_axes
# 相当于左外连接
pd.concat((df1,df3), sort=True, axis=1, join_axes=[df1.index])
输出:
A B C D C D E F
1 A1 B1 C1 D1 NaN NaN NaN NaN
2 A2 B2 C2 D2 NaN NaN NaN NaN
3 A3 B3 C3 D3 C3 D3 E3 F3
4 A4 B4 C4 D4 C4 D4 E4 F4
# 右外连接
pd.concat((df1,df3), sort=True, axis=1, join_axes=[df3.index])
输出:
A B C D C D E F
3 A3 B3 C3 D3 C3 D3 E3 F3
4 A4 B4 C4 D4 C4 D4 E4 F4
5 NaN NaN NaN NaN C5 D5 E5 F5
6 NaN NaN NaN NaN C6 D6 E6 F6
3) 使用append()函数添加
由于在后面级联的使用非常普遍,因此有一个函数append专门用于在后面添加
df1.append(df3)
2.2 合并:pd.merge
merge与concat的区别在于,merge需要依据某一共同的行或列来进行合并
使用pd.merge()合并时,会自动根据两者相同column名称的那一列,作为key来进行合并。
注意每一列元素的顺序不要求一致
1) 一对一合并
df1 = DataFrame({'name':['张三','李四','Chales'],'id':[1,2,3],'age':[22,21,25]})
df2 = DataFrame({'sex':['男','男','女'],'id':[2,3,4],'group':['sale','search','service']})
display(df1,df2)
输出:
name id age
0 张三 1 22
1 李四 2 21
2 Chales 3 25
sex id group
0 男 2 sale
1 男 3 search
2 女 4 service
pd.merge(df1, df2)
输出:
name id age sex group
0 李四 2 21 男 sale
1 Chales 3 25 男 search
2) 多对一合并
df1 = DataFrame({'name':['张三','李四','Chales'],'id':[1,2,2],'age':[22,21,25]})
df2 = DataFrame({'sex':['男','男','女'],'id':[2,3,4],'group':['sale','search','service']})
display(df1,df2)
输出:
name id age
0 张三 1 22
1 李四 2 21
2 Chales 2 25
sex id group
0 男 2 sale
1 男 3 search
2 女 4 service
#merge,即可用pd.merge,也可以df1.merge
df1.merge(df2)
输出:
name id age sex group
0 李四 2 21 男 sale
1 Chales 2 25 男 sale
3) 多对多合并
df1 = DataFrame({'name':['张三','李四','张三'],'salary':[10000,12000,20000],'age':[22,21,25]})
df2 = DataFrame({'sex':['男','男','女'],'name':['张三','张三','凡凡'],'group':['sale','search','service']})
display(df1,df2)
输出:
name salary age
0 张三 10000 22
1 李四 12000 21
2 张三 20000 25
sex name group
0 男 张三 sale
1 男 张三 search
2 女 凡凡 service
pd.merge(df1,df2)
输出:
name salary age sex group
0 张三 10000 22 男 sale
1 张三 10000 22 男 search
2 张三 20000 25 男 sale
3 张三 20000 25 男 search
4) key的规范化
- 使用on=显式指定哪一列为key,当有多个key相同时使用
df1 = DataFrame({'name':['张三','李四','张三'],'salary':[10000,12000,20000],'age':[22,21,25]})
df2 = DataFrame({'age':[21,18,29],'name':['张三','张三','凡凡'],'group':['sale','search','service']})
display(df1,df2)
输出:
name salary age
0 张三 10000 22
1 李四 12000 21
2 张三 20000 25
age name group
0 21 张三 sale
1 18 张三 search
2 29 凡凡 service
pd.merge(df1,df2, on='name', suffixes=['_df1', '_df2'])
输出:
name salary age_df1 age_df2 group
0 张三 10000 22 21 sale
1 张三 10000 22 18 search
2 张三 20000 25 21 sale
3 张三 20000 25 18 search
- 使用left_on和right_on指定左右两边的列作为key,当左右两边的key都不相等时使用
df1 = DataFrame({'name':['张三','李四','张三'],'salary':[10000,12000,20000],'age':[22,21,25]})
df2 = DataFrame({'年龄':[21,18,29],'名字':['张三','张三','凡凡'],'group':['sale','search','service']})
display(df1,df2)
输出:
name salary age
0 张三 10000 22
1 李四 12000 21
2 张三 20000 25
年龄 名字 group
0 21 张三 sale
1 18 张三 search
2 29 凡凡 service
pd.merge(df1,df2, left_on='name', right_on='名字')
输出:
name salary age 年龄 名字 group
0 张三 10000 22 21 张三 sale
1 张三 10000 22 18 张三 search
2 张三 20000 25 21 张三 sale
3 张三 20000 25 18 张三 search
- 当左边的列和右边的index相同的时候,使用right_index=True
df1 = DataFrame({'name':['张三','李四','张三'],'salary':[10000,12000,20000],'age':[22,21,25]})
df2 = DataFrame({'年龄':[21,18,29],'名字':['张三','张三','凡凡'],'group':['sale','search','service']},
index = [22,21,25])
display(df1,df2)
输出:
name salary age
0 张三 10000 22
1 李四 12000 21
2 张三 20000 25
年龄 名字 group
22 21 张三 sale
21 18 张三 search
25 29 凡凡 service
pd.merge(df1, df2, left_on='age', right_index=True)
输出:
name salary age 年龄 名字 group
0 张三 10000 22 21 张三 sale
1 李四 12000 21 18 张三 search
2 张三 20000 25 29 凡凡 service
5) 内合并与外合并
- 内合并:只保留两者都有的key(默认模式)
df1 = DataFrame({'name':['张三','李四','张三'],'salary':[10000,12000,20000],'age':[22,21,25]})
df2 = DataFrame({'age':[21,18,29],'名字':['张三','张三','凡凡'],'group':['sale','search','service']})
display(df1,df2)
输出:
name salary age
0 张三 10000 22
1 李四 12000 21
2 张三 20000 25
age 名字 group
0 21 张三 sale
1 18 张三 search
2 29 凡凡 service
pd.merge(df1,df2,left_on='name', right_on='名字')
# 默认是内连接 ,即显示匹配的内容,不匹配的内容不显示.
输出:
name salary age_x age_y 名字 group
0 张三 10000 22 21 张三 sale
1 张三 10000 22 18 张三 search
2 张三 20000 25 21 张三 sale
3 张三 20000 25 18 张三 search
- 外合并 how='outer':补NaN
# 外合并会显示两张表的全部信息.
pd.merge(df1,df2,left_on='name', right_on='名字', how='outer')
输出:
name salary age_x age_y 名字 group
0 张三 10000.0 22.0 21.0 张三 sale
1 张三 10000.0 22.0 18.0 张三 search
2 张三 20000.0 25.0 21.0 张三 sale
3 张三 20000.0 25.0 18.0 张三 search
4 李四 12000.0 21.0 NaN NaN NaN
5 NaN NaN NaN 29.0 凡凡 service
- 左合并、右合并:how='left',how='right'
# left左合并只显示左侧表的所有信息.
pd.merge(df1,df2,left_on='name', right_on='名字', how='left')
输出:
name salary age_x age_y 名字 group
0 张三 10000 22 21.0 张三 sale
1 张三 10000 22 18.0 张三 search
2 李四 12000 21 NaN NaN NaN
3 张三 20000 25 21.0 张三 sale
4 张三 20000 25 18.0 张三 search
# right 右合并,只显示右侧表的所有信息.
pd.merge(df1,df2,left_on='name', right_on='名字', how='right')
输出:
name salary age_x age_y 名字 group
0 张三 10000.0 22.0 21 张三 sale
1 张三 20000.0 25.0 21 张三 sale
2 张三 10000.0 22.0 18 张三 search
3 张三 20000.0 25.0 18 张三 search
4 NaN NaN NaN 29 凡凡 service
6) 列冲突的解决
当列冲突时,即有多个列名称相同时,需要使用on=来指定哪一个列作为key,配合suffixes指定冲突列名
#期中
df1 = DataFrame({'name':['张三','李四','张三'],'degree':[120,118,149],'age':[22,21,25]})
#期末考试
df2 = DataFrame({'degree':[99,97,129],'name':['张三','张三','凡凡'],'group':['sale','search','service']})
display(df1,df2)
输出:
name degree age
0 张三 120 22
1 李四 118 21
2 张三 149 25
degree name group
0 99 张三 sale
1 97 张三 search
2 129 凡凡 service
pd.merge(df1,df2,on='name', suffixes=['_df1','_df2'])
输出:
name degree_df1 age degree_df2 group
0 张三 120 22 99 sale
1 张三 120 22 97 search
2 张三 149 25 99 sale
3 张三 149 25 97 search
可以使用suffixes=自己指定后缀