分层索引
在轴向上拥有一个或多个索引层级,创建一个分层索引数据:
data = pd.Series(np.random.randn(9),index = [['a','a','a','b','b','c','c','d','d'],
[1,2,3,1,3,1,2,2,3]])
data
a 1 -0.843020
2 1.161747
3 -1.530431
b 1 -0.219945
3 -0.298198
c 1 -0.431312
2 1.041442
d 2 -0.186717
3 0.090959
dtype: float64
data.index
MultiIndex(levels=[['a', 'b', 'c', 'd'], [1, 2, 3]],
codes=[[0, 0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 2, 0, 2, 0, 1, 1, 2]])
这个结果有点意外吧,注意这里是标签索引loc[],返回的是全部第一层级,下第二层级中索引为‘2’的部分
data.loc[:,2]
a 1.161747
c 1.041442
d -0.186717
dtype: float64
使用unstack方法将数据在DataFrame中重新排列(一般是把最里面的层级作为列标签拿出来)
data.unstack()
1 2 3
a -0.843020 1.161747 -1.530431
b -0.219945 NaN -0.298198
c -0.431312 1.041442 NaN
d NaN -0.186717 0.090959
unstack的反操作是 stack:
a 1 -0.843020
2 1.161747
3 -1.530431
b 1 -0.219945
3 -0.298198
c 1 -0.431312
2 1.041442
d 2 -0.186717
3 0.090959
dtype: float64
Series中只有index轴有分层索引,在 DataFrame中,每个轴都可以有分层索引。索引方法同Series,分层的层级可以有名称,这些名称会在输出中显示。
frame = pd.DataFrame(np.arange(12).reshape((4,3)),
index = [['a','a','b','b'],[1,2,1,2]],
columns = [['Ohio','Ohio','Colorado'],['Green','Red','Green']])
frame
Ohio Colorado
Green Red Green
a 1 0 1 2
2 3 4 5
b 1 6 7 8
2 9 10 11
frame.index.names = ['key1','key2']
frame.columns.names = ['states','color']
frame
states Ohio Colorado
color Green Red Green
key1 key2
a 1 0 1 2
2 3 4 5
b 1 6 7 8
2 9 10 11
一个MultiIndex对象可以使用其自身的构造函数创建并复用,前面带有层级名称的DataFrame的 列 还可以这样创建:
columns=pd.MultiIndex.from_arrays([['Ohio','Ohio','Colorado'],['Green','Red','Green']],names = ['states','color'])
#两种方式等价,上面的复用性强,下面的简单明了
columns = [['Ohio','Ohio','Colorado'],['Green','Red','Green']]
重排序和层级排序
swaplevel接受两个层级序号或层级名称,返回一个进行了层及变更的新对象(但是数据不变)
frame.swaplevel('key1','key2')#表示key1与key2两个索引层级交换位置,数据不变
#里面‘key1’和‘key2参数互换位置输出是一样的,或者传递:
frame.swaplevel(0,1)
#sort_index只能在单一层级上对数据进行排序
frame.sort_index(level = 1)#对key2列排序
frame.sort_index(level = 0)#对key1排序
frame.swaplevel(0,1).sort_index(level = 0)#key1与key2互换后,传递level= 0便是与key2排序了
通过level选项你可以指定你想要在某个特定的轴上进行聚合。
frame.sum(level = 'key2')#默认对行axis=0
frame.sum(level = 'color',axis = 1)#必须指定轴向axis = 1
使用DataFrame的 列 进行索引
DataFrame的set_index函数会生成一个新的DataFrame,新的DataFrame使用一个或多个列作为索引
frame.set_index(['c','d'])
frame.set_index(['c','d'],drop = False)#默认情况下,变为索引的列会被删除,传递drop参数保留这些列
#reset_index是set_index 的反操作,分层索引的索引层级会被移动到列中
frame2.reset_index()
联合与合并数据集
pd.merge根据一个或多个件进行连接
pd.concat使对象在轴向上进行黏合或堆叠
combine_first实例方法允许将重叠数据拼接在一起,以使一个对象中的值填充另一个对象中的值。
df1 = pd.DataFrame({'key':['b','b','a','c','a','a','b'], 'data1':range(7)})
df2 = pd.DataFrame({'key':['a','b','d'],"data2":range(3)})
#调用merge处理我们获得的对象呢
pd.merge(df1,df2)#没有指定在那一列上连接时,自动将重叠列作为连接的键(内连接,取交集),这里重叠列为 ‘key’列
#等价于df1.merge(df2)
pd.merge(df1,df2,on = 'key',how = 'outer')#类似sql中的外连接,取并集
how参数选项
如果每个DataFrame对象的列名是不一样的:
pd.merge(df3,df4,left_on='lkey',right_on='rkey')#分别指定左键和右键,结果取交集
pd.merge(df1,df2,on = 'key',how = 'left')#对所有左表的键进行联合
pd.merge(df1,df2,how = 'inner')#只对两张表都有的键进行联合
当有多个重复列时,on参数仅仅指定其中一个重复列,其他列会自动添加后缀用以区分,当然你也可以指定后缀名suffixes = ('_left','_right')
left = DataFrame({'key1':['foo','foo','bar'],'key2':['one','two','one'],'lval':[1,2,3]})
right = pd.DataFrame({'key1':['foo','foo','bar','bar'],'key2':['one','one','one','two'],'rval':[4,5,6,7]})
pd.merge(left,right,on ='key1')#可以看到重复列有key1和key2,但只指定了key1
pd.merge(left,right,on = 'key2',suffixes = ('_left','_right'))
根据索引合并,你可以传递left_index = True 或 right_index = True来表示索引需要用来作为合并的键:
left1 = pd.DataFrame({'key':['a','b','a','a','b','c'],'value':range(6)})
right1 = pd.DataFrame({'group_val':[3.5,7]},index = ['a','b'])
pd.merge(left1,right1,left_on='key',right_index=True)
#默认的合并方法是取交集(内连接),你也可以使用外连接
pd.merge(left1,right1,left_on = 'key',right_index = True,how = 'outer')
多层索引数据的情况下,事情会更复杂,在索引上连接是一个隐式。
pd.merge(lefth,right,left_on = ['key1','key2'],right_index=True,how = 'outer')
使用两边的索引合并也是可以的
pd.merge(lefth2,right2,left_index=True,right_index=True,how = 'outer')
pd.merge(lefth2,right2,left_index=True,right_index=True)
merge函数的参数
DataFrame 有一个join示例方法,用于按照 索引 合并。该方法也可以用于 合并多个索引相同或相似但没有重叠列 的DataFrame对象。
lefth2.join(right2,how = 'outer')
DataFrame上的join方法进行连接键 上的左连接,完全保留左边DataFrame的 行索引,在调用该DataFrame的某一列(on参数指定的列)上,连接传递的DataFrame的索引:
left1.join(right1,on = 'key')
也可以向join方法中传入一个DataFrame列表,注意,始终是根据索引进行连接,且默认内连接,除非指定how参数为‘outer’
lefth2.join([right2,another],how = 'outer')
Numpy的concatenate函数可以实现在numpy数组上的 拼接、绑定或堆叠
arr = np.arange(12).reshape((3,4))
np.concatenate([arr,arr],axis = 1)
array([[ 0, 1, 2, 3, 0, 1, 2, 3],
[ 4, 5, 6, 7, 4, 5, 6, 7],
[ 8, 9, 10, 11, 8, 9, 10, 11]])
默认情况下,pd.concat方法时沿着axis = 0的轴向生效的,生成一个Series,如果你传递axis = 1(根据列),返回的结果则是一个DataFrame。
s1 = pd.Series([0,1],index = ['a','b'])
s2 = pd.Series([2,3,4],index = ['c','d','e'])
s3 = pd.Series([5,6],index = ['f','g'])
pd.concat([s1,s2,s3])
a 0
b 1
c 2
d 3
e 4
f 5
g 6
dtype: int64
pd.concat([s1,s2,s3],axis = 1)
0 1 2
a 0.0 NaN NaN
b 1.0 NaN NaN
c NaN 2.0 NaN
d NaN 3.0 NaN
e NaN 4.0 NaN
f NaN NaN 5.0
g NaN NaN 6.0
s4 = pd.concat([s1,s3])
s4
a 0
b 1
f 5
g 6
dtype: int64
pd.concat([s1,s4],axis = 1,sort=False)
0 1
a 0.0 0
b 1.0 1
f NaN 5
g NaN 6
#只有在轴向为axis=1时传入join参数才有意义
pd.concat([s1,s4],axis = 1,join ='inner')
0 1
a 0 0
b 1 1
#可以使用jreindex方法来指明用于连接其他轴向的轴,join_axes已被弃用
pd.concat([s1,s4],axis = 1).reindex(['a','c','b','e'])
0 1
a 0.0 0.0
c NaN NaN
b 1.0 1.0
e NaN NaN
#假设你想在轴上创建一个多层索引 ,可以使用keys参数来实现:
result = pd.concat([s1,s3,s4],keys = ['one','two','three'])
result
one a 0
b 1
two f 5
g 6
three a 0
b 1
f 5
g 6
dtype: int64
将相同逻辑拓展到DataFrame对象:
df1 = DataFrame(np.arange(6).reshape((3,2)),index = ['a','b','c'],
columns = ['one','two'])
df2 = DataFrame(5+np.arange(4).reshape(2,2),index = ['a','c'],
columns = ['three','four'])
pd.concat([df1,df2],sort=False)
four one three two
a NaN 0.0 NaN 1.0
b NaN 2.0 NaN 3.0
c NaN 4.0 NaN 5.0
a 6.0 NaN 5.0 NaN
c 8.0 NaN 7.0 NaN
pd.concat([df1,df2],axis = 1,keys= ['level1','level2'],sort=False)
level1 level2
one two three four
a 0 1 5.0 6.0
b 2 3 NaN NaN
c 4 5 7.0 8.0
#如果你传递的是对象的字典而不是列表的话,则字典的键会用于keys选项:
pd.concat({'level1':df1,'level2':df2},axis = 1,sort=False)
level1 level2
one two three four
a 0 1 5.0 6.0
b 2 3 NaN NaN
c 4 5 7.0 8.0
#我们使用names参数命名生成的轴层级
pd.concat([df1,df2],axis = 1,keys = ['level1','level2'],names = ['upper','lower'],sort=False)
upper level1 level2
lower one two three four
a 0 1 5.0 6.0
b 2 3 NaN NaN
c 4 5 7.0 8.0
最需要考虑的是行索引中不包含任何数据的DataFrame:
df1 = pd.DataFrame(np.random.randn(3,4),columns = ['a','b','c','d'])
df2 = pd.DataFrame(np.random.randn(2,3),columns = ['b','d','a'])
pd.concat([df1,df2],sort = False)
a b c d
0 -1.410499 0.539828 0.403357 -0.207983
1 0.222250 -0.001390 1.897430 0.742536
2 -0.320451 1.282065 1.041709 0.764900
0 1.311377 2.130122 NaN 0.494659
1 -0.046373 1.549453 NaN -0.410902
#我们传入ignore_index = True
pd.concat([df1,df2],ignore_index=True,sort=False)
a b c d
0 -1.410499 0.539828 0.403357 -0.207983
1 0.222250 -0.001390 1.897430 0.742536
2 -0.320451 1.282065 1.041709 0.764900
3 1.311377 2.130122 NaN 0.494659
4 -0.046373 1.549453 NaN -0.410902
concat函数的参数
联合重叠数据
有两个数据集,索引全部或部分重叠。考虑numpy的where函数,这个函数可以进行面向数组的if-else等价操作:
a = pd.Series([np.nan,2.5,0.0,3.5,4.5,np.nan],index = ['f','e','d','c','b','a'])
b= pd.Series([0.,np.nan,2.,np.nan,np.nan,5.],index = ['a','b','c','d','e','f'])
np.where(pd.isnull(a),b,a)#a为NAN就用b的值(但是修补时索引没有对齐)
array([0. , 2.5, 0. , 3.5, 4.5, 5. ])
#Series中有一个combine_first方法:
b.combine_first(a)#根据传入的a 的值来修补b的值
a 0.0
b 4.5
c 2.0
d 0.0
e 2.5
f 5.0
dtype: float64
在DataFrame中,combine_first逐 列做相同的操作:
df1 = pd.DataFrame({'a':[1.,np.nan,5.,np.nan],'b':[np.nan,2.,np.nan,6.], 'c':range(2,18,4)})
df2 = pd.DataFrame({'a':[5.,4.,np.nan,3.,7.], 'b':[np.nan,3.,4.,6.,8.]})
df1.combine_first(df2)
a b c
0 1.0 NaN 2.0
1 4.0 2.0 6.0
2 5.0 4.0 10.0
3 3.0 6.0 14.0
4 7.0 8.0 NaN
重塑和透视
适用多层索引进行重塑
stack(堆叠):将列的数据透视到行(旋转)
unstack(拆堆):将行中的数据透视到列
data = pd.DataFrame(np.arange(6).reshape((2,3)),
index = pd.Index(['Ohio','Colorado'],name = 'state'),
columns = pd.Index(['one','two','three'],name = 'number'))
data
number one two three
state
Ohio 0 1 2
Colorado 3 4 5
result = data.stack()
result
state number
Ohio one 0
two 1
three 2
Colorado one 3
two 4
three 5
dtype: int32
data.stack().unstack()
number one two three
state
Ohio 0 1 2
Colorado 3 4 5
# 你也可以传入一个层级序号(这里传入的是0) 或 名称(对应0的就是‘state’) 来拆分一个不同的层级;
#当然如果传入1或者‘number’返回的就是同上面一样默认情况下的出现结果
data.stack().unstack(0)
state Ohio Colorado
number
one 0 3
two 1 4
three 2 5
由于层级中的所有值并未包含于每个子分组中时,拆分可能会引入缺失值
s1 = pd.Series([0,1,2,3],index = ['a','b','c','d'])
s2 = pd.Series([4,5,6],index = ['c','d','e'])
data2 = pd.concat([s1,s2], keys = ['one','two'])
data2
one a 0
b 1
c 2
d 3
two c 4
d 5
e 6
dtype: int64
data2.unstack()#永远默认从最内侧拆分
a b c d e
one 0.0 1.0 2.0 3.0 NaN
two NaN NaN 4.0 5.0 6.0
data2.unstack(0)
one two
a 0.0 NaN
b 1.0 NaN
c 2.0 4.0
d 3.0 5.0
e NaN 6.0
data2.unstack().stack()#回到原来的样子
data2.unstack().stack(dropna = False)#保留不同层级中的缺失值
one a 0.0
b 1.0
c 2.0
d 3.0
e NaN
two a NaN
b NaN
c 4.0
d 5.0
e 6.0
dtype: float64
在DataFrame中拆堆时,被拆堆的层级会变为结果中最低的层级:
df = pd.DataFrame({'left':result,'right':result+5},
columns = pd.Index(['left','right'],name = 'side'))
df
side left right
state number
Ohio one 0 5
two 1 6
three 2 7
Colorado one 3 8
two 4 9
three 5 10
df.unstack()#可以看到最内侧(最低)层级的number行索引变成了行标签中最内侧(最低)的层级,等价于传入1或者‘number’
side left right
number one two three one two three
state
Ohio 0 1 2 5 6 7
Colorado 3 4 5 8 9 10
df.unstack('state')#等价于传入0
side left right
state Ohio Colorado Ohio Colorado
number
one 0 3 5 8
two 1 4 6 9
three 2 5 7 10
#来更多花里胡哨的变化
df.unstack('state').stack()
side left right
number state
one Ohio 0 5
Colorado 3 8
two Ohio 1 6
Colorado 4 9
three Ohio 2 7
Colorado 5 10
df.unstack('state').stack('side')#等价于把‘side’替换成0,若传入1等价于替换为‘state’
state Colorado Ohio
number side
one left 3 0
right 8 5
two left 4 1
right 9 6
three left 5 2
right 10 7
df.unstack('state').stack(['state','side'])#传入一个列表时,我裂开了
number state side
one Ohio left 0
right 5
Colorado left 3
right 8
two Ohio left 1
right 6
Colorado left 4
right 9
three Ohio left 2
right 7
Colorado left 5
right 10
dtype: int32
将“长”数据透视为“宽”数据
data = pd.read_csv('macrodata.csv')
data.head()
periods = pd.PeriodIndex(year=data.year,quarter = data.quarter,name = 'date')
periods#PeriodIndex将year和quarter等列进行联合生成了一种时间间隔索引
PeriodIndex(['1959Q1', '1959Q2', '1959Q3', '1959Q4', '1960Q1', '1960Q2',
'1960Q3', '1960Q4', '1961Q1', '1961Q2',
...
'2007Q2', '2007Q3', '2007Q4', '2008Q1', '2008Q2', '2008Q3',
'2008Q4', '2009Q1', '2009Q2', '2009Q3'],
dtype='period[Q-DEC]', name='date', length=203, freq='Q-DEC')
columns = pd.Index(['realgdp','infl','unemp'],name = 'item')
columns
Index(['realgdp', 'infl', 'unemp'], dtype='object', name='item')
data = data.reindex(columns = columns)
data.head()
item realgdp infl unemp
0 2710.349 0.00 5.8
1 2778.801 2.34 5.1
2 2775.488 2.74 5.3
3 2785.204 0.27 5.6
4 2847.699 2.31 5.2
data.index = periods.to_timestamp('D','end')
data.head()#此时数据索引变为
item realgdp infl unemp
date
1959-03-31 23:59:59.999999999 2710.349 0.00 5.8
1959-06-30 23:59:59.999999999 2778.801 2.34 5.1
1959-09-30 23:59:59.999999999 2775.488 2.74 5.3
1959-12-31 23:59:59.999999999 2785.204 0.27 5.6
1960-03-31 23:59:59.999999999 2847.699 2.31 5.2
ldata = data.stack().reset_index().rename(columns = {0:'value'})
ldata[:10]#可以看到数据堆叠为了一列value
date item value
0 1959-03-31 23:59:59.999999999 realgdp 2710.349
1 1959-03-31 23:59:59.999999999 infl 0.000
2 1959-03-31 23:59:59.999999999 unemp 5.800
3 1959-06-30 23:59:59.999999999 realgdp 2778.801
4 1959-06-30 23:59:59.999999999 infl 2.340
5 1959-06-30 23:59:59.999999999 unemp 5.100
6 1959-09-30 23:59:59.999999999 realgdp 2775.488
7 1959-09-30 23:59:59.999999999 infl 2.740
8 1959-09-30 23:59:59.999999999 unemp 5.300
9 1959-12-31 23:59:59.999999999 realgdp 2785.204
这里的reindex()在原有索引的基础上创建新的索引,相当于在原数据中取对应列的数据。(有点切片的意思)。
stack(),堆叠数据,这里直接将三列数据堆叠为一列,返回的是一个Series
reset_index,重建索引,返回一个DataFrame
rename(),是对列进行命名,这里将使用字典键指定索引值0对应堆叠出来的一列,命名为value。
要是突然又想变回去了怎么办,毕竟这样就一列values的直观效果不好,考虑DataFrame的pivot方法,传递的前两个值是用作行和列索引的列,然后是可选的values列以填充DataFrame。
ldata.pivot('date','item','value')
假设你有两个数值列
如果不指定第三个也就是values参数,会得到一个含有分层索引的DataFrame
ldata['value2'] = np.random.randn(len(ldata))
ldata.pivot('date','item')
这里的pivot方法等价于使用set_index(['date','item']),然后调用unstack方法拆堆
ldata.set_index(['date','item']).unstack()
将‘宽’透视为‘长’
pivot方法的反操作是pd.melt。将多列合并为一列,产生一个新的DataFrame,其长度比输入更长。
df = pd.DataFrame({'key':['foo','bar','baz'],'A':[1,2,3],'B':[4,5,6],'C':[7,8,9]})
key A B C
0 foo 1 4 7
1 bar 2 5 8
2 baz 3 6 9
#使用melt方法必须指明分组指标(如果有的话)
melted = pd.melt(df,['key'])
key variable value
0 foo A 1
1 bar A 2
2 baz A 3
3 foo B 4
4 bar B 5
5 baz B 6
6 foo C 7
7 bar C 8
8 baz C 9
reshaped = melted.pivot('key','variable','value')
variable A B C
key
bar 2 5 8
baz 3 6 9
foo 1 4 7
reshaped.reset_index()
variable key A B C
0 bar 2 5 8
1 baz 3 6 9
2 foo 1 4 7
#也可以指定列子集作为值列
pd.melt(df,id_vars=['key'],value_vars=['A','B'])
key variable value
0 foo A 1
1 bar A 2
2 baz A 3
3 foo B 4
4 bar B 5
5 baz B 6
#也可以无需任何分组指标
pd.melt(df,value_vars=['A','B','C'])
variable value
0 A 1
1 A 2
2 A 3
3 B 4
4 B 5
5 B 6
6 C 7
7 C 8
8 C 9
pd.melt(df,value_vars=['key','A','B'])
variable value
0 key foo
1 key bar
2 key baz
3 A 1
4 A 2
5 A 3
6 B 4
7 B 5
8 B 6