Чтение и обратное преобразование фрагмента данных в файл CSV и копирование в новый файл CSV

Предположим, я имею дело с очень большим 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()

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

Ответ 1

Используя 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

Ответ 2

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

Ответ 3

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

Я только что проверил его с помощью файла train.csv по предоставленной ссылке и 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 а затем возвращает line из файла снизу вверх. Довольно интересный способ сделать это.

Ответ 4

Это делает именно то, что вы просите, но без панд. Он читает 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()

Ответ 5

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

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

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

когда вы read_csv() вы не указываете никакого индекса. Если бы вы это сделали, вы могли бы вызвать sort_index() с или без 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 и его функций, вы будете запускать уже оптимизированный код, который не требует загрузки целых файлов в память. Это так легко, что это почти как измена :)