我正在尝试将在R中对数据框进行的一系列操作转换为其Python等效项。管道的基本示例如下,其中包括几个 mutate
和 filter
调用:
library(tidyverse)
calc_circle_area <- function(diam) pi / 4 * diam^2
calc_cylinder_vol <- function(area, length) area * length
raw_data <- tibble(cylinder_name=c('a', 'b', 'c'), length=c(3, 5, 9), diam=c(1, 2, 4))
new_table <- raw_data %>%
mutate(area = calc_circle_area(diam)) %>%
mutate(vol = calc_cylinder_vol(area, length)) %>%
mutate(is_small_vol = vol < 100) %>%
filter(is_small_vol)
我可以在pandas中很容易地复制这个操作,但是发现使用assign
和apply
时涉及一些嵌套的lambda
调用(首先是数据帧调用者作为参数,然后是数据帧行作为参数)。这往往会遮蔽assign操作的含义,如果可能的话,我想要指定更直接明了的内容(像R版本那样)。
import pandas as pd
import math
calc_circle_area = lambda diam: math.pi / 4 * diam**2
calc_cylinder_vol = lambda area, length: area * length
raw_data = pd.DataFrame({'cylinder_name': ['a', 'b', 'c'], 'length': [3, 5, 9], 'diam': [1, 2, 4]})
new_table = (
raw_data
.assign(area=lambda df: df.diam.apply(lambda r: calc_circle_area(r.diam), axis=1))
.assign(vol=lambda df: df.apply(lambda r: calc_cylinder_vol(r.area, r.length), axis=1))
.assign(is_small_vol=lambda df: df.vol < 100)
.loc[lambda df: df.is_small_vol]
)
我知道可以将 .assign(area=lambda df: df.diam.apply(calc_circle_area))
写成 .assign(area=raw_data.diam.apply(calc_circle_area))
,但这仅适用于原始数据帧中已存在 diam
列的情况,而这并非总是如此。
我也意识到这里的 calc_...
函数是可向量化的,这意味着我也可以进行以下操作:
.assign(area=lambda df: calc_circle_area(df.diam))
.assign(vol=lambda df: calc_cylinder_vol(df.area, df.length))
但是,由于大多数函数不能进行向量化处理,因此在大多数情况下,这种方法不起作用。
TL;DR我想知道是否有一种更干净的方法可以“改变”数据框的列,而不涉及像以下内容中双重嵌套的 lambda
语句:
.assign(vol=lambda df: df.apply(lambda r: calc_cylinder_vol(r.area, r.length), axis=1))
这种类型的应用程序是否有最佳实践,或者在方法链接的上下文中这是最好的方法吗?
df.apply
之前使用numpy.vectorize
的想法如何;这样做比使用df.apply
更快吗? - teepee