Я пытаюсь перевести поток манипуляций на кадре данных в R в его Python эквивалент. Базовый c пример конвейера выглядит следующим образом: несколько вызовов 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 без особых проблем, но обнаружу, что это включает в себя несколько вложенных lambda
вызовов при использовании assign
для выполнения apply
(сначала, когда вызывающий объект dataframe является аргументом, а затем со строками dataframe в качестве аргумента). Это имеет тенденцию затенять смысл вызова присваивания, где я хотел бы указать что-то большее в точку (например, версию 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))
Существуют ли передовые практики для приложений такого типа или это лучшее, что можно сделать в контексте цепочки методов?