эффективно агрегировать большой CSV-файл с несколькими столбцами типа int - PullRequest
0 голосов
/ 29 мая 2019

У меня есть большой файл CSV с некоторыми столбцами строкового типа (dtype object) и другими столбцами типа int64.
И столбцы строк, и целочисленные столбцы могут быть пустыми в CSV.Пустое значение в целочисленных столбцах представляет ноль, а пустая строка должна оставаться пустой строкой.

Я хочу объединить все целочисленные столбцы по всем другим столбцам.Каков наилучший подход для этого?

В итоге должен быть один агрегированный CSV-файл.Файл может не помещаться в память в целом, поэтому я пытался использовать Dask, но мне трудно понять, как это сделать.

Это рабочий код pandas для небольшого CSV, который подходитв память:

import pandas as pd
df = pd.read_csv("input_file.csv", na_filter=False)
df[metrics_to_aggregate] = df[metrics_to_aggregate].fillna(0).apply(pd.to_numeric)
df = df.groupby(dimensions_to_aggregate, as_index=False).sum()
df.to_csv("output_file.csv", index=False)

Например, ввод df может быть что-то вроде:

  dimension1 dimension2 dimension3 metric1 metric2 metric3
0        foo        bar                  1       1
1        foo        bar                  2               2
2                   foo        bar       1       4       2
3        foo        bar     foobar               2       1

Где вывод df должен быть:

  dimension1 dimension2 dimension3  metric1  metric2  metric3
0                   foo        bar        1        4        2
1        foo        bar                   3        1        2
2        foo        bar     foobar      NaN        2        1

Где:

metrics_to_aggregate=['metric1', 'metric2', 'metric3']
dimensions_to_aggregate=['dimension1', 'dimension2', 'dimension3']

1 Ответ

1 голос
/ 29 мая 2019

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

Простое и довольно общее решениесортировать первым.Команда unix sort удачно отсортирует файлы, которые слишком велики, чтобы уместиться в память.Затем отсортированный файл может быть легко обработан кусками.Вот последовательность, которая показывает принцип: вам может потребоваться изменить некоторые детали:

Сначала я немного расширил ваш файл, чтобы показать, что происходит, и удалил строку заголовка (которая sort будет восприниматься как данные).:

input_file.csv:
foo bar     1   1   
a       a   9   9   9
z   z       8   8   8
a       a   9   9   9
foo bar     2       2
    foo bar 1   4   2
foo bar foobar      2   1
z   z       7   7   7
a       a   9   9   9

Затем я использовал команду:

sort input_file.csv -o input_file_sorted.csv --key=1,3

Это дало мне:

input_file_sorted.csv
a       a   9   9   9
a       a   9   9   9
a       a   9   9   9
    foo bar 1   4   2
foo bar     1   1   
foo bar     2       2
foo bar foobar      2   1
z   z       7   7   7
z   z       8   8   8

Затем я запустил эту программу на Python:

import csv

number_of_dims = 3
number_of_aggs = 3

def aggregate(agg, data):
    for i,d in enumerate(data):
        if d != "":
            agg[i] += int(d)
    return

with open("input_file_sorted.csv", newline="") as f1:
    with open("output_file,csv", "w", newline="") as f2:
        csv_reader = csv.reader(f1, delimiter='\t')
        csv_writer = csv.writer(f2, delimiter='\t')
        key = None
        agg = [0] * number_of_aggs
        for l in csv_reader:
            new_key = l[:number_of_dims]
            if key is None:
                key = new_key
            if key != new_key:
                csv_writer.writerow(key + agg)
                agg = [0] * number_of_aggs
                key = new_key
            aggregate(agg, l[number_of_dims:])
        csv_writer.writerow(key + agg)

и это меня достало:

output_file.csv:
a       a   27  27  27
    foo bar 1   4   2
foo bar     3   1   2
foo bar foobar  0   2   1
z   z       15  15  15

Надеюсь, это поможет!

...