Python csv.DictReader не хватает памяти - PullRequest
0 голосов
/ 04 января 2019

Я хочу отсортировать значения в CSV-файле на основе метки времени и распечатать его в другой файл, но для файлов с несколькими строками в Python не хватает памяти (когда файл читается). Есть ли что-то, что я могу сделать, чтобы сделать это более эффективным, или я должен использовать что-то еще, чем csv.DictReader?

import csv, sys
import datetime
from pathlib import Path

localPath = "C:/MyPath"


    # data variables 
dataDir = localPath + "data/" dataExtension = ".dat" 

    pathlistData = Path(dataDir).glob('**/*'+ dataExtension)

    # Generated filename as date, Format: YYYY-DDDTHH
    generatedDataDir = localPath + "result/"
    #generatedExtension = ".dat"
    errorlog = 'errorlog.csv'

    fieldnames = ['TimeStamp', 'A', 'B', 'C', 'C', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L','M', 'N', 'O', 'P', 'Q', 'R'] 

    for dataPath in pathlistData:
        #stores our data in a dictionary
        dataDictionary = {}

        dataFileName = str(dataPath).replace('\\', '/')
        newFilePathString = dataFileName.replace(dataDir,generatedDataDir)

        with open(dataPath, 'r') as readFile:
            print(str("Reading data from " + dataFileName))
            keysAsDate = []#[datetime.datetime.strptime(ts, "%Y-%m-%d") for ts in timestamps]
            reader = csv.DictReader(readFile, fieldnames=fieldnames) 

            for row in reader:

                try:
                    timestamp = row['TimeStamp']
                    #create a key based on the timestamp
                    timestampKey = datetime.datetime.strptime(timestamp[0:16], "%Y-%jT%H:%M:%S")
                    #save this key as a date, used later for sorting
                    keysAsDate.append(timestampKey)
                    #save the row data in a dictionary
                    dataDictionary[timestampKey] = row

                except csv.Error as e:
                    sys.exit('file %s, line %d: %s' % (errorlog, reader.line_num, e))

            #sort the keys
            keysAsDate.sort()
        readFile.close()

        with open(newFilePathString, 'w') as writeFile:
            writer = csv.DictWriter(writeFile, fieldnames=fieldnames, lineterminator='\n')
            print(str("Writing data to " + newFilePathString))
            #loop over the sorted keys
            for idx in range(0, len(keysAsDate)):

                #get the row from our data dictionary 
                writeRow = dataDictionary[keysAsDate[idx]]
                #print(dataDictionary[keysAsDate[key]])
                writer.writerow(writeRow)
                if idx%30000 == 0:
                    print("Writing to new file: " + str(int(idx/len(keysAsDate) * 100)) + "%")


        print(str("Finished writing to file: " + newFilePathString))

        writeFile.close()

ОБНОВЛЕНИЕ: Я использовал панды и разделил большой файл на более мелкие куски, которые я мог отсортировать по отдельности. В настоящее время это не решает проблему для дико неуместных значений, если я добавляю файлы друг за другом.

for dataPath in pathlistData:

dataFileName = str(dataPath).replace('\\', '/')
#newFilePathString = dataFileName.replace(dataDir,generatedDataDir)


print(str("Reading data from " + dataFileName))
#divide our large data frame into smaller data frame chunks
#so we can sort the content in memory
for df_chunk in pd.read_csv(dataFileName, header = None, chunksize = chunk_size, names = fieldnames):
    dataDictionary = {}
    dataDictionary.clear()

    for idx in range(0, chunk_size):
        #print(df_chunk[idx:idx+1])
        row = df_chunk[idx:idx+1]
        dataDictionary = df_chunk.sort_values(['TimeStamp'], ascending=True)
    firstTimeStampInChunk = dataDictionary[0:1]['TimeStamp']
    #print("first: " + firstTimeStampInChunk)
    lastTimeStampInChunk = dataDictionary[chunk_size-1:chunk_size]['TimeStamp']
    #print("last: " + lastTimeStampInChunk)

    timestampStr = str(firstTimeStampInChunk)[chunk_shift:timestamp_size+chunk_shift] + str(lastTimeStampInChunk)[chunk_shift:timestamp_size+chunk_shift]
    tempFilePathString = str(timestampStr + dataExtension).replace(':', '_').replace('\\', '/')
    dataDictionary.to_csv('temp/'+tempFilePathString, header = None, index=False)

# data variables
tempDataDir = localPath + "temp/"
tempPathlistData = Path(tempDataDir).glob('**/*'+ dataExtension)

tempPathList = list(tempPathlistData)

Моя теория алгоритма (без кода) для решения проблемы со случайными значениями:

Шаг 1 - разделить на более мелкие куски, где "chunk_size = максимальное количество строк для обработки в памяти, разделенное на два"

Шаг 2 - Прокручивайте файлы по порядку, объединяйте два файла за раз и сортируйте их вместе, затем снова разделяйте, чтобы ни один файл не был больше, чем chunk_size.

Шаг 3 - Перебрать в обратном направлении, объединяя два файла за раз и сортируя их, а затем снова разделять, чтобы ни один файл не был больше, чем chunk_size.

Шаг 4 - Теперь все дико неуместные низкие значения должны были переместиться в самую низкую часть, а все дико неуместные высокие значения должны были переместиться в самую высокую часть. Добавьте файлы по порядку!

Против; Временная сложность для этого не является предпочтительной вообще, в основном O (N ^ 2), если я не ошибаюсь

Ответы [ 2 ]

0 голосов
/ 04 января 2019

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

  1. Сжатие длинных записей до одного смещения с эффективным использованием памяти. Вызывайте tell(), когда вы читаете каждую запись (или суммируете длины строк), и сохраняете только временные метки плюс смещения файлов в памяти. Сортировать смещения по отметке времени. Повторно вызывайте seek(), когда вы проходите через отсортированные кортежи, делаете случайное чтение записи и добавляете это к вашему выходному файлу.
  2. Гораздо лучший подход - позволить /usr/bin/sort выполнить внешнюю сортировку. Пользователи Windows могут получить coreutils GNU sort из https://git -scm.com / download / . Используйте для этого модуль подпроцесс .
0 голосов
/ 04 января 2019

Попробуйте pandas csv reader, который довольно эффективен. (https://pandas.pydata.org/pandas-docs/stable/generated/pandas.read_csv.html). Вы можете легко конвертировать между пандами и словарями, используя https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.to_dict.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...