现在的位置: 首页 > 综合 > 正文

利用Python进行数据分析——pandas入门(五)(5)

2014年10月08日 ⁄ 综合 ⁄ 共 7106字 ⁄ 字号 评论关闭

1、唯一值、值计数以及成员资格

还有一类方法可以从一维Series的值中抽取信息。以下面这个Series为例:

In [1]: import pandas as pd

In [2]: import numpy as np

In [3]: obj = pd.Series(['c', 'a', 'd', 'a', 'a', 'b', 'b', 'c', 'c'])

第一个函数是unique,它可以得到Series中的唯一值数组:

In [4]: uniques = obj.unique()

In [5]: uniques
Out[5]: array(['c', 'a', 'd', 'b'], dtype=object)

返回的唯一值是未排序的,如果需要的话,可以对结果再次进行排序(uniques.sort())。value_counts用于计算一个Series中各值出现的频率:

In [6]: obj.value_counts()
Out[6]: 
c    3
a    3
b    2
d    1
dtype: int64

为了便于查看,结果Series是按值频率降序排列的。value_counts还是一个顶级pandas方法,可用于任何数组或序列:

In [7]: pd.value_counts(obj.values, sort=False)
Out[7]: 
a    3
c    3
b    2
d    1
dtype: int64

最后是isin,它用于判断矢量化集合的成员资格,可用于选取Series中或DataFrame列中数据的子集:

In [8]: mask = obj.isin(['b', 'c'])

In [9]: mask
Out[9]: 
0     True
1    False
2    False
3    False
4    False
5     True
6     True
7     True
8     True
dtype: bool

In [10]: obj[mask]
Out[10]: 
0    c
5    b
6    b
7    c
8    c
dtype: object

有时,你可能希望得到DataFrame中多个相关列的一张柱状图。例如:

In [11]: data = pd.DataFrame({'Qu1': [1, 3, 4, 3, 4], 'Qu2': [2, 3, 1, 2, 3], 'Qu3': [1, 5, 2, 4, 4]})

In [12]: data
Out[12]: 
   Qu1  Qu2  Qu3
0    1    2    1
1    3    3    5
2    4    1    2
3    3    2    4
4    4    3    4

[5 rows x 3 columns]

将pandas.value_counts传给该DataFrame的apply函数,就会出现:

In [13]: result = data.apply(pd.value_counts).fillna(0)

In [14]: result
Out[14]: 
   Qu1  Qu2  Qu3
1    1    1    1
2    0    2    1
3    2    2    0
4    2    0    2
5    0    0    1

[5 rows x 3 columns]

2、处理缺失值

缺失数据(missing data)在大部分数据分析应用中都很常见。pandas的设计目标之一就是让缺失数据的处理任务尽量轻松。例如,pandas对象上的所有描述统计都排除了缺失数据。

pandas使用浮点值NaN(Not a Number)表示浮点和非浮点数组中的缺失数据。它只是一个便于被检测出来的标记而已:

In [15]: string_data = pd.Series(['aardvark', 'artichoke', np.nan, 'avocado'])

In [16]: string_data
Out[16]: 
0     aardvark
1    artichoke
2          NaN
3      avocado
dtype: object

In [17]: string_data.isnull()
Out[17]: 
0    False
1    False
2     True
3    False
dtype: bool

Python内置的None值也会被当做NA处理:

In [18]: string_data[0] = None

In [19]: string_data.isnull()
Out[19]: 
0     True
1    False
2     True
3    False
dtype: bool


3、滤除缺失数据

过滤掉缺失数据的办法有很多种。纯手工操作永远都是一个办法,但dropna可能会更实用一些。对于一个Series,dropna返回一个仅含非空数据和索引值的Series:

In [20]: from numpy import nan as NA

In [21]: data = pd.Series([1, NA, 3.5, NA, 7])

In [22]: data.dropna()
Out[22]: 
0    1.0
2    3.5
4    7.0
dtype: float64

当然,也可以通过布尔型索引达到这个目的:

In [23]: data[data.notnull()]
Out[23]: 
0    1.0
2    3.5
4    7.0
dtype: float64

而对于DataFrame对象,事情就有点复杂了。你可能希望丢弃全NA或含有NA的行或列。dropna默认丢弃任何含有缺失值的行:

In [24]: data = pd.DataFrame([[1., 6.5, 3.], [1., NA, NA], [NA, NA, NA], [NA, 6.5, 3.]])

In [25]: cleaned = data.dropna()

In [26]: data
Out[26]: 
    0    1   2
0   1  6.5   3
1   1  NaN NaN
2 NaN  NaN NaN
3 NaN  6.5   3

[4 rows x 3 columns]

In [27]: cleaned
Out[27]: 
   0    1  2
0  1  6.5  3

[1 rows x 3 columns]

传入how='all'将只丢弃全为NA的那些行:

In [29]: data.dropna(how='all')
Out[29]: 
    0    1   2
0   1  6.5   3
1   1  NaN NaN
3 NaN  6.5   3

[3 rows x 3 columns]

要用这种方式丢弃列,只需传入axis=1即可:

In [30]: data[4] = NA

In [31]: data
Out[31]: 
    0    1   2   4
0   1  6.5   3 NaN
1   1  NaN NaN NaN
2 NaN  NaN NaN NaN
3 NaN  6.5   3 NaN

[4 rows x 4 columns]

In [32]: data.dropna(axis=1, how='all')
Out[32]: 
    0    1   2
0   1  6.5   3
1   1  NaN NaN
2 NaN  NaN NaN
3 NaN  6.5   3

[4 rows x 3 columns]

另一个滤除DataFrame行的问题涉及时间序列数据。假设你只想留下一部分观测数据,可以用thresh参数实现此目的:

In [33]: df = pd.DataFrame(np.random.randn(7, 3))

In [34]: df.ix[:4, 1] = NA; df.ix[:2, 2] = NA

In [35]: df
Out[35]: 
          0         1         2
0  1.259190       NaN       NaN
1  0.181184       NaN       NaN
2 -0.305915       NaN       NaN
3  1.157043       NaN -0.305271
4  0.279270       NaN -2.275010
5  0.304463 -0.009671 -1.042706
6 -1.311779  0.158454  0.604686

[7 rows x 3 columns]

In [36]: df.dropna(thresh=3)
Out[36]: 
          0         1         2
5  0.304463 -0.009671 -1.042706
6 -1.311779  0.158454  0.604686

[2 rows x 3 columns]

4、填充缺失数据

你可能不想滤除缺失数据(有可能会丢弃跟它有关的其他数据),而是希望通过其他方式填充那些“空洞”。对于大多数情况而言,fillna方法是最主要的函数。通过一个常数调用fillna就会将缺失值替换为那个常数值:

In [37]: df.fillna(0)
Out[37]: 
          0         1         2
0  1.259190  0.000000  0.000000
1  0.181184  0.000000  0.000000
2 -0.305915  0.000000  0.000000
3  1.157043  0.000000 -0.305271
4  0.279270  0.000000 -2.275010
5  0.304463 -0.009671 -1.042706
6 -1.311779  0.158454  0.604686

[7 rows x 3 columns]

若是通过一个字典调用fillna,就可以实现对不同的列填充不同的值:

In [38]: df.fillna({1: 0.5, 3: -1})
Out[38]: 
          0         1         2
0  1.259190  0.500000       NaN
1  0.181184  0.500000       NaN
2 -0.305915  0.500000       NaN
3  1.157043  0.500000 -0.305271
4  0.279270  0.500000 -2.275010
5  0.304463 -0.009671 -1.042706
6 -1.311779  0.158454  0.604686

[7 rows x 3 columns]

fillna默认会返回新对象,但也可以对现有对象进行就地修改:

In [39]: # 总是返回被填充对象的引用

In [40]: _ = df.fillna(0, inplace=True)

In [41]: df
Out[41]: 
          0         1         2
0  1.259190  0.000000  0.000000
1  0.181184  0.000000  0.000000
2 -0.305915  0.000000  0.000000
3  1.157043  0.000000 -0.305271
4  0.279270  0.000000 -2.275010
5  0.304463 -0.009671 -1.042706
6 -1.311779  0.158454  0.604686

[7 rows x 3 columns]

对reindex有效的那些插值方法也可用于fillna

In [43]: df = pd.DataFrame(np.random.randn(6, 3))

In [44]: df.ix[2:, 1] = NA; df.ix[4:, 2] = NA

In [45]: df
Out[45]: 
          0         1         2
0  1.268895 -2.015949 -0.435110
1 -0.820148 -1.569651  1.176337
2 -1.488435       NaN  0.585900
3  1.118005       NaN -1.040445
4 -1.583587       NaN       NaN
5 -1.457912       NaN       NaN

[6 rows x 3 columns]

In [46]: df.fillna(method='ffill')
Out[46]: 
          0         1         2
0  1.268895 -2.015949 -0.435110
1 -0.820148 -1.569651  1.176337
2 -1.488435 -1.569651  0.585900
3  1.118005 -1.569651 -1.040445
4 -1.583587 -1.569651 -1.040445
5 -1.457912 -1.569651 -1.040445

[6 rows x 3 columns]

In [47]: df.fillna(method='ffill', limit=2)
Out[47]: 
          0         1         2
0  1.268895 -2.015949 -0.435110
1 -0.820148 -1.569651  1.176337
2 -1.488435 -1.569651  0.585900
3  1.118005 -1.569651 -1.040445
4 -1.583587       NaN -1.040445
5 -1.457912       NaN -1.040445

[6 rows x 3 columns]

只要稍微动动脑子,你就可以利用fillna实现许多别的功能。例如,你可以传入Series的平均值或中位数:

In [48]: data = pd.Series([1., NA, 3.5, NA, 7])

In [49]: data.fillna(data.mean())
Out[49]: 
0    1.000000
1    3.833333
2    3.500000
3    3.833333
4    7.000000
dtype: float64

5、层次化索引

层次化索引(hierarchical indexing)是pandas的一项重要功能,它使你能在一个轴上拥有多个(两个以上)索引级别。抽象点说,它使你能以低维度形式处理高维度数据。我们先来看一个简单的例子:创建一个Series,并用一个由列表或数组组成的列表作为索引。

In [50]: data = pd.Series(np.random.randn(10), index=[['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'd', 'd'], 
                                                      [1, 2, 3, 1, 2, 3, 1, 2, 2, 3]])

In [51]: data
Out[51]: 
a  1   -2.224864
   2   -0.234775
   3   -1.143708
b  1    0.186533
   2    0.251844
   3    0.680129
c  1   -1.360122
   2    0.906973
d  2   -0.727403
   3    1.494060
dtype: float64

这就是带有MultiIndex索引的Series的格式化输出形式。索引之间的“间隔”表示“直接使用上面的标签”:

In [52]: data.index
Out[52]: 
MultiIndex(levels=[[u'a', u'b', u'c', u'd'], [1, 2, 3]],
           labels=[[0, 0, 0, 1, 1, 1, 2, 2, 3, 3], [0, 1, 2, 0, 1, 2, 0, 1, 1, 2]])

对于一个层次化索引的对象,选取数据子集的操作很简单:

In [53]: data['b']
Out[53]: 
1    0.186533
2    0.251844
3    0.680129
dtype: float64

In [54]: data['b':'c']
Out[54]: 
b  1    0.186533
   2    0.251844
   3    0.680129
c  1   -1.360122
   2    0.906973
dtype: float64

In [55]: data[['b', 'd']]
Out[55]: 
b  1    0.186533
   2    0.251844
   3    0.680129
d  2   -0.727403
   3    1.494060
dtype: float64

有时甚至还可以在“内层”中进行选取:

In [1]: import pandas as pd
 
In [2]: import numpy as np

In [3]: data = pd.Series(np.random.randn(10), index=[['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'd', 'd'], 
                                                     [1, 2, 3, 1, 2, 3, 1, 2, 2, 3]])

In [4]: data[:, 2]
Out[4]: 
a    1.199212
b    1.098834
c   -0.483223
d    0.933022
dtype: float64

层次化索引在数据重塑和基于分组的操作(如透视表生成)中扮演着重要的角色。比如,这段数据可以通过其unstack方法被重新安排到一个DataFrame中:

In [5]: data.unstack()
Out[5]: 
          1         2         3
a  1.365918  1.199212  0.006834
b -1.329883  1.098834 -0.179380
c  0.756257 -0.483223       NaN
d       NaN  0.933022 -0.304902

[4 rows x 3 columns]

unstack的逆运算是stack:

In [6]: data.unstack().stack()
Out[6]: 
a  1    1.365918
   2    1.199212
   3    0.006834
b  1   -1.329883
   2    1.098834
   3   -0.179380
c  1    0.756257
   2   -0.483223
d  2    0.933022
   3   -0.304902
dtype: float64

对于一个DataFrame,每条轴都可以有分层索引:

In [7]: frame = pd.DataFrame(np.arange(12).reshape((4, 3)), index=[['a', 'a', 'b', 'b'], [1, 2, 1, 2]], 
                             columns=[['Ohio', 'Ohio', 'Colorado'], ['Green', 'Red', 'Green']])

In [8]: frame
Out[8]: 
      Ohio       Colorado
     Green  Red     Green
a 1      0    1         2
  2      3    4         5
b 1      6    7         8
  2      9   10        11

[4 rows x 3 columns]

各层都可以有名字(可以是字符串,也可以是别的Python对象)。如果指定了名称,它们会显示在控制台输出中(不要将索引名称跟轴标签混为一谈):

In [9]: frame.index.names = ['key1', 'key2']

In [10]: frame.columns.names = ['state', 'color']

In [11]: frame
Out[11]: 
state       Ohio       Colorado
color      Green  Red     Green
key1 key2                      
a    1         0    1         2
     2         3    4         5
b    1         6    7         8
     2         9   10        11

[4 rows x 3 columns]

由于有了分部的列索引,因此可以轻松选取列分组:

In [12]: frame['Ohio']
Out[12]: 
color      Green  Red
key1 key2            
a    1         0    1
     2         3    4
b    1         6    7
     2         9   10

[4 rows x 2 columns]

可以单独创建MultiIndex然后复用。上面那个DataFrame中的(分级的)列可以这样创建:

In [15]: pd.MultiIndex.from_arrays([['Ohio', 'Ohio', 'Colorado'], ['Green', 'Red', 'Green']], names=['state', 'color'])
Out[15]: 
MultiIndex(levels=[[u'Colorado', u'Ohio'], [u'Green', u'Red']],
           labels=[[1, 1, 0], [0, 1, 0]],
           names=[u'state', u'color'])

抱歉!评论已关闭.