目录
概述
Head和Tail
属性和原始的ndarray(Attributes and the raw ndarray(s))
操作性能加速(Accelerated operations)
灵活的二元操作(Flexible binary operations)
匹配/广播行为
缺失数据及其充填操作(Missing data / operations with fill values)
灵活的比较操作(Flexible Comparisons)
布尔值推断(Boolean Reductions)
比较对象是否相等(Comparing if objects are equivalent)
比较类数组对象(Comparing array-like objects)
组合重叠数据集(Combining overlapping data sets)
一般的DataFrame数据合并(General DataFrame Combine)
描述性统计
数据概要(Summarizing data: describe)
最小/最大值的索引(Index of Min/Max Values)
数据计数(直方图)/模式(Value counts (histogramming) / Mode)
离散化和分位数(Discretization and quantiling)
函数应用(Function application)
表级范围应用函数(Tablewise Function Application)
行级或列级范围函数应用(Row or Column-wise Function Application)
元素级范围应用函数(Applying Elementwise Functions)
Agg聚合API
多函数聚合(Aggregating with multiple functions)
字典聚合(Aggregating with a dict)
多类型数据聚合(Mixed Dtypes)
定制统计概要(Custom describe)
Transform API
多函数使用transform(Transform with multiple functions)
字典使用transform(Transforming with a dict)
重新索引和修改标签
与另外的对象进行重索引和对齐(Reindexing to align with another object)
使用align方法使对象彼此对齐(Aligning objects with each other with align)
重新索引时进行填充(Filling while reindexing)
重索引时的填充限制(Limits on filling while reindexing)
删除标签(Dropping labels from an axis)
重命名/映射标签(Renaming / mapping labels)
迭代(Iteration)
iteritems方法
iterrows方法
itertuples方法
.dt 访问器(.dt accessor)
矢量化字符方法(Vectorized string methods)
排序(Sorting)
索引排序(By Index)
按数据值排序(By Values)
索引和数据值结合排序(By Indexes and Values)
searchsorted方法
最大/最小值(smallest / largest values)
多层级标签排序(Sorting by a multi-index column)
复制(Copying)
Dtype
默认类型(defaults)
向上映射(upcasting)
astype方法
对象转换(object conversion)
性能和可延伸性(gotchas)
基于dtype选择列(Selecting columns based on dtype)
本文将介绍Pandas中的一些必要的基础功能,同样只介绍与Series和DataFrame两种数据结构相关的.
先构造出用于演示的对象:
In [1]: index = pd.date_range('1/1/2000', periods=8)
In [2]: s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])
In [3]: df = pd.DataFrame(np.random.randn(8, 3), index=index,
...: columns=['A', 'B', 'C'])
...:
这两个方法可以快速的查看一组数据的小抽样,默认的设置是5行.当然也可以自行设定行数.
head表示表头部分,tail表示表尾部分.
In [5]: long_series = pd.Series(np.random.randn(1000))
In [6]: long_series.head()
Out[6]:
0 0.229453
1 0.304418
2 0.736135
3 -0.859631
4 -0.424100
dtype: float64
In [7]: long_series.tail(3)
Out[7]:
997 -0.351587
998 1.136249
999 -0.448789
dtype: float64
Pandas对象都有一些属性值,用来查看对象的原始数据结构.
shape:返回对象的数据轴的维度
Axis Lable: 轴标签对于Series就是index,对于DataFrame就包含index和columns.
我们知道Pandas对象数据的不可变性,但对于这两个属性来说,是可以在原对象基础上被修改的,而不是返回新的视图.
In [8]: df[:2]
Out[8]:
A B C
2000-01-01 0.048869 -1.360687 -0.47901
2000-01-02 -0.859661 -0.231595 -0.52775
In [9]: df.columns = [x.lower() for x in df.columns]
In [10]: df
Out[10]:
a b c
2000-01-01 0.048869 -1.360687 -0.479010
2000-01-02 -0.859661 -0.231595 -0.527750
2000-01-03 -1.296337 0.150680 0.123836
2000-01-04 0.571764 1.555563 -0.823761
2000-01-05 0.535420 -1.032853 1.469725
2000-01-06 1.304124 1.449735 0.203109
2000-01-07 -1.032011 0.969818 -0.962723
2000-01-08 1.382083 -0.938794 0.669142
如果想要获取Pandas对象中的真正的数据,访问values属性即可:
In [11]: s.values
Out[11]: array([-1.9339, 0.3773, 0.7341, 2.1416, -0.0112])
In [12]: df.values
Out[12]:
array([[ 0.0489, -1.3607, -0.479 ],
[-0.8597, -0.2316, -0.5278],
[-1.2963, 0.1507, 0.1238],
[ 0.5718, 1.5556, -0.8238],
[ 0.5354, -1.0329, 1.4697],
[ 1.3041, 1.4497, 0.2031],
[-1.032 , 0.9698, -0.9627],
[ 1.3821, -0.9388, 0.6691]])
从访问values属性得到的数据结果可以看是ndarray类型的.我们知道ndarray是有dtype属性的(dtype种类很多,可以参阅Numpy).
若DataFrame对象中包含多种数据类型,如果其中含有字符类型的话
那么values属性返回的ndarray的dtype属性就被自适应为object类型
如果仅包含整型和浮点型数字的话,那么ndarray的dtype属性会被调整为float64.
In [11]: df= pd.DataFrame({'a' : [1, 2, 1], 'b' : [1, 'B', 3] })
In [12]: df.values.dtype
Out[12]: object
In [13]: df= pd.DataFrame({'a' : [1, 1.8, 1], 'b' : [1, 2.5, 3] })
In [14]: df.values.dtype
Out[14]: float64
Pandas使用了三方的numexpr库和bottleneck库来对某些数据类型的二进制和bool值操作进行加速.
这些库在处理超大数据集时特别有用,大大的提高了操作速度.
采用100列 X 100000行的数据集测试性能结果如下:
Operation | 0.11.0 (ms) | Prior Version (ms) | Ratio to Prior |
---|---|---|---|
df1 > df2 |
13.32 | 125.35 | 0.1063 |
df1 * df2 |
21.71 | 36.63 | 0.5928 |
df1 + df2 |
22.04 | 36.50 | 0.6039 |
因为性能的大幅提高,强烈建议安装这些三方库,而且这些第三方库的使用都是默认的,当然也可以通过设置关闭:
pd.set_option('compute.use_bottleneck', False)
pd.set_option('compute.use_numexpr', False)
对pandas对象的结构数据进行二元操作,有两点是需要特别注意的:
1.从高维度数据向低维数据的广播(broadcasting )
2.缺失值(Missing Value)的计算中的处理
DataFrame对象的二元操作体现在add(),sub(),mul(),div()等方法,以及与这些方法相关的radd(),rsub()........等等.
对于广播行为来说,Series的输入是最重要的.
在使用二元操作的相关方法中,可以使用axis参数来匹配广播是传播方向,按index传播或者按columns传播.
In [14]: df = pd.DataFrame({'one' : pd.Series(np.random.randn(3), index=['a', 'b', 'c']),
....: 'two' : pd.Series(np.random.randn(4), index=['a', 'b', 'c', 'd']),
....: 'three' : pd.Series(np.random.randn(3), index=['b', 'c', 'd'])})
....:
In [15]: df
Out[15]:
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
In [16]: row = df.iloc[1]
In [17]: column = df['two']
In [18]: df.sub(row, axis='columns')
Out[18]:
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
In [19]: df.sub(row, axis=1)
Out[19]:
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
In [20]: df.sub(column, axis='index')
Out[20]:
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
In [21]: df.sub(column, axis=0)
Out[21]:
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
对于多级索引的数据来说,可以增加一个level参数以控制层级
In [22]: dfmi = df.copy()
In [23]: dfmi.index = pd.MultiIndex.from_tuples([(1,'a'),(1,'b'),(1,'c'),(2,'a')],
....: names=['first','second'])
....:
In [24]: dfmi.sub(column, axis=0, level='second')
Out[24]:
one two three
first second
1 a -2.226031 0.00000 NaN
b -2.664393 0.00000 -3.121397
c 0.948280 0.00000 2.417260
2 a NaN -1.58076 -2.347391
Pandas中的Series对象和Index对象可支持python内置的divmod()方法.该内置方法是以元组的方式返回商和余数.
但在处理Series对象和Index对象时,是以相同索引的Series方式返回结果:
In [28]: s = pd.Series(np.arange(10))
In [29]: s
Out[29]:
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
dtype: int64
In [30]: div, rem = divmod(s, 3)
In [31]: div
Out[31]:
0 0
1 0
2 0
3 1
4 1
5 1
6 2
7 2
8 2
9 3
dtype: int64
In [32]: rem
Out[32]:
0 0
1 1
2 2
3 0
4 1
5 2
6 0
7 1
8 2
9 0
dtype: int64
In [33]: idx = pd.Index(np.arange(10))
In [34]: idx
Out[34]: Int64Index([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype='int64')
In [35]: div, rem = divmod(idx, 3)
In [36]: div
Out[36]: Int64Index([0, 0, 0, 1, 1, 1, 2, 2, 2, 3], dtype='int64')
In [37]: rem
Out[37]: Int64Index([0, 1, 2, 0, 1, 2, 0, 1, 2, 0], dtype='int64')
内置divmod()方法同时也可以元素级别范围的应用:
In [38]: div, rem = divmod(s, [2, 2, 3, 3, 4, 4, 5, 5, 6, 6])
In [39]: div
Out[39]:
0 0
1 0
2 0
3 1
4 1
5 1
6 1
7 1
8 1
9 1
dtype: int64
In [40]: rem
Out[40]:
0 0
1 1
2 2
3 0
4 0
5 1
6 1
7 2
8 2
9 3
dtype: int64
在对Series对象或DataFrame对象进行算术函数方法时,fill_value参数可以指定缺失值的替换值.
需要强调两点,一是只有在相同shape属性的对象进行算术函数且对应位置的元素不同时为缺失值时,fill_value参数才起作用.
因为在Pandas中,两个缺失值之间的计算结果永远是缺失值.
二是存在广播行为的计算中,fill_value也是无起作用的.
In [41]: df
Out[41]:
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
In [42]: df2
Out[42]:
one two three
a -1.101558 1.124472 1.000000
b -0.177289 2.487104 -0.634293
c 0.462215 -0.486066 1.931194
d NaN -0.456288 -1.222918
In [43]: df + df2
Out[43]:
one two three
a -2.203116 2.248945 NaN
b -0.354579 4.974208 -1.268586
c 0.924429 -0.972131 3.862388
d NaN -0.912575 -2.445837
In [44]: df.add(df2, fill_value=0)
Out[44]:
one two three
a -2.203116 2.248945 1.000000
b -0.354579 4.974208 -1.268586
c 0.924429 -0.972131 3.862388
d NaN -0.912575 -2.445837
Pandas中提供了eq,ne,lt,gt,le,ge 等比较方法,操作方式与上面介绍的算术函数的基本一致,这些比较方法返回的都是与原比较数据同shape的bool类型的数据.
在比较中,NaN与任何值(包括标量和NaN)进行大小比较永远都是返回False
只有进行不等于比较永远返回True
np.nan 与 np.nan 进行相等比较时永远是返回False
In [45]: df.gt(df2)
Out[45]:
one two three
a False False False
b False False False
c False False False
d False False False
In [46]: df2.ne(df)
Out[46]:
one two three
a False False True
b False False False
c False False False
d True False False
在Pandas中提供了empty,any(),all(),bool()这几个方法进行布尔值推断.
在DataFrame中,默认是按columns方法进行推断的,可以通过axis参数控制推断方向.
补充下:布尔值推断并不是值判断标量的True或False,而是指仅对bool值进行判断.
注意下面例子中也是对仅含bool值的数据对象进行判断的.
In [47]: (df > 0).all()
Out[47]:
one False
two False
three False
dtype: bool
In [48]: (df > 0).any()
Out[48]:
one True
two True
three True
dtype: bool
也可以进行链式判断:
In [49]: (df > 0).any().any()
Out[49]: True
通过empty属性可以判断对象是否为空:
In [50]: df.empty
Out[50]: False
In [51]: pd.DataFrame(columns=list('ABC')).empty
Out[51]: True
推断仅含单个bool值元素的Pandas对象:
In [52]: pd.Series([True]).bool()
Out[52]: True
In [53]: pd.Series([False]).bool()
Out[53]: False
In [54]: pd.DataFrame([[True]]).bool()
Out[54]: True
In [55]: pd.DataFrame([[False]]).bool()
Out[55]: False
警告:
如果想python语法一样,尝试如下判断,都将报错.
if df:
...
df and df2
ValueError: The truth value of an array is ambiguous. Use a.empty, a.any() or a.all().
一般情况下,我们通过不同的计算方式可以得到同样的结果,比如df+df 和df *2.结合之前的bool值推断方法,
为了判断这两种计算方式的结果是否相同(结果是否出乎意料?):
In [56]: df+df == df*2
Out[56]:
one two three
a True True False
b True True True
c True True True
d False True True
In [57]: (df+df == df*2).all()
Out[57]:
one False
two True
three False
dtype: bool
这是因为对象中包含的了缺失值.因为在Pandas中缺失值进行等于比较永远都是False.
In [58]: np.nan == np.nan
Out[58]: False
所以,Pandas中提供了equals()方法来判断对象是否相同或相等.只要对象中的缺失值都是在同样的位置,则判断为相同或相等.
In [59]: (df+df).equals(df*2)
Out[59]: True
需要注意的,进行相同或相等推断是,对象的index属性也必须一致.
In [60]: df1 = pd.DataFrame({'col':['foo', 0, np.nan]})
In [61]: df2 = pd.DataFrame({'col':[np.nan, 0, 'foo']}, index=[2,1,0])
In [62]: df1.equals(df2)
Out[62]: False
In [63]: df1.equals(df2.sort_index())
Out[63]: True
注意,此处讨论的是array-like对象,在Pandas中,也就只有Series对象和Index对象才是array-like对象.
在元素级别范围上,比较array-like对象和标量是很简单的:
In [64]: pd.Series(['foo', 'bar', 'baz']) == 'foo'
Out[64]:
0 True
1 False
2 False
dtype: bool
In [65]: pd.Index(['foo', 'bar', 'baz']) == 'foo'
Out[65]: array([ True, False, False], dtype=bool)
同样,在元素级别的范围上,也支持两个长度相等的array-like对象的比较:
In [66]: pd.Series(['foo', 'bar', 'baz']) == pd.Index(['foo', 'bar', 'qux'])
Out[66]:
0 True
1 True
2 False
dtype: bool
In [67]: pd.Series(['foo', 'bar', 'baz']) == np.array(['foo', 'bar', 'qux'])
Out[67]:
0 True
1 True
2 False
dtype: bool
如果比较长度不同的两个array-like对象,将发生错误:
In [55]: pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo', 'bar'])
ValueError: Series lengths must match to compare
In [56]: pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo'])
ValueError: Series lengths must match to compare
注意,与Numpy库的方式不同,在Pandas中,ndarray对象比较也是可以进行广播的:
In [68]: np.array([1, 2, 3]) == np.array([2])
Out[68]: array([False, True, False], dtype=bool)
但如果两个比较对象之间不能进行广播,则返回False:
In [69]: np.array([1, 2, 3]) == np.array([1, 2])
Out[69]: False
当两个相似数据集进行重叠合并时,我们是希望将第一个数据中的缺失值替换为第一个数据中的已知的值.
这时可以使用combine_first方法:
In [70]: df1 = pd.DataFrame({'A' : [1., np.nan, 3., 5., np.nan],
....: 'B' : [np.nan, 2., 3., np.nan, 6.]})
....:
In [71]: df2 = pd.DataFrame({'A' : [5., 2., 4., np.nan, 3., 7.],
....: 'B' : [np.nan, np.nan, 3., 4., 6., 8.]})
....:
In [72]: df1
Out[72]:
A B
0 1.0 NaN
1 NaN 2.0
2 3.0 3.0
3 5.0 NaN
4 NaN 6.0
In [73]: df2
Out[73]:
A B
0 5.0 NaN
1 2.0 NaN
2 4.0 3.0
3 NaN 4.0
4 3.0 6.0
5 7.0 8.0
In [74]: df1.combine_first(df2)
Out[74]:
A B
0 1.0 NaN
1 2.0 2.0
2 3.0 3.0
3 5.0 4.0
4 3.0 6.0
5 7.0 8.0
实质上,combine_first方法是调用的DataFrame的combine方法.
该方法接收一个与要合并的Dataframe对象和一个合并函数
首先将输入的DataFrame对象进行数据对齐后,调用合并函数进行数据合并:
combiner = lambda x, y: np.where(pd.isna(x), y, x)
df1.combine(df,combiner)
Out[139]:
one two three
a 2.0 9 10.0
b 6.0 5 1.0
c 5.0 8 100.0
d 10.0 6 1.0
Pandas中提供许多的描述性统计方法,有些返回聚合数据后的结构,如sum,mean等
有些返回一个和原数据相同shape的数据集,如:cumsum,cumprod等
总得来说,这些方法都支持通过指定asix参数从而控制计算的轴方向.
axis参数可以是轴的名称,不如'index','columns',或者整数,如0,1.
n [77]: df
Out[77]:
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
In [78]: df.mean(0)
Out[78]:
one -0.272211
two 0.667306
three 0.024661
dtype: float64
In [79]: df.mean(1)
Out[79]:
a 0.011457
b 0.558507
c 0.635781
d -0.839603
dtype: float64
所以的方法都有一个skipna参数,默认为True,可以指定是否忽略缺失值.
In [80]: df.sum(0, skipna=False)
Out[80]:
one NaN
two 2.669223
three NaN
dtype: float64
In [81]: df.sum(axis=1, skipna=True)
Out[81]:
a 0.022914
b 1.675522
c 1.907343
d -1.679206
dtype: float64
结合广播/算术行为,可以很简洁地描述各种统计过程,如标准化(使数据的均值为零,标准差为1):
In [82]: ts_stand = (df - df.mean()) / df.std()
In [83]: ts_stand.std()
Out[83]:
one 1.0
two 1.0
three 1.0
dtype: float64
In [84]: xs_stand = df.sub(df.mean(1), axis=0).div(df.std(1), axis=0)
In [85]: xs_stand.std(1)
Out[85]:
a 1.0
b 1.0
c 1.0
d 1.0
dtype: float64
注意cumsum()方法和comprod()方法都保留了缺失值NAN,而expanding()方法和rolling()方法则不同
关于这方面更多的信息,可以参阅: Pandas的计算工具.
下面是一个常用函数的快速参考汇总表。每个方法还接受一个可选的level参数,该参数仅适用于对象具有分层索引的情况。
注意,一些NumPy方法,如mean、std和sum,在默认情况下会排除缺失值:
In [87]: np.mean(df['one'])
Out[87]: -0.27221094480450114
In [88]: np.mean(df['one'].values)
Out[88]: nan
Series.nunique() 方法返回Series中非缺失值的唯一性数据的数量.
In [89]: series = pd.Series(np.random.randn(500))
In [90]: series[20:500] = np.nan
In [91]: series[10:20] = 5
In [92]: series.nunique()
Out[92]: 11
使用describe()方法可以快速统计Series对象或者DataFrame对象的摘要数据:
In [93]: series = pd.Series(np.random.randn(1000))
In [94]: series[::2] = np.nan
In [95]: series.describe()
Out[95]:
count 500.000000
mean -0.032127
std 1.067484
min -3.463789
25% -0.725523
50% -0.053230
75% 0.679790
max 3.120271
dtype: float64
In [96]: frame = pd.DataFrame(np.random.randn(1000, 5), columns=['a', 'b', 'c', 'd', 'e'])
In [97]: frame.iloc[::2] = np.nan
In [98]: frame.describe()
Out[98]:
a b c d e
count 500.000000 500.000000 500.000000 500.000000 500.000000
mean -0.045109 -0.052045 0.024520 0.006117 0.001141
std 1.029268 1.002320 1.042793 1.040134 1.005207
min -2.915767 -3.294023 -3.610499 -2.907036 -3.010899
25% -0.763783 -0.720389 -0.609600 -0.665896 -0.682900
50% -0.086033 -0.048843 0.006093 0.043191 -0.001651
75% 0.663399 0.620980 0.728382 0.735973 0.656439
max 3.400646 2.925597 3.416896 3.331522 3.007143
同时可以通过percentiles参数指定自定义的分位数(中位数总是默认显示的):
In [99]: series.describe(percentiles=[.05, .25, .75, .95])
Out[99]:
count 500.000000
mean -0.032127
std 1.067484
min -3.463789
5% -1.733545
25% -0.725523
50% -0.053230
75% 0.679790
95% 1.854383
max 3.120271
dtype: float64
对应非数字类型的Series对象,describe()方法将给出唯一值和最常见值的数量:
In [100]: s = pd.Series(['a', 'a', 'b', 'b', 'a', 'a', np.nan, 'c', 'd', 'a'])
In [101]: s.describe()
Out[101]:
count 9
unique 4
top a
freq 5
dtype: object
注意,在多类型混合的DataFrame对象中,describle()方法将只对数字类型的columns进行摘要统计
如不存在数字类型的columns,则类似Series对象一样,返回出唯一值和最常见值的数量
In [102]: frame = pd.DataFrame({'a': ['Yes', 'Yes', 'No', 'No'], 'b': range(4)})
In [103]: frame.describe()
Out[103]:
b
count 4.000000
mean 1.500000
std 1.290994
min 0.000000
25% 0.750000
50% 1.500000
75% 2.250000
max 3.000000
可以通过include和exclude两个参数指定列表的方式控制describle方法的这种行为
特殊值all也可以作为include和exclude两个参数的值:
In [104]: frame.describe(include=['object'])
Out[104]:
a
count 4
unique 2
top Yes
freq 2
In [105]: frame.describe(include=['number'])
Out[105]:
b
count 4.000000
mean 1.500000
std 1.290994
min 0.000000
25% 0.750000
50% 1.500000
75% 2.250000
max 3.000000
In [106]: frame.describe(include='all')
Out[106]:
a b
count 4 4.000000
unique 2 NaN
top Yes NaN
freq 2 NaN
mean NaN 1.500000
std NaN 1.290994
min NaN 0.000000
25% NaN 0.750000
50% NaN 1.500000
75% NaN 2.250000
max NaN 3.000000
使用idxmax()/idxmin()方法查找Series对象或DataFrame对象中的最大/最小值的索引值(Index):
In [107]: s1 = pd.Series(np.random.randn(5))
In [108]: s1
Out[108]:
0 -1.649461
1 0.169660
2 1.246181
3 0.131682
4 -2.001988
dtype: float64
In [109]: s1.idxmin(), s1.idxmax()
Out[109]: (4, 2)
In [110]: df1 = pd.DataFrame(np.random.randn(5,3), columns=['A','B','C'])
In [111]: df1
Out[111]:
A B C
0 -1.273023 0.870502 0.214583
1 0.088452 -0.173364 1.207466
2 0.546121 0.409515 -0.310515
3 0.585014 -0.490528 -0.054639
4 -0.239226 0.701089 0.228656
In [112]: df1.idxmin(axis=0)
Out[112]:
A 0
B 3
C 2
dtype: int64
In [113]: df1.idxmax(axis=1)
Out[113]:
0 B
1 C
2 A
3 A
4 B
dtype: object
当多行或者多列同时存在查找的最值时,仅返回第一个最值的索引值:
In [114]: df3 = pd.DataFrame([2, 1, 1, 3, np.nan], columns=['A'], index=list('edcba'))
In [115]: df3
Out[115]:
A
e 2.0
d 1.0
c 1.0
b 3.0
a NaN
In [116]: df3['A'].idxmin()
Out[116]: 'd'
其实idxmax()/idxmin()方法是调用的Numpy中的argmax和agrmin方法.
value_counts方法可以进行元素计数统计,但仅适用于Series对象或者维度为1的序列,DataFrame不适用:
In [117]: data = np.random.randint(0, 7, size=50)
In [118]: data
Out[118]:
array([3, 3, 0, 2, 1, 0, 5, 5, 3, 6, 1, 5, 6, 2, 0, 0, 6, 3, 3, 5, 0, 4, 3,
3, 3, 0, 6, 1, 3, 5, 5, 0, 4, 0, 6, 3, 6, 5, 4, 3, 2, 1, 5, 0, 1, 1,
6, 4, 1, 4])
In [119]: s = pd.Series(data)
In [120]: s.value_counts()
Out[120]:
3 11
0 9
5 8
6 7
1 7
4 5
2 3
dtype: int64
In [121]: pd.value_counts(data)
Out[121]:
3 11
0 9
5 8
6 7
1 7
4 5
2 3
dtype: int64
而mode方法可以统计最高频的元素,该方法可以适用Seies和DataFrame:
s = pd.Series([1, 1, 3, 3, 3, 3, 5, 7, 7, 7])
s.mode()
Out[189]:
0 3
dtype: int64
df5 = pd.DataFrame({"A": np.random.randint(0, 7, size=50),
.....: "B": np.random.randint(-10, 15, size=50)})
df5.mode()
Out[193]:
A B
0 2 -7
连续数据可以使用cut方法(基于值)和qcut方法(基于分位数)将其离散化
In [126]: arr = np.random.randn(20)
In [127]: factor = pd.cut(arr, 4)
In [128]: factor
Out[128]:
[(-2.611, -1.58], (0.473, 1.499], (-2.611, -1.58], (-1.58, -0.554], (-0.554, 0.473], ..., (0.473, 1.499], (0.473, 1.499], (-0.554, 0.473], (-0.554, 0.473], (-0.554, 0.473]]
Length: 20
Categories (4, interval[float64]): [(-2.611, -1.58] < (-1.58, -0.554] < (-0.554, 0.473] <
(0.473, 1.499]]
In [129]: factor = pd.cut(arr, [-5, -1, 0, 1, 5])
In [130]: factor
Out[130]:
[(-5, -1], (0, 1], (-5, -1], (-1, 0], (-1, 0], ..., (1, 5], (1, 5], (-1, 0], (-1, 0], (-1, 0]]
Length: 20
Categories (4, interval[int64]): [(-5, -1] < (-1, 0] < (0, 1] < (1, 5]]
qcut方法计算样本分位数,例如我们可以将一些正态分布的数据分割成等大小的四分位数:
In [131]: arr = np.random.randn(30)
In [132]: factor = pd.qcut(arr, [0, .25, .5, .75, 1])
In [133]: factor
Out[133]:
[(0.544, 1.976], (0.544, 1.976], (-1.255, -0.375], (0.544, 1.976], (-0.103, 0.544], ..., (-0.103, 0.544], (0.544, 1.976], (-0.103, 0.544], (-1.255, -0.375], (-0.375, -0.103]]
Length: 30
Categories (4, interval[float64]): [(-1.255, -0.375] < (-0.375, -0.103] < (-0.103, 0.544] <
(0.544, 1.976]]
In [134]: pd.value_counts(factor)
Out[134]:
(0.544, 1.976] 8
(-1.255, -0.375] 8
(-0.103, 0.544] 7
(-0.375, -0.103] 7
dtype: int64
还可以传递无限个值来定义容器:
In [135]: arr = np.random.randn(20)
In [136]: factor = pd.cut(arr, [-np.inf, 0, np.inf])
In [137]: factor
Out[137]:
[(0.0, inf], (0.0, inf], (0.0, inf], (0.0, inf], (-inf, 0.0], ..., (-inf, 0.0], (-inf, 0.0], (0.0, inf], (-inf, 0.0], (0.0, inf]]
Length: 20
Categories (2, interval[float64]): [(-inf, 0.0] < (0.0, inf]]
将自己编写的函数或者其他三方库的函数应用到Pandas数据对象上时,函数是否被合理的应用,
取决于函数要应用的范围,要搞清楚范围,那就必须清楚下面三个函数应用方法的作用范围.
1.表级范围应用函数:pipe()
2.行级或列级范围应用函数:apply()
3.元素级范围应用函数:applymap()
4.聚合API方法:agg()和transform()
Pandas的数据对象Series和DataFrame当然也可以作为参数,传递给其他函数使用.
但如果某个函数需要链式调用的话,伪代码如下:
# f, g, and h are functions taking and returning ``DataFrames``
f(g(h(df), arg1=1), arg2=2, arg3=3)
这种情况可以考虑使用pipe()方法了,以下代码与之前的伪代码功能一致:
(df.pipe(h)
.pipe(g, arg1=1)
.pipe(f, arg2=2, arg3=3)
)
在Pandas中,更鼓励使用第二种方式.使用pipe方法来进行链式调用会更加的简单和清晰.
在上面的例子中,函数f、g和h都期望DataFrame作为第一个位置参数。
如果希望应用的函数将其数据作为第二个参数,该怎么办?
在这种情况下,pipe方法提供一个元组(callble,data_keyword),可以将指定的DataFrame数据的路径传入.
例如,我们可以使用statsmodels拟合回归。它们的API首先需要一个公式,然后第二个参数是DataFrame,即data.
我们将元组(sm.ols, 'data')作为参数传入给pipe方法:
In [138]: import statsmodels.formula.api as sm
In [139]: bb = pd.read_csv('data/baseball.csv', index_col='id')
In [140]: (bb.query('h > 0')
.....: .assign(ln_h = lambda df: np.log(df.h))
.....: .pipe((sm.ols, 'data'), 'hr ~ ln_h + year + g + C(lg)')
.....: .fit()
.....: .summary()
.....: )
.....:
Out[140]:
"""
OLS Regression Results
==============================================================================
Dep. Variable: hr R-squared: 0.685
Model: OLS Adj. R-squared: 0.665
Method: Least Squares F-statistic: 34.28
Date: Sun, 05 Aug 2018 Prob (F-statistic): 3.48e-15
Time: 11:57:36 Log-Likelihood: -205.92
No. Observations: 68 AIC: 421.8
Df Residuals: 63 BIC: 432.9
Df Model: 4
Covariance Type: nonrobust
===============================================================================
coef std err t P>|t| [0.025 0.975]
-------------------------------------------------------------------------------
Intercept -8484.7720 4664.146 -1.819 0.074 -1.78e+04 835.780
C(lg)[T.NL] -2.2736 1.325 -1.716 0.091 -4.922 0.375
ln_h -1.3542 0.875 -1.547 0.127 -3.103 0.395
year 4.2277 2.324 1.819 0.074 -0.417 8.872
g 0.1841 0.029 6.258 0.000 0.125 0.243
==============================================================================
Omnibus: 10.875 Durbin-Watson: 1.999
Prob(Omnibus): 0.004 Jarque-Bera (JB): 17.298
Skew: 0.537 Prob(JB): 0.000175
Kurtosis: 5.225 Cond. No. 1.49e+07
==============================================================================
Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 1.49e+07. This might indicate that there are
strong multicollinearity or other numerical problems.
"""
pipe方法受到了niux系统pipe方法和新的数据操作的语法(dplyr和magrittr)的影响,如果有兴趣可以阅读pipe的源码.
可以使用apply()方法沿着DataFrame的轴应用任意函数,它与描述性统计方法一样,提供了一个可选的axis参数:
In [141]: df.apply(np.mean)
Out[141]:
one -0.272211
two 0.667306
three 0.024661
dtype: float64
In [142]: df.apply(np.mean, axis=1)
Out[142]:
a 0.011457
b 0.558507
c 0.635781
d -0.839603
dtype: float64
In [143]: df.apply(lambda x: x.max() - x.min())
Out[143]:
one 1.563773
two 2.973170
three 3.154112
dtype: float64
In [144]: df.apply(np.cumsum)
Out[144]:
one two three
a -1.101558 1.124472 NaN
b -1.278848 3.611576 -0.634293
c -0.816633 3.125511 1.296901
d NaN 2.669223 0.073983
In [145]: df.apply(np.exp)
Out[145]:
one two three
a 0.332353 3.078592 NaN
b 0.837537 12.026397 0.53031
c 1.587586 0.615041 6.89774
d NaN 0.633631 0.29437
apply方法还可以接收一个描述性统计方法的名称字符为参数:
In [146]: df.apply('mean')
Out[146]:
one -0.272211
two 0.667306
three 0.024661
dtype: float64
In [147]: df.apply('mean', axis=1)
Out[147]:
a 0.011457
b 0.558507
c 0.635781
d -0.839603
dtype: float64
传递给apply()方法的函数的返回值类型影响DataFrame最终输出的类型。默认为规则如下:
1.如果被应用的函数返回一个Series,那么最后输出就是一个DataFrame。列标签会与返回的Series的index进行匹配
2.如果被应用的函数返回其他任何类型,则最终输出是一个Series.
可以使用result_type参数来覆盖此默认行为,该参数接受三个选项:reduce、broadcast和expand。
这将决定like-list类型的返回值如何扩展(或不扩展)到一个DataFrame。
apply()结合一些巧妙的方法,可以用来查询数据集的数据。例如,想提取每个列的最大值发生的日期:
In [148]: tsdf = pd.DataFrame(np.random.randn(1000, 3), columns=['A', 'B', 'C'],
.....: index=pd.date_range('1/1/2000', periods=1000))
.....:
In [149]: tsdf.apply(lambda x: x.idxmax())
Out[149]:
A 2001-04-25
B 2002-05-31
C 2002-09-25
dtype: datetime64[ns]
还可以将其他参数和关键字参数传递给apply()方法。例如,想应用如下的功能:
def subtract_and_divide(x, sub, divide=1):
return (x - sub) / divide
可以这样使用:
df.apply(subtract_and_divide, args=(5,), divide=3)
另一个有用的特性是能够传递Series方法来对每一列或每一行执行一些Series操作:
In [150]: tsdf
Out[150]:
A B C
2000-01-01 -0.720299 0.546303 -0.082042
2000-01-02 0.200295 -0.577554 -0.908402
2000-01-03 0.102533 1.653614 0.303319
2000-01-04 NaN NaN NaN
2000-01-05 NaN NaN NaN
2000-01-06 NaN NaN NaN
2000-01-07 NaN NaN NaN
2000-01-08 0.532566 0.341548 0.150493
2000-01-09 0.330418 1.761200 0.567133
2000-01-10 -0.251020 1.020099 1.893177
In [151]: tsdf.apply(pd.Series.interpolate)
Out[151]:
A B C
2000-01-01 -0.720299 0.546303 -0.082042
2000-01-02 0.200295 -0.577554 -0.908402
2000-01-03 0.102533 1.653614 0.303319
2000-01-04 0.188539 1.391201 0.272754
2000-01-05 0.274546 1.128788 0.242189
2000-01-06 0.360553 0.866374 0.211624
2000-01-07 0.446559 0.603961 0.181059
2000-01-08 0.532566 0.341548 0.150493
2000-01-09 0.330418 1.761200 0.567133
2000-01-10 -0.251020 1.020099 1.893177
最后补充一点,apply()接受一个默认为False的raw参数,该参数在应用函数之前将每一行或每一列转换成一个Series对象。当设置为True时,传递的函数将接收一个ndarray对象,如果不需要Series的索引功能,那么raw设置为True将改善执行性能。
不是所有的函数都可以向量化(接受NumPy数组并返回另一个数组或值),因此DataFrame上的applymap()方法和Series上的map()方法类似,都可以接受 参数为单个值且返回单个值的任何Python函数。例如:
In [188]: df4
Out[188]:
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
In [189]: f = lambda x: len(str(x))
In [190]: df4['one'].map(f)
Out[190]:
a 19
b 20
c 18
d 3
Name: one, dtype: int64
In [191]: df4.applymap(f)
Out[191]:
one two three
a 19 18 3
b 20 18 19
c 18 20 18
d 3 19 19
Series.map()方法有一个额外的特性
可以用来“链接”或“映射”由定义的值。这与Pandas的合并/连接功能密切相关:
In [192]: s = pd.Series(['six', 'seven', 'six', 'seven', 'six'],
.....: index=['a', 'b', 'c', 'd', 'e'])
.....:
In [193]: t = pd.Series({'six' : 6., 'seven' : 7.})
In [194]: s
Out[194]:
a six
b seven
c six
d seven
e six
dtype: object
In [195]: s.map(t)
Out[195]:
a 6.0
b 7.0
c 6.0
d 7.0
e 6.0
dtype: float64
聚合API允许以一种简洁的方式进行可能的多个聚合操作。
聚合的入口点是DataFrame.aggregate(),或者别名DataFrame.agg()。
以之前使用过的类似的数据为例子:
In [152]: tsdf = pd.DataFrame(np.random.randn(10, 3), columns=['A', 'B', 'C'],
.....: index=pd.date_range('1/1/2000', periods=10))
.....:
In [153]: tsdf.iloc[3:7] = np.nan
In [154]: tsdf
Out[154]:
A B C
2000-01-01 0.170247 -0.916844 0.835024
2000-01-02 1.259919 0.801111 0.445614
2000-01-03 1.453046 2.430373 0.653093
2000-01-04 NaN NaN NaN
2000-01-05 NaN NaN NaN
2000-01-06 NaN NaN NaN
2000-01-07 NaN NaN NaN
2000-01-08 -1.874526 0.569822 -0.609644
2000-01-09 0.812462 0.565894 -1.461363
2000-01-10 -0.985475 1.388154 -0.078747
和apply()方法类似,可以将函数或者函数名称字符作为参数传入,会返回一个聚合数据后的Series.
In [155]: tsdf.agg(np.sum)
Out[155]:
A 0.835673
B 4.838510
C -0.216025
dtype: float64
In [156]: tsdf.agg('sum')
Out[156]:
A 0.835673
B 4.838510
C -0.216025
dtype: float64
# 以上两个方法等同于:
In [157]: tsdf.sum()
Out[157]:
A 0.835673
B 4.838510
C -0.216025
dtype: float64
单个的Series聚合将返回一个标量值:
In [158]: tsdf.A.agg('sum')
Out[158]: 0.83567297915820504
可以将多个函数的名称字符以列表形式传递,传递的每个函数的结果都是返回的DataFrame中的一行,行名称是传入的函数名。
In [159]: tsdf.agg(['sum'])
Out[159]:
A B C
sum 0.835673 4.83851 -0.216025
In [160]: tsdf.agg(['sum', 'mean'])
Out[160]:
A B C
sum 0.835673 4.838510 -0.216025
mean 0.139279 0.806418 -0.036004
对应Series对象来说,返回值也是一个Series,由传入的函数名作为索引值.
In [161]: tsdf.A.agg(['sum', 'mean'])
Out[161]:
sum 0.835673
mean 0.139279
Name: A, dtype: float64
如果传入的是匿名函数,那么就将以
In [162]: tsdf.A.agg(['sum', lambda x: x.mean()])
Out[162]:
sum 0.835673
0.139279
Name: A, dtype: float64
如果传入的是自定义函数,那么函数名将作为名称:
In [163]: def mymean(x):
.....: return x.mean()
.....:
In [164]: tsdf.A.agg(['sum', mymean])
Out[164]:
sum 0.835673
mymean 0.139279
Name: A, dtype: float64
传入以列标签为主键,应用函数为值的字典,可以自定义哪些函数应用于哪些列。
请注意,结果有特定的顺序,可以使用OrderedDict来保证顺序.
In [165]: tsdf.agg({'A': 'mean', 'B': 'sum'})
Out[165]:
A 0.139279
B 4.838510
dtype: float64
如果传入的应用函数是列表形式,那么将得到以函数名为行标签的DataFrame对象.
没有执行对应函数的列将被标记为缺失值.
In [166]: tsdf.agg({'A': ['mean', 'min'], 'B': 'sum'})
Out[166]:
A B
mean 0.139279 NaN
min -1.874526 NaN
sum NaN 4.83851
当聚合多类型数据时,Agg方法只接受能够有效聚合的类型的数据,无法聚合的将被忽略.
In [167]: mdf = pd.DataFrame({'A': [1, 2, 3],
.....: 'B': [1., 2., 3.],
.....: 'C': ['foo', 'bar', 'baz'],
.....: 'D': pd.date_range('20130101', periods=3)})
.....:
In [168]: mdf.dtypes
Out[168]:
A int64
B float64
C object
D datetime64[ns]
dtype: object
In [169]: mdf.agg(['min', 'sum'])
Out[169]:
A B C D
min 1 1.0 bar 2013-01-01
使用agg方法自定义要应用的函数,可以起到类似于descirble方法的作用
In [170]: from functools import partial
In [171]: q_25 = partial(pd.Series.quantile, q=0.25)
In [172]: q_25.__name__ = '25%'
In [173]: q_75 = partial(pd.Series.quantile, q=0.75)
In [174]: q_75.__name__ = '75%'
In [175]: tsdf.agg(['count', 'mean', 'std', 'min', q_25, 'median', q_75, 'max'])
Out[175]:
A B C
count 6.000000 6.000000 6.000000
mean 0.139279 0.806418 -0.036004
std 1.323362 1.100830 0.874990
min -1.874526 -0.916844 -1.461363
25% -0.696544 0.566876 -0.476920
median 0.491354 0.685467 0.183433
75% 1.148055 1.241393 0.601223
max 1.453046 2.430373 0.835024
transform()方法返回与原始对象索引和size大小都相同的对象.
这个API允许您同时进行多个函数应用操作,而不是逐个应用操作。非常类似于Agg的API。
同样,创建一个用于举例的数据:
In [176]: tsdf = pd.DataFrame(np.random.randn(10, 3), columns=['A', 'B', 'C'],
.....: index=pd.date_range('1/1/2000', periods=10))
.....:
In [177]: tsdf.iloc[3:7] = np.nan
In [178]: tsdf
Out[178]:
A B C
2000-01-01 -0.578465 -0.503335 -0.987140
2000-01-02 -0.767147 -0.266046 1.083797
2000-01-03 0.195348 0.722247 -0.894537
2000-01-04 NaN NaN NaN
2000-01-05 NaN NaN NaN
2000-01-06 NaN NaN NaN
2000-01-07 NaN NaN NaN
2000-01-08 -0.556397 0.542165 -0.308675
2000-01-09 -1.010924 -0.672504 -1.139222
2000-01-10 0.354653 0.563622 -0.365106
同样,也支持传入函数名称字符,函数名,自定义函数已经匿名函数:
In [179]: tsdf.transform(np.abs)
Out[179]:
A B C
2000-01-01 0.578465 0.503335 0.987140
2000-01-02 0.767147 0.266046 1.083797
2000-01-03 0.195348 0.722247 0.894537
2000-01-04 NaN NaN NaN
2000-01-05 NaN NaN NaN
2000-01-06 NaN NaN NaN
2000-01-07 NaN NaN NaN
2000-01-08 0.556397 0.542165 0.308675
2000-01-09 1.010924 0.672504 1.139222
2000-01-10 0.354653 0.563622 0.365106
In [180]: tsdf.transform('abs')
Out[180]:
A B C
2000-01-01 0.578465 0.503335 0.987140
2000-01-02 0.767147 0.266046 1.083797
2000-01-03 0.195348 0.722247 0.894537
2000-01-04 NaN NaN NaN
2000-01-05 NaN NaN NaN
2000-01-06 NaN NaN NaN
2000-01-07 NaN NaN NaN
2000-01-08 0.556397 0.542165 0.308675
2000-01-09 1.010924 0.672504 1.139222
2000-01-10 0.354653 0.563622 0.365106
In [181]: tsdf.transform(lambda x: x.abs())
Out[181]:
A B C
2000-01-01 0.578465 0.503335 0.987140
2000-01-02 0.767147 0.266046 1.083797
2000-01-03 0.195348 0.722247 0.894537
2000-01-04 NaN NaN NaN
2000-01-05 NaN NaN NaN
2000-01-06 NaN NaN NaN
2000-01-07 NaN NaN NaN
2000-01-08 0.556397 0.542165 0.308675
2000-01-09 1.010924 0.672504 1.139222
2000-01-10 0.354653 0.563622 0.365106
上面例子中,transform接收的是单个函数,其作用类似于使用Numpy的一元函数:
In [182]: np.abs(tsdf)
Out[182]:
A B C
2000-01-01 0.578465 0.503335 0.987140
2000-01-02 0.767147 0.266046 1.083797
2000-01-03 0.195348 0.722247 0.894537
2000-01-04 NaN NaN NaN
2000-01-05 NaN NaN NaN
2000-01-06 NaN NaN NaN
2000-01-07 NaN NaN NaN
2000-01-08 0.556397 0.542165 0.308675
2000-01-09 1.010924 0.672504 1.139222
2000-01-10 0.354653 0.563622 0.365106
同样的,对Series对象运用 transform方法也将得到一个Series对象:
In [183]: tsdf.A.transform(np.abs)
Out[183]:
2000-01-01 0.578465
2000-01-02 0.767147
2000-01-03 0.195348
2000-01-04 NaN
2000-01-05 NaN
2000-01-06 NaN
2000-01-07 NaN
2000-01-08 0.556397
2000-01-09 1.010924
2000-01-10 0.354653
Freq: D, Name: A, dtype: float64
为transform方法传递多个函数将生成一个多重列标签的DataFrame。
第一层是原始的DataFrame对象的列名;第二层是转换应用函数的名称。
In [184]: tsdf.transform([np.abs, lambda x: x+1])
Out[184]:
A B C
absolute absolute absolute
2000-01-01 0.578465 0.421535 0.503335 0.496665 0.987140 0.012860
2000-01-02 0.767147 0.232853 0.266046 0.733954 1.083797 2.083797
2000-01-03 0.195348 1.195348 0.722247 1.722247 0.894537 0.105463
2000-01-04 NaN NaN NaN NaN NaN NaN
2000-01-05 NaN NaN NaN NaN NaN NaN
2000-01-06 NaN NaN NaN NaN NaN NaN
2000-01-07 NaN NaN NaN NaN NaN NaN
2000-01-08 0.556397 0.443603 0.542165 1.542165 0.308675 0.691325
2000-01-09 1.010924 -0.010924 0.672504 0.327496 1.139222 -0.139222
2000-01-10 0.354653 1.354653 0.563622 1.563622 0.365106 0.634894
对Series对象进行多个函数的transform方法将得到一个DataFrame,列名将是应用的函数。
In [185]: tsdf.A.transform([np.abs, lambda x: x+1])
Out[185]:
absolute
2000-01-01 0.578465 0.421535
2000-01-02 0.767147 0.232853
2000-01-03 0.195348 1.195348
2000-01-04 NaN NaN
2000-01-05 NaN NaN
2000-01-06 NaN NaN
2000-01-07 NaN NaN
2000-01-08 0.556397 0.443603
2000-01-09 1.010924 -0.010924
2000-01-10 0.354653 1.354653
与Agg类似,传入以列标签为主键,应用函数为值的字典,可以自定义哪些函数应用于哪些列。
In [186]: tsdf.transform({'A': np.abs, 'B': lambda x: x+1})
Out[186]:
A B
2000-01-01 0.578465 0.496665
2000-01-02 0.767147 0.733954
2000-01-03 0.195348 1.722247
2000-01-04 NaN NaN
2000-01-05 NaN NaN
2000-01-06 NaN NaN
2000-01-07 NaN NaN
2000-01-08 0.556397 1.542165
2000-01-09 1.010924 0.327496
2000-01-10 0.354653 1.563622
若字典的键值是多个应用函数构造的列表,则
In [187]: tsdf.transform({'A': np.abs, 'B': [lambda x: x+1, 'sqrt']})
Out[187]:
A B
absolute sqrt
2000-01-01 0.578465 0.496665 NaN
2000-01-02 0.767147 0.733954 NaN
2000-01-03 0.195348 1.722247 0.849851
2000-01-04 NaN NaN NaN
2000-01-05 NaN NaN NaN
2000-01-06 NaN NaN NaN
2000-01-07 NaN NaN NaN
2000-01-08 0.556397 1.542165 0.736318
2000-01-09 1.010924 0.327496 NaN
2000-01-10 0.354653 1.563622 0.750748
reindex()是Pandas中数据对齐的基本方法。几乎所有其他依赖于标签对齐功能的实现都依赖于它.
重新索引意味着所有数据要根据给定索引在指定的轴方向上进行重新匹配.
reindex方法可以实现以下三个方面:
1.重新排序现有数据以匹配新标签
2.在匹配不到新标签的数据的位置重新填充缺失值
3.如果有指定逻辑,则逻辑方法重新填充缺失数据
看一个例子:
In [216]: s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])
In [217]: s
Out[217]:
a -0.454087
b -0.360309
c -0.951631
d -0.535459
e 0.835231
dtype: float64
In [218]: s.reindex(['e', 'b', 'f', 'd'])
Out[218]:
e 0.835231
b -0.360309
f NaN
d -0.535459
dtype: float64
此处,f标签并不在原数据中,所以重新匹配索引后被标记为缺失值了.
对DataFrame数据,可以重新索引行标签和列标签
In [219]: df
Out[219]:
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
In [220]: df.reindex(index=['c', 'f', 'b'], columns=['three', 'two', 'one'])
Out[220]:
three two one
c 1.931194 -0.486066 0.462215
f NaN NaN NaN
b -0.634293 2.487104 -0.177289
也可以使用axis参数:
In [221]: df.reindex(['c', 'f', 'b'], axis='index')
Out[221]:
one two three
c 0.462215 -0.486066 1.931194
f NaN NaN NaN
b -0.177289 2.487104 -0.634293
注意,实际上索引对象可以在各个对象之间共享。因此,如果我们有一个Series和一个DataFrame,可以完成以下操作:
In [222]: rs = s.reindex(df.index)
In [223]: rs
Out[223]:
a -0.454087
b -0.360309
c -0.951631
d -0.535459
dtype: float64
In [224]: rs.index is df.index
Out[224]: True
这意味着重新索引的Series的索引与DataFrame的索引是相同的对象。
DataFrame.reindex()
方法也有一种约定性的调用,可以直接使用axis参数指定应用的轴方向.
In [225]: df.reindex(['c', 'f', 'b'], axis='index')
Out[225]:
one two three
c 0.462215 -0.486066 1.931194
f NaN NaN NaN
b -0.177289 2.487104 -0.634293
In [226]: df.reindex(['three', 'two', 'one'], axis='columns')
Out[226]:
three two one
a NaN 1.124472 -1.101558
b -0.634293 2.487104 -0.177289
c 1.931194 -0.486066 0.462215
d -1.222918 -0.456288 NaN
当希望将一个对象按照另外一个对象的标签进行重新索引和对齐时
reindex_like()方法将简化操作:
In [227]: df2
Out[227]:
one two
a -1.101558 1.124472
b -0.177289 2.487104
c 0.462215 -0.486066
In [228]: df3
Out[228]:
one two
a -0.829347 0.082635
b 0.094922 1.445267
c 0.734426 -1.527903
In [229]: df.reindex_like(df2)
Out[229]:
one two
a -1.101558 1.124472
b -0.177289 2.487104
c 0.462215 -0.486066
align
)align()方法是同时对齐两个对象的最快方法。它支持一个join参数,该参数有几个选项:
1.outer:按两个对象索引的并集对齐
2.left:使用调用对象的索引对齐
3.right:使用传入的对象的索引对齐
4.inner:按两个对象索引的交集对齐
对于Series对象,align()方法返回的是包含两个重新对齐的Series对象的元组.
In [230]: s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])
In [231]: s1 = s[:4]
In [232]: s2 = s[1:]
In [233]: s1.align(s2)
Out[233]:
(a 0.505453
b 1.788110
c -0.405908
d -0.801912
e NaN
dtype: float64, a NaN
b 1.788110
c -0.405908
d -0.801912
e 0.768460
dtype: float64)
In [234]: s1.align(s2, join='inner')
Out[234]:
(b 1.788110
c -0.405908
d -0.801912
dtype: float64, b 1.788110
c -0.405908
d -0.801912
dtype: float64)
In [235]: s1.align(s2, join='left')
Out[235]:
(a 0.505453
b 1.788110
c -0.405908
d -0.801912
dtype: float64, a NaN
b 1.788110
c -0.405908
d -0.801912
dtype: float64)
对于DataFrame对象,默认情况下是index和colmuns两条轴方向都要对齐的.
In [236]: df.align(df2, join='inner')
Out[236]:
( one two
a -1.101558 1.124472
b -0.177289 2.487104
c 0.462215 -0.486066, one two
a -1.101558 1.124472
b -0.177289 2.487104
c 0.462215 -0.486066)
当然也可以通过axis参数指定对齐的轴:
In [237]: df.align(df2, join='inner', axis=0)
Out[237]:
( one two three
a -1.101558 1.124472 NaN
b -0.177289 2.487104 -0.634293
c 0.462215 -0.486066 1.931194, one two
a -1.101558 1.124472
b -0.177289 2.487104
c 0.462215 -0.486066)
如果是一个DataFrame对象和一个Series对象进行对齐的话,必须要指定axis参数.
In [238]: df.align(df2.iloc[0], axis=1)
Out[238]:
( one three two
a -1.101558 NaN 1.124472
b -0.177289 -0.634293 2.487104
c 0.462215 1.931194 -0.486066
d NaN -1.222918 -0.456288, one -1.101558
three NaN
two 1.124472
Name: a, dtype: float64)
reindex()方法还有一个可选的method参数,该参数支持选择进行数据填充的方式,具体选择如下:
方式选择 | 含义 |
---|---|
pad/ffill | 向前填充 |
bfill/backfill | 向后填充 |
nearest | 用最邻近的index填充 |
用一个Series对象来展示下用法:
In [239]: rng = pd.date_range('1/3/2000', periods=8)
In [240]: ts = pd.Series(np.random.randn(8), index=rng)
In [241]: ts2 = ts[[0, 3, 6]]
In [242]: ts
Out[242]:
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
In [243]: ts2
Out[243]:
2000-01-03 0.466284
2000-01-06 0.785367
2000-01-09 -0.493153
dtype: float64
In [244]: ts2.reindex(ts.index)
Out[244]:
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
In [245]: ts2.reindex(ts.index, method='ffill')
Out[245]:
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
In [246]: ts2.reindex(ts.index, method='bfill')
Out[246]:
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
In [247]: ts2.reindex(ts.index, method='nearest')
Out[247]:
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
需要注意的是,这些方法都要求index是单调递增或者单调递减的.
同时,除了nearest选项外,fillna()方法和interpolate()方法同样可以实现reindex的效果:
In [248]: ts2.reindex(ts.index).fillna(method='ffill')
Out[248]:
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
但是此处又有一个坑,reindex执行时必须要求index是单调递增或者递减的,如果不满足该要求则报错.
而fillna()方法和interpolate()方法执行时则不会坚持index的顺序.
在重索引填充时,limit和tolerance参数提供了额外的控制.
Limit指定连续填充的最大计数
In [249]: ts2.reindex(ts.index, method='ffill', limit=1)
Out[249]:
2000-01-03 0.466284
2000-01-04 0.466284
2000-01-05 NaN
2000-01-06 0.785367
2000-01-07 0.785367
2000-01-08 NaN
2000-01-09 -0.493153
2000-01-10 -0.493153
Freq: D, dtype: float64
与之不同的是,tolerance参数限制的是指定索引和索引值之间的最大距离
In [250]: ts2.reindex(ts.index, method='ffill', tolerance='1 day')
Out[250]:
2000-01-03 0.466284
2000-01-04 0.466284
2000-01-05 NaN
2000-01-06 0.785367
2000-01-07 0.785367
2000-01-08 NaN
2000-01-09 -0.493153
2000-01-10 -0.493153
Freq: D, dtype: float64
请注意,当使用DatetimeIndex、TimedeltaIndex或PeriodIndex时,tolerance将转换为Timedelta
这样在指定tolerance参数的值是就可以是合适的字符了
与reindex密切相关的方法是drop()方法。它从一个轴上移除一组指定的标签:
In [251]: df
Out[251]:
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
In [252]: df.drop(['a', 'd'], axis=0)
Out[252]:
one two three
b -0.177289 2.487104 -0.634293
c 0.462215 -0.486066 1.931194
In [253]: df.drop(['one'], axis=1)
Out[253]:
two three
a 1.124472 NaN
b 2.487104 -0.634293
c -0.486066 1.931194
d -0.456288 -1.222918
注意下面的方法也可以,但是逻辑上台复杂:
In [254]: df.reindex(df.index.difference(['a', 'd']))
Out[254]:
one two three
b -0.177289 2.487104 -0.634293
c 0.462215 -0.486066 1.931194
rename()方法允许基于某些数据对标签进行映射(比如dict),也可以使用函数的方式重新标记标签。
In [255]: s
Out[255]:
a 0.505453
b 1.788110
c -0.405908
d -0.801912
e 0.768460
dtype: float64
In [256]: s.rename(str.upper)
Out[256]:
A 0.505453
B 1.788110
C -0.405908
D -0.801912
E 0.768460
dtype: float64
如果使用函数,这个函数只能以每一个标签为参数并且必须要有唯一一个返回值。当然字典或序列也可以使用:
In [257]: df.rename(columns={'one': 'foo', 'two': 'bar'},
.....: index={'a': 'apple', 'b': 'banana', 'd': 'durian'})
.....:
Out[257]:
foo bar three
apple -1.101558 1.124472 NaN
banana -0.177289 2.487104 -0.634293
c 0.462215 -0.486066 1.931194
durian NaN -0.456288 -1.222918
如果字典的主键中与不现有数据的标签匹配不上,那么该键值对不会生效,但也不会报错.
对于DataFrame对象,也支持axis参数指定轴方向上的映射对象.
In [258]: df.rename({'one': 'foo', 'two': 'bar'}, axis='columns')
Out[258]:
foo bar 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
In [259]: df.rename({'a': 'apple', 'b': 'banana', 'd': 'durian'}, axis='index')
Out[259]:
one two three
apple -1.101558 1.124472 NaN
banana -0.177289 2.487104 -0.634293
c 0.462215 -0.486066 1.931194
durian NaN -0.456288 -1.222918
默认情况下,rename方法不会在原数据上进行修改,而是返回新的对象,这也是Pandas数据不可变性的一种体现.
但提供给了inplace参数,默认值为False.如果将该参数设置为True,则所有操作将在原数据上进行.
rename方法还可以接受标量字符,代表修改对象的name属性.
In [260]: s.rename("scalar-name")
Out[260]:
a 0.505453
b 1.788110
c -0.405908
d -0.801912
e 0.768460
Name: scalar-name, dtype: float64
对Pandas对象迭代的结果,取决于对象的类型.
迭代Series对象,就类似于迭代数组,会得到每一个元素
迭代DataFrame对象,就类似对字典进行迭代,主键将相当于列名.
例如:
In [261]: df = pd.DataFrame({'col1' : np.random.randn(3), 'col2' : np.random.randn(3)},
.....: index=['a', 'b', 'c'])
.....:
In [262]: for col in df:
.....: print(col)
.....:
col1
col2
Pandas对象也具有类似于dict.items()方法,该方法用于变量DataFrame的列标签,会返回一个元组,该元组的第一个元素是列标签,
第二个元素是该列的Series对象.
如果要遍历DataFrame对象的行,则可以采用如下两个方法:
iterrows()方法:得到一个元组,元组的第一个元素是行对应的索引值,第二个元素是一个Series对象,该Series对象由每行数据转换而来,且索引值为列标签.正是由于这样的转换,所以会有一些性能上的问题.
itertuples()方法:会将每行转换为一个以列标签为值命名的命名元组,也包括每行的索引值,这样方法在性能上想较与iterrows()会更快,一般用来迭代DataFrame的值.
但总的来说,迭代Pandas对象都会比较慢,一般情况下不建议也没必要对Pandas对象进行迭代,可以使用一下的方法代替:
1.寻找一个向量化的解决方案:许多操作可以使用内置的方法或NumPy函数执行.
2.当需要对整个DataFrame或者Series进行操作时,最好使用apply()等方法而不是遍历这些值.
3.如果确实需要对值进行迭代操作,但又要保证性能,那么可以考虑使用cython或numba编写内部循环。
注意,永远不要修改正在迭代的东西,因为不保证在所有修改都有效.据数据类型的不同,迭代器可能返回一个副本而不是一个视图,并且修改它不会有任何效果.如:
In [263]: df = pd.DataFrame({'a': [1, 2, 3], 'b': ['a', 'b', 'c']})
In [264]: for index, row in df.iterrows():
.....: row['a'] = 10
.....:
In [265]: df
Out[265]:
a b
0 1 a
1 2 b
2 3 c
iteritems()通过键-值对进行迭代,这与类似于dict的接口一致.
In [266]: df = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]})
In [267]: for col, series in df.iteritems():
.....: print(col)
.....: print(series)
.....:
a
0 1
1 2
2 3
Name: a, dtype: int64
b
0 4
1 5
2 6
Name: b, dtype: int64
iterrows方法对DataFrame的行数据进行迭代,返回值是每行索引值和每行数据转换而成的Series对象组成的原则
for row_index, row in df.iterrows():
.....: print('%s\n%s' % (row_index, row))
.....:
0
a 1
b a
Name: 0, dtype: object
1
a 2
b b
Name: 1, dtype: object
2
a 3
b c
Name: 2, dtype: object
注意,因为iterrows()为每一行返回一个序列,所以它不会跨行保存dtypes信息,例如:
In [268]: df_orig = pd.DataFrame([[1, 1.5]], columns=['int', 'float'])
In [269]: df_orig.dtypes
Out[269]:
int int64
float float64
dtype: object
In [270]: row = next(df_orig.iterrows())[1]
In [271]: row
Out[271]:
int 1.0
float 1.5
Name: 0, dtype: float64
因为每行数据是作为一个Series对象返回的,所以整个Series对象的dtype都被转换成了浮点数对象了.
In [272]: row['int'].dtype
Out[272]: dtype('float64')
In [273]: df_orig['int'].dtype
Out[273]: dtype('int64')
如果要保留dtypes信息,最好使用itertuples()进行遍历,它返回值的名称元组,而且通常比iterrows()快得多。
例如,使用迭代的方法也可以达到转置的效果:
In [274]: df2 = pd.DataFrame({'x': [1, 2, 3], 'y': [4, 5, 6]})
In [275]: print(df2)
x y
0 1 4
1 2 5
2 3 6
In [276]: print(df2.T)
0 1 2
x 1 2 3
y 4 5 6
In [277]: df2_t = pd.DataFrame(dict((idx,values) for idx, values in df2.iterrows()))
In [278]: print(df2_t)
0 1 2
x 1 2 3
y 4 5 6
itertuples()方法将返回一个迭代器,为DataFrame中的每一行生成一个namedtuple。元组的第一个元素将是行对应的索引值,而其余的值是行的数据。
In [279]: for row in df.itertuples():
.....: print(row)
.....:
Pandas(Index=0, a=1, b='a')
Pandas(Index=1, a=2, b='b')
Pandas(Index=2, a=3, b='c')
此方法不将行数据转换为Series对象;它只返回namedtuple中的值。所以,itertuples()保留行数据的每一个数据类型,并且通常比iterrows()更快。
注意:如果列名是无效的Python标识符、重复的或以下划线开头的,则它们将被重命名为位置名称。对于大量的列(>255),将返回常规元组。
如果是一个datetime/period类型的Series对象,则Series有一个访问器,可以简洁地返回该Series对象的值的datetime类属性,并且返回一个与现有Series对象相同索引的Series对象。
# datetime
In [280]: s = pd.Series(pd.date_range('20130101 09:10:12', periods=4))
In [281]: s
Out[281]:
0 2013-01-01 09:10:12
1 2013-01-02 09:10:12
2 2013-01-03 09:10:12
3 2013-01-04 09:10:12
dtype: datetime64[ns]
In [282]: s.dt.hour
Out[282]:
0 9
1 9
2 9
3 9
dtype: int64
In [283]: s.dt.second
Out[283]:
0 12
1 12
2 12
3 12
dtype: int64
In [284]: s.dt.day
Out[284]:
0 1
1 2
2 3
3 4
dtype: int64
这样的访问方法,可以使表达式简化,例如:
In [285]: s[s.dt.day==2]
Out[285]:
1 2013-01-02 09:10:12
dtype: datetime64[ns]
可以很轻松的生成时区转换:
In [286]: stz = s.dt.tz_localize('US/Eastern')
In [287]: stz
Out[287]:
0 2013-01-01 09:10:12-05:00
1 2013-01-02 09:10:12-05:00
2 2013-01-03 09:10:12-05:00
3 2013-01-04 09:10:12-05:00
dtype: datetime64[ns, US/Eastern]
In [288]: stz.dt.tz
Out[288]:
也可以链式操作:
In [289]: s.dt.tz_localize('UTC').dt.tz_convert('US/Eastern')
Out[289]:
0 2013-01-01 04:10:12-05:00
1 2013-01-02 04:10:12-05:00
2 2013-01-03 04:10:12-05:00
3 2013-01-04 04:10:12-05:00
dtype: datetime64[ns, US/Eastern]
也可以使用Series.dt.strftime方法类似于标准的strftime方法,将时间数据转换为字符:
# DatetimeIndex
In [290]: s = pd.Series(pd.date_range('20130101', periods=4))
In [291]: s
Out[291]:
0 2013-01-01
1 2013-01-02
2 2013-01-03
3 2013-01-04
dtype: datetime64[ns]
In [292]: s.dt.strftime('%Y/%m/%d')
Out[292]:
0 2013/01/01
1 2013/01/02
2 2013/01/03
3 2013/01/04
dtype: object
# PeriodIndex
In [293]: s = pd.Series(pd.period_range('20130101', periods=4))
In [294]: s
Out[294]:
0 2013-01-01
1 2013-01-02
2 2013-01-03
3 2013-01-04
dtype: object
In [295]: s.dt.strftime('%Y/%m/%d')
Out[295]:
0 2013/01/01
1 2013/01/02
2 2013/01/03
3 2013/01/04
dtype: object
dt访问器同样也适用于period 和 timedelta 类型的时间:
# period
In [296]: s = pd.Series(pd.period_range('20130101', periods=4, freq='D'))
In [297]: s
Out[297]:
0 2013-01-01
1 2013-01-02
2 2013-01-03
3 2013-01-04
dtype: object
In [298]: s.dt.year
Out[298]:
0 2013
1 2013
2 2013
3 2013
dtype: int64
In [299]: s.dt.day
Out[299]:
0 1
1 2
2 3
3 4
dtype: int64
# timedelta
In [300]: s = pd.Series(pd.timedelta_range('1 day 00:00:05', periods=4, freq='s'))
In [301]: s
Out[301]:
0 1 days 00:00:05
1 1 days 00:00:06
2 1 days 00:00:07
3 1 days 00:00:08
dtype: timedelta64[ns]
In [302]: s.dt.days
Out[302]:
0 1
1 1
2 1
3 1
dtype: int64
In [303]: s.dt.seconds
Out[303]:
0 5
1 6
2 7
3 8
dtype: int64
In [304]: s.dt.components
Out[304]:
days hours minutes seconds milliseconds microseconds nanoseconds
0 1 0 0 5 0 0 0
1 1 0 0 6 0 0 0
2 1 0 0 7 0 0 0
3 1 0 0 8 0 0 0
最后补充下,如果使用dt访问器访问非时间类型的数据会引发错误.
Series具有一组字符串处理方法,可以轻松地对数组中的每个元素进行操作。最重要的是,这些方法自动排除缺失/NA值。可以通过该Series的str属性调用这些方法,这些方法的名称通常与内置字符串方法匹配。例如:
In [305]: s = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan, 'CABA', 'dog', 'cat'])
In [306]: s.str.lower()
Out[306]:
0 a
1 b
2 c
3 aaba
4 baca
5 NaN
6 caba
7 dog
8 cat
dtype: object
除此之外,还提供了强大的模式匹配方法,匹配方法一般是正则,信息信息可以参阅:Pandas的字符处理.
Panda支持三种排序方法:按索引排序、按元素值排序、按两者的组合排序。
Series.sort_index方法和DataFrame.sort_index方法用于按索引排序.
其中ascending参数可以控制升降序,而对于DataFrame对象,axis参数控制排序的轴方向.
In [307]: df = pd.DataFrame({'one' : pd.Series(np.random.randn(3), index=['a', 'b', 'c']),
.....: 'two' : pd.Series(np.random.randn(4), index=['a', 'b', 'c', 'd']),
.....: 'three' : pd.Series(np.random.randn(3), index=['b', 'c', 'd'])})
.....:
In [308]: unsorted_df = df.reindex(index=['a', 'd', 'c', 'b'],
.....: columns=['three', 'two', 'one'])
.....:
In [309]: unsorted_df
Out[309]:
three two one
a NaN 0.708543 0.036274
d -0.540166 0.586626 NaN
c 0.410238 1.121731 1.044630
b -0.282532 -2.038777 -0.490032
# DataFrame
In [310]: unsorted_df.sort_index()
Out[310]:
three two one
a NaN 0.708543 0.036274
b -0.282532 -2.038777 -0.490032
c 0.410238 1.121731 1.044630
d -0.540166 0.586626 NaN
In [311]: unsorted_df.sort_index(ascending=False)
Out[311]:
three two one
d -0.540166 0.586626 NaN
c 0.410238 1.121731 1.044630
b -0.282532 -2.038777 -0.490032
a NaN 0.708543 0.036274
In [312]: unsorted_df.sort_index(axis=1)
Out[312]:
one three two
a 0.036274 NaN 0.708543
d NaN -0.540166 0.586626
c 1.044630 0.410238 1.121731
b -0.490032 -0.282532 -2.038777
# Series
In [313]: unsorted_df['three'].sort_index()
Out[313]:
a NaN
b -0.282532
c 0.410238
d -0.540166
Name: three, dtype: float64
Series.sort_values方法用于Series对象的数据值排序.
DataFrame.sort_values方法用于按行数据值或列数据值进行排序.by参数用于指定需要排序的一列或多列.
In [314]: df1 = pd.DataFrame({'one':[2,1,1,1],'two':[1,3,2,4],'three':[5,4,3,2]})
In [315]: df1.sort_values(by='two')
Out[315]:
one two three
0 2 1 5
2 1 2 3
1 1 3 4
3 1 4 2
by参数可以接受列标签组成的列表:
In [316]: df1[['one', 'two', 'three']].sort_values(by=['one','two'])
Out[316]:
one two three
2 1 2 3
1 1 3 4
3 1 4 2
0 2 1 5
通过na_position参数对可以对NA值进行特殊的排序处理:
In [317]: s[2] = np.nan
In [318]: s.sort_values()
Out[318]:
0 A
3 Aaba
1 B
4 Baca
6 CABA
8 cat
7 dog
2 NaN
5 NaN
dtype: object
In [319]: s.sort_values(na_position='first')
Out[319]:
2 NaN
5 NaN
0 A
3 Aaba
1 B
4 Baca
6 CABA
8 cat
7 dog
dtype: object
列或索引的字符名称可以传递给by参数,从而指定排序规则
# Build MultiIndex
In [320]: idx = pd.MultiIndex.from_tuples([('a', 1), ('a', 2), ('a', 2),
.....: ('b', 2), ('b', 1), ('b', 1)])
.....:
In [321]: idx.names = ['first', 'second']
# Build DataFrame
In [322]: df_multi = pd.DataFrame({'A': np.arange(6, 0, -1)},
.....: index=idx)
.....:
In [323]: df_multi
Out[323]:
A
first second
a 1 6
2 5
2 4
b 2 3
1 2
1 1
按'second'索引和'A'列排序:
In [324]: df_multi.sort_values(by=['second', 'A'])
Out[324]:
A
first second
b 1 1
1 2
a 1 6
b 2 3
a 2 4
2 5
注意:如果字符串同时匹配列名和索名,则发出警告,但列名优先级更高。
但Pandas会将来的版本中修正导致歧义的错误。
该方法类似于numpy.ndarray.searchsorted方法,在维持序列顺序的情况下,查找要插入的元素的索引.
In [325]: ser = pd.Series([1, 2, 3])
In [326]: ser.searchsorted([0, 3])
Out[326]: array([0, 2])
In [327]: ser.searchsorted([0, 4])
Out[327]: array([0, 3])
In [328]: ser.searchsorted([1, 3], side='right')
Out[328]: array([1, 3])
In [329]: ser.searchsorted([1, 3], side='left')
Out[329]: array([0, 2])
In [330]: ser = pd.Series([3, 1, 2])
In [331]: ser.searchsorted([0, 3], sorter=np.argsort(ser))
Out[331]: array([0, 2])
Series对象有nsmallest和nlargest方法,它们返回指定个数的最小或最大的值。对于大型数,这比对整个数据排序后再对结果调用head(n)要快得多。
In [332]: s = pd.Series(np.random.permutation(10))
In [333]: s
Out[333]:
0 8
1 2
2 9
3 5
4 6
5 0
6 1
7 7
8 4
9 3
dtype: int64
In [334]: s.sort_values()
Out[334]:
5 0
6 1
1 2
9 3
8 4
3 5
4 6
7 7
0 8
2 9
dtype: int64
In [335]: s.nsmallest(3)
Out[335]:
5 0
6 1
1 2
dtype: int64
In [336]: s.nlargest(3)
Out[336]:
2 9
0 8
7 7
dtype: int64
DataFrame对象也有nsmallest和nlargest方法
In [337]: df = pd.DataFrame({'a': [-2, -1, 1, 10, 8, 11, -1],
.....: 'b': list('abdceff'),
.....: 'c': [1.0, 2.0, 4.0, 3.2, np.nan, 3.0, 4.0]})
.....:
In [338]: df.nlargest(3, 'a')
Out[338]:
a b c
5 11 f 3.0
3 10 c 3.2
4 8 e NaN
In [339]: df.nlargest(5, ['a', 'c'])
Out[339]:
a b c
6 -1 f 4.0
5 11 f 3.0
3 10 c 3.2
4 8 e NaN
2 1 d 4.0
In [340]: df.nsmallest(3, 'a')
Out[340]:
a b c
0 -2 a 1.0
1 -1 b 2.0
6 -1 f 4.0
In [341]: df.nsmallest(5, ['a', 'c'])
Out[341]:
a b c
0 -2 a 1.0
2 1 d 4.0
4 8 e NaN
1 -1 b 2.0
6 -1 f 4.0
当列是一个多层级时,必须明确的指定要排序的所有级别。
In [342]: df1.columns = pd.MultiIndex.from_tuples([('a','one'),('a','two'),('b','three')])
In [343]: df1.sort_values(by=('a','two'))
Out[343]:
a b
one two three
0 2 1 5
2 1 2 3
1 1 3 4
3 1 4 2
Pandas的copy()方法会复制底层数据并返回一个新的对象,但是大多数情况下并不需要复制对象.
只需要使用部分方法就可以就地更改原对象:
需要明确指出的是,任何Panda方法都不会修改原数据,几乎每个方法都返回一个新对象,不改变原始对象。如果原数据被修改,那一定是显式地修改了数据。
Panda对象中存储的主要类型有float、int、bool、datetime64[ns]和datetime64[ns, tz]、timedelta[ns]、category和object。
此外,这些dtype具有所占的存储空间大小不一,例如储存int64的空间就要大于int32。
有关datetime64[ns, TZ] dtypes的详细信息,请参阅Pandas的时间序列/日期功能.
DataFrame的dtypes属性会以Series对象的方式返回所有列的类型信息
In [344]: dft = pd.DataFrame(dict(A = np.random.rand(3),
.....: B = 1,
.....: C = 'foo',
.....: D = pd.Timestamp('20010102'),
.....: E = pd.Series([1.0]*3).astype('float32'),
.....: F = False,
.....: G = pd.Series([1]*3,dtype='int8')))
.....:
In [345]: dft
Out[345]:
A B C D E F G
0 0.809585 1 foo 2001-01-02 1.0 False 1
1 0.128238 1 foo 2001-01-02 1.0 False 1
2 0.775752 1 foo 2001-01-02 1.0 False 1
In [346]: dft.dtypes
Out[346]:
A float64
B int64
C object
D datetime64[ns]
E float32
F bool
G int8
dtype: object
对于Series对象,请使用dtype属性:
In [347]: dft['A'].dtype
Out[347]: dtype('float64')
如果Pandas对象中某一列含有多种类型,则会自动匹配一种可以概括全部类型的类型似来作为代表.
如果无法自动适应出适合的具体类型,则使用object类型.
# these ints are coerced to floats
In [348]: pd.Series([1, 2, 3, 4, 5, 6.])
Out[348]:
0 1.0
1 2.0
2 3.0
3 4.0
4 5.0
5 6.0
dtype: float64
# string data forces an ``object`` dtype
In [349]: pd.Series([1, 2, 3, 6., 'foo'])
Out[349]:
0 1
1 2
2 3
3 6
4 foo
dtype: object
通过调用get_dtype_counts()可以找到DataFrame中每种类型的数量。
In [350]: dft.get_dtype_counts()
Out[350]:
float64 1
float32 1
int64 1
int8 1
datetime64[ns] 1
bool 1
object 1
dtype: int64
数字类型的dtype可以传播,可以在数据中共存。如果指定了dtype(通过dtype关键字、传递ndarray或传递的Series),那么dtype信息将保存在DataFrame操作中。此外,将不合并不同的数字dtype信息。下面的示例将可以体验一下。
In [351]: df1 = pd.DataFrame(np.random.randn(8, 1), columns=['A'], dtype='float32')
In [352]: df1
Out[352]:
A
0 0.890400
1 0.283331
2 -0.303613
3 -1.192210
4 0.065420
5 0.455918
6 2.008328
7 0.188942
In [353]: df1.dtypes
Out[353]:
A float32
dtype: object
In [354]: df2 = pd.DataFrame(dict( A = pd.Series(np.random.randn(8), dtype='float16'),
.....: B = pd.Series(np.random.randn(8)),
.....: C = pd.Series(np.array(np.random.randn(8), dtype='uint8')) ))
.....:
In [355]: df2
Out[355]:
A B C
0 -0.454346 0.200071 255
1 -0.916504 -0.557756 255
2 0.640625 -0.141988 0
3 2.675781 -0.174060 0
4 -0.007866 0.258626 0
5 -0.204224 0.941688 0
6 -0.100098 -1.849045 0
7 -0.402100 -0.949458 0
In [356]: df2.dtypes
Out[356]:
A float16
B float64
C uint8
dtype: object
默认情况下,整数类型是int64,浮点类型是float64,与平台无关(32位或64位)。下面的结果都是int64类型
In [357]: pd.DataFrame([1, 2], columns=['a']).dtypes
Out[357]:
a int64
dtype: object
In [358]: pd.DataFrame({'a': [1, 2]}).dtypes
Out[358]:
a int64
dtype: object
In [359]: pd.DataFrame({'a': 1 }, index=list(range(2))).dtypes
Out[359]:
a int64
dtype: object
注意,在创建数组时,Numpy将类型的选择依赖于平台。下面将在32位平台上生成int32。
In [360]: frame = pd.DataFrame(np.array([1, 2]))
当不同类型进行合并时,类型可能被向上映射或者提升.
In [361]: df3 = df1.reindex_like(df2).fillna(value=0.0) + df2
In [362]: df3
Out[362]:
A B C
0 0.436054 0.200071 255.0
1 -0.633173 -0.557756 255.0
2 0.337012 -0.141988 0.0
3 1.483571 -0.174060 0.0
4 0.057555 0.258626 0.0
5 0.251695 0.941688 0.0
6 1.908231 -1.849045 0.0
7 -0.213158 -0.949458 0.0
In [363]: df3.dtypes
Out[363]:
A float32
B float64
C float64
dtype: object
属性values的dtype属性总是返回更低维度的类型,这意味着可以使用Numpy中的所有类型.
In [364]: df3.values.dtype
Out[364]: dtype('float64')
使用astype()方法可以显式地将dtypes从一种类型转换为另一种类型。默认情况下,这些将返回一个副本,即使dtype没有更改(传递copy=False以更改此行为)。此外,如果astype操作无效,它们将引发异常。
向上映射(upcasting)总是遵循numpy规则。如果一个操作涉及两个不同的dtype,那么将使用更通用的dtype作为操作的结果。
In [365]: df3
Out[365]:
A B C
0 0.436054 0.200071 255.0
1 -0.633173 -0.557756 255.0
2 0.337012 -0.141988 0.0
3 1.483571 -0.174060 0.0
4 0.057555 0.258626 0.0
5 0.251695 0.941688 0.0
6 1.908231 -1.849045 0.0
7 -0.213158 -0.949458 0.0
In [366]: df3.dtypes
Out[366]:
A float32
B float64
C float64
dtype: object
# conversion of dtypes
In [367]: df3.astype('float32').dtypes
Out[367]:
A float32
B float32
C float32
dtype: object
使用astype()将列的子集转换为指定的类型。
In [368]: dft = pd.DataFrame({'a': [1,2,3], 'b': [4,5,6], 'c': [7, 8, 9]})
In [369]: dft[['a','b']] = dft[['a','b']].astype(np.uint8)
In [370]: dft
Out[370]:
a b c
0 1 4 7
1 2 5 8
2 3 6 9
In [371]: dft.dtypes
Out[371]:
a uint8
b uint8
c int64
dtype: object
使用dict可以将某些列转换为特定的dtype。
In [372]: dft1 = pd.DataFrame({'a': [1,0,1], 'b': [4,5,6], 'c': [7, 8, 9]})
In [373]: dft1 = dft1.astype({'a': np.bool, 'c': np.float64})
In [374]: dft1
Out[374]:
a b c
0 True 4 7.0
1 False 5 8.0
2 True 6 9.0
In [375]: dft1.dtypes
Out[375]:
a bool
b int64
c float64
dtype: object
当尝试使用astype()和loc()将列的子集转换为指定类型时,会发生向上映射(upcasting)。
因此,下面这段代码产生了意想不到的结果。
In [376]: dft = pd.DataFrame({'a': [1,2,3], 'b': [4,5,6], 'c': [7, 8, 9]})
In [377]: dft.loc[:, ['a', 'b']].astype(np.uint8).dtypes
Out[377]:
a uint8
b uint8
dtype: object
In [378]: dft.loc[:, ['a', 'b']] = dft.loc[:, ['a', 'b']].astype(np.uint8)
In [379]: dft.dtypes
Out[379]:
a int64
b int64
c int64
dtype: object
Panda提供了各种函数,可以强制将对象的dtype转换为其他类型。如果数据已经具有正确的类型,但是该类型是object,
就可以使用DataFrame.infer_objects()和Series.infer_objects()方法将数据软转换为正确的类型。
In [380]: import datetime
In [381]: df = pd.DataFrame([[1, 2],
.....: ['a', 'b'],
.....: [datetime.datetime(2016, 3, 2), datetime.datetime(2016, 3, 2)]])
.....:
In [382]: df = df.T
In [383]: df
Out[383]:
0 1 2
0 1 a 2016-03-02 00:00:00
1 2 b 2016-03-02 00:00:00
In [384]: df.dtypes
Out[384]:
0 object
1 object
2 object
dtype: object
因为数据被转置,导致原始推断的类型被储存为object, infer_objects将纠正这一错误。
In [385]: df.infer_objects().dtypes
Out[385]:
0 int64
1 object
2 datetime64[ns]
dtype: object
以下函数可用于一维对象数组或标量对象硬转换到指定类型:
In [386]: m = ['1.1', 2, 3]
In [387]: pd.to_numeric(m)
Out[387]: array([ 1.1, 2. , 3. ])
In [388]: import datetime
In [389]: m = ['2016-07-09', datetime.datetime(2016, 3, 2)]
In [390]: pd.to_datetime(m)
Out[390]: DatetimeIndex(['2016-07-09', '2016-03-02'], dtype='datetime64[ns]', freq=None)
In [391]: m = ['5us', pd.Timedelta('1day')]
In [392]: pd.to_timedelta(m)
Out[392]: TimedeltaIndex(['0 days 00:00:00.000005', '1 days 00:00:00'], dtype='timedelta64[ns]', freq=None)
如果要强制转换,我们可以传入一个errors参数,该参数指定Panda应该如何处理无法转换的对象。默认情况下,errors='raise',这意味着在转换过程中遇到的任何错误都会被抛出。但是,如果error ='coerce',这些错误将被忽略,panda将把有问题的元素转换为pd.NaT(datetime 和 timedelta))或np.nan(数字类型)。如果正在读取的数据大部分是所需的dtype(例如numeric、datetime),但是偶尔会有不符合要求的元素混杂在一起,希望将其表示为缺失的数据,那么这种方法可能会很有用:
In [393]: import datetime
In [394]: m = ['apple', datetime.datetime(2016, 3, 2)]
In [395]: pd.to_datetime(m, errors='coerce')
Out[395]: DatetimeIndex(['NaT', '2016-03-02'], dtype='datetime64[ns]', freq=None)
In [396]: m = ['apple', 2, 3]
In [397]: pd.to_numeric(m, errors='coerce')
Out[397]: array([ nan, 2., 3.])
In [398]: m = ['apple', pd.Timedelta('1day')]
In [399]: pd.to_timedelta(m, errors='coerce')
Out[399]: TimedeltaIndex([NaT, '1 days'], dtype='timedelta64[ns]', freq=None)
errors参数还有第三个选项errors='ignore',如果在转换到指定的数据类型时遇到任何错误,则返回传入的原数据:
In [400]: import datetime
In [401]: m = ['apple', datetime.datetime(2016, 3, 2)]
In [402]: pd.to_datetime(m, errors='ignore')
Out[402]: array(['apple', datetime.datetime(2016, 3, 2, 0, 0)], dtype=object)
In [403]: m = ['apple', 2, 3]
In [404]: pd.to_numeric(m, errors='ignore')
Out[404]: array(['apple', 2, 3], dtype=object)
In [405]: m = ['apple', pd.Timedelta('1day')]
In [406]: pd.to_timedelta(m, errors='ignore')
Out[406]: array(['apple', Timedelta('1 days 00:00:00')], dtype=object)
除了对象转换之外,to_numeric()还提供了另一个向下级类型转换的参数downcast,它提供了将数字数据向下转换为较小的dtype的选项,这可以节省内存:
In [407]: m = ['1', 2, 3]
In [408]: pd.to_numeric(m, downcast='integer') # smallest signed int dtype
Out[408]: array([1, 2, 3], dtype=int8)
In [409]: pd.to_numeric(m, downcast='signed') # same as 'integer'
Out[409]: array([1, 2, 3], dtype=int8)
In [410]: pd.to_numeric(m, downcast='unsigned') # smallest unsigned int dtype
Out[410]: array([1, 2, 3], dtype=uint8)
In [411]: pd.to_numeric(m, downcast='float') # smallest float dtype
Out[411]: array([ 1., 2., 3.], dtype=float32)
由于这些方法仅适用于一维数组、列表或标量;它们不能直接用于多维对象,如DataFrames。但是,使用apply(),我们可以有效地在每一列上“应用”该函数
In [412]: import datetime
In [413]: df = pd.DataFrame([['2016-07-09', datetime.datetime(2016, 3, 2)]] * 2, dtype='O')
In [414]: df
Out[414]:
0 1
0 2016-07-09 2016-03-02 00:00:00
1 2016-07-09 2016-03-02 00:00:00
In [415]: df.apply(pd.to_datetime)
Out[415]:
0 1
0 2016-07-09 2016-03-02
1 2016-07-09 2016-03-02
In [416]: df = pd.DataFrame([['1.1', 2, 3]] * 2, dtype='O')
In [417]: df
Out[417]:
0 1 2
0 1.1 2 3
1 1.1 2 3
In [418]: df.apply(pd.to_numeric)
Out[418]:
0 1 2
0 1.1 2 3
1 1.1 2 3
In [419]: df = pd.DataFrame([['5us', pd.Timedelta('1day')]] * 2, dtype='O')
In [420]: df
Out[420]:
0 1
0 5us 1 days 00:00:00
1 5us 1 days 00:00:00
In [421]: df.apply(pd.to_timedelta)
Out[421]:
0 1
0 00:00:00.000005 1 days
1 00:00:00.000005 1 days
对整数类型数据执行选择操作可以很容易地将数据上移到浮点类型。在不引入nans的情况下,将保留输入数据的dtype。
In [422]: dfi = df3.astype('int32')
In [423]: dfi['E'] = 1
In [424]: dfi
Out[424]:
A B C E
0 0 0 255 1
1 0 0 255 1
2 0 0 0 1
3 1 0 0 1
4 0 0 0 1
5 0 0 0 1
6 1 -1 0 1
7 0 0 0 1
In [425]: dfi.dtypes
Out[425]:
A int32
B int32
C int32
E int64
dtype: object
In [426]: casted = dfi[dfi>0]
In [427]: casted
Out[427]:
A B C E
0 NaN NaN 255.0 1
1 NaN NaN 255.0 1
2 NaN NaN NaN 1
3 1.0 NaN NaN 1
4 NaN NaN NaN 1
5 NaN NaN NaN 1
6 1.0 NaN NaN 1
7 NaN NaN NaN 1
In [428]: casted.dtypes
Out[428]:
A float64
B float64
C float64
E int64
dtype: object
而float dtypes是不变的
In [429]: dfa = df3.copy()
In [430]: dfa['A'] = dfa['A'].astype('float32')
In [431]: dfa.dtypes
Out[431]:
A float32
B float64
C float64
dtype: object
In [432]: casted = dfa[df2>0]
In [433]: casted
Out[433]:
A B C
0 NaN 0.200071 255.0
1 NaN NaN 255.0
2 0.337012 NaN NaN
3 1.483571 NaN NaN
4 NaN 0.258626 NaN
5 NaN 0.941688 NaN
6 NaN NaN NaN
7 NaN NaN NaN
In [434]: casted.dtypes
Out[434]:
A float32
B float64
C float64
dtype: object
dtype
)select_dtypes()方法实现基于列的dtype类型的子设置。
In [435]: df = pd.DataFrame({'string': list('abc'),
.....: 'int64': list(range(1, 4)),
.....: 'uint8': np.arange(3, 6).astype('u1'),
.....: 'float64': np.arange(4.0, 7.0),
.....: 'bool1': [True, False, True],
.....: 'bool2': [False, True, False],
.....: 'dates': pd.date_range('now', periods=3).values,
.....: 'category': pd.Series(list("ABC")).astype('category')})
.....:
In [436]: df['tdeltas'] = df.dates.diff()
In [437]: df['uint64'] = np.arange(3, 6).astype('u8')
In [438]: df['other_dates'] = pd.date_range('20130101', periods=3).values
In [439]: df['tz_aware_dates'] = pd.date_range('20130101', periods=3, tz='US/Eastern')
In [440]: df
Out[440]:
string int64 uint8 float64 bool1 bool2 dates category tdeltas uint64 other_dates tz_aware_dates
0 a 1 3 4.0 True False 2018-08-05 11:57:39.507525 A NaT 3 2013-01-01 2013-01-01 00:00:00-05:00
1 b 2 4 5.0 False True 2018-08-06 11:57:39.507525 B 1 days 4 2013-01-02 2013-01-02 00:00:00-05:00
2 c 3 5 6.0 True False 2018-08-07 11:57:39.507525 C 1 days 5 2013-01-03 2013-01-03 00:00:00-05:00
所有的types信息如下:
In [441]: df.dtypes
Out[441]:
string object
int64 int64
uint8 uint8
float64 float64
bool1 bool
bool2 bool
dates datetime64[ns]
category category
tdeltas timedelta64[ns]
uint64 uint64
other_dates datetime64[ns]
tz_aware_dates datetime64[ns, US/Eastern]
dtype: object
select_dtypes()有两个参数include和exclude,前者表示接受的类型,后者表示排除的类型
例如,选择bool列:
In [442]: df.select_dtypes(include=[bool])
Out[442]:
bool1 bool2
0 True False
1 False True
2 True False
传入的也可以是Numpy中定义的类型的字符名称:
In [443]: df.select_dtypes(include=['bool'])
Out[443]:
bool1 bool2
0 True False
1 False True
2 True False
select_dtypes()也可以用于泛型dtypes。
例如,要选择所有数字和布尔列,同时排除无符号整数:
In [444]: df.select_dtypes(include=['number', 'bool'], exclude=['unsignedinteger'])
Out[444]:
int64 float64 bool1 bool2 tdeltas
0 1 4.0 True False NaT
1 2 5.0 False True 1 days
2 3 6.0 True False 1 days
要选择字符串,必须使用object:
In [445]: df.select_dtypes(include=['object'])
Out[445]:
string
0 a
1 b
2 c
如要要查看泛型dtype(如numpy)的所有子dtype。你可以定义一个函数,将子类型继承序列:
In [446]: def subdtypes(dtype):
.....: subs = dtype.__subclasses__()
.....: if not subs:
.....: return dtype
.....: return [dtype, [subdtypes(dt) for dt in subs]]
.....:
所有的NumPy类型都是NumPy .generic的子类:
In [447]: subdtypes(np.generic)
Out[447]:
[numpy.generic,
[[numpy.number,
[[numpy.integer,
[[numpy.signedinteger,
[numpy.int8,
numpy.int16,
numpy.int32,
numpy.int64,
numpy.int64,
numpy.timedelta64]],
[numpy.unsignedinteger,
[numpy.uint8,
numpy.uint16,
numpy.uint32,
numpy.uint64,
numpy.uint64]]]],
[numpy.inexact,
[[numpy.floating,
[numpy.float16, numpy.float32, numpy.float64, numpy.float128]],
[numpy.complexfloating,
[numpy.complex64, numpy.complex128, numpy.complex256]]]]]],
[numpy.flexible,
[[numpy.character, [numpy.bytes_, numpy.str_]],
[numpy.void, [numpy.record]]]],
numpy.bool_,
numpy.datetime64,
numpy.object_]]
注意:Panda还定义了types类别和datetime64[ns, tz],它们没有集成到常规的NumPy层次结构中,不会显示在上面的函数结果中。