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

合并数据集

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

pandas.merge根据一个或多个键将不同DataFrame的行连接起来;
它实现数据库的连接操作

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

实例方法combine_first可以将重复数据编接在一起,用一个对象中的值填充另一个对象中的缺失值

数据库风格的DataFrame合并

pd.merge(df1, df2, on='key') #这里是列名相同,指定'key'列当做键的内连接(对键取交集)
当两个列名不同时,需要分别指定:
pd.merge(df3, df4, left_on='key1', right_on='key2')

如果是左连接\右连接\外连接(取并集),也需要分别指定
how = 'left'\'right'\'outer'

要根据多个键进行合并,传入一个由列名组成的列表即可
on=['key1', 'key2']

警告:在进行列-列连接时,DataFrame对象中的索引会被丢弃

重复列名的处理(如何重命名轴标签)
merge有一个suffixes选项,可以指定附加到左右连个DataFrame对象的重叠列名的字符串
suffixes =('_left', '_right') #这里就是分别添加字符串到重叠列名后面,可自定义(默认为_x, _y)

表7-1: merge函数的参数

索引上的合并

连接键位于索引中,可以传入left_index=True\right_index=True
pd.merge(df1, df2, left_on='key', right_index=True)

对于层次化索引的数据,必须以列表的形式指明用作合并键的多个列(注意对重复索引值的处理)
pd.merge(lefth,righth,left_on=['key1','key2'],right_index=True)

DataFrame的join实例方法,能更为方便的实现按索引合并
left2.join(right2, how='outer')
对于简单的索引合并,还可以向join传入一组DataFrame(后面会介绍更为通用的concat函数)
left2.join([right2,another],how='outer')

轴向连接

另一种数据合并运算被称作连接(concatenation)\绑定(binding)或堆叠(stacking).
NumPy有一个用于合并原始NumPy数组的concatenation函数
np.concatenate([arr,arr],axis=1) #如果没有axis=1,会从上往下拼接\否则从左到右拼接

对于pandas对象,需要考虑:
1. 如果各对象其他轴上的索引不同,那些轴应该是做并集还是交集
2. 结果对象中的分组需要各不相同吗
3. 用于连接的轴重要

对于Series:
pd.concat([s1,s2,s3]) #默认是在axis=0上工作,产生新的Series(从上往下拼接);
如果传入axis=1,结果变成一个DataFrame;
默认是外连接,传入join='inner'可得到交集(内连接)
pd.concat([s1,s4],axis=1,join='inner')
可以通过join_axes指定要在其他轴上使用的索引
pd.concat([s1,s4],axis=1,join_axes=[['a','c','b','e']])
如果想创建层次化索引,将参与连接的片段在结果中区分开,使用keys参数
result=pd.concat([s1,s1,s3],keys=['one','two','three'])

同样的逻辑对于DataFrame对象也是一样
pd.concat([df1,df2],axis=1,keys=['level1','level2'])
pd.concat({'level1':df1,'level2':df2},axis=1) #这两个表达式效果是一样的

可以传入ignore_index=True,不保留连接轴上的索引,产生一组新索引range(total_length)

表7-2: concat函数的参数

合并重叠数据

np.where(pd.isnull(a),b,a) #NumPy的where函数,pd.isnull(a)表示判断条件,若为True,返回b\False则返回a
Series的combine_first方法实现同样功能
b.combine_first(a) #可以对a\b进行切片;这里表示若b不为空返回b,否则返回a

对于DataFrame,combine_first也是做同样的事情,可以看做用参数对象(a)中的数据为调用者对象(b)缺失数据'打补丁'
df1.combine_first(df2)

重塑和轴向旋转

有许多用于重新排列表格型数据的基础运算,这些函数成为重塑(reshape)或轴向旋转(pivot)运算

重塑层次化索引

层次化索引为DataFrame数据的重排提供一种良好的一致性方式
1. stack: 将数据的列'转换'为行
2. unstack: 将数据的行'转换'为列

如果不是所有的级别值都能在各分组中找到,unstack操作可能引入缺失数据

stack会默认滤除缺失数据,可以传入dropna=False选择不滤除缺失数据

将「长格式」旋转为「宽格式」

pivoted=ldata.pivot('date','item','value')
前两个参数值分别作为行和列索引的列名,最后一个参数用于填充DataFrame的数据列的列名

假设有两个需要参与重塑的数据列,如果忽略最后一个参数,得到的DataFrame就会带有层次化的索引

注意,pivot其实只是一个快捷方式,用set_index创建层次化索引,再用unstack重塑

数据转换

过滤、清理以及其他的转换

移除重复数据

data.duplicated()
返回一个布尔型Series,表示各行是否有重复行

data.drop_duplicates()
可以传入根据某一列来过滤: data.drop_duplicates(['k1'])
默认保留第一个出现值组合,传入take_last=True,则保留最后一个

利用函数或映射进行数据转换

data=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]})

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

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

data['food'].map(lambda x:meat_to_animal[x.lower()])

替换值

利用fillna方法填充缺失数据可以看做值替换的一种特殊情况

data=Series([1.,-999.,2.,-999.,-1000.,3.])

data.replace(-999,np.nan)

data.replace([-999,-1000],np.nan)
替换多个值

data.replace([-999,-1000],[np.nan,0])
可以是列表形式

data.replace({-999:np.nan,-1000:0})
可以是字典形式

重命名轴索引

In [9]: data.index.map(str.upper)
Out[9]: array(['OHIO', 'COLORADO', 'NEW YORK'], dtype=object)
返回值为array类型
也可以直接修改data = ***In[9]***

In [28]: data.rename(index=str.title, columns=str.upper)
Out[28]:
ONE  TWO  THREE  FOUR
Indiana    0    1      2    3
Colorado    4    5      6    7
New York    8    9    10    11
修改columns标签值,返回Dataframe类型

特别说明,rename可以结合字典型对象实现对部分轴标签的更新
In [31]: data.rename(index={'INDIANA':'Los Angeles'},columns={'four':'peekaboo'})
如果想直接修改,可以传入inplace=True

离散化和面元划分

为了便于分析,连续数据常常被离散化或拆分为「面元」(bin),比如年龄组的划分.

ages = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32]
bins = [18, 25, 35, 60, 100]
cats = pd.cut(ages, bins)
cats
pandas返回一个特殊的Categorical对象,包含不同分类名称的categories数组和一个为年龄数据进行标号的labels属性
cats.labels
array([0, 0, 0, 1, 0, 0, 2, 1, 3, 2, 2, 1], dtype=int8)
In [45]: cats.categories(书中是levels,py3不包含该方法)
Out[45]: Index(['(18, 25]', '(25, 35]', '(35, 60]', '(60, 100]'], dtype='object')
In [50]: pd.value_counts(cats)
Out[50]:
(18, 25]    5
(35, 60]    3
(25, 35]    3
(60, 100]    1
dtype: int64

区间默认是左开右闭,传入right = False表示左闭右开

可以设置面元名称,将labels选项设置为一个列表或数组即可
即在cut函数中,传入labels=***列表或元祖***

pd.cut(data,4,precision=2)
表示将data计算4个等长面元,precision表示小数点精确度

qcut可以根据样本分位数对数据进行划分
也可以设置自定义的分位数,传入0到1的数值列表即可,比如[0, 0.1 ,0.5, 0.9, 1]

检测和过滤异常值(孤立点或离群值outlier)

data = DataFrame(np.random.randn(1000, 4))

col = data[3]
col[np.abs(col) > 3]
找出某列中绝对值超过3的值

data[(np.abs(data)>3).any(1)]
找出全部绝对值超过3的(存在超过3的数值的行即返回)

data[np.abs(data)>3]=np.sign(data)*3
将最大最小值改为3和-3
其中np.sign返回一个由1和-3组成的数组

排列和随机采样

numpy.random.permutation函数可以返回一个新顺序的数组
比如***permutation***(5)返回的是0,1,2,3,4的随机顺序的数组

然后就可以基于ix的索引操作或take函数使用该数组对DataFrame进行排序
df = DataFrame(np.arange(5 * 4).reshape((5, 4)))
sampler = np.random.permutation(5)
df.take(sampler)

选取随机子集
df.take(np.random.permutation(len(df))[:3])黑体可以用上文的sampler代替

计算指标/哑变量

常用于统计建模或机器学习的转换方式:
分类变量(categorical variable)转换为哑变量矩阵(dummy matrix)指标矩阵(indicator matrix)

如果DataFrame的某一列中含有k个不同的值,则可以派生出一个k列矩阵或DataFrame,其值全为1或0

df = DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'b'],'data1': range(6)})
pd.get_dummies(df['key'])
data1这一列没有重复的,将key这一列的所有元素展开到column,0和1为布尔值

加前缀
dummies=pd.get_dummies(df['key'],prefix='key')
df_with_dummy=df[['data1']].join(dummies)
df_with_dummy

将get_dummies和诸如cut之类的离散化结合起来使用
values=np.random.rand(10)
values
bins=[0,0.2,0.4,0.6,0.8,1]
pd.get_dummies(pd.cut(values,bins))

(0, 0.2]  (0.2, 0.4]  (0.4, 0.6]  (0.6, 0.8]  (0.8, 1]

0        0          0          0          0        1

1        0          1          0          0        0

2        1          0          0          0        0

3        0          1          0          0        0

4        0          0          1          0        0

5        0          0          1          0        0

6        0          0          0          0        1

7        0          0          0          1        0

8        0          0          0          1        0

9        0          0          0          1        0

返回的结果为某一栏在第几个Categorial array中出现

字符串操作

字符串对象方法

表7-3
包含count\join等等

正则表达式

pandas中矢量化的字符串函数

清理散乱数据时,需要做一些字符串的规整化工作(比如处理缺失数据)
data.map,所有字符串和正则表达式方法能被用于各个值(传入lambda表达式或其他函数),但是遇到NA就回报错;
Series的str属性可以访问这些方法跳过NA值

data.str.contains('gmail') 返回布尔值True False

有两个办法可以实现矢量化的元素获取操作:str.get或者str属性上使用索引

示例: USDA食品数据库

推荐阅读更多精彩内容