数据清理、合并、重塑、转换

1. 合并数据集
pandas.merge:
根据一个或多个键将不同DataFrame的行连接起来,它实现的是数据库的连接操作。具体如下:

  • 多对一的合并:
df1
Out[4]: 
   data1 key
0      0   b
1      1   b
2      2   a
3      3   c
4      4   a
5      5   a
6      6   b

df2
Out[5]: 
   data2 key
0      0   a
1      1   b
2      2   d

pd.merge(df1,df2)
Out[6]: 
   data1 key  data2
0      0   b      1
1      1   b      1
2      6   b      1
3      2   a      0
4      4   a      0
5      5   a      0

在上述代码中,df1中的数据有多个被标记为a和b的行,而df2中key列的每个值仅对应一行,因此为多对一的合并。还可以显示的指定用哪个列进行连接,如on=’key’,如果进行连接的两个DataFrame没有相同的列名,则可以这样显示指定:

pd.merge(df1,df2,left_on='lkey',right_on='rkey')

我们观察结果集可以发现,c和d以及与子相关的数据都不见了,所以merge默认情况下做的是“inner”连接(即结果中的键是交集),根据已知SQL知识,自然还有其他方式,如left,right和outer(并集)。

  • 多对多连接
    多对多产生的是行的笛卡尔积,如下所示,左边的DataFrame有3个’b’行,右边有两个,那么最终结果中就有6个’b’行:
df1
Out[10]: 
   data1 key
0      0   b
1      1   b
2      2   a
3      3   c
4      4   a
5      5   b

df2
Out[11]: 
   data2 key
0      0   a
1      1   b
2      2   a
3      3   b
4      4   d

pd.merge(df1,df2,on='key',how='left')
Out[12]: 
    data1 key  data2
0       0   b    1.0
1       0   b    3.0
2       1   b    1.0
3       1   b    3.0
4       2   a    0.0
5       2   a    2.0
6       3   c    NaN
7       4   a    0.0
8       4   a    2.0
9       5   b    1.0
10      5   b    3.0
  • 还可以通过多个键进行连接,只需传入一个由列名组成的列表即可,可以做如下理解:多个键形成一系列元组,并将其当做单个键连接(事实并非如此)。
left
Out[19]: 
  key1 key2  lval
0  foo  one     1
1  foo  two     2
2  bar  one     3

right
Out[20]: 
  key1 key2  lval
0  foo  one     4
1  foo  one     5
2  bar  one     6
3  bar  two     7

pd.merge(left,right,on=['key1','key2'],how='outer')
Out[21]: 
  key1 key2  lval_x  lval_y
0  foo  one     1.0     4.0
1  foo  one     1.0     5.0
2  foo  two     2.0     NaN
3  bar  one     3.0     6.0
4  bar  two     NaN     7.0
  • 对重复列名的处理
    suffixes选项:用于指定附加到左右两个DataFrame对象的重叠列名上的字符串。
pd.merge(left,right,on='key1')
Out[22]: 
  key1 key2_x  lval_x key2_y  lval_y
0  foo    one       1    one       4
1  foo    one       1    one       5
2  foo    two       2    one       4
3  foo    two       2    one       5
4  bar    one       3    one       6
5  bar    one       3    two       7

pd.merge(left,right,on='key1',suffixes=('_left','_right'))
Out[23]: 
  key1 key2_left  lval_left key2_right  lval_right
0  foo       one          1        one           4
1  foo       one          1        one           5
2  foo       two          2        one           4
3  foo       two          2        one           5
4  bar       one          3        one           6
5  bar       one          3        two           7
  • 轴向连接
    1. 首先来看numpy中用于合并原始Numpy数组的concatenation函数:
arr
Out[30]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

np.concatenate([arr,arr])
Out[31]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

np.concatenate([arr,arr],axis=1)
Out[32]: 
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]])
  • 在pandas中为concat函数
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])
Out[36]: 
a    0
b    1
c    2
d    3
e    4
f    5
g    6
dtype: int64

pd.concat([s1,s2,s3],axis=1)
Out[37]: 
     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

默认情况下,concat是在axis=0上工作的,最终产生一个新的Series,如果传入axis=1,则结果就会变成一个DataFrame。

  • 合并重叠数据
    首先使用numpy的where函数
a
Out[41]: 
f    NaN
e    2.5
d    NaN
c    3.5
b    4.5
a    NaN
dtype: float64

b
Out[42]: 
f    0.0
e    1.0
d    2.0
c    3.0
b    4.0
a    5.0
dtype: float64

np.where(pd.isnull(a),b,a)
Out[43]: array([0. , 2.5, 2. , 3.5, 4.5, 5. ])

b[-1]=np.nan

np.where(pd.isnull(a),b,a)
Out[45]: array([0. , 2.5, 2. , 3.5, 4.5, nan])

Series的comnine_first方法有同样的功能,并且可以数据对齐

b.combine_first(a)
Out[46]: 
f    0.0
e    1.0
d    2.0
c    3.0
b    4.0
a    NaN
dtype: float64

同样对于DataFrame,这个函数也会做同样的事情,因此你可以将此看做,用参数对象中的数据为调用者对象的缺失数据“打补丁”
1. 数据转换

  • 移除重复值
    DataFrame的duplicated方法会返回一个布尔型的Series,表示各行是否是重复行
data
Out[48]: 
    k1  k2
0  one   1
1  one   1
2  one   2
3  two   3
4  two   3
5  two   4
6  two   4

data.duplicated()
Out[49]: 
0    False
1     True
2    False
3    False
4     True
5    False
6     True
dtype: bool

drop_duplicates方法,返回一个移除了重复行的DataFrame

data.drop_duplicates()
Out[50]: 
    k1  k2
0  one   1
2  one   2
3  two   3
5  two   4

这两个方法默认保留的是第一个出现的值组合。传入take_last=True则保留最后一个。

  • 利用函数或映射进行数据转换(map)
    我们来看下面有关肉类的数据
data
Out[52]: 
          food  ounces
0        bacon     4.0
1  pulled pork     3.0
2        bacon    12.0
3     Pastrami     6.0
4  corned beef     7.5
5        Bacon     8.0
6     pastrami     3.0
7    honey ham     5.0
8     nova lox     6.0

假设想要添加一列表示该肉类食物的来源,具体映射为:


meat_to_animal
Out[54]: 
{'bacon': 'pig',
 'corned beef': 'cow',
 'honey ham': 'pig',
 'nova lox': 'salmon',
 'pastrami': 'cow',
 'pulled pock': 'pig'}

为了保持一致,先进行大小写转换,然后进行关联(添加列):

data['animal']=data['food'].map(str.lower).map(meat_to_animal)

data
Out[56]: 
          food  ounces  animal
0        bacon     4.0     pig
1  pulled pork     3.0     NaN
2        bacon    12.0     pig
3     Pastrami     6.0     cow
4  corned beef     7.5     cow
5        Bacon     8.0     pig
6     pastrami     3.0     cow
7    honey ham     5.0     pig
8     nova lox     6.0  salmon
  • 离散化和面元划分
    为了便于分析,常常将连续数据离散化为面元。
    假设有一组人员数据,我们希望把它划分为不同的年龄组。
ages
Out[61]: [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32]

bins
Out[62]: [18, 25, 35, 60, 100]

cats=pd.cut(ages,bins)

cats
Out[64]: 
[(18, 25], (18, 25], (18, 25], (25, 35], (18, 25], ..., (25, 35], (60, 100], (35, 60], (35, 60], (25, 35]]
Length: 12
Categories (4, interval[int64]): [(18, 25] < (25, 35] < (35, 60] < (60, 100]]

这里采用pandas的cut函数,它返回的是一个特殊的Categorical对象,它含有一个表示不同分类名称的categories(之前为levels)数组以及一个为年龄数据进行标号的codes(之前是labels)属性:

cats.codes
Out[70]: array([0, 0, 0, 1, 0, 0, 2, 1, 3, 2, 2, 1], dtype=int8)

cats.categories
Out[71]: 
IntervalIndex([(18, 25], (25, 35], (35, 60], (60, 100]]
              closed='right',
              dtype='interval[int64]')

pd.value_counts(cats)
Out[72]: 
(18, 25]     5
(35, 60]     3
(25, 35]     3
(60, 100]    1
dtype: int64

还可以设置属性labels设置为一个列表或元组即可:

group_names=['Youth','YouthAdult','MiddleAged','Senior']

pd.cut(ages,bins,labels=group_names)
Out[74]: 
[Youth, Youth, Youth, YouthAdult, Youth, ..., YouthAdult, Senior, MiddleAged, MiddleAged, YouthAdult]
Length: 12
Categories (4, object): [Youth < YouthAdult < MiddleAged < Senior]

qcut可以根据样板分位数对数据进行面元划分,得到大小基本相等的面元。

data=np.random.randn(1000)  # 正态分布

cats=pd.qcut(data,4)  #按四分位数进行分割

cats
Out[78]: 
[(-2.893, -0.64], (0.0127, 0.739], (0.739, 3.357], (0.0127, 0.739], (-0.64, 0.0127], ..., (-0.64, 0.0127], (0.0127, 0.739], (-0.64, 0.0127], (0.0127, 0.739], (0.739, 3.357]]
Length: 1000
Categories (4, interval[float64]): [(-2.893, -0.64] < (-0.64, 0.0127] < (0.0127, 0.739] < (0.739, 3.357]]

pd.value_counts(cats)
Out[79]: 
(0.739, 3.357]     250
(0.0127, 0.739]    250
(-0.64, 0.0127]    250
(-2.893, -0.64]    250
dtype: int64

跟cut一样,也可以设置自定义的分位数:

pd.qcut(data,[0,0.1,0.5,0.9,1.])
Out[80]: 
[(-2.893, -1.288], (0.0127, 1.316], (0.0127, 1.316], (0.0127, 1.316], (-1.288, 0.0127], ..., (-1.288, 0.0127], (0.0127, 1.316], (-1.288, 0.0127], (0.0127, 1.316], (0.0127, 1.316]]
Length: 1000
Categories (4, interval[float64]): [(-2.893, -1.288] < (-1.288, 0.0127] < (0.0127, 1.316] < (1.316, 3.357]]
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页