看过来
《pandas 教程》 持续更新中,提供建议、纠错、催更等加作者微信: gairuo123(备注:pandas教程)和关注公众号「盖若」ID: gairuo。跟作者学习,请进入 Python学习课程。欢迎关注作者出版的书籍:《深入浅出Pandas》 和 《Python之光》。
数据框和序列可以传递到函数中,但是,如果需要在链式方法中调用函数,这时候就可以考虑使用 pipe() 方法。它可以返回经过函数处理后的任意类型和形式的数据。
Pandas 在 v0.16.2 中添加了pipe() 方法。它受 R 语言的的 {magrittr} 启发,可以对数据连续操作形成方法链(多个方法连续调用对数据进行处理)。
Series.pipe,DataFrame.pipe 意味着 x.pipe(f, *args, **kwargs)
和 f(x, *args, **kwargs)
效果相同。换句话说,该函数应用于整个数据。
DataFrame、Series、GroupBy 等使用 pipe 时的语法相同,以下以 DataFrame 为例:
DataFrame.pipe(
func: 'Callable[..., T] | tuple[Callable[..., T], str]',
*args,
**kwargs,
) -> 'T'
参数:
返回:
相当于执行 func(self, *args, **kwargs)
,self 则是原数据。其中函数可以用以下几种形式表示:
df.pipe(fun) # 自定义
df.pipe(max) # python 内置函数
df.pipe(lambda x: x*2) # lambda
df.pipe(np.mean) # numpy 等其他库的函数 ufunc
df.pipe(pd.Series.mean) # Pandas 自己的函数
使用 .pipe
将需要 Series、DataFrames 或 GroupBy 对象的函数链接在一起。而不是写作:
func(g(h(df), arg1=a), arg2=b, arg3=c)
而写做以下链式调用的形式(也叫链式方法),可以清晰地看到每一步对数据做了什么操作:
(df.pipe(h)
.pipe(g, arg1=a)
.pipe(func, arg2=b, arg3=c)
)
如果有一个函数将数据作为第二个参数,则传递一个元组,指示哪个关键字需要数据。例如,假设 f 的数据为 arg2:
(df.pipe(h)
.pipe(g, arg1=a)
.pipe((func, 'arg2'), arg1=a, arg3=c)
)
接下来介绍常用的数据类型使用 pipe() 的方法:
以下是一些示例:
# 被传递的类型是调用的实例
df.pipe(type)
# pandas.core.frame.DataFrame
df.pipe(len)
# 4 可以返回 df 的长度
np.random.seed(1)
df = pd.DataFrame(np.random.randn(10, 10))
# DataFrame 定义绘制 heatmap 作为参数的函数
def heatmap(df):
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
return ax.pcolor(df.values, cmap='Greens')
# 如同 heatmap(df)
df.pipe(heatmap)
如果函数 fun 的第一个参数不是数据本身,可采用一元组来传递,元组第一个元素为 fun 的函数名,第二个为传入数据的参数的字符串形式,函数 fun 剩余参数则依位置或者关键字传入,如:
# 数据是第二个参数
def fun(x, my_df):
return my_df*x
# 调用和传值方法
df.pipe((fun, 'my_df'), 2)
df.pipe((fun, 'my_df'), x=2)
上例函数 fun 的第二个参数 my_df 是要传入的数据,在 pipe 调用时第一个参数需要用元组表示,元组的第一位 fun 代表这个函数,fun 的参数 my_df 有字符串形式放在第二位,其余的参数在元组后传入。
以下是一些示例:
# 被传递的类型是调用的实例
s.pipe(type)
# pandas.core.series.Series
df.Q1.pipe(len)
# 100 可以返回 Series 的长度
# 数据是第二个参数
def fun(x, ser):
return ser*x
# 调用和传值方法
df.Q1.pipe((fun, 'ser'), 2)
'''
0 178
1 72
2 114
3 186
4 130
...
95 96
96 42
97 196
98 22
99 42
Name: Q1, Length: 100, dtype: int64
'''
# 取标签名
df.Q1.pipe(lambda x: x.name)
# 'Q1'
语法如:
GroupBy.pipe(self, func: 'Callable[..., T] | tuple[Callable[..., T], str]',
*args,
**kwargs) -> 'T'
将带有参数的函数 func 应用于此 GroupBy 对象并返回函数的结果。也是支持 DataFrame 的元组形式传入函数,即 fun 处理数据不在第一个参数位的情况。
以下是一些示例:
# 构造数据
df = pd.DataFrame({'A': 'a b a b'.split(), 'B': [1, 2, 3, 4]})
df
'''
A B
0 a 1
1 b 2
2 a 3
3 b 4
'''
要一次性获得每组最大值和最小值之间的差异,可以执行以下操作:
df.groupby('A').pipe(lambda x: x.max() - x.min())
'''
B
A
a 2
b 2
'''
与 DataFrame 和 Series 提供的功能类似,可以使用管道方法将采用 GroupBy 对象的函数链接在一起,以实现更清晰、更可读的语法。
当需要重用 groupby 对象时,将 .groupby 和 .pipe 结合起来通常很有用。举个例子,想象一下有一个数据,其中包含商店、产品、收入和销售数量的列。我们想对每个商店和每个产品的价格(即收入/数量)进行分组计算。我们可以通过多步骤操作来实现这一点,但是用管道表示可以使代码更具可读性。首先我们创建数据:
n = 1000
df = pd.DataFrame(
{
"Store": np.random.choice(["Store_1", "Store_2"], n),
"Product": np.random.choice(["Product_1", "Product_2"], n),
"Revenue": (np.random.random(n) * 50 + 10).round(2),
"Quantity": np.random.randint(1, 10, size=n),
}
)
df.head(2)
'''
Store Product Revenue Quantity
0 Store_2 Product_1 26.12 1
1 Store_2 Product_1 28.86 1
'''
# 要找到每个商店/产品的价格
(
df.groupby(["Store", "Product"])
.pipe(lambda grp: grp.Revenue.sum() / grp.Quantity.sum())
.unstack()
.round(2)
)
'''
Product Product_1 Product_2
Store
Store_1 6.82 7.05
Store_2 6.30 6.64
'''
# 将分组对象传递给某个任意函数
def mean(groupby):
return groupby.mean()
df.groupby(["Store", "Product"]).pipe(mean)
'''
Revenue Quantity
Store Product
Store_1 Product_1 34.622727 5.075758
Product_2 35.482815 5.029630
Store_2 Product_1 32.972837 5.237589
Product_2 34.684360 5.224000
'''
其中 mean 取 GroupBy 对象,并分别查找每个商店产品组合的 Revenue 和 Quantity 列的平均值。平均函数可以是 GroupBy 对象中的任何函数;.pipe 将 GroupBy 对象作为参数传递到指定的函数中。
与 DataFrame.pipe 类似,此方法可以简化多个用户定义函数对样式器的应用。而不用这么写:
f(g(df.style.set_precision(3), arg1=a), arg2=b, arg3=c)
可简化为:
(df.style.set_precision(3)
.pipe(g, arg1=a)
.pipe(f, arg2=b, arg3=c))
特别是,这允许用户定义接受 styler 对象以及其他参数的函数,并在进行样式更改后返回 styler(例如调用 styler.apply() 或 Styler.set_properties()
)。使用 .pipe,这些用户定义的样式“转换”可以与对内置样式器接口的调用交织在一起。
例如定义一个样式处理函数:
def format_conversion(styler):
return (styler.set_properties(**{'text-align': 'right'})
.format({'conversion': '{:.1%}'}))
可以在其他样式修改序列中调用上述用户定义的格式转换函数:
df = pd.DataFrame({'trial': list(range(5)),
'conversion': [0.75, 0.85, np.nan, 0.7, 0.72]})
# 调用
(df.style
.highlight_min(subset=['conversion'], color='yellow')
.pipe(format_conversion)
.set_caption("Results with minimum conversion highlighted.")
)
pipe 与 apply、map、applymap 相关方法对比示意图如下:
可以调用 pipe()
的对象还有以下对象:
更新时间:2022-01-22 14:38:47 标签:pandas pipe 函数