《Python数据分析实战》
上一章讲解了从数据库或文件等数据源获取数据的方法。
将数据转换为DataFrame格式后,你就可以对其进行处理了。数据处理的目的时准备数据,便于分析。数据处理很大程度上取决于必须进行数据分析的人员的目的,数据处理可以使要寻找的信息以更清晰的方式呈现出来。
这一章,我们深入讲解pandas库在数据处理阶段的功能。
数据处理又可以细分为三个阶段,数据处理的三个阶段为:数据准备、数据转换和数据聚合。
1.数据准备
数据准备阶段包括以下步骤:
- 加载
- 组装(合并、拼接、组合)
- 变形(轴向旋转)
- 删除
在本章,尤其是这一节,你将会学到数据转换为统一的数据结构所需的各种操作。
对于存储在pandas对象中的各种数据,其组装方法有以下几种。
- 合并—pandas.merge()函数根据一个或多个键连接多行。
- 拼接—pandas.concat()函数按照轴把多个对象拼接起来。
- 组合—pandas.DataFrame.combine_first()函数从另一个数据结构获取数据,连接重合的数据,以填充缺失值。
此外,数据准备过程还可能涉及变换行、列位置的变形操作。
2.合并
合并:它使用一个或多个键把多行数据结合在一起。
合并类似SQL的JOIN查询,用几个表共有的引用值(键)从不同的表获取数据。以这些键为基础,我们能够获取到列表形式的新数据,这些数据是对几个表中的数据进行组合得到的。
pandas库中这类操作叫作合并,执行合并操作的函数为merge()。
frame1
>>
id price
0 ball 12.33
1 pencil 11.44
2 pen 33.21
3 mug 13.23
4 ashtray 33.62
frame2
>>
color id
0 white pencil
1 red pencil
2 red ball
3 black pen
#merge()函数,执行合并操作。
pd.merge(frame1,frame2)
>>
id price color
0 ball 12.33 red
1 pencil 11.44 white
2 pencil 11.44 red
3 pen 33.21 black
由结果可见,返回的DataFrame对象由原来两个DataFrame对象中ID相同的行组成。
这个例子中,我们没有为merge()指定基于哪一列进行合并。实际应用中,绝大部分情况下需要指定基于哪一列进行合并。
具体做法是增加on选项,把列的名称作为用于合并的键赋给它。
frame1 =pd.DataFrame({
'id':['ball','pencil','pen','mug','ashtray'],
'color':['white','red','red','black','green'],
'brand':['OMG','ABC','ABC','POD','POD']
})
frame2 =pd.DataFrame({
'id':['pencil','pencil','ball','pen'],
'brand':['OMG','POD','ABC','POD']
})
#1.因为一个对象的列名称完全在另一个对象中也存在,所以对它们执行合并操作将得到一个空DataFrame对象
pd.merge(frame1,frame2)
>> Empty DataFrame
Columns:[barnds,color,id]
Index:[]
#2.我们使用on选项指定合并操作所依据的基准列
pd.merge(frame1,frame2,on='id')
>>
brand_x color id brand_y
0 OMG white ball ABC
1 ABC red pencil OMG
2 ABC red pencil POD
3 ABC red pen POD
pd.merge(frame1,frame2,on='brand')
>>
brand color id_x id_y
0 OMG white ball pencil
1 ABC red pencil ball
2 ABC red pen ball
3 POD black mug pencil
4 POD black mug pen
5 POD green ashtray pencil
6 POD green ashtray pen
如你所料,合并标准不同,结果差异很大。
然而,问题随之就来了。假如两个DataFrame基准列的名称不一致,该怎样进行合并呢?为了解决这个问题,你可以用left_on和right_on选项指定第一个和第二个DataFrame的基准列。
pd.merge(frame1,frame2,left_on='id',right_on='sid')
>>
brand_x color id brand_y sid
0 OMG white ball ABC ball
1 ABC red pencil OMG pencil
2 ABC red pencil POD pencil
3 ABC red pen POD pen
merge()函数默认执行的是内连接操作;上述结果中的键是由交叉操作得到的。
其他选项由左连接、右连接和外连接。外连接把所有的键整合到一起,其效果相当于左连接和右连接的效果之和。连接类型用how选项指定。
frame2.columns['brand','id']
#1.默认内连接
pd.merge(frame1,frame2,on='id')
>>
brand_x color id brand_y
0 OMG white ball ABC
1 ABC red pencil OMG
2 ABC red pencil POD
3 ABC red pen POD
#2.外连接
pd.merge(frame1,frame2,on='id',how='outer')
>>
brand_x color id brand_y
0 OMG white ball ABC
1 ABC red pencil OMG
2 ABC red pencil POD
3 ABC red pen POD
4 POD black mug NaN
5 POD green ashtray NaN
#3.左连接
pd.merge(frame1,frame2,on='id',how='left')
>>
brand_x color id brand_y
0 OMG white ball ABC
1 ABC red pencil OMG
2 ABC red pencil POD
3 ABC red pen POD
4 POD black mug NaN
5 POD green ashtray NaN
#4.右连接
pd.merge(frame1,frame2,on='id',how='right')
>>
brand_x color id brand_y
0 OMG white ball ABC
1 ABC red pencil OMG
2 ABC red pencil POD
3 ABC red pen POD
#5.要合并多个键,则把多个键赋给on选项
pd.merge(frame1,frame2,on=['id','brand'],how='outer')
>>
brand color id
0 OMG white ball
1 ABC red pencil
2 ABC red pen
3 POD black mug
4 POD green ashtray
5 OMG NaN pencil
6 POD NaN pencil
7 ABC NaN ball
8 POD NaN pen
根据索引合并:
有时,合并操作不是用DataFrame的列而是用索引作为键。
把left_index和right_index选项的值置为True,将其激活,就可以将其作为合并DataFrame的基准。
pd.merge(frame1,frame2,right_index=True,left_index=True)
>>
brand_x color id_x brand_y id_y
0 OMG white ball OMG pencil
1 ABC red pencil POD pencil
2 ABC red pen ABC ball
3 POD black mug POD pen
但是DataFrame对象的join()函数更适合于根据索引进行合并。我们还可以用它合并多个索引相同或索引相同但列却不一致的DataFrame对象。
fram1.join(frame2)
pandas将会给出错误信息,因为frame1的列名称与frame2有重合。因此在使用join()函数之前,要重命名frame2的列。
frame2.columns = ['brand2','id2']
frame1.join(frame2)
>>
brand color id brand2 id2
0 OMG white ball OMG pencil
1 ABC red pencil POD pencil
2 ABC red pen ABC ball
3 POD black mug POD pen
4 POD green ashtray NaN NaN
上述合并操作是以索引而不是列为基准。
合并后得到的DataFrame对象包含只存在于frame1中的索引4,但是整合自frame2、索引号为4的各元素均为NaN。
3.拼接
另外一种数据整合操作叫作拼接。Numpy的concatenate()函数就是用于数组的拼接操作。Pandas的concat()函数实现了按轴拼接的功能。
array1
>>
array([
[0,1,2],
[3,4,5],
[6,7,8]
])
array2
>>
array([
[6,7,8],
[9.10.11],
[12,13,14]
])
#Numpy的concatenate()函数
np.concatenate([array1,array2],axis=0)
>>
array([
[0,1,2],
[3,4,5],
[6,7,8],
[6,7,8],
[9.10.11],
[12,13,14]
])
np.concatenate([array1,array2],axis=1)
>>
array([
[0,1,2,6,7,8],
[3,4,5,9,10,11],
[6,7,8,12,13,14]
])
#Pandas的concat()函数
ser1
>>
1 0.636
2 0.345
3 0.157
4 0.070
ser2
>>
5 0.411
6 0.359
7 0.987
8 0.329
pd.concat([ser1,ser2])
>>
1 0.636
2 0.345
3 0.157
4 0.070
5 0.411
6 0.359
7 0.987
8 0.329
#concat()函数默认按照axis=0这条轴拼接数据,返回series对象。如果指定axis=1,返回结果将是DataFrame对象。
pd.concat([ser1,ser2],axis=1)
>>
0 1
1 0.636 NaN
2 0.345 NaN
3 0.157 NaN
4 0.070 NaN
5 NaN 0.411
6 NaN 0.359
7 NaN 0.987
8 NaN 0.329
结果中无重合数据,因而刚执行的其实是外连接操作。把join选项设置为‘inner’,可执行内连接操作。
pd.concat([ser1,ser3],axis=1,join='inner')
>>
0 0 1
1 0.636 0.636 NaN
2 0.345 0.345 NaN
3 0.157 0.157 NaN
4 0.070 0.070 NaN
这种操作的问题是,从结果中无法识别被拼接的部分。如果你想在用于拼接的轴上创建等级索引,就需要借助key选项来完成。
pd.concat([ser1,ser2],key=[1,2])
>>
1 1 0.636
2 0.345
3 0.157
4 0.070
2 5 0.411
6 0.359
7 0.987
8 0.329
按照axis=1拼接拼接Series对象,所指定的键变为拼接后得到的DataFrame对象各列的名称。
pd.concat([ser1,ser2],axis=1,key=[1,2])
>>
1 2
1 0.636 NaN
2 0.345 NaN
3 0.157 NaN
4 0.070 NaN
5 NaN 0.411
6 NaN 0.359
7 NaN 0.987
8 NaN 0.329
到目前为止,我们拼接的是Series对象,而DataFrame对象的拼接方法与之相同。
4.组合
还有另外一种情况,我们无法通过合并或拼接方法组合数据。例如,两个数据集的索引完全或部分重合。
combine_first()函数可以用来组合Series对象,同时对齐数据。
ser1
>>
1 0.942
2 0.035
3 0.886
4 0.809
5 0.800
ser2
>>
2 0.739
4 0.225
5 0.709
6 0.214
ser1.combine_fisrt(ser2)
>>
1 0.942
2 0.033
3 0.886
4 0.809
5 0.800
6 0.214
ser2.combine_first(ser1)
>>
1 0.942
2 0.739
3 0.886
4 0.225
5 0.709
6 0.214
反之,如果你想进行部分合并,仅指定要合并的部分即可。
ser1[:3].combine(ser2[:3])
>>
1 0.942
2 0.033
3 0.886
4 0.225
5 0.709
5.轴向旋转:
除了整合统一来自不同数据源的数据,另外一种常用操作为轴向旋转。
实际应用中,按行或列调整元素并不总能满足目标。
有时,需要按照行重新调整列的元素或是按列调整行。
按等级索引旋转:
轴向旋转有两个基础操作:
- 入栈:旋转数据结构,把列转换为行
- 出栈:把行转换为列
frame1
>>
ball pen pencil
white 0 1 2
black 3 4 5
red 6 7 8
#对DataFrame对象应用stack()函数,会把列转换为行,从而得到一个Series对象
frame1.stack()
>>
white ball 0
pen 1
pencil 2
black ball 3
pen 4
pencil 5
red ball 6
pen 7
pencil 8
在这个具有等级索引结构的Series对象上执行unstack()操作,可以重建之前的DataFrame对象,从而可以以数据透视表的形式来展示Series对象中的等级索引结构。
ser5
>>
white ball 0
pen 1
pencil 2
black ball 3
pen 4
pencil 5
red ball 6
pen 7
pencil 8
ser5.unstack()
>>
ball pen pencil
white 0 1 2
black 3 4 5
red 6 7 8
出栈操作可以应用于不同的层级,为unstack()函数传入表示层级的编号或名称,即可对相应层级进行操作。
ser5.unstack(0)
>>
ball pen pencil
white 0 1 2
black 3 4 5
red 6 7 8
从“长”格式向“宽”格式旋转:
longframe
>>
color item value
0 white ball 0.091
1 white pen 0.495
2 white mug 0.956
3 red ball 0.394
4 red pen 0.501
5 red mug 0.561
6 black ball 0.879
7 black pen 0.610
8 black mug 0.093
这种记录数据的一个缺点是,因为一些字段具有多样性和重复性特点,是以选取列作为键时,这种格式的数据可读性差,尤其时无法理解基准列和其他列之间的关系。
pandas提供了能够把长格式DataFrame格式转换为宽格式的pivot()函数,它以用作键的一列或多列作为参数。
#选择color列作为主键,item列作为第二主键
wideframe = longframe.pivot('color','item')
wideframe
>>
value
item ball mug pen
color
balck 0.879 0.093 0.610
red 0.394 0.561 0.501
white 0.091 0.956 0.495
6.删除:
del命令和drop()函数
frame1
>>
ball pen pencil
white 0 1 2
balck 3 4 5
red 6 7 8
#要删除一列,对DataFrame对象应用del命令,指定列名。
del frame1['ball']
frame1
>>
pen pencil
white 1 2
balck 4 5
red 7 8
#要删除多余的行,使用drop()函数,将索引的名作为参数
frame1.drop('white')
frame1
>>
ball pen pencil
balck 3 4 5
red 6 7 8