Коротко
- Большинство методов возвращают новую таблицу, поэтому их можно вызывать подряд:
df.dropna().assign(...).query(...). - Цепочку берут в круглые скобки и пишут по методу на строку — так её легко читать и комментировать.
- Внутри цепочки нельзя сослаться на промежуточный результат по имени — используйте
lambda d: …вassignиlocилиpd.col("…"). pipe(функция, …)встраивает в цепочку свою функцию от всей таблицы, в том числе для отладки.- С Copy-on-Write цепочки не копируют данные лишний раз, а исходная таблица остаётся нетронутой.
Синтаксис
(
df
.rename(columns={...})
.dropna(subset=[...])
.assign(new=lambda d: d["a"] * 2, other=pd.col("b") + 1)
.query("new > 10")
.loc[lambda d: d["other"] > 0, ["a", "new"]]
.pipe(my_func, arg=1)
)Примеры
raw = pd.DataFrame({
"Город": ["омск", "тула", "омск", "сочи", "тула", "омск"],
"Чашки": [30, 12, None, 25, 20, 16],
"Цена": [180, 165, 180, 210, 165, 190],
})df = raw.rename(columns={"Город": "city", "Чашки": "cups", "Цена": "price"})
df = df.dropna(subset=["cups"])
df["city"] = df["city"].str.title()
df["revenue"] = df["cups"] * df["price"]
df = df[df["revenue"] > 3000]
df.groupby("city")["revenue"].sum()Вывод
city Омск 8440.0 Сочи 5250.0 Тула 3300.0 Name: revenue, dtype: float64
(
raw
.rename(columns={"Город": "city", "Чашки": "cups", "Цена": "price"})
.dropna(subset=["cups"])
.assign(
city=lambda d: d["city"].str.title(),
revenue=lambda d: d["cups"] * d["price"],
)
.query("revenue > 3000")
.groupby("city")["revenue"]
.sum()
)Вывод
city Омск 8440.0 Сочи 5250.0 Тула 3300.0 Name: revenue, dtype: float64
(
raw
.rename(columns={"Город": "city", "Чашки": "cups", "Цена": "price"})
.assign(revenue=pd.col("cups") * pd.col("price")) # pandas 3.0
.loc[lambda d: d["revenue"] > 3000, ["city", "revenue"]]
)Вывод
city revenue 0 омск 5400.0 3 сочи 5250.0 4 тула 3300.0 5 омск 3040.0
def top(df, n, by):
return df.nlargest(n, by)
(
raw
.rename(columns={"Город": "city", "Чашки": "cups"})
.pipe(top, n=2, by="cups")
)Вывод
city cups Цена 0 омск 30.0 180 3 сочи 25.0 210
Подводные камни
Длинную цепочку трудно отлаживать
Если результат неожиданный, вставьте между шагами pipe с печатью размера или head() — цепочку не придётся разбирать.
def show_shape(df, label):
print(label, df.shape)
return df
(
raw
.pipe(show_shape, "исходно:")
.dropna()
.pipe(show_shape, "без пропусков:")
.query("Цена > 170")
.pipe(show_shape, "дороже 170:")
["Чашки"].sum()
)Вывод
исходно: (6, 3) без пропусков: (5, 3) дороже 170: (3, 3) np.float64(71.0)
lambda видит промежуточный результат
В assign(revenue=lambda d: …) переменная d — таблица на этом шаге цепочки, а не исходная raw. Поэтому после rename в ней уже новые имена столбцов.