呆鸟云:“觉得有用,就请点个在看,哈哈”
函数应用
不管是为 pandas 对象应用自定义函数,还是应用其它第三方函数,都离不开以下三种方法。用哪种方法取决于操作的对象是
DataFrame 或
Series ,是行或列,还是元素。
表级函数应用:`pipe()`
行列级函数应用:
apply()
聚合 API:`agg()` 与 `transform()`
元素级函数应用:`applymap()`
表级函数应用
虽然可以把
DataFrame 与
Series 传递给函数。不过,通过链式调用函数时,最好使用
pipe() 方法。对比以下两种方式:
>>> f(g(h(df), arg1=
1), arg2=
2, arg3=
3)
下列代码与上述代码等效
>>> (df.pipe(h)
... .pipe(g, arg1=
1)
... .pipe(f, arg2=
2, arg3=
3))
pandas 鼓励使用第二种方式,即链式方法。在链式方法中调用自定义函数或第三方支持库函数时,用
pipe 更容易,与用 pandas 自身方法一样。
上例中,
f、
g 与
h 这几个函数都把
DataFrame 当作首位参数。要是想把数据作为第二个参数,该怎么办?本例中,
pipe 为元组 (
callable,data_keyword)形式。
.pipe 把
DataFrame 作为元组里指定的参数。
下例用 statsmodels 拟合回归。该 API 先接收一个公式,
DataFrame 是第二个参数,
data。要传递函数,则要用
pipe 接收关键词对 (
sm.ols,'data')。
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]:
<
class 'statsmodels.iolib.summary.Summary'>""" 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: Thu,
22 Aug
2019 Prob (F-statistic):
3.48e-15
Time:
15:
48:
59 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.
unix 的
pipe 与后来出现的 dplyr 及 magrittr 启发了
pipe 方法,在此,引入了 R 语言里用于读取 pipe 的操作符 (
%>%)。
pipe 的实现思路非常清晰,仿佛 Python 源生的一样。强烈建议大家阅读
pipe() 的源代码。
行列级函数应用
apply() 方法可以沿着 DataFrame 的轴应用任何函数,比如,描述性统计方法,该方法支持
axis 参数。
In [
141]: df.apply(np.mean)
Out[
141]:
one
0.811094
two
1.360588
three
0.187958
dtype: float64
In [
142]: df.apply(np.mean, axis=
1)
Out[
142]:
a
1.583749
b
0.734929
c
1.133683
d
-0.166914
dtype: float64
In [
143]: df.apply(
lambda x: x.max() - x.min())
Out[
143]:
one
1.051928
two
1.632779
three
1.840607
dtype: float64
In [
144]: df.apply(np.cumsum)
Out[
144]:
one two three
a
1.394981
1.772517 NaN
b
1.738035
3.684640
-0.050390
c
2.433281
5.163008
1.177045
d NaN
5.442353
0.563873
In [
145]: df.apply(np.exp)
Out[
145]:
one two three
a
4.034899
5.885648 NaN
b
1.409244
6.767440
0.950858
c
2.004201
4.385785
3.412466
d NaN
1.322262
0.541630
apply() 方法还支持通过函数名字符串调用函数。
In [
146]: df.apply(
'mean')
Out[
146]:
one
0.811094
two
1.360588
three
0.187958
dtype: float64
In [
147]: df.apply(
'mean', axis=
1)
Out[
147]:
a
1.583749
b
0.734929
c
1.133683
d
-0.166914
dtype: float64
默认情况下,
apply() 调用的函数返回的类型会影响
DataFrame.apply 输出结果的类型。
函数返回的是
Series 时,最终输出的结果是
DataFrame。输出的列与函数返回的
Series 索引相匹配。
函数返回其它任意类型时,输出结果是
Series。
result_type 会覆盖默认行为,该参数有三个选项:
reduce、
broadcast、
expand。这些选项决定了列表型返回值是否扩展为
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
2000
-08
-06
B
2001
-01
-18
C
2001
-07
-18
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 方法的功能也很实用:
In [
150]: tsdf
Out[
150]:
A B C
2000
-01
-01
-0.158131
-0.232466
0.321604
2000
-01
-02
-1.810340
-3.105758
0.433834
2000
-01
-03
-1.209847
-1.156793
-0.136794
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.653602
0.178875
1.008298
2000
-01
-09
1.007996
0.462824
0.254472
2000
-01
-10
0.307473
0.600337
1.643950
In [
151]: tsdf.apply(pd.Series.interpolate)
Out[
151]:
A B C
2000
-01
-01
-0.158131
-0.232466
0.321604
2000
-01
-02
-1.810340
-3.105758
0.433834
2000
-01
-03
-1.209847
-1.156793
-0.136794
2000
-01
-04
-1.098598
-0.889659
0.092225
2000
-01
-05
-0.987349
-0.622526
0.321243
2000
-01
-06
-0.876100
-0.355392
0.550262
2000
-01
-07
-0.764851
-0.088259
0.779280
2000
-01
-08
-0.653602
0.178875
1.008298
2000
-01
-09
1.007996
0.462824
0.254472
2000
-01
-10
0.307473
0.600337
1.643950
apply() 有一个参数
raw,默认值为
False,在应用函数前,使用该参数可以将每行或列转换为
Series。该参数为
True 时,传递的函数接收 ndarray 对象,若不需要索引功能,这种操作能显著提高性能。
聚合 API
0.20.0 版新增。
聚合 API 可以快速、简洁地执行多个聚合操作。Pandas 对象支持多个类似的 API,如 groupby API、window functions API、resample API。聚合函数为
DataFrame.aggregate(),它的别名是
DataFrame.agg()。
这里使用与前例类似的
DataFrame:
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
1.257606
1.004194
0.167574
2000
-01
-02
-0.749892
0.288112
-0.757304
2000
-01
-03
-0.207550
-0.298599
0.116018
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.814347
-0.257623
0.869226
2000
-01
-09
-0.250663
-1.206601
0.896839
2000
-01
-10
2.169758
-1.333363
0.283157
应用单个函数时,该操作与
apply() 等效,这里也可以用字符串表示聚合函数名。下面的聚合函数输出的结果为
Series:
In [
155]: tsdf.agg(np.sum)
Out[
155]:
A
3.033606
B
-1.803879
C
1.575510
dtype: float64
In [
156]: tsdf.agg(
'sum')
Out[
156]:
A
3.033606
B
-1.803879
C
1.575510
dtype: float64
In [
157]: tsdf.sum()
Out[
157]:
A
3.033606
B
-1.803879
C
1.575510
dtype: float64
对
Series 进行单个聚合操作,返回的是标量值:
In [
158]: tsdf.A.agg(
'sum')
Out[
158]:
3.033606102414146
多函数聚合
还可以用列表形式传递多个聚合函数。每个函数在输出结果
DataFrame 里以行的形式显示,行名是每个聚合函数的函数名。
In [
159]: tsdf.agg([
'sum'])
Out[
159]:
A B C
sum
3.033606
-1.803879
1.57551
多个函数输出多行:
In [
160]: tsdf.agg([
'sum',
'mean'])
Out[
160]:
A B C
sum
3.033606
-1.803879
1.575510
mean
0.505601
-0.300647
0.262585
对于
Series,多个函数返回的结果也是
Series,其索引为函数名:
In [
161]: tsdf.A.agg([
'sum',
'mean'])
Out[
161]:
sum
3.033606
mean
0.505601
Name: A, dtype: float64
传递
lambda 函数时,输出名为
<lambda> 的行:
In [
162]: tsdf.A.agg([
'sum',
lambda x: x.mean()])
Out[
162]:
sum
3.033606
<
lambda>
0.505601
Name: A, dtype: float64
应用自定义函数时,则该函数名为输出结果的行名:
In [
163]:
def mymean(x):
.....:
return x.mean()
.....:
In [
164]: tsdf.A.agg([
'sum', mymean])
Out[
164]:
sum
3.033606
mymean
0.505601
Name: A, dtype: float64
用字典实现聚合
指定为哪些列应用哪些聚合函数时,需要把包含列名与标量(或标量列表)的字典传递给
DataFrame.agg。
注意:这里输出结果的顺序不是固定的,要想让输出顺序与输入顺序一致,请使用
OrderedDict。
In [
165]: tsdf.agg({
'A':
'mean',
'B':
'sum'})
Out[
165]:
A
0.505601
B
-1.803879
dtype: float64
输入的参数是列表时,输出结果为
DataFrame,并以矩阵形式显示所有聚合函数的计算结果,且输出结果由所有唯一函数组成。未执行聚合操作的列输出结果为
NaN 值:
In [
166]: tsdf.agg({
'A': [
'mean',
'min'],
'B':
'sum'})
Out[
166]:
A B
mean
0.505601 NaN
min
-0.749892 NaN
sum NaN
-1.803879
多种 Dtype
DataFrame 里包含不能执行聚合操作的多种 Dtype 时,
.agg 只计算可以执行聚合的列。这与
groupby 的
.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
sum
6
6.0 foobarbaz NaT
自定义 Describe
用
.agg() 可以轻松地创建与内置 describe 函数类似的自定义 describe 函数。
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.505601
-0.300647
0.262585
std
1.103362
0.887508
0.606860
min
-0.749892
-1.333363
-0.757304
25%
-0.239885
-0.979600
0.128907
median
0.303398
-0.278111
0.225365
75%
1.146791
0.151678
0.722709
max
2.169758
1.004194
0.896839
Transform API
0.20.0 版新增。
transform() 方法返回的结果与原始数据具有同样索引,且大小相同。这个 API 支持同时处理多种操作,不用一个一个操作,且该 API 与
.agg API 类似。
下面先创建一个 DataFrame:
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.428759
-0.864890
-0.675341
2000
-01
-02
-0.168731
1.338144
-1.279321
2000
-01
-03
-1.621034
0.438107
0.903794
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.254374
-1.240447
-0.201052
2000
-01
-09
-0.157795
0.791197
-1.144209
2000
-01
-10
-0.030876
0.371900
0.061932
这里转换的是整个 DataFrame。
.transform() 支持 Numpy 函数、字符串函数及自定义函数。
In [
179]: tsdf.transform(np.abs)
Out[
179]:
A B C
2000
-01
-01
0.428759
0.864890
0.675341
2000
-01
-02
0.168731
1.338144
1.279321
2000
-01
-03
1.621034
0.438107
0.903794
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.254374
1.240447
0.201052
2000
-01
-09
0.157795
0.791197
1.144209
2000
-01
-10
0.030876
0.371900
0.061932
In [
180]: tsdf.transform(
'abs')
Out[
180]:
A B C
2000
-01
-01
0.428759
0.864890
0.675341
2000
-01
-02
0.168731
1.338144
1.279321
2000
-01
-03
1.621034
0.438107
0.903794
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.254374
1.240447
0.201052
2000
-01
-09
0.157795
0.791197
1.144209
2000
-01
-10
0.030876
0.371900
0.061932
In [
181]: tsdf.transform(
lambda x: x.abs())
Out[
181]:
A B C
2000
-01
-01
0.428759
0.864890
0.675341
2000
-01
-02
0.168731
1.338144
1.279321
2000
-01
-03
1.621034
0.438107
0.903794
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.254374
1.240447
0.201052
2000
-01
-09
0.157795
0.791197
1.144209
2000
-01
-10
0.030876
0.371900
0.061932
这里的
transform() 接受单个函数;与 ufunc 等效。
In [
182]: np.abs(tsdf)
Out[
182]:
A B C
2000
-01
-01
0.428759
0.864890
0.675341
2000
-01
-02
0.168731
1.338144
1.279321
2000
-01
-03
1.621034
0.438107
0.903794
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.254374
1.240447
0.201052
2000
-01
-09
0.157795
0.791197
1.144209
2000
-01
-10
0.030876
0.371900
0.061932
.transform() 向
Series 传递单个函数时,返回的结果也是单个
Series。
In [
183]: tsdf.A.transform(np.abs)
Out[
183]:
2000
-01
-01
0.428759
2000
-01
-02
0.168731
2000
-01
-03
1.621034
2000
-01
-04 NaN
2000
-01
-05 NaN
2000
-01
-06 NaN
2000
-01
-07 NaN
2000
-01
-08
0.254374
2000
-01
-09
0.157795
2000
-01
-10
0.030876
Freq: D, Name: A, dtype: float64
多函数 Transform
transform() 调用多个函数时,将生成多重索引 DataFrame。第一层是原始数据集的列名;第二层是
transform() 调用的函数名。
In [
184]: tsdf.transform([np.abs,
lambda x: x +
1])
Out[
184]:
A B C
absolute <
lambda> absolute <
lambda> absolute <
lambda>
2000
-01
-01
0.428759
0.571241
0.864890
0.135110
0.675341
0.324659
2000
-01
-02
0.168731
0.831269
1.338144
2.338144
1.279321
-0.279321
2000
-01
-03
1.621034
-0.621034
0.438107
1.438107
0.903794
1.903794
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.254374
1.254374
1.240447
-0.240447
0.201052
0.798948
2000
-01
-09
0.157795
0.842205
0.791197
1.791197
1.144209
-0.144209
2000
-01
-10
0.030876
0.969124
0.371900
1.371900
0.061932
1.061932
为 Series 应用多个函数时,输出结果是 DataFrame,列名是
transform() 调用的函数名。
In [
185]: tsdf.A.transform([np.abs,
lambda x: x +
1])
Out[
185]:
absolute <
lambda>
2000
-01
-01
0.428759
0.571241
2000
-01
-02
0.168731
0.831269
2000
-01
-03
1.621034
-0.621034
2000
-01
-04 NaN NaN
2000
-01
-05 NaN NaN
2000
-01
-06 NaN NaN
2000
-01
-07 NaN NaN
2000
-01
-08
0.254374
1.254374
2000
-01
-09
0.157795
0.842205
2000
-01
-10
0.030876
0.969124
用字典执行 `transform` 操作
函数字典可以为每列执行指定
transform() 操作。
In [
186]: tsdf.transform({
'A': np.abs,
'B':
lambda x: x +
1})
Out[
186]:
A B
2000
-01
-01
0.428759
0.135110
2000
-01
-02
0.168731
2.338144
2000
-01
-03
1.621034
1.438107
2000
-01
-04 NaN NaN
2000
-01
-05 NaN NaN
2000
-01
-06 NaN NaN
2000
-01
-07 NaN NaN
2000
-01
-08
0.254374
-0.240447
2000
-01
-09
0.157795
1.791197
2000
-01
-10
0.030876
1.371900
transform() 的参数是列表字典时,生成的是以
transform() 调用的函数为名的多重索引 DataFrame。
In [
187]: tsdf.transform({
'A': np.abs,
'B': [
lambda x: x +
1,
'sqrt']})
Out[
187]:
A B
absolute <
lambda> sqrt
2000
-01
-01
0.428759
0.135110 NaN
2000
-01
-02
0.168731
2.338144
1.156782
2000
-01
-03
1.621034
1.438107
0.661897
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.254374
-0.240447 NaN
2000
-01
-09
0.157795
1.791197
0.889493
2000
-01
-10
0.030876
1.371900
0.609836
元素级函数应用
并非所有函数都能矢量化,即接受 Numpy 数组,返回另一个数组或值,DataFrame 的
applymap() 及 Series 的
map() ,支持任何接收单个值并返回单个值的 Python 函数。
示例如下:
In [
188]: df4
Out[
188]:
one two three
a
1.394981
1.772517 NaN
b
0.343054
1.912123
-0.050390
c
0.695246
1.478369
1.227435
d NaN
0.279344
-0.613172
In [
189]:
def f(x):
.....:
return len(str(x))
.....:
In [
190]: df4[
'one'].map(f)
Out[
190]:
a
18
b
19
c
18
d
3
Name: one, dtype: int64
In [
191]: df4.applymap(f)
Out[
191]:
one two three
a
18
17
3
b
19
18
20
c
18
18
16
d
3
19
19
Series.map() 还有个功能,可以“连接”或“映射”第二个 Series 定义的值。这与 merging/joining 功能联系非常紧密:
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
精选好文: