Supposons que je traite avec un très gros fichier csv. Donc, je ne peux que lire le bloc de données par bloc dans la mémoire. Le flux d'événements attendu devrait être le suivant:
1) Lire un bloc (par exemple: 10 lignes) de données provenant de CSV à l’aide de pandas.
2) Inverser l'ordre des données
3) Copiez chaque ligne dans le nouveau fichier csv en sens inverse. Donc, chaque morceau (10 lignes) est écrit sur csv depuis le début dans l’ordre inverse.
En fin de compte, le fichier csv doit être inversé et ceci sans charger tout le fichier en mémoire pour Windows OS.
J'essaie de faire une prévision dans une série chronologique. J'ai besoin que les données soient les plus anciennes à la plus récente (entrée la plus ancienne de la première ligne). Je ne peux pas charger le fichier entier en mémoire. Je cherche un moyen de le faire chaque morceau à la fois si c'est possible.
Le jeu de données que j'ai essayé sur train.csv
du jeu de données Rossmann from kaggle. Vous pouvez l'obtenir de cette github repo
Ma tentative ne copie pas correctement les lignes dans le nouveau fichier csv.
Voir ci-dessous est mon code:
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()
Merci d'avance
En utilisant bash, vous pouvez mettre le fichier entier à part, sauf la première ligne, puis l'inverser et le stocker avec ceci:
tail -n +2 train.csv | tac > train_rev.csv
Si vous souhaitez conserver l'en-tête dans le fichier inversé, écrivez-le d'abord, puis ajoutez le contenu inversé.
head -1 train.csv > train_rev.csv; tail -n +2 train.csv | tac >> train_rev.csv
Cela fait exactement ce que vous demandez, mais sans les pandas. Il lit intest.csv ligne par ligne (par opposition à la lecture du fichier entier dans la RAM). Il effectue la majeure partie du traitement à l'aide du système de fichiers à l'aide d'une série de fichiers fragmentés qui sont ensuite agrégés dans le fichier outtest.csv. Si vous modifiez maxLines, vous pouvez optimiser le nombre de fichiers de bloc produits par rapport à RAM consommée (des nombres plus élevés consomment plus de RAM mais produisent moins de fichiers de bloc). Si vous souhaitez conserver la première ligne de l'en-tête CSV, définissez keepHeader sur True; Si la valeur est False, le fichier entier est inversé, y compris la première ligne.
Pour le plaisir, j’ai exécuté cela sur un vieux Raspberry Pi en utilisant un lecteur flash de 128 Go sur un fichier de test csv de 6Mo et j’ai pensé que quelque chose s’était mal passé car il est revenu presque immédiatement, il est donc rapide, même sur du matériel plus lent. Il importe seulement une fonction de bibliothèque python standard (remove), il est donc très portable. L'un des avantages de ce code est qu'il ne repositionne aucun pointeur de fichier. Une des limites est que cela ne fonctionnera pas sur les fichiers CSV contenant des nouvelles lignes dans les données. Pour ce cas d’utilisation, les pandas seraient la meilleure solution pour lire les morceaux.
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()
Si vous avez suffisamment d’espace disque, vous pouvez lire, inverser et stocker des morceaux. Ensuite, récupérez les morceaux stockés dans l'ordre inverse et écrivez dans un nouveau fichier csv.
Vous trouverez ci-dessous un exemple avec Pandas qui utilise également pickle (efficacité des performances) et gzip (efficacité de stockage).
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
Je ne recommanderais pas d'utiliser pandas
pour analyser ou diffuser des fichiers, car vous n'introduisez que du temps système supplémentaire. La meilleure façon de le faire est de lire le fichier de bas en haut. Eh bien, une grande partie de ce code provient en fait de ici où il prend un fichier et retourne l’inverse dans un générateur, ce qui, je crois, est ce que vous voulez.
Ce que j'ai fait vient de le tester avec votre fichier train.csv
à partir du lien fourni et de générer les résultats dans un nouveau fichier.
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))
En gros, il le lit en sens inverse jusqu'à ce qu'il trouve une nouvelle ligne, puis génère une variable line
du fichier de bas en haut. Une façon très intéressante de le faire.