Angenommen, ich habe es mit einer sehr großen CSV-Datei zu tun. So kann ich die Datenmenge nur für Datenmenge in den Speicher einlesen. Der erwartete Fluss von Ereignissen sollte wie folgt aussehen:
1) Lesen Sie mit Hilfe von Pandas einen Datenblock (z. B. 10 Zeilen) aus der csv.
2) Kehren Sie die Reihenfolge der Daten um
3) Kopieren Sie jede Zeile in eine neue CSV-Datei. Jeder Chunk (10 Zeilen) ist also in csv von Anfang an in umgekehrter Reihenfolge geschrieben.
Am Ende sollte die CSV-Datei in umgekehrter Reihenfolge sein und dies sollte ohne Laden der gesamten Datei in den Arbeitsspeicher für Windows OS erfolgen.
Ich versuche eine Zeitserie zu prognostizieren. Ich brauche Daten, um vom alten bis zum neuesten Wert zu sein (älteste Eintrag in der ersten Zeile). Ich kann nicht die gesamte Datei in den Speicher laden. Ich suche nach einer Möglichkeit, jeden Block zu einem Zeitpunkt zu bearbeiten, wenn dies möglich ist.
Der Datensatz, den ich an train.csv
des Rossmann-Datensatzes von kaggle ausprobiert habe. Sie können es aus diesem github repo
bekommen
Mein Versuch kopiert die Zeilen nicht ordnungsgemäß in die neue CSV-Datei.
Zeigen Sie unten meinen Code an:
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()
Danke im Voraus
Mit bash können Sie die gesamte Datei bis auf die erste Zeile ausrichten und dann umkehren und sie wie folgt speichern:
tail -n +2 train.csv | tac > train_rev.csv
Wenn Sie den Header in der umgekehrten Datei behalten möchten, schreiben Sie ihn zuerst und hängen Sie dann den umgekehrten Inhalt an
head -1 train.csv > train_rev.csv; tail -n +2 train.csv | tac >> train_rev.csv
Dies tut genau das, was Sie anfordern, aber ohne Pandas. Es liest intest.csv Zeile für Zeile (anstatt die gesamte Datei in den RAM zu lesen). Die Verarbeitung erfolgt überwiegend über das Dateisystem mit einer Reihe von Chunk-Dateien, die am Ende in der Datei outtest.csv zusammengefasst werden. Wenn Sie die maxLines ändern, können Sie die Anzahl der produzierten Chunk-Dateien im Vergleich zu RAM optimieren (verbrauchte höhere Zahlen verbrauchen mehr RAM, erzeugen jedoch weniger Chunk-Dateien). Wenn Sie den CSV-Header in der ersten Zeile behalten möchten, setzen Sie keepHeader auf True. Wenn auf False gesetzt, wird die gesamte Datei einschließlich der ersten Zeile umgekehrt.
Für Tritte habe ich dies auf einem alten Raspberry Pi mit einem 128-GB-Flashlaufwerk mit einer 6-MB-CSV-Testdatei ausgeführt. Ich dachte, etwas wäre schief gelaufen, weil es fast sofort zurückgekehrt ist, also ist es auch auf langsamerer Hardware schnell. Es importiert nur eine Standard-Python-Bibliotheksfunktion (remove) und ist daher sehr portabel. Ein Vorteil dieses Codes besteht darin, dass keine Dateizeiger neu positioniert werden. Eine Einschränkung ist, dass CSV-Dateien mit Zeilenumbrüchen nicht funktionieren. Für diesen Anwendungsfall wären Pandas die beste Lösung, um die Brocken zu lesen.
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 __== '__main__':
main()
Wenn Sie über ausreichend Festplattenspeicher verfügen, können Sie Blöcke einlesen, umkehren und speichern. Nehmen Sie dann die gespeicherten Blöcke in umgekehrter Reihenfolge auf und schreiben Sie in eine neue CSV-Datei.
Im Folgenden finden Sie ein Beispiel für Pandas, bei dem auch Pickle (aus Leistungsgründen) und gzip (für Speichereffizienz) verwendet werden.
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
Ich würde nicht die Verwendung von pandas
zum Analysieren oder Streamen von Dateien empfehlen, da Sie nur zusätzlichen Aufwand einführen. Am besten lesen Sie die Datei von unten nach oben. Nun, ein großer Teil dieses Codes kommt tatsächlich von hier , wo er eine Datei aufnimmt und die Umkehrung in einem Generator zurückgibt, von dem ich glaube, dass er das ist, was Sie wollen.
Was ich getan habe, habe ich es einfach mit Ihrer Datei train.csv
aus dem bereitgestellten Link getestet und die Ergebnisse in einer neuen Datei ausgegeben.
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))
Es liest es im Grunde in umgekehrter Richtung, bis es eine neue Zeile findet und dann eine line
aus der Datei von unten nach oben ergibt. Ein ziemlich interessanter Weg.