Большой CSV по частям: как правильно объединять средние
CSV не помещается в привычный объём памяти. Вы читаете файл частями и сохраняете средний чек каждой порции. Среднее этих средних меняется вместе с размером порции, хотя исходные заказы остаются теми же.
Выбрать объединяемое состояние
Среднее нельзя объединить простым средним, если в порциях разное число наблюдений. Нужно сохранить сумму и количество известных значений. После обработки всех порций складывают эти величины по группе и делят один раз. Так результат не зависит от границ chunks.
Для других показателей состояние отличается. Уникальных клиентов нельзя посчитать суммой числа уникальных в порциях: один клиент встречается в нескольких. Медиана тоже не восстанавливается из медиан порций. Перед чтением большого файла определите, какие промежуточные сведения действительно достаточны для показателя.
Проверить неполную последнюю порцию
В примере пять строк и chunksize=2. Последняя порция содержит один заказ, поэтому усреднение средних особенно заметно искажает результат. Есть пропуск суммы: число строк и число известных чеков различаются. Для среднего используем count по сумме.
read_csv получает явные типы и список столбцов. Это уменьшает ненужные данные в памяти, а не только дробит уже полностью загруженную таблицу.
from io import StringIO
import pandas as pd
csv = """channel,amount
web,10
web,20
web,100
web,
web,200
"""
parts = []
for chunk in pd.read_csv(StringIO(csv), chunksize=2, usecols=["channel", "amount"],
dtype={"channel": "string", "amount": "float64"}):
parts.append(chunk.groupby("channel")["amount"].agg(total="sum", known="count"))
state = pd.concat(parts).groupby(level=0).sum()
state["mean"] = state["total"] / state["known"]
print(state)
assert state.loc["web", "total"] == 330
assert state.loc["web", "known"] == 4
assert state.loc["web", "mean"] == 82.5Средний известный чек равен 82.5. Пропущенная сумма не стала нулевым чеком. Все промежуточные суммы и количества можно сверить с полным чтением на небольшом контрольном файле.
Оценить размер состояния
Чтение по частям помогает, если итоговое состояние заметно меньше файла. Если групп почти столько же, сколько строк, список parts тоже вырастет. В таком случае объединяйте состояния постепенно или используйте хранилище, подходящее для объёма и ключей. chunksize сам по себе не гарантирует ограниченный расход памяти всего процесса.
Сохраняйте полный набор групп: пропущенный ключ groupby требует явной политики. В примере канал обязателен; если неизвестный допустим, используйте dropna=False на каждом этапе. Важно, чтобы промежуточная и финальная группировка одинаково трактовали ключ.
Для регулярного отчёта запишите число прочитанных строк, известные суммы и число порций. Проверьте результат при нескольких chunksize. Итоговое значение не должно зависеть от удобного размера чтения. Для float возможны малые различия порядка сложения; финансовую точность задайте отдельно, например целыми минимальными единицами.
Закрепить на практике
В бесплатном уроке read_csv вы закрепите типы и загрузку столбцов. Затем переходите к чтению по частям из статьи: выберите состояние, которое можно корректно объединить, и испытайте разные границы порций. Откройте бесплатный урок и задачи. Полная последовательность тем — в бесплатном курсе.
Продолжить на практике
- NumPy и pandas: практический тренажёр ↗
Бесплатный тренажёр NumPy и pandas для тех, кто знает основы Python. Научитесь очищать таблицы, объединять данные и собирать отчёты: 31 урок и 124 задачи с кодом и автоматической проверкой.
