Организация конвейеров данных в библиотеке Pandas

Раздел: Обработка данных -> pandas

Организация потоков данных в Pandas

При работе с табличными данными часто требуется выполнить последовательность преобразований: фильтрацию, добавление столбцов, группировку, сортировку. Потоковый подход (chaining, piping) позволяет записать эти шаги компактно и читаемо, избегая создания множества промежуточных переменных. Рассмотрим несколько способов построения таких цепочек.

Основной подход: метод .pipe() для создания модульных потоков

Как сделать цепочку преобразований, которую легко поддерживать и переиспользовать?

Метод .pipe() позволяет передать DataFrame в пользовательскую функцию и вернуть результат, встраивая её в цепочку вызовов. Это удобно, когда шаги обработки вынесены в отдельные функции.

import pandas as pd

df = pd.DataFrame({'x': [1,2,3], 'y': [4,5,6]})

def add_z(df, val):
    df['z'] = df['x'] + df['y'] + val
    return df

def filter_gt(df, col, thresh):
    return df[df[col] > thresh]

result = (df
          .pipe(add_z, val=10)
          .pipe(filter_gt, col='z', thresh=12)
         )

print(result)

Column values python (значения столбца в python (pandas))

   x  y   z
1  2  5  17

Python наборы данных (наборы данных в python)

Пояснение: каждая функция принимает DataFrame как первый аргумент, а дополнительные параметры передаются через .pipe(). Цепочка выполняется последовательно, результат последней функции становится итогом.

Типичные ошибки:

  • Изменение исходного DataFrame внутри функции (используйте копию, если нужна сохранность оригинала).
  • Забывание возвращать DataFrame – без return pipe вернёт None.
  • Слишком длинные цепочки затрудняют отладку – разбивайте на логические блоки с комментариями.

Вариант 1: Простое chaining без пользовательских функций

Как объединить несколько встроенных методов в одну строку без pipe?

Многие методы Pandas возвращают DataFrame, поэтому их можно вызывать последовательно:

result = (df
          .query('x > 1')
          .assign(w = lambda d: d['x'] * 2)
          .sort_values('w', ascending=False)
         )

обработка больших данных python (обработка больших данных в python)

Это просто и быстро для типовых операций. Проблема: если понадобится вставить отладочный вывод или логику, потребуется разорвать цепочку.

Частая ошибка – использование .assign() с мутирующей операцией (например, df['x'] = ...) внутри лямбды, что может привести к неожиданным результатам из-за копирования.

Вариант 2: Поток с .assign() и .query() для фильтрации и создания столбцов

Как одновременно создавать новые столбцы и фильтровать данные в одном потоке?

Методы .assign() (добавление столбцов) и .query() (фильтрация) идеально сочетаются:

result = (df
          .assign(ratio = lambda d: d['x'] / d['y'])
          .query('ratio > 0.5')
         )

работа с dataframe python (работа с dataframe в python)

Цель: быстрая обработка без создания временных переменных. Случаи использования – разведочный анализ, подготовка данных для визуализации.

Ошибка: .query() использует свой синтаксис (например, @variable для внешних переменных). Забывание @ приводит к ошибке NameError.

Вариант 3: Потоковая обработка больших данных по частям (chunking)

Как обрабатывать файлы, которые не помещаются в память, используя итеративный поток?

При чтении CSV с параметром chunksize возвращается итератор по частям DataFrame. Каждый chunk можно обработать в цепочке и накопить результат:

chunks = pd.read_csv('large.csv', chunksize=10000)
results = []

for chunk in chunks:
    processed = (chunk
                 .query('value > 0')
                 .assign(doubled = lambda d: d['value'] * 2)
                )
    results.append(processed.sum())

final = pd.concat(results).to_frame().T

Цель: работа с данными, не влезающими в RAM. Проблема: агрегация требует аккуратного слияния частичных результатов.

Типичная ошибка – забыть, что chunk – это копия, и попытка изменить оригинальный файл через него не сработает.

- Python dataframe строки (строки в dataframe pandas)
- структурированные данные python (структурированные данные в python)
- Pandas python таблица (таблица pandas в python)

Расширенные примеры потоковой обработки с Pandas

Пример 1: Многоступенчатый пайплайн с .pipe() и передачей нескольких аргументов

Пример
import pandas as pd
import numpy as np

df = pd.DataFrame({'x': np.random.rand(5), 'y': np.random.rand(5)})

def scale(df, col, factor):
    df[col] = df[col] * factor
    return df

def clip_and_label(df, col, low, high, label):
    df['label'] = np.where((df[col] >= low) & (df[col] <= high), label, 'other')
    return df

result = (df
          .pipe(scale, col='x', factor=100)
          .pipe(clip_and_label, col='x', low=20, high=80, label='mid')
          .query('label == "mid"')
         )
print(result)
          x         y label
3  51.278...  0.123...   mid

Пояснение: каждая функция модифицирует DataFrame и возвращает его. Такой пайплайн легко тестировать по шагам.

Пример 2: Использование .pipe() с lambda для inline-преобразований

Пример
result = (df
          .pipe(lambda d: d.assign(z = d['x'] + d['y']))
          .pipe(lambda d: d[d['z'] > d['z'].median()])
         )
print(result)

Lambda позволяет выполнить короткое преобразование без объявления отдельной функции. Проблема: сложность отладки.

Пример 3: Цепочка с .groupby() и .agg() – потоковая агрегация

Пример
df = pd.DataFrame({'group': ['A','A','B'], 'val': [1,2,3]})
result = (df
          .groupby('group')
          .agg(mean_val=('val', 'mean'), sum_val=('val', 'sum'))
          .reset_index()
          .assign(ratio = lambda d: d['sum_val'] / d['mean_val'])
         )
print(result)
  group  mean_val  sum_val  ratio
0     A       1.5        3    2.0
1     B       3.0        3    1.0

Здесь groupby возвращает GroupBy объект, который не поддерживает chaining с assign напрямую, поэтому сначала агрегируем, затем сбрасываем индекс.

Пример 4: Комбинирование chunked чтения с агрегацией по ключу

Пример
import pandas as pd
from io import StringIO

# Имитация большого CSV
data = """key,val
A,10
B,20
A,30
B,40
"""
chunks = pd.read_csv(StringIO(data), chunksize=2)

total = {}
for chunk in chunks:
    grouped = chunk.groupby('key')['val'].sum()
    for key, val in grouped.items():
        total[key] = total.get(key, 0) + val

print(total)
{'A': 40, 'B': 60}

Пояснение: каждый chunk группируется отдельно, результаты суммируются в словаре. Это эффективно при больших объёмах.

Пример 5: Поток с .eval() для быстрых вычислений без копирования

Пример
df = pd.DataFrame({'a': [1,2], 'b': [3,4]})
result = df.eval('c = a + b')
print(result)
   a  b  c
0  1  3  4
1  2  4  6

.eval() использует NumExpr и работает быстрее assign для сложных формул. Однако он не поддерживает прямую фильтрацию – для этого combine с .query().

Потоки данных в Python - comments

En
потоки данных python (python)