DASK — чтение огромного CSV и запись в 255 разных CSV-файлов

#python #csv #dask

#python #csv #dask

Вопрос:

Я использую DASK для чтения CSV-файла размером около 2 ГБ. Я хочу записать каждую строку в отдельные CSV-файлы с 255 номерами на основе некоторой хэш-функции, как показано ниже.

Мое наивное решение:

 from dask import dataframe as dd

if __name__ == '__main__':
    df = dd.read_csv('train.csv', header=None, dtype='str')
    df = df.fillna()
    for _, line in df.iterrows():
        number = hash(line[2]) % 256
        with open("{}.csv".format(number), 'a ') as f:
            f.write(', '.join(line))
  

Этот способ занимает около 15 минут. Есть ли какой-либо способ, которым мы можем сделать это быстрее.

Комментарии:

1. Поскольку вы не делаете ничего необычного с вашим CSV, почему бы просто не использовать встроенный csv модуль? Кроме того, вы должны кэшировать свои обработчики файлов, чтобы не открывать и закрывать файлы в каждой строке вашего CSV-файла — в вашем случае нормально сохранить 256 дескрипторов открытых файлов и закрыть их в конце.

2. Спасибо @zwer итак, должен ли я открывать все файлы раньше и пытаться перебирать данные?

3. Вы можете открывать их по запросу… Создайте словарь для хранения вашей карты, скажем, file_map , затем просто сделайте что-то вроде f = file_map.get(number) и if f is None: f = file_map[number] = open('{}.csv'.format(number), 'a ') вместо вашей with ... строки. Затем просто просмотрите их все и закройте дескрипторы, как только вы выполните итерацию по всей длине вашего CSV.

4. Хорошо, прямо сейчас пытаюсь с помощью csv Reader и записываю только в один файл, но это все еще занимает больше времени.

5. По вашему мнению, какое идеальное время для такого рода задач?

Ответ №1:

Поскольку в вашей процедуре преобладает IO, маловероятно, что Dask в этом случае сделает что-либо, кроме добавления накладных расходов, если ваша хэш-функция действительно очень медленная. Я предполагаю, что это не так.

решение @zwer будет выглядеть примерно так

 files = [open("{}.csv".format(number), 'a ') for number in range(255)]
for _, line in df.iterrows():
    number = hash(line[2]) % 256
    files[number].write(', '.join(line))
[f.close() for f in files]
  

Однако ваши данные, похоже, умещаются в памяти, поэтому вы можете найти гораздо лучшую производительность

 for (number, group) in df.groupby(df.iloc[:, 2].map(hash)):
    group.to_csv("{}.csv".format(number))
  

потому что вы записываете в каждый файл непрерывно, а не переходите между ними. В зависимости от вашего устройства ввода-вывода и буферизации разница может быть нулевой или огромной.

Комментарии:

1. Спасибо mdurant, я бы обязательно попробовал это.