将数据转换为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