Чтение и обратное преобразование фрагмента данных в файл CSV и копирование в новый файл CSV - PullRequest
0 голосов
/ 29 октября 2018

Предположим, я имею дело с очень большим CSV-файлом. Таким образом, я могу только читать данные по частям в память. Ожидаемый поток событий должен быть следующим:

1) Считать порцию (например: 10 строк) данных из csv, используя панд.

2) Обратный порядок данных

3) Скопируйте каждую строку в новый CSV-файл в обратном порядке. Таким образом, каждый кусок (10 строк) записывается в CSV с начала в обратном порядке.

В конце файл csv должен быть в обратном порядке, и это должно быть сделано без загрузки всего файла в память для ОС Windows.

Я пытаюсь сделать прогноз по временным рядам. Мне нужны данные от старого к последнему (1-я строка, самая старая запись). Я не могу загрузить весь файл в память. Я ищу способ сделать это каждый блок за раз, если это возможно.

Набор данных, который я пробовал на train.csv набора данных Россмана из kaggle. Вы можете получить это от этого github repo

Моя попытка не копирует строки в новый CSV-файл должным образом.

Показать ниже мой код:

import pandas as pd
import csv

def reverse():

    fields = ["Store","DayOfWeek","Date","Sales","Customers","Open","Promo","StateHoliday",
              "SchoolHoliday"]
    with open('processed_train.csv', mode='a') as stock_file:
        writer = csv.writer(stock_file,delimiter=',', quotechar='"', 
                                                quoting=csv.QUOTE_MINIMAL)
        writer.writerow(fields)

    for chunk in pd.read_csv("train.csv", chunksize=10):
        store_data = chunk.reindex(index=chunk.index[::-1])
        append_data_csv(store_data)

def append_data_csv(store_data):
    with open('processed_train.csv', mode='a') as store_file:
        writer = csv.writer(store_file,delimiter=',', quotechar='"',
                                           quoting=csv.QUOTE_MINIMAL)
        for index, row in store_data.iterrows():
            print(row)
            writer.writerow([row['Store'],row['DayOfWeek'],row['Date'],row['Sales'],
            row['Customers'],row['Open'],row['Promo'],
            row['StateHoliday'],row['SchoolHoliday']])

reverse()

Заранее спасибо

Ответы [ 5 ]

0 голосов
/ 20 ноября 2018

Используя bash, вы можете привязать весь файл, кроме первой строки, а затем повернуть его вспять и сохранить с этим:

tail -n +2 train.csv  | tac > train_rev.csv

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

head -1 train.csv > train_rev.csv; tail -n +2 train.csv  | tac >> train_rev.csv
0 голосов
/ 17 ноября 2018

Это делает именно то, что вы просите, но без панд. Он читает intest.csv построчно (в отличие от чтения всего файла в ОЗУ). Он выполняет большую часть обработки с использованием файловой системы, используя серию чанков, которые в конце объединяются в файл outtest.csv. Если вы измените maxLines, вы можете оптимизировать количество создаваемых чанков по сравнению с потребляемой оперативной памятью (большее число потребляет больше оперативной памяти, но создает меньше чанковых файлов). Если вы хотите сохранить первую строку заголовка CSV, установите для свойства keepHeader значение True; если установлено значение False, оно переворачивает весь файл, включая первую строку.

Для удовольствия я запустил это на старом Raspberry Pi, используя флэш-диск 128 ГБ в тестовом файле csv 6 МБ, и я подумал, что что-то пошло не так, потому что оно почти сразу же вернулось, поэтому работает быстро даже на более медленном оборудовании. Он импортирует только одну стандартную функцию библиотеки Python (удалить), поэтому он очень переносим. Одним из преимуществ этого кода является то, что он не перемещает файловые указатели. Одним из ограничений является то, что он не будет работать с файлами CSV, в которых есть новые строки в данных. Для этого случая использования панды были бы лучшим решением для чтения кусков.

from os import remove

def writechunk(fileCounter, reverseString):
    outFile = 'tmpfile' + str(fileCounter) + '.csv'
    with open(outFile, 'w') as outfp:
        outfp.write(reverseString)
    return

def main():
    inFile = 'intest.csv'
    outFile = 'outtest.csv'
    # This is our chunk expressed in lines
    maxLines = 10
    # Is there a header line we want to keep at the top of the output file?
    keepHeader = True

    fileCounter = 0
    lineCounter = 0
    with open(inFile) as infp:
        reverseString = ''
        line = infp.readline()
        if (line and keepHeader):
            headerLine = line
            line = infp.readline()
        while (line):
            lineCounter += 1
            reverseString = line + reverseString
            if (lineCounter == maxLines):
                fileCounter += 1
                lineCounter = 0
                writechunk(fileCounter, reverseString)
                reverseString = ''
            line = infp.readline()
    # Write any leftovers to a chunk file
    if (lineCounter != 0):
        fileCounter += 1
        writechunk(fileCounter,reverseString)
    # Read the chunk files backwards and append each to the outFile
    with open(outFile, 'w') as outfp:
        if (keepHeader):
            outfp.write(headerLine)
        while (fileCounter > 0):
            chunkFile = 'tmpfile' + str(fileCounter) + '.csv'
            with open(chunkFile, 'r') as infp:
                outfp.write(infp.read())
            remove(chunkFile)
            fileCounter -= 1

if __name__ == '__main__':
    main()
0 голосов
/ 17 ноября 2018

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

Ниже приведен пример с Pandas, который также использует pickle (для повышения производительности) и gzip (для эффективности хранения).

import pandas as pd, numpy as np

# create a dataframe for demonstration purposes
df = pd.DataFrame(np.arange(5*9).reshape((-1, 5)))
df.to_csv('file.csv', index=False)

# number of rows we want to chunk by
n = 3

# iterate chunks, output to pickle files
for idx, chunk in enumerate(pd.read_csv('file.csv', chunksize=n)):
    chunk.iloc[::-1].to_pickle(f'file_pkl_{idx:03}.pkl.gzip', compression='gzip')

# open file in amend mode and write chunks in reverse
# idx stores the index of the last pickle file written
with open('out.csv', 'a') as fout:
    for i in range(idx, -1, -1):
        chunk_pkl = pd.read_pickle(f'file_pkl_{i:03}.pkl.gzip', compression='gzip')
        chunk_pkl.to_csv(fout, index=False, header=False if i!=idx else True)

# read new file to check results
df_new = pd.read_csv('out.csv')

print(df_new)

    0   1   2   3   4
0  40  41  42  43  44
1  35  36  37  38  39
2  30  31  32  33  34
3  25  26  27  28  29
4  20  21  22  23  24
5  15  16  17  18  19
6  10  11  12  13  14
7   5   6   7   8   9
8   0   1   2   3   4
0 голосов
/ 17 ноября 2018

Я бы не рекомендовал использовать pandas для разбора или потоковой передачи любых файлов, поскольку вы только вносите дополнительные издержки. Лучший способ сделать это - прочитать файл снизу вверх. Ну, большая часть этого кода на самом деле взята из здесь , где он принимает файл и возвращает реверс в генераторе, что, я считаю, то, что вы хотите.

Я только что проверил его с вашим файлом train.csv по предоставленной ссылке и вывел результаты в новый файл.

import os

def reverse_readline(filename, buf_size=8192):
    """a generator that returns the lines of a file in reverse order"""
    with open(filename) as fh:
        segment = None
        offset = 0
        fh.seek(0, os.SEEK_END)
        file_size = remaining_size = fh.tell()
        while remaining_size > 0:
            offset = min(file_size, offset + buf_size)
            fh.seek(file_size - offset)
            buffer = fh.read(min(remaining_size, buf_size))
            remaining_size -= buf_size
            lines = buffer.split('\n')
            # the first line of the buffer is probably not a complete line so
            # we'll save it and append it to the last line of the next buffer
            # we read
            if segment is not None:
                # if the previous chunk starts right from the beginning of line
                # do not concact the segment to the last line of new chunk
                # instead, yield the segment first 
                if buffer[-1] != '\n':
                    lines[-1] += segment
                else:
                    yield segment
            segment = lines[0]
            for index in range(len(lines) - 1, 0, -1):
                if lines[index]:
                    yield lines[index]
        # Don't yield None if the file was empty
        if segment is not None:
            yield segment

reverse_gen = reverse_readline('train.csv')

with open('rev_train.csv','w') as f:
    for row in reverse_gen:
        f.write('{}\n'.format(row))

Он в основном читает его в обратном порядке, пока не найдет новую строку, а затем не получит line из файла снизу вверх. Довольно интересный способ сделать это.

0 голосов
/ 14 ноября 2018

У вас есть повторяющиеся блоки кода, и вы вообще не пользуетесь пандами.

То, на что указал @sujay kumar, очень правильно, я бы прочитал это более внимательно.

Файл совсем не большой. Я использую данные тиков OHLCV, которые находятся в ГБ без проблем. Если вы используете pandas.read_csv(), вам не нужно делать частичную передачу. Конечно, это займет время, но это будет работать нормально. Если вы не собираетесь в Terrabytes. Я не проверял это.

когда вы read_csv() не указываете индекс. Если вы это сделаете, вы можете позвонить sort_index() с или без ascending=False в зависимости от заказа.

Панды тоже могут писать CSV, используйте это вместо этого. Я вставляю пример кода, который вы можете собрать.

df_temp = pd.read_csv(file_path, parse_dates=True, index_col="Date", usecols=["Date", "Adj Close"], na_values=["nan"])

Сортировка серии

s = pd.Series(list('abcde'), index=[0,3,2,5,4]) s.sort_index()

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

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