Python数据规整化:清理,转换,合并,重塑



数据分析和建模方面的大量编程工作都是用在数据准备上的:加载、清理、转换以及重塑。有时候,存放在文件或是数据库中的数据并不能满足数据处理应用的要求,多数人都会通过Python、R、Excel、Perl、Java或UNIX文本处理工具对数据进行专门处理。pandas和Python的标准库提供了一组高级的、灵活的、高效的核心函数和算法,可以进行轻松的将数据规整化化为正确的形式。对于Python没有的标准库来说,可以通过Github或是其他的开源网站进行包的扩充以及扩展函数的应用。


合并数据集

运行环境:Pycharm、py3.6、pandas、numpy

pandas对象中的数据可以通过内置的方式进行合并:

● pandas.merge可根据一个或多个键将不同的DataFrame中的行连接起来。SQL或其他关系型数据库,可以用此来实现数据库的连接操作

● pandas.concat可以沿着一条轴将多个对象堆叠到一起

● combine_first可以将重复的数据编织在一起,用一个对象的值填充另一个对象中的缺失值


数据库风格的DataFrame合并


数据集的合并(merge)或连接(join)运算是通过一个或多个键将行链接起来,然而这些运算是关系型数据库的核心。且pandas的merge函数是对数据应用这些算法的切入点。

in:

import pandas as pd
import numpy as np

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:',df1,'\ndf2:',df2)

print('pd.merge(df1,df2):',pd.merge(df1,df2))

out:

df1:    data1 key
0      0   b
1      1   b
2      2   a
3      3   c
4      4   a
5      5   a
6      6   b 
df2:    data2 key
0      0   a
1      1   b
2      2   d
pd.merge(df1,df2):    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

pd.merge是一种多对一的合并。df1中的数据有多个被标记为a和b的行,而df2中key列的每个值有且只对应一行,在这种没有指定的情况下,merge将会把重叠列的列名当做键值来重组。对于此情况最好显示指定。

in:

print("pd.merge(df1,df2,on = 'key'):",pd.merge(df1,df2,on = 'key'))

out:

pd.merge(df1,df2,on = 'key'):    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

merge函数的参数

参数

说明

left

参与合并的左侧DataFrame

right

参与合并的右侧DataFrame

how

"inner"、“outer、“left”、“right”其中之一。默认为"inner"(键的交集)

on

用于连接的列名。必须存在于两个DataFrame对象中。如果为指定,且其他连接键也为指定,则以left和right列名的交集做为连接键

left_on

左侧DataFrame中用作连接键的列

right_on

右侧DataFrame中作用连接键的列

left_index

将左侧的行索引作用其连接键

right_index

类似于left_index

sort

根据连接键对合并后的数据进行排序,默认为True。

suffixes

字符串值元组,用于追加到重叠列名的末尾,默认为('_x','_y')

copy

设置为False,可以在某些特殊的情况下避免将数据复制到结果数据结构中


索引上的合并


DataFrame的连接键有时候存在于其索引之中。DataFrame一般运用join的方法,能方便的实现按索引合并。而且它可以用于合并多个带有相同或相似索引的DataFrame对象,且不管它们之间有没有重叠的列。

in:

df1  = pd.DataFrame(np.arange(9).reshape(3,3),columns = ["one","two","three"])

df2  = pd.DataFrame(np.arange(6).reshape(3,2),columns = ["four","five"])

print("df1.join(df2):",df1.join(df2))

out:

df1.join(df2):    one  two  three  four  five
               0    0    1      2     0     1
               1    3    4      5     2     3
               2    6    7      8     4     5


轴向连接


pandas的concat函数提供了一种能够解决这些问题的可靠方式。通过concat来对这些值或者是索引进行连接,只不过concat可以通过函数设置来决定从何轴向来连接函数。

in:

df1  = pd.DataFrame(np.arange(9).reshape(3,3),columns = ["one","two","three"])

df2  = pd.DataFrame(6+np.arange(6).reshape(3,2),columns = ["four","five"])

print("pd.concat([df1,df2],axis = 1):",pd.concat([df1,df2],axis = 1))

out:

pd.concat([df1,df2],axis = 1):    one  two  three  four  five
                               0    0    1      2     6     7
                               1    3    4      5     8     9
                               2    6    7      8    10    11

此外concat还可以进行层次化索引创建方式的参数

in:

df1  = pd.DataFrame(np.arange(9).reshape(3,3),columns = ["one","two","three"])

df2  = pd.DataFrame(6+np.arange(6).reshape(3,2),columns = ["four","five"])

print("pd.concat([df1,df2],axis = 1,keys= ['Level1','Level2'],names = ['upper','lower']):",pd.concat([df1,df2],axis = 1,keys= ['Level1','Level2'],names = ['upper','lower']))

out:

pd.concat([df1,df2],axis = 1,keys= ['Level1','Level2'],names = ['upper','lower']): upper Level1           Level2     
                                                                                   lower    one two three   four five
                                                                                   0          0   1     2      6    7
                                                                                   1          3   4     5      8    9
                                                                                   2          6   7     8     10   11


concat函数的参数

参数

说明

objs

参与连接的pandas对的列表或字典。唯一的必须的参数

axis

指明连接的轴向,默认为0

join

“inner”、“outer”,默认为outer

join_axes

指明用于其他n-1条轴的索引,不执行并集/交集运算

keys

与连接对象有关的值,用于形成连接轴向上的层次化

索引。可以是任意值的列表或数组、元组数组、数组列表

levels

指定用作层次化索引各级别上的索引需keys为前提

names

用于创建分层级别的名称,且需keys和levels

verify_integrity

检查结果对象新轴上的重复情况,如果发现则引发异常。默认False允许重复

ignore_index

不保留连接轴上的索引,产生一组新索引range(total_length)



数据转换


移除重复数据


在DataFrame中经常会出现重复行。在DataFrame中的duplicated方法返回一个布尔型的Series来表示各行是否重复,于此相关的还有个drop_duplicated方法,它用于返回一个移除后重复行的DataFrame,且这两个方法都会默认判断全部列,也可以由用户自定义部分列进行重复项判断。

in:

data = pd.DataFrame({'k1':['one']*3+['two']*4,'k2':[1,1,2,2,3,4,4]})

print("data:",data)
print("data.duplicated():",data.duplicated())
print("data.drop_duplicated():",data.drop_duplicates())
print("data.drop_duplicated(['k2']):",data.drop_duplicates(['k2']))

out:

data:     k1  k2
0  one   1
1  one   1
2  one   2
3  two   2
4  two   3
5  two   4
6  two   4
data.duplicated(): 0    False
1     True
2    False
3    False
4    False
5    False
6     True
dtype: bool
data.drop_duplicated():     k1  k2
0  one   1
2  one   2
3  two   2
4  two   3
5  two   4
data.drop_duplicated(['k2']):     k1  k2
0  one   1
2  one   2
4  two   3
5  two   4

利用函数映射进行数据替换


在对数据集进行转换时,都会希望根据数组、Series和DataFrame列中的值来实现转换工作,Series的map方法可以接受函数或含有映射关系的字典型对象,使用map是一种实现元素级转换以及其他数据清理工作的便捷方式

in:

data  = pd.DataFrame({'food':['bacon','pulled pork','bacon','Pastrami','corned beef','Bacon','pastrami','honey ham','nova lox'],
                      'ounces':[4,3,12,6,7.5,8,3,5,6]})

print("data:",data)

meat_anl = {'bacon':'pig',
            'pulled pork':'pig',
            'pastrami':'cow',
            'corned beef':'cow',
            'honey ham':'pig',
            'nova lox':'salmon'}

data['animal'] =  data['food'].map(lambda x:meat_anl[x.lower()])

print("data:",data)

out:

data:           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
data:           food  ounces  animal
0        bacon     4.0     pig
1  pulled pork     3.0     pig
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


替换值


利用fillna可以对NaN值进行填补,算是值替换的一种特殊情况。且map可以用于修改对象的数组子集,而replace则提供了一种实现该功能更简单、更灵活的方式。

in:

data = pd.Series([-1,999,0,999,223])

data = data.replace(999,np.nan)

print("data:",data)

out:

data: 0     -1.0
1      NaN
2      0.0
3      NaN
4    223.0

DataFrame.str.replace("v1","V2")可以进行正则表达式的替换,其方式是套不犯法正则匹配的方式进行模糊搜索且将搜索到的值进行替换



离散化和面元划分


在分析时,为了方便分析连续的数据,我们常常将离散化或拆分为'面元'(bin),通过这个面元的划分来将数据进行划分成所设置的面元

in:

ages = [20,22,25,27,33,35,37]

bins = [18,27,35,60,100]

cats = pd.cut(ages,bins)

print('cats:',cats)

group_names = ['Younge','YoungAdult','MiddleAged','Senior']

cats = pd.cut(ages,bins,labels = group_names)

print("cats:",cats)

out:

cats: [(18, 27], (18, 27], (18, 27], (18, 27], (27, 35], (27, 35], (35, 60]]
Categories (4, interval[int64]): [(18, 27] < (27, 35] < (35, 60] < (60, 100]]
cats: [Younge, Younge, Younge, Younge, YoungAdult, YoungAdult, MiddleAged]
Categories (4, object): [Younge < YoungAdult < MiddleAged < Senior]


qcut是一个类似于cut的函数,它可以根据样本分位数对数据进行面元划分,根据数据分布情况,cut无法使各面元中含有相同数量的数据点。而qcut使用的是样本分位数,可以得到大小基本相等的面元


in:

data = np.random.randn(100)

cats = pd.qcut(data,4)

print("cats:",cats,"\npd.value_counts(cats):",pd.value_counts(cats))

out:

cats: [(-0.45, 0.155], (0.155, 0.558], (-0.45, 0.155], (-2.198, -0.45], (-0.45, 0.155], ..., (0.558, 2.227], (-0.45, 0.155], (0.558, 2.227], (-2.198, -0.45], (-0.45, 0.155]]
Length: 100
Categories (4, interval[float64]): [(-2.198, -0.45] < (-0.45, 0.155] < (0.155, 0.558] < (0.558, 2.227]] 
pd.value_counts(cats): (0.558, 2.227]     25
(0.155, 0.558]     25
(-0.45, 0.155]     25
(-2.198, -0.45]    25
dtype: int64


排列和随机采样


使用numpy.random.permutation函数可以轻松实现对Series或DataFrame的列的排列工作。通过需要排列的轴长度调用permutation可以产生一个新顺序的整数数组

in:

df = pd.DataFrame(np.arange(5*4).reshape(5,4))

sample = np.random.permutation(len(df))[:2]#选取两行(随机采样)

print('data:',df.ix[sample,:])

out:

data:     0   1   2   3
3  12  13  14  15
1   4   5   6   7


计算指标/哑变量



一种常用于统计建模或机器学习的转换方式是:将分类变量转换为'哑变量矩阵'(dummy matrix)或‘指标矩阵’。如果DataFrame的某一列中含有k个不同的值,则可以派生出一个k列矩阵或DataFrame。pandas中的get_dummies函数可以实现该功能

in:

df = pd.DataFrame({'key':['b','b','a','c','a','b'],
                   'data1':range(6)})

df1 = df[['data1']].join(pd.get_dummies(df['key']),how = "outer")

print(df1)

out:

data1  a  b  c
0      0  0  1  0
1      1  0  1  0
2      2  1  0  0
3      3  0  0  1
4      4  1  0  0
5      5  0  1  0

对于统计学的应用来说(逻辑回归、分类模型)可以通过dummies和cut混合使用来创建哑变量矩阵来反应特征值的分布和变化


in:

values = np.random.rand(10)

bins = [0,.2,.4,.6,.8,1]

value = pd.get_dummies(pd.cut(values,bins))

print("value:",value)

out:

value:    (0.0, 0.2]  (0.2, 0.4]  (0.4, 0.6]  (0.6, 0.8]  (0.8, 1.0]
0           0           0           0           0           1
1           1           0           0           0           0
2           0           0           0           0           1
3           0           0           0           0           1
4           0           0           0           0           1
5           0           1           0           0           0
6           0           0           0           0           1
7           0           0           0           1           0
8           1           0           0           0           0
9           0           0           0           1           0