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

数据分析和建模方面的大量编程工作都是用在数据准备上的:加载、清理、转换以及重塑。
许多人选择使用通用编程语言或unix文本处理工具(如sed或awk)对数据格式进行专门处理。

幸运的是,pandas和python标准库提供了一组高级的、灵活的、高效的核心函数和算法,将数据规整化正确的形式。

1. 合并数据集:

pandas对象中的数据可以通过一些内置的方式进行合并:
(1)pandas.merge可根据一个名多个键将不同DataFrame中的行连接起来。sql关系型数据库也有merge法,它是用merge实现的就是数据库的连接操作(inner内连接交集,outer外连接,左连接,右连接=>并集)。
(2)pandas.concat可以沿着一条轴将多个对象堆叠到一起。

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


下面分别讲解这些方法:

数据库风格的DataFrame合并:

数据集的合并(merge)或连接(join)运算是通过一个或多个键将行连接起来的。这些运算是关系型数据库的核心。

pandas的merge函数是对数据应用这些算法的主要切入点。

(1)merge的用法

import pandas as pd
import numpy as np
from pandas import Series,DataFrame

df1=DataFrame({'key':['b','b','a','c','a','a','b'],
               'data1':range(7)})
df2=DataFrame({'key':['a','b','d'],
               'data2':range(3)})
print df1
#输出结果如下:
#    data1 key
# 0      0   b
# 1      1   b
# 2      2   a
# 3      3   c
# 4      4   a
# 5      5   a
# 6      6   b
# print df2
# #输出结果如下:
#    data2 key
# 0      0   a
# 1      1   b
# 2      2   d
(1)这是一种多对一的合并:df1中的数据有多个被标记为a和b行,而df2中key列的每个值则仅对应一行。
    这些对象调用merge即可得到
print 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
# #注意:上面的例子并没有指明要用哪个列进行连接。如果没有指定,merge就会将重叠列的列名当做键。
(2)下面指定列:用能数on=
# print pd.merge(df1,df2,on='key') #输出结果与上面一样


(3)如果两个对象的列名不同,也可以分别进行指定:left_on,right_on
df3=DataFrame({'lkey':['b','b','a','c','a','a','b'],
               'data1':range(7)})
df4=DataFrame({'rkey':['a','b','d'],
               'data2':range(3)})
print pd.merge(df3,df4,left_on='lkey',right_on='rkey')
#输出结果如下:
#    data1 lkey  data2 rkey
# 0      0    b      1    b
# 1      1    b      1    b
# 2      6    b      1    b
# 3      2    a      0    a
# 4      4    a      0    a
# 5      5    a      0    a
#
#注意:结果里面c和d以及与之相关的数据消失了。默认情况下,merge做的是"inner"连接:结果中的键是交集。
#     而其它外连接left,right,outer求取的是键的并集,组合了左连接和右连接的效果。
(4) outer外连接:并集,两个数据Key都要
# print pd.merge(df1,df2,how='outer')  #外连接,两边的key都要传入
# #输出结果如下:并集,df1,df2所有的值都放入
# #    data1 key  data2
# # 0    0.0   b    1.0
# # 1    1.0   b    1.0
# # 2    6.0   b    1.0
# # 3    2.0   a    0.0
# # 4    4.0   a    0.0
# # 5    5.0   a    0.0
# # 6    3.0   c    NaN
# # 7    NaN   d    2.0

(5) left左连接:并集,左连接是以左边的为主
# print pd.merge(df1,df2,on='key',how='left') #左连接
# #输出结果如下:  左连接是以左边的为主,即df1为主,顾只有df1的key
# #    data1 key  data2
# # 0      0   b    1.0
# # 1      1   b    1.0
# # 2      2   a    0.0
# # 3      3   c    NaN
# # 4      4   a    0.0
# # 5      5   a    0.0
# # 6      6   b    1.0

(6)right右连接:并集,右连接是以右边的为主
# print pd.merge(df1,df2,how='right')  #右连接
# #输出结果如下:右连接,以df2的为主,顾只有df2的key,其它的值是全部写入
# #    data1 key  data2
# # 0    0.0   b      1
# # 1    1.0   b      1
# # 2    6.0   b      1
# # 3    2.0   a      0
# # 4    4.0   a      0
# # 5    5.0   a      0
# # 6    NaN   d      2

(7)inner内连接:交集,默认就是内连接
# print pd.merge(df1,df2,how='inner') #即merge默认情况下是inner,inner是交集
# #输出结果如下:取df1,df2都有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

(8)若多个键要进行合并,传入一个由列名组成的列表即可
left=DataFrame({'key1':['foo','foo','bar'],
                'key2':['one','two','one'],
                'lval':[1,2,3]})
right=DataFrame({'key1':['foo','foo','bar','bar'],
                 'key2':['one','one','one','two'],
                 'rval':[4,5,6,7]})
print left
#输出结果如下:
#   key1 key2  lval
# 0  foo  one     1
# 1  foo  two     2
# 2  bar  one     3
print right
#输出结果如下:
#   key1 key2  rval
# 0  foo  one     4
# 1  foo  one     5
# 2  bar  one     6
# 3  bar  two     7
print pd.merge(left,right,on=['key1','key2'],how='outer')
#输出结果如下:看key1,key2两个键对应的lval的值。外连接,即所有的key都要。
#right中的key组合[foo,one],[foo,one],[foo,two]...用right的key看left有没有。
#   key1 key2  lval  rval
# 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

(9)合并运算需要考虑对重复列名的处理,merge有一个实用的suffixes选项,用于指定附加到左右两个DataFrame对象的重叠列名上的字符串。
print pd.merge(left,right,on='key1') #只用单个key,它就有单个key1分别和left,right的key2的组合
#输出结果如下:
#   key1 key2_x  lval key2_y  rval
# 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
print pd.merge(left,right,on='key1',suffixes=('_left','_right'))
#输出结果如下:
#   key1 key2_left  lval key2_right  rval
# 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

merger函数的参数
参数 说明
left 参与合并的左侧DataFrame
right 参与合并的右侧DataFrame
how “inner”、“outer”、“left”、“right”其中之一。默认为"inner"(交集),其它为并集
on 用于连接的列名。必须存在于左右两个DataFrame对象中。如果未指定,且其它连接键也未指定,由以
left和right列名的交集作为连接键
left_on 左侧DataFrame中用作连接键的列
right_on 右侧DataFrame中用作连接键的列
left_index 将左侧的行索引用作其连接键
righgt_index 将右侧的行索引用作其连接键
sort 根据连接键对合并后的数据进行排序,默认为True.有时处理大数据集时,禁用该选项可获得更好的性能
suffixes 字符串值元组,用于追加到重叠列名的末尾,默认为('_x','_y').
copy 设置为False,可以在某些特殊情况下避免将数据复制到结果数据结构中。默认总是复制。


索引上DataFrame的合并:

DataFrame中的连接键位于其索引中。在这种情况下,可以传入left_index=True或right_index=True(或两个都传)
以说明索引应该被用作连接键。

(1)merge的用法:索引上合并用left_index=True、right_index=True

import pandas as pd
import numpy as np
from pandas import Series,DataFrame


(1)DataFrame中的连接键位于其索引中:left_index=True或right_index=True(或两个都传)来说明索引应该被用作连接键。
left1=DataFrame({'key':['a','b','a','a','b','c'],
                 'value':range(6)})
right1=DataFrame({'group_val':[3.5,7]},index=['a','b'])
print left1
#输出结果如下:
#   key  value
# 0   a      0
# 1   b      1
# 2   a      2
# 3   a      3
# 4   b      4
# 5   c      5
print right1
#输出结果如下:
#    group_val
# a        3.5
# b        7.0
print pd.merge(left1,right1,left_on='key',right_index=True)
#输出结果如下:
#   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

(2)由于默认的merge方法是求取连接键的交集,因此可以通过外连接的方式得到它们的并集。how='outer'
print pd.merge(left1,right1,left_on='key',right_index=True,how='outer')
#输出结果如下:
#   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

(3)层次化索引,多层行/列索引
lefth=DataFrame({'key1':['Ohio','Ohio','Ohio','Nevada','Nevada'],
                 'key2':[2000,2001,2002,2001,2002],
                 'data':np.arange(5.)})
righth=DataFrame(np.arange(12).reshape(6,2),
                 index=[['Nevada','Nevada','Ohio','Ohio','Ohio','Ohio'],
                 [2001,2000,2000,2000,2001,2002]],
                 columns=['event1','event2'])
print lefth
#输出结果如下:
#    data    key1  key2
# 0   0.0    Ohio  2000
# 1   1.0    Ohio  2001
# 2   2.0    Ohio  2002
# 3   3.0  Nevada  2001
# 4   4.0  Nevada  2002
print righth
#输出结果如下:
#              event1  event2
# Nevada 2001       0       1
#        2000       2       3
# Ohio   2000       4       5
#        2000       6       7
#        2001       8       9
#        2002      10      11
#必须以列表的形式指明用作合并键的多个列(注意对重复索引值的处理)
#left_on左侧DataFrame作为连接键,即lefth,而索引是right_index,即右侧数据为行索引即righth的行索引为行索引
print pd.merge(lefth,righth,left_on=['key1','key2'],right_index=True)
#没有how参数,默认是inner连接,即交集,取lefth的['key1','key2']的数据与righth的行有交集,即相同的取出
#输出结果如下:
#    data    key1  key2  event1  event2
# 0   0.0    Ohio  2000       4       5
# 0   0.0    Ohio  2000       6       7
# 1   1.0    Ohio  2001       8       9
# 2   2.0    Ohio  2002      10      11
# 3   3.0  Nevada  2001       0       1

#(4)外连接:how='outer' 并集
print pd.merge(lefth,righth,left_on=['key1','key2'],right_index=True,how='outer')
#输出结果如下:
#    data    key1  key2  event1  event2
# 0   0.0    Ohio  2000     4.0     5.0
# 0   0.0    Ohio  2000     6.0     7.0
# 1   1.0    Ohio  2001     8.0     9.0
# 2   2.0    Ohio  2002    10.0    11.0
# 3   3.0  Nevada  2001     0.0     1.0
# 4   4.0  Nevada  2002     NaN     NaN
# 4   NaN  Nevada  2000     2.0     3.0

#(5)用merge同时使用合并双方的索引
left2=DataFrame([[1.,2.],[3.,4.],[5.,6.]],index=['a','c','e'],
                columns=['Ohio','Nevada'])
right2=DataFrame([[7.,8.],[9.,10.],[11.,12.],[13,14]],
                 index=['b','c','d','e'],columns=['Missouri','Alabama'])
print left2
#输出结果如下:
#    Ohio  Nevada
# a   1.0     2.0
# c   3.0     4.0
# e   5.0     6.0
print right2
#输出结果如下:
#    Missouri  Alabama
# b       7.0      8.0
# c       9.0     10.0
# d      11.0     12.0
# e      13.0     14.0
print pd.merge(left2,right2,how='outer',left_index=True,right_index=True)
#同时使用了left_index,right_index同时合并双方索引,且outer外连接交集
#输出结果如下:
#    Ohio  Nevada  Missouri  Alabama
# a   1.0     2.0       NaN      NaN
# b   NaN     NaN       7.0      8.0
# c   3.0     4.0       9.0     10.0
# d   NaN     NaN      11.0     12.0
# e   5.0     6.0      13.0     14.0

(2)join的用法:索引上合并用left_index=True、right_index=True

DataFrame的一个实例方法Join:更为方便地实现按索引合并。它还可以用于合并多个带有相同或相似索引的DataFrame对象,而不管它们之间有没有重叠的列。
#Join:在连接键上是做左连接
# (1)DataFrame的一个实例方法Join:更为方便地实现按索引合并。不管它们之间有没有重叠的列。
#上面的例子可以编写如下:
print left2.join(right2,how='outer')  #类似python的字符串连接Join
#输出结果如下:
#    Ohio  Nevada  Missouri  Alabama
# a   1.0     2.0       NaN      NaN
# b   NaN     NaN       7.0      8.0
# c   3.0     4.0       9.0     10.0
# d   NaN     NaN      11.0     12.0
# e   5.0     6.0      13.0     14.0

# (2)它还支持DataFrame的索引跟调用者DataFrame的某个列之间的连接(即第一个DataFrame的行索引与第二个DataFrame的列连接)
print left1.join(right1,on='key')
#输出结果如下:
#   key  value  group_val
# 0   a      0        3.5
# 1   b      1        7.0
# 2   a      2        3.5
# 3   a      3        3.5
# 4   b      4        7.0
# 5   c      5        NaN

#(3)可以向join传入一组DataFrame
another=DataFrame([[7.,8.],[9.,10.],[11.,12.],[16.,17.]],
                 index=['a','c','e','f'],columns=['New York','Oregon'])
print another
#输出结果如下:
#    New York  Oregon
# a       7.0     8.0
# c       9.0    10.0
# e      11.0    12.0
# f      16.0    17.0
print left2.join([right2,another]) #left2左分别连接right2,another
# left2内容如下:
# #    Ohio  Nevada
# # a   1.0     2.0
# # c   3.0     4.0
# # e   5.0     6.0
# right2内容如下:
# #    Missouri  Alabama
# # b       7.0      8.0
# # c       9.0     10.0
# # d      11.0     12.0
# # e      13.0     14.0
#left2.join([right2,another])输出结果如下:
#    Ohio  Nevada  Missouri  Alabama  New York  Oregon
# a   1.0     2.0       NaN      NaN       7.0     8.0
# c   3.0     4.0       9.0     10.0       9.0    10.0
# e   5.0     6.0      13.0     14.0      11.0    12.0

轴上连接:

另一种数据合并运算也被称作连接、绑定或堆叠。numpy有一个用于合并原始Numpy数组的concatenation函数。concatenate是Numpy的方法,

而concate是pandas的方法。

concatenate连接:
arr=np.arange(12).reshape((3,4))
print arr
#输出的结果如下:
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]
print np.concatenate([arr,arr],axis=1) #axis=1是列
#输出的结果如下:
# [[ 0  1  2  3  0  1  2  3]
#  [ 4  5  6  7  4  5  6  7]
#  [ 8  9 10 11  8  9 10 11]]
concate连接:
# 如果各对象其他轴上的索引不同,那些轴应该是做并集还是交集?
# 结果对象中的分组需要各不相同吗?
# 用于连接的轴axis重要吗?
concate用于解决上面的三个问题,它与concatenate没多大区别,只是多了一些参数
(1)三个没有重叠索引的Series,对这些对象调用concat可以将值和索引粘合在一起
s1=Series([0,1],index=['a','b'])
s2=Series([2,3,4],index=['c','d','e'])
s3=Series([5,6],index=['f','g'])
print s1
# a    0
# b    1
print s2
# c    2
# d    3
# e    4
print s3
# f    5
# g    6
print pd.concat([s1,s2,s3])
#输出结果如下:
# a    0
# b    1
# c    2
# d    3
# e    4
# f    5
# g    6

(2)默认情况下,concat是在axix=0行上作用的,最终产生一个新的Series.如果传入axis=1,则结果就会变成一个DataFrame(axis=1是列)
#series行向合并,本来series只有行索引,故产生一个新的Series.如果列合并,有几个数组,列不一定相同,所以会产生一个DataFrame
print pd.concat([s1,s2,s3],axis=1)
#输出结果如下:
#      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

(3)这种情况下,另外一条轴上没有重叠,从索引的有序并集上就可以看出。。传入join='inner'即可得到它们的交集
s4=pd.concat([s1*5,s3])
print pd.concat([s1,s4],axis=1)
#输出结果如下:
#      0  1
# a  0.0  0
# b  1.0  5
# f  NaN  5
# g  NaN  6
print pd.concat([s1,s4],axis=1,join='inner')
#输出结果如下:
#    0  1
# a  0  0
# b  1  5

(4)可以通过join_axes指字要在其他轴上使用的索引
print pd.concat([s1,s4],axis=1,join_axes=[['a','c','b','e']])
#输出结果如下:
#      0    1
# a  0.0  0.0
# c  NaN  NaN
# b  1.0  5.0
# e  NaN  NaN

(5)不过有个问题,参与连接的片段在结果中区分不开。假设你想要在连接轴上创建一个层次化索引。使用keys参数即可达到目的
result=pd.concat([s1,s1,s3],keys=['one','two','three'])
print result
#输出结果如下:
# one    a    0
#        b    1
# two    a    0
#        b    1
# three  f    5
#        g    6
# dtype: int64
print result.unstack()
#输出结果如下:
#          a    b    f    g
# one    0.0  1.0  NaN  NaN
# two    0.0  1.0  NaN  NaN
# three  NaN  NaN  5.0  6.0

(6)如果沿着axis=1对Series进行合并,则keys就会成为DataFrame的列头
print pd.concat([s1,s2,s3],axis=1,keys=['one','two','three'])
#输出结果如下:
#    one  two  three
# 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

(7)同样的逻辑对DataFrame对象也是一样:
df1=DataFrame(np.arange(6).reshape(3,2),index=['a','b','c'],
              columns=['one','two'])
df2=DataFrame(5+np.arange(4).reshape(2,2),index=['a','c'],
              columns=['three','four'])
print pd.concat([df1,df2],axis=1,keys=['level1','level2'])
#输出结果如下:
#   level1     level2
#      one two  three four
# a      0   1    5.0  6.0
# b      2   3    NaN  NaN
# c      4   5    7.0  8.0

(8)如果传入的不是列表而是一个字典,则字黄的键就会被做Keys选项的值。
print pd.concat({'level1':df1,'level2':df2},axis=1)
#   level1     level2
#      one two  three four
# a      0   1    5.0  6.0
# b      2   3    NaN  NaN
# c      4   5    7.0  8.0

(9)此外还有两个用于管理层次化索引创建方式的参数:
  a:key,names
print pd.concat([df1,df2],axis=1,keys=['level1','level2'],
                names=['upper','lower'])
#输出的结果如下:
# upper level1     level2
# lower    one two  three four
# a          0   1    5.0  6.0
# b          2   3    NaN  NaN
# c          4   5    7.0  8.0

  b:ignore_index=True
df1=DataFrame(np.random.randn(3,4),columns=['a','b','c','d'])
df2=DataFrame(np.random.randn(2,3),columns=['b','d','a'])
print df1
print df2
#在这种情况下,传入ignore_index=True即可:
print pd.concat([df1,df2],ignore_index=True)
#输出结果如下:
#           a         b         c         d
# 0 -0.540071 -0.225394 -0.851317 -1.638987
# 1  0.163700  1.375732  0.255257  1.290061
# 2  0.787074  0.137692  0.929145 -0.793582
# 3 -0.502263  0.717226       NaN -1.224583
# 4 -0.023067 -0.556030       NaN -0.819273
concat函数的参数
参数 说明
objs 参与连接的pandas对象的列表或字典。唯一必需的参数
axis 指明连接的轴向,默认为0
join “inner”、“outer”其中之一,默认为"outer".指明其他轴向上的索引是按交集(inner)
还是并集(outer)进行合并
join_axes 指明用于其他n-1条轴的索引,不执行并集/交集运算
keys 与连接对象有关的值,用于形成连接轴向上的层次化索引。可以是任意值的列表或数组、元组数组、
数组列表(如果将levels设置成多级数组的话)
levels 指定用作层次化索引各级别上的索引,如果设置了keys的话
names 用于创建分层级别的名称,如果设置了keys和levels的话
verify_integrity         检查结果对象新轴上的重复情况,如果发现则引发异常。默认(False)允许重复
ignore_index 不保留连接轴上的索引,产生一组新索引


合并重叠数据:numpy.where, pd.combine_first

还有一种数据组合问题不能用简单的合并(merge)或连接(concatenation)运算来处理。

比如,可能有索引全部或部分重叠的两个数据集。我们使用Numpy的where函数,它用于表达一种矢量化的if-else:

1)Numpy的where
import pandas as pd
import numpy as np
from pandas import Series,DataFrame

(1)Numpy的where
a=Series([np.nan,2.5,np.nan,3.5,4.5,np.nan],
         index=['f','e','d','c','b','a'])
b=Series(np.arange(len(a),dtype=np.float64),
         index=['f','e','d','c','b','a'])
b[-1]=np.nan
print a
#输出结果如下:
# f    NaN
# e    2.5
# d    NaN
# c    3.5
# b    4.5
# a    NaN
print b
#输出结果如下:
# f    0.0
# e    1.0
# d    2.0
# c    3.0
# b    4.0
# a    NaN
print np.where(pd.isnull(a),b,a) #a如果是Nan,则用b的数据代替,否则还是a的值
#输出结果如下:
# [ 0.   2.5  2.   3.5  4.5  nan]
(2)Series有一个combine_first方法,可以实现上面一样的功能,而且会时行数据对齐
print b[:-2].combine_first(a[2:]) #b[:-2]的值若有nan则用a[2:]的值代替。但是b[:-2]是不包括右边的倒数第二个b的,
# 则b的值用第一个4.5
#输出结果如下:
# a    NaN
# b    4.5
# c    3.0
# d    2.0
# e    1.0
# f    0.0
#Series的combine_first方法实例:DataFrame也有combine_first方法
s1=pd.Series([1,np.nan])
s2=pd.Series([3,4])
print s1
# 0    1.0
# 1    NaN
print s2
# 0    3
# 1    4
print s1.combine_first(s2) #s1里有nan就换成s2的值
# 0    1.0
# 1    4.0
3)DataFrame的combine_first也是同上,因此可以将其看做:用参数对象中的数据为调用者对象的缺失数据"打补丁"
df1=DataFrame({'a':[1.,np.nan,5.,np.nan],
               'b':[np.nan,2.,np.nan,6.],
               'c':range(2,18,4)})
df2=DataFrame({'a':[5.,4.,np.nan,3.,7.],
               'b':[np.nan,3.,4.,6.,8.]})
print df1
#输出结果如下:
#      a    b   c
# 0  1.0  NaN   2
# 1  NaN  2.0   6
# 2  5.0  NaN  10
# 3  NaN  6.0  14
print df2
#输出结果如下:
#      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
print df1.combine_first(df2) #用df2里的数据为df1中的数据为nan的数据打补丁
#输出结果如下:
#      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

2. 重塑和轴向旋转:

pandas对象中有许多用于重新排列表格型数据的基础运算。这些函数也称作重塑或轴向旋转运算。

(1)stack:将数据的列"旋转"为行。(将DataFrame转换成Series,而Series无stack方法)

(2)unstack:与stack相反的操作,将数据的行“旋转”为列。(将Series转换成DataFrame,DataFrame还是DataFrame)

 

重塑层次化索引:

层次化索引为DataFrame数据的重排任务提供了一个具有良好一致性的方式。主要功能有以下二个方法:

层次化索引为DataFrame数据的
stack:将数据的列"旋转"为行,将DataFrame转换成Series,Series无stack()方法
unstack:将数据的行"旋转"为列,将Series转换成DataFrame了,DataFrame还是DataFrame
(1)将DataFrame的列旋转为行:DataFrame.stack()
data=DataFrame(np.arange(6).reshape((2,3)),
               index=pd.Index(['Ohio','Colorado'],name='state'),
               columns=pd.Index(['one','two','three'],name='number'))
print data
#输出结果如下:
# number    one  two  three
# state
# Ohio        0    1      2
# Colorado    3    4      5
result=data.stack()
print result
# #输出结果如下:
# # state     number
# # Ohio      one       0
# #           two       1
# #           three     2
# # Colorado  one       3
# #           two       4
# #           three     5

 (2)对于一个层次化索引的Series,可以用unstack将其重排为一个DataFrame.
print result.unstack()
#输出结果如下:
# number    one  two  three
# state
# Ohio        0    1      2
# Colorado    3    4      5
默认情况下,unstack操作的是最内层(stack也是如此)。传入分层级别的编号或名称即可对其他级别进行unstack操作
print result.unstack(0)
#输出结果如下:
# state   Ohio  Colorado
# number
# one        0         3
# two        1         4
# three      2         5
print result.unstack('state')
#输出结果如下:
# state   Ohio  Colorado
# number
# one        0         3
# two        1         4
# three      2         5

(3)如果不是所有级别值都能在各分组中找到的话,则unstack操作可能会引入缺失数据:
s1=Series([0,1,2,3],index=['a','b','c','d'])
s2=Series([4,5,6],index=['c','d','e'])
data2=pd.concat([s1,s2],keys=['one','two'])
print data2
#输出结果如下:
# one  a    0
#      b    1
#      c    2
#      d    3
# two  c    4
#      d    5
#      e    6
# dtype: int64
print data2.unstack()
#输出结果如下:
#        a    b    c    d    e
# one  0.0  1.0  2.0  3.0  NaN
# two  NaN  NaN  4.0  5.0  6.0

(4)stack默认会滤除缺失数据,因此该运算是可逆的:
# print data2.stack() #报错'Series' object has no attribute 'stack'
#可以对data2.unstack()后的DataFrame进行stack()
print data2.unstack().stack(dropna=False) #DataFrame转换成Series了,且列变成行了
#输出结果如下:
# 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
print data2.unstack().unstack() #data2.unstack()的行变成列
#输出结果如下:
# a  one    0.0
#    two    NaN
# b  one    1.0
#    two    NaN
# c  one    2.0
#    two    4.0
# d  one    3.0
#    two    5.0
# e  one    NaN
#    two    6.0

(5)对DataFrame进行unstack操作时,作为旋转轴的级别将会成为结果中的最低级别
df=DataFrame({'left':result,'right':result+5},
             columns=pd.Index(['left','right'],name='side'))
print df
#result的内容如下:
# # state     number
# # Ohio      one       0
# #           two       1
# #           three     2
# # Colorado  one       3
# #           two       4
# #           three     5
#df的结果如下:
# side             left  right
# state    number
# Ohio     one        0      5
#          two        1      6
#          three      2      7
# Colorado one        3      8
#          two        4      9
#          three      5     10
print df.unstack('state') #将state的行旋转为列
#输出结果如下:
# side   left          right
# state  Ohio Colorado  Ohio Colorado
# number
# one       0        3     5        8
# two       1        4     6        9
# three     2        5     7       10
print df.unstack('state').stack('side') #将上面的再stack将列为side的旋转为行(即left,right)
#输出结果如下:
# state         Colorado  Ohio
# number side
# one    left          3     0
#        right         8     5
# two    left          4     1
#        right         9     6
# three  left          5     2
#        right        10     7

将"长格式"转换为"宽格式":

时间序列数据通常是以所谓的"长格式"(long)或"堆叠格式"(stacked)存储在数据库和CSV中的。

关系型数据库通常是主键形式提供了关系的完整性,而且提供了更为简单的查询支持。但对于长格式的数据操作起来就不那么轻松了。

而DataFrame可以把主键中不同的Item值分别形成一列,date列中的时间值则用作索引,可以用DataFrame的pivot方法完全可以实现这个转换。

import pandas as pd
import numpy as np
from pandas import Series,DataFrame

ldata=DataFrame({'date':['1959-03-31 00:00:00','1959-03-31 00:00:00','1959-03-31 00:00:00',
                         '1959-06-30 00:00:00','1959-06-30 00:00:00','1959-06-30 00:00:00',
                         '1959-09-30 00:00:00','1959-09-30 00:00:00','1959-09-30 00:00:00','1959-12-31 00:00:00'],
                 'item':['realgdp','infl','unemp','realgdp','infl','unemp','realgdp','infl','unemp','realgdp'],
                 'value':['2710.349','0.000','5.800','2778.801','2.340','5.100','2775.488','2.740','5.300','2785.204']

})
print ldata
#输出结果如下:
#                   date     item     value
# 0  1959-03-31 00:00:00  realgdp  2710.349
# 1  1959-03-31 00:00:00     infl     0.000
# 2  1959-03-31 00:00:00    unemp     5.800
# 3  1959-06-30 00:00:00  realgdp  2778.801
# 4  1959-06-30 00:00:00     infl     2.340
# 5  1959-06-30 00:00:00    unemp     5.100
# 6  1959-09-30 00:00:00  realgdp  2775.488
# 7  1959-09-30 00:00:00     infl     2.740
# 8  1959-09-30 00:00:00    unemp     5.300
# 9  1959-12-31 00:00:00  realgdp  2785.204

(1)使用DataFrame的pivot的方法
pivoted=ldata.pivot('date','item','value')
print pivoted.head()
#输出结果如下:
# item                  infl   realgdp  unemp
# date
# 1959-03-31 00:00:00  0.000  2710.349  5.800
# 1959-06-30 00:00:00  2.340  2778.801  5.100
# 1959-09-30 00:00:00  2.740  2775.488  5.300
# 1959-12-31 00:00:00   None  2785.204   None

(2)前两个参数值分别用作行和列索引的列名,最后一个参数值则是用于填充DataFrame的数据列的列名。
#假设有两个需要参与重塑的数据列:
ldata['value2']=np.random.randn(len(ldata))
print ldata[:10]
#输出结果如下:
#                   date     item     value    value2
# 0  1959-03-31 00:00:00  realgdp  2710.349  0.935557
# 1  1959-03-31 00:00:00     infl     0.000  1.609324
# 2  1959-03-31 00:00:00    unemp     5.800 -0.753734
# 3  1959-06-30 00:00:00  realgdp  2778.801 -0.668879
# 4  1959-06-30 00:00:00     infl     2.340 -0.256601
# 5  1959-06-30 00:00:00    unemp     5.100  0.537770
# 6  1959-09-30 00:00:00  realgdp  2775.488  1.073817
# 7  1959-09-30 00:00:00     infl     2.740 -0.027340
# 8  1959-09-30 00:00:00    unemp     5.300 -0.531161
# 9  1959-12-31 00:00:00  realgdp  2785.204  0.706080

(3)如果忽略掉最后一个参数,得到的DataFrame就会带有层次化的列
pivoted=ldata.pivot('date','item')
print pivoted[:5]
#输出结果如下:
#                      value                     value2
# item                  infl   realgdp  unemp      infl   realgdp     unemp
# date
# 1959-03-31 00:00:00  0.000  2710.349  5.800 -0.505690 -1.090732 -2.123859
# 1959-06-30 00:00:00  2.340  2778.801  5.100 -0.940028 -2.204997 -1.195357
# 1959-09-30 00:00:00  2.740  2775.488  5.300 -0.636424  0.510898  1.105585
# 1959-12-31 00:00:00   None  2785.204   None       NaN -0.203154       NaN

(4)注意,pivot其实只是一个快捷方式而己:用set_index创建层次化索引,再用unstack重塑。
unstacked=ldata.set_index(['date','item']).unstack('item')
print unstacked[:7]
#输出结果如下:
#                      value                     value2
# item                  infl   realgdp  unemp      infl   realgdp     unemp
# date
# 1959-03-31 00:00:00  0.000  2710.349  5.800 -0.380805 -2.499867 -1.398593
# 1959-06-30 00:00:00  2.340  2778.801  5.100 -0.168914  0.435372 -1.454938
# 1959-09-30 00:00:00  2.740  2775.488  5.300 -0.297850 -1.693196  1.615807
# 1959-12-31 00:00:00   None  2785.204   None       NaN  1.551105       NaN
import pandas as pd
import numpy as np
from pandas import Series,DataFrame

df = pd.DataFrame({'foo': ['one', 'one', 'one', 'two', 'two',
                           'two'],
                   'bar': ['A', 'B', 'C', 'A', 'B', 'C'],
                   'baz': [1, 2, 3, 4, 5, 6],
                   'zoo': ['x', 'y', 'z', 'q', 'w', 't']})
print df
#输出结果如下:
#   bar  baz  foo zoo
# 0   A    1  one   x
# 1   B    2  one   y
# 2   C    3  one   z
# 3   A    4  two   q
# 4   B    5  two   w
# 5   C    6  two   t
print df.pivot(index='foo',columns='bar')['baz'] #行索引foo,列bar,值baz
#输出结果如下:
# bar  A  B  C
# foo
# one  1  2  3
# two  4  5  6
print df.pivot(index='foo',columns='bar',values='baz') #与上面的一致
# print df.pivot(index='foo',columns='bar',values=['baz', 'zoo'])
#若是两行的索引一样的话会报错,因为值就不知道对应哪个了
#如下例中bar中的两个索一样
df=pd.DataFrame({"foo":['one','one','two','two'],
                 "bar":['A','A','B','C'],
                 "baz":[1,2,3,4]})
print df
print df.pivot(index='foo',columns='bar',values='baz') #报错,foo,bar为[one,A]不知道对应哪个值了,两个一样的索引

3. 数据转换:

前面讲的都是数据重排,另一类重要操作则是过滤、清理以及其它的转换工作。

(1)drop_duplicates:删除重复数据

(2)map:函数或映射进行数据转换(map,lambda),map用于修改对象的子集

(3)fillna: 用于填充缺失数据,看做值替换的一种特殊情况。或者用pandas的replace方法替换缺失值(与python的str.replace方法一样)

(4)rename:复制DataFrame并对其行索引和列标签进行修改。若要就地修改某个数据集,传入inplace=True即可

(5)cut:将数据划分阶段(即元面)例不同的年龄阶段,它返回的Categorical对象。而qcut是根据样本分位数对数据进行划分(可以等分)

(6)abs:过滤绝对值,np.abs(data)>3取绝对值>3的数。

(7)np.sign:过滤的区间为-1到1的数组,表示原始值的符号。

(8)take:选取子集,从DataFrame或Series中选取部分子集

(9)get_dummies:DataFrame的某一列中有多个不同的值,可将该列的值作为列名,其值全为1和0.例:第一行只有a的值,则a为1,其它为0

                            Series只有panda.str.get_dummies.


 

删除重复数据:

import pandas as pd
import numpy as np
from pandas import Series,DataFrame

#前面讲的都是数据重排,另一类重要操作则是过滤、清理以及其它的转换工作。
#删除重复数据
#DataFrame中常常会出现重复行,举例如下:
data=DataFrame({'k1':['one']*3+['two']*4,
                'k2':[1,1,2,3,3,4,4]})
print data
#输出结果如下:
#     k1  k2
# 0  one   1
# 1  one   1
# 2  one   2
# 3  two   3
# 4  two   3
# 5  two   4
# 6  two   4
(1)DataFrame的duplicated方法返回一个布尔型Series,表示各行是否是重复行:
print data.duplicated()
#输出结果如下:
# 0    False
# 1     True
# 2    False
# 3    False
# 4     True
# 5    False
# 6     True
# dtype: bool

(2)还有一个与此相关的drop_duplicates方法,它用于返回一个移除了重复行的DataFrame
print data.drop_duplicates()
#输出结果如下:
#     k1  k2
# 0  one   1
# 2  one   2
# 3  two   3
# 5  two   4

(3)这两个方法默认会判断全部列,你可以指定部分列进行重复项判断。假设你还有一列值,且只希望根据k1列过滤重复项。
data['v1']=range(7)
print data.drop_duplicates(['k1']) #过滤k1列的重复项
#输出结果如下:
#     k1  k2  v1
# 0  one   1   0
# 3  two   3   3

(4)duplicated和drop_duplicates默认保留的是第一个出现的值组合。传入last则保留最后一个
#它的参数值有:first是保留第一个过滤,False则删除全是重复(即k1,k2,v1)
print data.drop_duplicates(['k1','k2'],'last') #即以k2值过滤
#输出结果如下:
#     k1  k2  v1
# 1  one   1   1
# 2  one   2   2
# 4  two   3   4
# 6  two   4   6

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

map用于修改对象的子集:

 
import pandas as pd
import numpy as np
from pandas import Series,DataFrame

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]})
print 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

(1)假如你想要添加一列表示该肉类食物来源的动物类型,我们可以先编写一个肉类到动物的映射:映射用map
meat_to_animal={'bacon':'pig','pulled pork':'pig','pastrami':'cow',
                'corned beef':'cow','honey ham':'pig','nova lox':'salmon'}
#Series的map方法可以接受一个函数或含有映射关系的字典型对象,但是这里有个小问题,即有些肉类的首字母大写了,而另一些则没有。
#因此我们还需要将各个值转换为小写:
data['animal']=data['food'].map(str.lower).map(meat_to_animal)
print 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

(2)也可以传入一个能够完成全部这些工作的函数:lambda
print '++++++++++++++++++++++++++++++++'
print data['food'].map(lambda x:meat_to_animal[x.lower()])
#输出结果如下:
# 0       pig
# 1       pig
# 2       pig
# 3       cow
# 4       cow
# 5       pig
# 6       cow
# 7       pig
# 8    salmon
# Name: food, dtype: object
data['animal']=data['food'].map(lambda x:meat_to_animal[x.lower()])
print 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
#map是实现元素级转换以及其它数据工作的便捷方式
替换值:replace
import pandas as pd
import numpy as np
from pandas import Series,DataFrame

data=Series([1.,-999.,2.,-999.,-1000.,3.])
print data
#输出结果如下:
# 0       1.0
# 1    -999.0
# 2       2.0
# 3    -999.0
# 4   -1000.0
# 5       3.0
(1)-999这个值可能是一个表示缺失数据的标记值,要将其替换为Pandas能够理解的NA值、我们可以利用replace来产生一个新的Series.
print data.replace(-999,np.nan)
#输出结果如下:
# 0       1.0
# 1       NaN
# 2       2.0
# 3       NaN
# 4   -1000.0
# 5       3.0

(2)如果希望一次性替换多个值,可以传入一个由待替换值组成的列表以及一个替换值。
print data.replace([-999,-1000],np.nan) #将-999,-1000的都替换成NaN
#输出结果如下:
# 0    1.0
# 1    NaN
# 2    2.0
# 3    NaN
# 4    NaN
# 5    3.0

(3)如果希望对不同的值进行不同的替换,则传入一个由替换关系组成的列表即可:
print data.replace([-999,-1000],[np.nan,0]) #将-999替换成NaN,-1000替换成0
#输出的结果如下:
# 0    1.0
# 1    NaN
# 2    2.0
# 3    NaN
# 4    0.0
# 5    3.0

(4)传入的参数也可以是字典
print data.replace({-999:np.nan,-1000:0})
#输出的结果如下:
# 0    1.0
# 1    NaN
# 2    2.0
# 3    NaN
# 4    0.0
# 5    3.0
重命名轴索引:

跟Series中的值一样,轴标签也可以通过函数或映射进行转换,从而得到一个新对象。
轴还可以被就地修改,而无需新建一个数据结构。

import pandas as pd
import numpy as np
from pandas import Series,DataFrame

data=DataFrame(np.arange(12).reshape((3,4)),
               index=['Ohio','Colorado','New York'],
               columns=['one','two','three','four'])
(1)和轴标签一样,轴标签也有一map方法:
print data.index.map(str.upper) #data的index用map转成全是大写
#输出结果如下:
# Index([u'OHIO', u'COLORADO', u'NEW YORK'], dtype='object')
#可以将其直接赋给index,这样就达到了就地修改了。
data.index=data.index.map(str.upper)
print data
#输出结果如下:
#           one  two  three  four
# OHIO        0    1      2     3
# COLORADO    4    5      6     7
# NEW YORK    8    9     10    11

(2)如果想要创建数据集的转换版(而不是修改原始数据),比较实用的方法是rename:
print data.rename(index=str.title,columns=str.upper)
#输出结果如下:
#           ONE  TWO  THREE  FOUR
# Ohio        0    1      2     3
# Colorado    4    5      6     7
# New York    8    9     10    11

(3)rename可以结合字典对象实现对部分轴标签的更新
print data.rename(index={'OHIO':'INDIANA'},
                  columns={'three':'peekaboo'})
#输出结果如下:
#           one  two  peekaboo  four
# Ohio        0    1         2     3
# Colorado    4    5         6     7
# New York    8    9        10    11

(4)rename帮我们实现了:复制DataFrame并对其索引和列标签进行赋值。如果希望就地修改某个数据集,传入inplace=True即可
_=data.rename(index={'OHIO':'INDIANA'},inplace=True)
print data
离散化和面元划分:cut,pcut
为了便于分析,连续数据常常被离散化或拆分为“面元”(bin).假设有一组人员数据,而你希望将它们划分为不同年龄组:
import pandas as pd
import numpy as np
from pandas import Series,DataFrame

ages=[20,22,25,27,21,23,37,31,61,45,41,32]
(1)接下来将这些数据划分为"18-25"、"26到35"、"35到60"以及"60以上"几个面元,要实现该功能,需要使用pandas的cut函数.
    cut返回的是一个特殊的Categorical对象。可以看做一组表示面元名称的字符串。
bins=[18,25,35,60,100]
cats=pd.cut(ages,bins)
print cats
#输出结果如下:
# [(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]]

(2)实际上,它含有一个表示不同分类名称的levels数组以及一个为年龄数据进行标号的labels属性,现在用codes替换了labels
# print cats.labels
#输出结果如下:
# [0 0 0 1 0 0 2 1 3 2 2 1]
print cats.codes
#输出结果如下:
# [0 0 0 1 0 0 2 1 3 2 2 1]
print pd.value_counts(cats) #符合各区间的个数
#输出结果如下:
# (18, 25]     5
# (35, 60]     3
# (25, 35]     3
# (60, 100]    1

(3)跟"区间"的数学符号一样,圆括号表示开端,而方括号则表示闭端。哪边是闭端可以通过right-False进行修改:
print pd.cut(ages,[18,26,36,61,100],right=False)
#输出结果如下:
# [[18, 26), [18, 26), [18, 26), [26, 36), [18, 26), ..., [26, 36), [61, 100), [36, 61), [36, 61), [26, 36)]
# Length: 12
# Categories (4, interval[int64]): [[18, 26) < [26, 36) < [36, 61) < [61, 100)]

(4)你也可以设置自己的面元名称,将labels选项设置为一个列表或数组即可:
group_names=['Youth','YoungAdult','MiddleAged','Senior']
print pd.cut(ages,bins,labels=group_names)
#输出结果如下:
# [Youth, Youth, Youth, YoungAdult, Youth, ..., YoungAdult, Senior, MiddleAged, MiddleAged, YoungAdult]
# Length: 12
# Categories (4, object): [Youth < YoungAdult < MiddleAged < Senior]

(5)如果向cut传入的是面元的数量而不是确切的面元边界,则它会根据数据的最小值和最大值计算等长面元。
#下面这个例子,我们将一些均匀分布的数据分成四组:
data=np.random.rand(20)
print pd.cut(data,4,precision=2)
#输出结果如下:
# [(0.046, 0.28], (0.046, 0.28], (0.28, 0.52], (0.28, 0.52], (0.76, 0.99], ..., (0.76, 0.99], (0.76, 0.99], (0.76, 0.99], (0.76, 0.99], (0.28, 0.52]]
# Length: 20
# Categories (4, interval[float64]): [(0.046, 0.28] < (0.28, 0.52] < (0.52, 0.76] < (0.76, 0.99]]

(6)qcut是一个非常类似于cut函数,它可以根据样本分位数对数据进行面元划分
#cut可能无法使各个面元中含有相同数量的数据点。而qcut使用的是样本分位数,因此可以得到大小基本相等的面元。
data=np.random.randn(1000) #randn正态分布
cats=pd.qcut(data,4) #按4分位进行切割
print cats
#输出结果如下:
# [(-0.658, 0.0366], (-4.299, -0.658], (-0.658, 0.0366], (-4.299, -0.658], (0.0366, 0.686], ..., (0.686, 3.005], (0.686, 3.005], (0.686, 3.005], (-4.299, -0.658], (0.686, 3.005]]
# Length: 1000
# Categories (4, interval[float64]): [(-4.299, -0.658] < (-0.658, 0.0366] < (0.0366, 0.686] <
#                                     (0.686, 3.005]]

(7)和cut一样,也可以设置自定义的分位数(0到1之间的数值,包含端点):
print pd.qcut(data,[0,0.1,0.5,0.9,1.])

注意:本章稍后在讲解聚合和分组运算时会再次用到cut和qcut,因为这两个离散化函数对分量和分组分析非常重要。
检测和过滤异常值:abs(col)>3,np.sign(data)*3

异常值的过滤或变换运算在很大程度上其实就是数组运算。来看一个含有正态分布数据的DataFrame:

import pandas as pd
import numpy as np
from pandas import Series,DataFrame

np.random.seed(12345)
data=DataFrame(np.random.randn(1000,4))
print data.describe()
#输出结果如下:
#                  0            1            2            3
# count  1000.000000  1000.000000  1000.000000  1000.000000
# mean     -0.067684     0.067924     0.025598    -0.002298
# std       0.998035     0.992106     1.006835     0.996794
# min      -3.428254    -3.548824    -3.184377    -3.745356
# 25%      -0.774890    -0.591841    -0.641675    -0.644144
# 50%      -0.116401     0.101143     0.002073    -0.013611
# 75%       0.616366     0.780282     0.680391     0.654328
# max       3.366626     2.653656     3.260383     3.927528
(1)假如想找出某列中绝对值大小超过3的值:abs(col)>3
col=data[3] #取data[3]列
print col[np.abs(col)>3]
#输出结果如下:
# 97     3.927528
# 305   -3.399312
# 400   -3.745356
# Name: 3, dtype: float64

(2)要选出全部含有"超过3或-3的值"的行,可以利用布尔型DataFrame以及any方法:
print data[(np.abs(data)>3).any(1)]

(3)根据这些条件,可轻松地对值进行设置。下面的代码可以将值限制在区间-3到3以内:
   np.sign这个函数返回的是一个由-1到1组成的数组,表示原始值的符号
data[np.abs(data)>3]=np.sign(data)*3
print data.describe()
#输出结果如下:
#                  0            1            2            3
# count  1000.000000  1000.000000  1000.000000  1000.000000
# mean     -0.067623     0.068473     0.025153    -0.002081
# std       0.995485     0.990253     1.003977     0.989736
# min      -3.000000    -3.000000    -3.000000    -3.000000
# 25%      -0.774890    -0.591841    -0.641675    -0.644144
# 50%      -0.116401     0.101143     0.002073    -0.013611
# 75%       0.616366     0.780282     0.680391     0.654328
# max       3.000000     2.653656     3.000000     3.000000
排列和随机采样:permutation(5)需要排列的长度为5,take选取子集

利用numpy.random.permutation函数可以轻松实现对Series或DataFrame的列的排列工作(permuting).通过需要排列的轴的长度

调用permutation,可产生一个表示新顺序的整数数组:

import numpy as np
from pandas import DataFrame,Series

(1)take:选取子集,df.take[0,3]是选取第0行和第3行,而不是选取行索引为0和3的行
df=DataFrame(np.arange(5*4).reshape(5,4))
sampler=np.random.permutation(5) #排列的轴的长度调用permutation,可产生一个整数数组
print sampler
#输出结果如下:
# [1 2 3 0 4]
print df
#输出结果如下:
#     0   1   2   3
# 0   0   1   2   3
# 1   4   5   6   7
# 2   8   9  10  11
# 3  12  13  14  15
# 4  16  17  18  19
print df.take(sampler) #取df的第1,2,3,0,4行。而不是取索行号为1,2,3,0,4的行。
#输出结果如下:
#     0   1   2   3
# 1   4   5   6   7
# 2   8   9  10  11
# 3  12  13  14  15
# 0   0   1   2   3
# 4  16  17  18  19

(2)如果不想用替换的方式选取随机子集,则可以使用permutation:从permutation返回的数组中切下前k个元素,其中k为期望的子集大小。
    直接切下选出permutation的值
#虽然有很多高效的算法可以实现非替换式采样,但是手边有的工具为什么不用呢
print '+++++++++++++++++++++++++'
print np.random.permutation(len(df))
#输出结果:[2 1 3 0 4]
print df.take(np.random.permutation(len(df))[:3]) #取出[:3]下标从0-3不包括3的行,即的是[2,1,3]行的数据
#输出结果如下:
#     0   1   2   3
# 1   4   5   6   7
# 3  12  13  14  15
# 0   0   1   2   3

(3)要通过替换的方式产生样本,最快的方式是通过np.random.randint得到一组随机整数:
bag=np.array([5,7,-1,6,4])
sampler=np.random.randint(0,len(bag),size=10)
print sampler
#输出结果如下:
# [2 2 4 3 0 4 4 3 0 4]
draw=bag.take(sampler)
print draw
#输出结果如下:按上面sampler的值取第几个,如sample为2,则取第二个值
# [-1 -1  4  6  5  4  4  6  5  4]
计算指标/哑变量:get_dummies

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

"指标矩阵"(indicator matrix).如果DataFrame的某一列中含有K个不同的值,则可以派生出一个k列矩阵或DataFrame

(其值全为1和0)。pandas有一个get_dummies函数可以实现该功能.

import numpy as np
import pandas as pd
from pandas import DataFrame,Series

(1) get_dummies某一列中有多个不同的值,可将该列的值作为列名,其值全为1和0。例:第一行只有a的值,则a为1,其它为0

df=DataFrame({'key':['b','b','a','c','a','b'],
              'data1':range(6)})
print df
#输出结果如下:
#    data1 key
# 0      0   b
# 1      1   b
# 2      2   a
# 3      3   c
# 4      4   a
# 5      5   b
print pd.get_dummies(df['key'])
#输出结果如下:
#    a  b  c
# 0  0  1  0
# 1  0  1  0
# 2  1  0  0
# 3  0  0  1
# 4  1  0  0
# 5  0  1  0

(2)若想给DataFrame的列加上一个前缀,以便能够跟其他数据进行合并。可以用prefix='key'
dummies=pd.get_dummies(df['key'],prefix='key') #prefix是将列分成好几个子列的表示如:key_a,key_b,key_c...
print dummies
#输出结果如下:
#    key_a  key_b  key_c
# 0      0      1      0
# 1      0      1      0
# 2      1      0      0
# 3      0      0      1
# 4      1      0      0
# 5      0      1      0
df_with_dummy=df[['data1']].join(dummies) #data1列和dummies合并
print df_with_dummy
#输出结果如下:
#    data1  key_a  key_b  key_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

(3)如果DataFrame中某行同属于多个分类,则事情就会有点复杂,具体的操作如下
#movies.dat的内容如下:movies的数据就是同一行的数据是多个分类
# 1::Toy Story(1995)::Animation|Children's|Comedy
# 2::Jumanji(1995)::Adventure|Children's|Fantasy
# 3::Grumpier Old Men(1995)::Comedy|Romance
# 4::Waiting to Exhale(1995)::Comedy|Drama
# 5::Father of the Bride Part II(1995)::Comedy
# 6::Heat(1995)::Action|Crime|Thriller
mnames=['movie_id','title','genres']
#下面的engine不是read_table的参数,它是为了消除pycharm产生的warning
movies=pd.read_table('movies.dat',sep='::',header=None,names=mnames,engine='python')
print '++++++++++++++++++'
print len(movies) #输出结果为:6
print movies[:3]
#输出结果如下:
#    movie_id                   title                        genres
# 0         1         Toy Story(1995)   Animation|Children's|Comedy
# 1         2           Jumanji(1995)  Adventure|Children's|Fantasy
# 2         3  Grumpier Old Men(1995)                Comedy|Romance
(a)要为每个genre添加指标变量就需要做一些数据的规整。首先,我们从数据集中抽取出不同的genre值(巧用set.union),set.union集合的并集
genre_iter=(set(x.split('|')) for x in movies.genres) #取出movies.genres的每个数据,以'|'分开后放入set集合
print genre_iter
#输出结果:<generator object <genexpr> at 0x103e7c0f0> 是个集合对象
genres=sorted(set.union(*genre_iter)) #接收的参数是一个元组,并且排序
print genres
#输出结果如下:
# ['Action', 'Adventure', 'Animation', "Children's", 'Comedy', 'Crime', 'Drama', 'Fantasy', 'Romance', 'Thriller']
(b)现在,我们从一个全零DataFrame开始构建指标DataFrame:
dummies=DataFrame(np.zeros((len(movies),len(genres))),columns=genres)#构建movies的行数,genres的个数为列的全为0的数据
# print dummies #6行10列的全为0的数据
(c)接下来,迭代每一部电影并将dummies各行的项设置为1:
for i,gen in enumerate(movies.genres): #enumerate会自动加上索引,故要两个变量接收,i是索引,gen是值
    dummies.ix[i,gen.split('|')]=1 #ix取行,ix[i,gen.split('|')]若i=0,则表示取第一行,gen的'|'分开的值都赋1;
    # 即假如这一行有该值,若有Action则该值为1.
print dummies
#输出结果如下:
#  Action  Adventure  Animation  Children's  Comedy  Crime  Drama  Fantasy  \
# 0     0.0        0.0        1.0         1.0     1.0    0.0    0.0      0.0
# 1     0.0        1.0        0.0         1.0     0.0    0.0    0.0      1.0
# 2     0.0        0.0        0.0         0.0     1.0    0.0    0.0      0.0
# 3     0.0        0.0        0.0         0.0     1.0    0.0    1.0      0.0
# 4     0.0        0.0        0.0         0.0     1.0    0.0    0.0      0.0
# 5     1.0        0.0        0.0         0.0     0.0    1.0    0.0      0.0
(d)genres列处理好了,再将其与moivies合并起来
movies_windic=movies.join(dummies.add_prefix('Genre')) #
print movies_windic
# print movies_windic.ix[0]#取第一行的数据
#输出结果如下:
# movie_id                                     1
# title                          Toy Story(1995)
# genres             Animation|Children's|Comedy
# GenreAction                                  0
# GenreAdventure                               0
# GenreAnimation                               1
# GenreChildren's                              1
# GenreComedy                                  1
# GenreCrime                                   0
# GenreDrama                                   0
# GenreFantasy                                 0
# GenreRomance                                 0
# GenreThriller                                0

下面简单介绍一下get_dummies函数

import pandas as pd
import numpy as np
from pandas import DataFrame,Series

#get_dummies 某列的有多个值(a,b,c),没有值的用0表示
s=pd.Series(list('abca'))
print s
#输出结果如下:
# 0    a
# 1    b
# 2    c
# 3    a

print pd.get_dummies(s) #转换categoriacl variable为dummy/indicator 变量
#输出结果如下:
#    a  b  c
# 0  1  0  0
# 1  0  1  0
# 2  0  0  1
# 3  1  0  0
s1=['a','b',np.nan]
print pd.get_dummies(s1)
#输出结果如下:
#    a  b
# 0  1  0
# 1  0  1
# 2  0  0
print pd.get_dummies(s1,dummy_na=True) #包含NaN值
#输出结果如下:
#    a  b  NaN
# 0  1  0    0
# 1  0  1    0
# 2  0  0    1
df=pd.DataFrame({'A':['a','b','a'],'B':['b','a','c'],
                 'C':[1,2,3]})
print pd.get_dummies(df,prefix=['col1','col2'])
4. 字符串操作:
对于更为复杂的模式匹配和文本操作,则可能需要用到正由表达式。pandas对些进行了加强,它使你能够对数组数据应用字符串表达式和正则表达式,
而且能处理烦人的缺失数据。

python内置的字符串方法

(1)split: 拆分,用于以一种格式(,)之类的可以拆分成数据的数据.它常与strip一起使用,用于去除空格

(2)+/join:连接字符串,join用的多

(3)in/index/find/count:python子串的定位,in返回True/False;index与find区别是,若没找到由报异常,find返回-1,找到都返回1.

        count用返回子串出现的次数(非重叠),若没有出现则为0.

(4)replace:用于指字模式替换另一模式

(5)startswith/endswith:如果字符串以某个前缀/后缀开头/结尾,则返回True.

(6)rfind:如果在子符串找到子串,则返回第一个发现的子串的第一个字符所在的位置 。如没找到则返回-1.

(7)strip,rstrip,lstrip:去除空白符

(8)lower,upper:转换为小写/大写

(9)ljust,rjust:用空格(或其它字符)填充字符串的空白侧以返回符合最低宽度的字符串


字符串对象方法:

对于大部分字符串处理应用而言,内置的字符串方法已经够满足要求了。例如,以逗号分隔的字符串可以用split拆分成数段。

内置python字符串的方法举例:

val='a,b, guido'
print val.split(',')

(1)split常结合strip(用于去除空白)一起使用:
pieces=[x.strip() for x in val.split(',')]
print pieces
#输出结果如下:
['a', 'b', 'guido']

(2)python:'+'连接各字符串(用的少)  或者 join连接字符串(join常用)
first,second,third=pieces #将pieces的值分别赋给一个个变量
print first+"::"+second+"::"+third
#输出结果如下:
# a::b::guido
#可用join达到一样的效果
print "::".join(pieces)

(3)python子串的定位:in方法,index,find查找.index未找到会抛异常,而find未找到返回-1。
# count函数用于返回子串出现的次数,若为0未找到
print 'guido' in val  #输出:True
print val.index(',')  #输出:1
print val.find(':')   #输出:-1
print val.count(',')  #输出:2

(4)replace用于指定模式替换为另一个模式
print val.replace(',','::')
#输出结果如下:
# a::b:: guido
print val.replace(',','')
#输出结果如下:
# ab guido
正则表达式:
import re
#正则表达式。python内置的re模块负责对字符串应用正则表达式
text='foo bar\t baz \tqux'
print re.split('\s+',text)
#输出结果如下:
# ['foo', 'bar', 'baz', 'qux']

(1)调用re.split('\s+',text)时,正则表达式会先被编译,然后再在text上调用其split方法。
    你可以用re.compile自己编译regex以得到一个可重用的regex对象
regex=re.compile('\s+')
print regex.split(text)
#输出结果如下:
# ['foo', 'bar', 'baz', 'qux']
#如果只希望得到匹配regex的所有模式,则可以使用findall方法:
print regex.findall(text)
#输出结果如下:
# [' ', '\t ', ' \t']
#如果打算对许多字符串应用同一条正则表达式,强列建议通过re.compile创建regex对象,这样可以节省大量的CPU时间。


(2)match和search跟findall功能类似。findall返回的是字符串所有的匹配项,而search则只返回第一个匹配项。match更加严格,
#它只匹配字符串的首部。
#如果想避免正则表达式中不需要的转义(\),则可以使用原始字符串字面r'
text="""
Dava dava@google.com
Steven@gmail.com
Rob rob@gmail.com
Ryan ryan@yahoo.com
"""
pattern=r'[A-Z0-9._%+-]@[A-Z0-9.-]+\.[A-Z]{2,4}'
#re.IGNORECASE的作用是使用正则表达式对大小写不敏感
regex=re.compile(pattern,flags=re.IGNORECASE)
#对text使用findall将得到一组电子邮件地址:
print regex.findall(text)
#输出结果如下:
# ['a@google.com', 'n@gmail.com', 'b@gmail.com', 'n@yahoo.com']
#search返回的是文本中第一个电子邮件地址。
m=regex.search(text)
print m
#输出结果如下:
# <_sre.SRE_Match object at 0x1078c9d30>
print text[m.start():m.end()]
#regex.match则将返回None,因为它只匹配出现在字符串开头的模式:
print regex.match(text) #返回None

(3)另外还有一个sub方法,它会将匹配到模式替换为指定字符串,并返回所得到的新符串:
print regex.sub('REDACTED',text)
#输出结果如下:
# Dava davREDACTED
# SteveREDACTED
# Rob roREDACTED
# Ryan ryaREDACTED

(4)假设你不仅想找要找出电子邮件地址,还想将各个地址分成3个部分:用户名、域名以及域后缀
    要想实现此功能,只需将待分段的模式的各部分用圆括号包起来即可:
pattern=r'([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\.([A-Z]{2,4})'
regex=re.compile(pattern,flags=re.IGNORECASE)
#由这种正则表达式所产生的匹配项对象,可以通过其groups方法返回一个由模式各段组成的元组。
m=regex.match('wesm@bright.net')
print m.groups()
#输出结果如下:
# ('wesm', 'bright', 'net')
#对于带有分组功能的模式,findall会返回一个元组列表:
print regex.findall(text)
#输出结果如下:
# [('dava', 'google', 'com'), ('Steven', 'gmail', 'com'), ('rob', 'gmail', 'com'), ('ryan', 'yahoo', 'com')]
#sub还能通过诸如\1、\2之类的特殊符号访问各匹配项中的分组:
print regex.sub(r'Username:\1,Domain:\2,Suffix:\3',text)
#输出结果如下:
# Dava Username:dava,Domain:google,Suffix:com
# Username:Steven,Domain:gmail,Suffix:com
# Rob Username:rob,Domain:gmail,Suffix:com
# Ryan Username:ryan,Domain:yahoo,Suffix:com
#对上面那个电子邮件正则表达式做一点小变动:为各个匹配分组加上一个名称:
regex=re.compile(r"""
(?P<username>[A-Z0-9._%+-]+)
@
(?P<domain>[A-Z0-9.-]+)
\.
(?P<suffix>[A-Z]{2,4})
""",flags=re.IGNORECASE|re.VERBOSE)
m=regex.match('wes@bright.net')
print m.groupdict()
#输出结果如下:
# {'username': 'wes', 'domain': 'bright', 'suffix': 'net'}
正则表达式方法
正则                          说明
findall、finditer 返回字符串中所有的非重叠匹配模式。findall返回的是由所有模式组成的列表,
而finditer则通过一个迭代器逐个返回
match 从字符串起始位置匹配模式,还可以对模式各部分进行分组。如果匹配到模式,则
返回一个匹配对象,否则返回None
search 扫描整个字符串以匹配模式。如果找到则返回一个匹配项对象。跟match不同,其
匹配可以位于字符串的任意位置,而不仅仅是起始处
split 根据找到的模式将字符串拆分为数段
sub、subn 将字符串所有的sub或前n个subn模式替换为指定表达式。在替换字符串中可以通过\1、\2等

符号表示各分组项

注意:正则表达式细节还是看正则表达式的专题。

pandas中矢量化的字符串函数:

清理待分析的散乱数据时,常常需要做一些字符串规整化工作。更为复杂的情况是,含有字符串的列有时还含有缺失数据:

import pandas as pd
import numpy as np
from pandas import DataFrame,Series
import re

data={'Dava':'dava@google.com','Steve':'steve@gmail.com','Rob':'rob@gmail.com','Wes':np.nan}
data=Series(data)
print data
#输出结果如下:
# Dava     dava@google.com
# Rob        rob@gmail.com
# Steve    steve@gmail.com
# Wes                  NaN
print data.isnull()

(1)通过data.map,所有字符串和正则表达式方法都能被应用于(传入lambda表达式或其他函数)各个值,但是如果存在NA就会报错。
#为了解决这个问题,Series有一些能够跳过NA值的字符串操作方法。通过Series的str属性即可访问这些方法。例如,我们可以
#通过str.contains检查各个电子邮件地址是否含有"gmail".
print data.str.contains('gmail')
#输出结果如下:
# Dava     False
# Rob       True
# Steve     True
# Wes        NaN

(2)这里也可以使用正则表达式,还可以加上任意re选项(如IGNORECASE)
pattern='([A-Z0-9._%+-]@([A-Z0-9.-]+)\\.([A-Z]{2,4}))'
print data.str.findall(pattern,flags=re.IGNORECASE)
#输出结果如下:
# Dava     [(a@google.com, google, com)]
# Rob        [(b@gmail.com, gmail, com)]
# Steve      [(e@gmail.com, gmail, com)]
# Wes                                NaN

(3)有两个方法可以实现矢量化的元素获取操作:要么使用str.get,要么在str属性上使用索引。
matches=data.str.match(pattern,flags=re.IGNORECASE)
print matches
print matches.str.get(1)
print matches.str[0]
矢量化的字符串方法
方法    说明
cat     实现元素级的字符串连接操作,可指定分隔符
contains     返回表示各字符串是否含有指定模式的布尔型数组
count     模式的出现次数
endswith、startwith        相当于对各个元素执行x.endswith(pattern)或x.startswith(pattern)
findall     计算各字符串的模式列表
get     获取各元素的第i个字符
join     根据指定的分隔符将Series中各元素的字符串连接起来
len     计算各字符串的长度
lower、upper     转换大小写。相当于对各个元素执行x.lower()或x.upper()
match     根据指定的正则表达式对各个元素执行re.match
pad     在字符串的左边、右边或左右两边添加空白符
center     相当于pad(side='both')
repeat     重复值。s.str.repeat(3)相当于执行x*x*x即x的3次方
replace     用指定字符串替换找到的模式
slice     对Series中的种个字符串进行子串截取
split     根据分隔符或正则表达式对字符串进行拆分
strip、rstrip、lstrip         去除空白符,所括换行符。相当于执行x.strip(),x.rstrip(),x.lstrip()


5. 示例:usda食品数据库

import json
from pandas import DataFrame,Series
import pandas as pd

db=json.load(open('foods-2011-10-03.json'))
print len(db)  #6636
print db[0].keys()
#输出结果:
#[u'portions', u'description', u'tags', u'nutrients', u'group', u'id', u'manufacturer']
print db[0]['nutrients'][0]
#输出结果如下:
# {u'units': u'g', u'group': u'Composition', u'description': u'Protein', u'value': 25.18}
nutrients=DataFrame(db[0]['nutrients'])
print nutrients[:7]
#输出结果如下:
#                    description        group units    value
# 0                      Protein  Composition     g    25.18
# 1            Total lipid (fat)  Composition     g    29.20
# 2  Carbohydrate, by difference  Composition     g     3.06
# 3                          Ash        Other     g     3.28
# 4                       Energy       Energy  kcal   376.00
# 5                        Water  Composition     g    39.28
# 6                       Energy       Energy    kJ  1573.00
#将字典列表转换为DataFrame时,可以只抽取其中的一部分字段。这里,我们将取出食物的名称、分类、编号以及制造商等信息。
info_keys=['description','group','id','manufacturer']
info=DataFrame(db,columns=info_keys)
print info[:5]

#通过value_counts,可以查看食物类别的分布情况:
print pd.value_counts(info.group)[:10]
#输出结果如下:
# Vegetables and Vegetable Products    812
# Beef Products                        618
# Baked Products                       496
# Breakfast Cereals                    403
# Legumes and Legume Products          365
# Fast Foods                           365
# Lamb, Veal, and Game Products        345
# Sweets                               341
# Fruits and Fruit Juices              328
# Pork Products                        328
# Name: group, dtype: int64

#为了对全部营养数据做一些分析,最简单的办法是将所有食物的营养成分整合到一个大表中。我们分几步骤来
#实现该目的。首先,将各食物的营养成分列表转换为一个DataFrame,并添加一个表示编号的列,然后将该DataFrmae添
#加到一个列表中,最后通过concat将这些东西连接起来就可以了。
nutrients=[]
for rec in db:
    fnuts=DataFrame(rec['nutrients'])
    fnuts['id']=rec['id']
    nutrients.append(fnuts)
nutrients=pd.concat(nutrients,ignore_index=True)
print nutrients
#输出结果如下:
# Vegetables and Vegetable Products    812
# Beef Products                        618
# Baked Products                       496
# Breakfast Cereals                    403
# Legumes and Legume Products          365
# Fast Foods                           365
# Lamb, Veal, and Game Products        345
# Sweets                               341
# Fruits and Fruit Juices              328
# Pork Products                        328
# Name: group, dtype: int64
print nutrients.duplicated().sum()
#输出结果如下:14179

#由于两个DataFrmae对象中都有"group"和"description",所以为了明确到底谁是谁,我们需要对它们进行重命名。
col_mapping={'description':'food',
             'group':'fgroup'}
info=info.rename(columns=col_mapping,copy=False)
print info
col_mapping={'description':'nutrient',
             'group':'nutgroup'}
nutrients=nutrients.rename(columns=col_mapping,copy=False)
print nutrients

#做完这些事情之后,就可以将info跟nutrients合并起来:
ndata=pd.merge(nutrients,info,on='id',how='outer')
print ndata












  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值