pandas是基于numpy的一个高级数据结构和操作的数据分析与探索工具,本文基于pandas API文档对pandas的两个重要的数据结构、基本函数、函数应用、排序以及层次化索引进行分析,对于本文的示例代码做如下约定:
import numpy as np
from pandas import Series, DataFrame
import pandas as pd
1、pandas 数据结构
1.1 Series
Series是一种类似于一维数组的对象,它的索引由参数index指定,创建一个Series的语法如下:
s = Series(data, index=index)
data的类型可以是:Python的dict、numpy的ndarray或标量值
Python的dict
>>> d = {'b' : 1, 'a' : 0, 'c' : 2}
>>> pd.Series(d)
b 1
a 0
c 2
dtype: int64
numpy的ndarray
>>> s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])
>>> s
a 0.4691
b -0.2829
c -1.5091
d -1.1356
e 1.2121
dtype: float64
标量值
>>> pd.Series(5., index=['a', 'b', 'c', 'd', 'e'])
a 5.0
b 5.0
c 5.0
d 5.0
e 5.0
dtype: float64
如果不指定index的值,会自动创建一个0~N-1(N为数据的长度)的整数型索引。
与ndarray类似,你也可以对Series进行切片、索引等操作,如下:
>>> s
a 0.4691
b -0.2829
c -1.5091
d -1.1356
e 1.2121
>>> s[0]
0.4691
>>> s[:3]
a 0.4691
b -0.2829
c -1.5091
dtype: float64
>>> s[s > s.median()]
a 0.4691
e 1.2121
dtype: float64
>>> s[[4, 3, 1]]
e 1.2121
d -1.1356
b -0.2829
dtype: float64
>>> np.exp(s)
a 1.5986
b 0.7536
c 0.2211
d 0.3212
e 3.3606
dtype: float64
同时也可以将Series看出是一个定长的有序字典,如下:
>>> s['a’]
0.4691
>>> s['e'] = 12.
>>> s
a 0.4691
b -0.2829
c -1.5091
d -1.1356
e 12.0000
dtype: float64
>>> 'e' in s
True
>>> 'f' in s
False
Series和ndarray之间的一个关键区别是,Series在算术运算中会自动对齐不同索引的数据。因此,我们在操作Series时不必考虑Series是否具有相同的标签,比如:
>>> s[1:] + s[:-1]
a NaN
b -0.5657
c -3.0181
d -2.2713
e NaN
dtype: float64
在pandas中,如果一个索引在一个Series中而不存在另一个Series中,那么它们操作的结果用NaN(即非数字,not a number)来表示,在pandas中常用它来表示缺失或NA值,pandas的isnull和notnull函数可以来检测Series中的缺失数据。
>>> pd.isnull(s) # 等同于s.isnull()
a True
b False
c False
d False
e True
>>> pd.notnull(s) # 等同于s.notnull()
a False
b True
c True
d True
e False
1.2 DataFrame
DataFrame是一个表格型的数据结构,它包含一组有序的列,每列可以是不同类型的值。DataFrame既有行索引也有列索引,可看成是由Series组成的字典。
创建DataFrame的方式有很多种,下图展示了可以输给DataFrame构造器的数据:
其中最常见的是直接传入一个由等长列表或numpy数组组成的字典:
>>> d = {'one' : [1., 2., 3., 4.],
....: 'two' : [4., 3., 2., 1.]}
....:
>>> pd.DataFrame(d)
one two
0 1.0 4.0
1 2.0 3.0
2 3.0 2.0
3 4.0 1.0
或者由Series组成的字典
>>> d = {'one' : Series([1., 2., 3.], index=['a', 'b', 'c']),
....: 'two' : Series([1., 2., 3., 4.], index=['a', 'b', 'c', 'd'])}
....:
>>> df = DataFrame(d)
>>> df
one two
a 1.0 1.0
b 2.0 2.0
c 3.0 3.0
d NaN 4.0
或者由字典组成的列表
>>> data2 = [{'a': 1, 'b': 2}, {'a': 5, 'b': 10, 'c': 20}]
>>> DataFrame(data2)
a b c
0 1 2 NaN
1 5 10 20.0
>>> df = DataFrame(data2, index=['first', 'second’])
>>> df
a b c
first 1 2 NaN
second 5 10 20.0
处理时间序列数据
>>> index = pd.date_range('1/1/2000', periods=8)
>>> df = pd.DataFrame(np.random.randn(8, 3), index=index, columns=list('ABC'))
>>> df
A B C
2000-01-01 -1.2268 0.7698 -1.2812
2000-01-02 -0.7277 -0.1213 -0.0979
2000-01-03 0.6958 0.3417 0.9597
2000-01-04 -1.1103 -0.6200 0.1497
2000-01-05 -0.7323 0.6877 0.1764
2000-01-06 0.4033 -0.1550 0.3016
2000-01-07 -2.1799 -1.3698 -0.9542
2000-01-08 1.4627 -1.7432 -0.8266
创建bool类型的DataFrame
>>> df1 = pd.DataFrame({'a' : [1, 0, 1], 'b' : [0, 1, 1] }, dtype=bool)
>>> df2 = pd.DataFrame({'a' : [0, 1, 1], 'b' : [1, 1, 0] }, dtype=bool)
>>> df1 & df2
a b
0 False False
1 False True
2 True False
>>> df1 | df2
a b
0 True True
1 True True
2 True True
>>> df1 ^ df2
a b
0 True True
1 True False
2 False True
>>> -df1
a b
0 False True
1 True False
2 False False
DaraFrame的转置:
>>> df.T
first second
a 1 5
b 2 10
c NaN 20.0
DataFrame的索引
和Series类似,DataFrame的索引方式有:
>>> df
one two
a 1.0 1.0
b 2.0 2.0
c 3.0 3.0
d NaN 4.0
>>> df['one’] # 列索引,等同于df.one
a 1.0
b 2.0
c 3.0
d NaN
对DataFrame进行索引其实是获取一个或多个列,但对DataFrame进行切片或布尔型数组时选取的是行:
>>> df[:2]
one two
a 1.0 1.0
b 2.0 2.0
>>> df[df[‘one’] < 3]
one two
a 1.0 1.0
b 2.0 2.0
在DataFrame的行上进行标签索引的方式
>>> df
one bar flag foo one_trunc
a 1.0 1.0 False bar 1.0
b 2.0 2.0 False bar 2.0
c 3.0 3.0 True bar NaN
d NaN NaN False bar NaN
>>> df.loc['b’] # 等同于df.iloc[1]、df.ix[‘b’]、df.ix[1]
one 2
bar 2
flag False
foo bar
one_trunc 2
Name: b, dtype: object
通过索引方式返回的列是相应数据的视图(view),不是副本(copy),故对返回的Series所做的任何就地修改全部会反映到源DataFrame上。
重新索引reindex
Series.reindex(index=None, **kwargs)
调用reindex方法将会根据新索引对Series或DataFrame进行重新索引,reindex函数的参数如下图所示。
如果某个索引值在当前数据结构不存在,那么用缺失值NaN填充,当然也可以使用fill_value参数来指定填充的值,或用mothod参数来指定填充的方式(填充的方式有ffill:前向填充值;bfill:后向填充值),下图展示了mothod参数可选的参数。
>>> rng = pd.date_range('1/3/2000', periods=8)
>>> ts = pd.Series(np.random.randn(8), index=rng)
>>> ts2 = ts[[0, 3, 6]]
>>> ts
2000-01-03 0.466284
2000-01-04 -0.457411
2000-01-05 -0.364060
2000-01-06 0.785367
2000-01-07 -1.463093
2000-01-08 1.187315
2000-01-09 -0.493153
2000-01-10 -1.323445
Freq: D, dtype: float64
>>> ts2
2000-01-03 0.466284
2000-01-06 0.785367
2000-01-09 -0.493153
dtype: float64
>>> ts2.reindex(ts.index)
2000-01-03 0.466284
2000-01-04 NaN
2000-01-05 NaN
2000-01-06 0.785367
2000-01-07 NaN
2000-01-08 NaN
2000-01-09 -0.493153
2000-01-10 NaN
Freq: D, dtype: float64
>>> ts2.reindex(ts.index, method='ffill')
2000-01-03 0.466284
2000-01-04 0.466284
2000-01-05 0.466284
2000-01-06 0.785367
2000-01-07 0.785367
2000-01-08 0.785367
2000-01-09 -0.493153
2000-01-10 -0.493153
Freq: D, dtype: float64
>>> ts2.reindex(ts.index, method='bfill')
2000-01-03 0.466284
2000-01-04 0.785367
2000-01-05 0.785367
2000-01-06 0.785367
2000-01-07 -0.493153
2000-01-08 -0.493153
2000-01-09 -0.493153
2000-01-10 NaN
Freq: D, dtype: float64
>>> ts2.reindex(ts.index, method='nearest')
2000-01-03 0.466284
2000-01-04 0.466284
2000-01-05 0.785367
2000-01-06 0.785367
2000-01-07 0.785367
2000-01-08 -0.493153
2000-01-09 -0.493153
2000-01-10 -0.493153
Freq: D, dtype: float64
为DataFrame添加列(修改也是类似思路,将列表或数组赋值给某个列时,要求其长度必须与DataFrame的长度相匹配;而如果赋值的是一个Series,会自动精确匹配DataFrame的索引,空位将自动被填上缺失值):
>>> df['three'] = df['one'] * df['two’] # 添加列,列索引为“three”
>>> df['flag'] = df['one'] > 2 # 添加列,列索引为“flag”
>>> df
one two three flag
a 1.0 1.0 1.0 False
b 2.0 2.0 4.0 False
c 3.0 3.0 9.0 True
d NaN 4.0 NaN False
>>> val=Series([1.2, 1.5, 1.7], index=[‘a’, ‘c’, ‘d’])
>>> df[‘debt’] = val
>>> df
one two three flag debt
a 1.0 1.0 1.0 False 1.2
b 2.0 2.0 4.0 False NaN
c 3.0 3.0 9.0 True 1.5
d NaN 4.0 NaN False 1.7
也可以在指定位置插入列:
>>> df.insert(1, 'bar', df['one'])
>>> df
one bar two three flag
a 1.0 1.0 1.0 1.0 False
b 2.0 2.0 2.0 4.0 False
c 3.0 3.0 3.0 9.0 True
d NaN NaN 4.0 NaN False
我们也可以删除列:
>>> del df['two’] # 删除列索引为“two”的列
>>> three = df.pop('three’) # 删除列索引为“three”的列,等同于df.drop(’three’, axis=1)
>>> df
one flag
a 1.0 False
b 2.0 False
c 3.0 True
d NaN False
删除行
>>> df.drop([‘c’, ‘d'])
>>> df
one flag
a 1.0 False
b 2.0 False
广播(broadcasting)
>>> df = DataFrame({'one' : pd.Series(np.random.randn(3), index=['a', 'b', 'c']), # 构造DataFrame
....: 'two' : pd.Series(np.random.randn(4), index=['a', 'b', 'c', 'd']),
....: 'three' : pd.Series(np.random.randn(3), index=['b', 'c', 'd'])})
>>> df
one two three
a -1.101558 1.124472 NaN
b -0.177289 2.487104 -0.634293
c 0.462215 -0.486066 1.931194
d NaN -0.456288 -1.222918
>>> row = df.iloc[1] # 取出第二行
>>> column = df['two’] #取出第二列
>>> df.sub(row, axis='columns’) #广播,每一行都减去第二行,等同于df - row
one two three
a -0.924269 -1.362632 NaN
b 0.000000 0.000000 0.000000
c 0.639504 -2.973170 2.565487
d NaN -2.943392 -0.588625
>>> df.sub(row, axis=1)
one two three
a -0.924269 -1.362632 NaN
b 0.000000 0.000000 0.000000
c 0.639504 -2.973170 2.565487
d NaN -2.943392 -0.588625
>>> df.sub(column, axis='index’) # 广播,每一列都减去第二列
one two three
a -2.226031 0.0 NaN
b -2.664393 0.0 -3.121397
c 0.948280 0.0 2.417260
d NaN 0.0 -0.766631
>>> df.sub(column, axis=0)
one two three
a -2.226031 0.0 NaN
b -2.664393 0.0 -3.121397
c 0.948280 0.0 2.417260
d NaN 0.0 -0.766631
参考
pandas API文档:http://pandas.pydata.org/pandas-docs/stable/index.html