这部分的数据规整化主要包括合并、重塑以及转化,其中的转换又包括清理。
合并数据集
pandas对象中的数据通过一些内置方法进行合并,pandas.merge可以根据一个或多个键将不同DataFrame中的行连接起来;pandas.concat可以沿着一条轴将多个对象堆叠到一起;实例方法combine_first可以将重复数据编接在一起,用一个对象中的值填充另一个对象中的缺失值。
默认键连接
数据集的merge和join运算是通过一个或多个键将行链接起来的。如果熟悉数据库,我们知道连接分为左连接、右连接、内连接、外连接。
首先是内连接和外连接,merge默认情况下是内连接,也可以用inner关键字来指定,内连接就是找两个数组的交集;外连接的关键字是outer,是找两个数组的并集。
df1 = pd.DataFrame({'key':['b','b','a','c','a','a','b'],'data1':range(7)})
df2 = pd.DataFrame({'key':['a','b','d'],'data2':range(3)})
print(df1)
print(df2)
print(pd.merge(df1,df2))
print(pd.merge(df1,df2,how='inner')) #内连接
print(pd.merge(df1,df2,how='outer')) #外连接
# key data1
# 0 b 0
# 1 b 1
# 2 a 2
# 3 c 3
# 4 a 4
# 5 a 5
# 6 b 6
# key data2
# 0 a 0
# 1 b 1
# 2 d 2
# key data1 data2
# 0 b 0 1
# 1 b 1 1
# 2 b 6 1
# 3 a 2 0
# 4 a 4 0
# 5 a 5 0
# key data1 data2
# 0 b 0 1
# 1 b 1 1
# 2 b 6 1
# 3 a 2 0
# 4 a 4 0
# 5 a 5 0
# key data1 data2
# 0 b 0.0 1.0
# 1 b 1.0 1.0
# 2 b 6.0 1.0
# 3 a 2.0 0.0
# 4 a 4.0 0.0
# 5 a 5.0 0.0
# 6 c 3.0 NaN
# 7 d NaN 2.0
左连接就是在右边找到左边有的值,组成新的数组;如下例所示,在左连接中,df1的键有a、b、c三个,在df2中有a、b、d,因为d在df1中没有,所以最后的左连接就不会有d,只会将df2的a、b连接起来。右连接同理。
df1 = pd.DataFrame({'key':['b','b','a','c','a','a','b'],'data1':range(7)})
df2 = pd.DataFrame({'key':['a','b','d'],'data2':range(3)})
print(df1)
print(df2)
print(pd.merge(df1,df2,how='left'))
print(pd.merge(df1,df2,how='right'))
# key data1
# 0 b 0
# 1 b 1
# 2 a 2
# 3 c 3
# 4 a 4
# 5 a 5
# 6 b 6
# key data2
# 0 a 0
# 1 b 1
# 2 d 2
# key data1 data2
# 0 b 0 1.0
# 1 b 1 1.0
# 2 a 2 0.0
# 3 c 3 NaN
# 4 a 4 0.0
# 5 a 5 0.0
# 6 b 6 1.0
# key data1 data2
# 0 b 0.0 1
# 1 b 1.0 1
# 2 b 6.0 1
# 3 a 2.0 0
# 4 a 4.0 0
# 5 a 5.0 0
# 6 d NaN 2
merge的参数如下图所示。
索引连接
上面的连接是键的连接,有的时候,我们连接键是在索引中。这个时候又是怎么样的呢?对于merge的连接方式,我们通过传入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'])
print(left1)
print(right1)
print(pd.merge(left1,right1,left_on='key',right_index=True,how='outer'))
# key value
# 0 a 0
# 1 b 1
# 2 a 2
# 3 a 3
# 4 b 4
# 5 c 5
# group_val
# a 3.5
# b 7.0
# key value group_val
# 0 a 0 3.5
# 2 a 2 3.5
# 3 a 3 3.5
# 1 b 1 7.0
# 4 b 4 7.0
# 5 c 5 NaN
前面我们说连接的另一种方法是join实例方法,它能更方便的实现按索引合并。它还能用于合并多个带有相同或相似索引的DataFrame对象,而不管它们之间有没有重叠的列。
注意,join默认的是左连接。
left1 = pd.DataFrame({'key':['a','b','a','a','b','c'],'value':range(6)})
right1 = pd.DataFrame({'group_val':[3.5,7]},index=['0','1'])
print(left1)
print(right1)
print(left1.join(right1))
# key value
# 0 a 0
# 1 b 1
# 2 a 2
# 3 a 3
# 4 b 4
# 5 c 5
# group_val
# 0 3.5
# 1 7.0
# key value group_val
# 0 a 0 NaN
# 1 b 1 NaN
# 2 a 2 NaN
# 3 a 3 NaN
# 4 b 4 NaN
# 5 c 5 NaN
轴上连接
我们还有可以指定沿着轴来连接的函数,对于NumPy,我们使用concatenate函数;对于Pandas,我们使用concat函数。
arr = np.arange(9).reshape((3,3))
print(arr)
print(np.concatenate([arr,arr],axis=1))
df = pd.DataFrame(arr)
print(pd.concat([df,df],axis=0))
# [[0 1 2]
# [3 4 5]
# [6 7 8]]
# [[0 1 2 0 1 2]
# [3 4 5 3 4 5]
# [6 7 8 6 7 8]]
# 0 1 2
# 0 0 1 2
# 1 3 4 5
# 2 6 7 8
# 0 0 1 2
# 1 3 4 5
# 2 6 7 8
concat函数的参数
合并重叠数据
我们用一种方法来实现数据的填充与替代。可以看到在df1中,如果df1是NaN,而df2中存在数字,就会用df2中的数据来填补。
import numpy as np
import pandas as pd
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]})
print(df1)
print(df2)
print(df1.combine_first(df2))
# a b c
# 0 1.0 NaN 2
# 1 NaN 2.0 6
# 2 5.0 NaN 10
# 3 NaN 6.0 14
# a b
# 0 5.0 NaN
# 1 4.0 3.0
# 2 NaN 4.0
# 3 3.0 6.0
# 4 7.0 8.0
# 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。其中stack是将数据的列旋转成行;unstack是将数据的行旋转成列。可以看到在进行stack运算的时候,默认会滤除缺失数据。
s1 = pd.Series([0,1,2,3],index=['a','b','c','d'])
s2 = pd.Series([4,5,6],index=['c','d','e'])
data = pd.concat([s1,s2],keys=['one','two'])
print(data)
print(data.unstack())
print(data.unstack().stack())
print(data.unstack().stack(dropna=False))
# one a 0
# b 1
# c 2
# d 3
# two c 4
# d 5
# e 6
# dtype: int64
# a b c d e
# one 0.0 1.0 2.0 3.0 NaN
# two NaN NaN 4.0 5.0 6.0
# one a 0.0
# b 1.0
# c 2.0
# d 3.0
# two c 4.0
# d 5.0
# e 6.0
# dtype: float64
# 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
数据转换
移除重复数据
import pandas as pd
data = pd.DataFrame({'k1': ['one'] * 3 + ['two'] * 4,
'k2': [1,1,2,3,3,4,4]})
print(data)
print(data.duplicated()) #该方法用于检测该行是否是重复数据
print(data.drop_duplicates()) #过滤重复项
print(data.drop_duplicates('k1')) #指定要过滤的是哪一列
# k1 k2
# 0 one 1
# 1 one 1
# 2 one 2
# 3 two 3
# 4 two 3
# 5 two 4
# 6 two 4
# 0 False
# 1 True
# 2 False
# 3 False
# 4 True
# 5 False
# 6 True
# dtype: bool
# k1 k2
# 0 one 1
# 2 one 2
# 3 two 3
# 5 two 4
# k1 k2
# 0 one 1
# 3 two 3
利用函数或映射进行数据转换
import pandas as pd
data = pd.DataFrame({'food':['bacon','pulled pork','bacon',
'Pastrami','corned beef','Bacon',
'pastrami','honey ham','nova lox'],
'ounces':[4,3,12,6,7,8,3,5,6]})
meat_to_animal = {'bacon':'pig',
'pulled pork':'pig',
'pastrami':'cow',
'honey ham':'pig',
'nova lox':'salmon'}
print(data)
data['animal'] = data['food'].map(str.lower).map(meat_to_animal)
#data['animal'] = data['food'].map(lambda x : meat_to_animal[x.lower()])
print(data) #使用map是一种实现元素级转换以及其他数据清理工作的便捷方式。
# food ounces
# 0 bacon 4
# 1 pulled pork 3
# 2 bacon 12
# 3 Pastrami 6
# 4 corned beef 7
# 5 Bacon 8
# 6 pastrami 3
# 7 honey ham 5
# 8 nova lox 6
# food ounces animal
# 0 bacon 4 pig
# 1 pulled pork 3 pig
# 2 bacon 12 pig
# 3 Pastrami 6 cow
# 4 corned beef 7 NaN
# 5 Bacon 8 pig
# 6 pastrami 3 cow
# 7 honey ham 5 pig
# 8 nova lox 6 salmon
替换值:利用replace函数可以实现值的替换,例如用任意值x替换NaN replace(x,NaN)。
重命名索引:利用rename来实现索引的重命名。