Czy istnieje sposób na dodanie wiersza nagłówka do pliku CSV bez wczytywania pliku CSV do pamięci w pythonie? Mam plik CSV o pojemności 18 GB, do którego chcę dodać nagłówek, a wszystkie metody, które widziałem, wymagają załadowania pliku CSV do pamięci, co jest oczywiście niewykonalne.Dodaj nagłówek do CSV bez ładowania CSV
Odpowiedz
Wystarczy skorzystać z faktu, że csv
Moduł iteracje w wierszach, więc nigdy nie wczytuje całego pliku w pamięci
import csv
with open("huge_csv.csv") as fr, open("huge_output.csv","w",newline='') as fw:
cr = csv.reader(fr)
cw = csv.writer(fw)
cw.writerow(["title1","title2","title3"])
cw.writerows(cr)
wykorzystaniem writerows
zapewniają bardzo dobrą prędkość. Pamięć zostaje tutaj oszczędzona. Wszystko odbywa się liniowo. Ponieważ dane są poprawnie przetwarzane, można nawet zmienić separator i/lub cytowanie w pliku wyjściowym.
Będziesz musiał przepisać cały plik. Najprostszy nie jest użycie pytona
echo 'col1, col2, col2,... ' > out.csv
cat in.csv >> out.csv
Rozwiązania oparte na Pythonie będą działały na znacznie wyższych poziomach i będą dużo wolniejsze. 18 GB to jednak dużo danych. Lepiej pracować z funkcjami systemu operacyjnego, które będą najszybsze.
A jeśli nie chcesz opuścić Pythona, możesz zawsze zawinąć to w wywołanie 'subprocess.call()'. Lub nawet napisać pierwszą linię w Pythonie, jak w odpowiedzi @ maximilian-peters. –
To tak naprawdę najszybsze rozwiązanie, zobacz punkt odniesienia w drugiej odpowiedzi. –
dziękuję @MaximilianPeters za poświęcenie czasu na porównanie – e4c5
Oto porównanie trzech sugerowanych rozwiązań dla pliku CSV o wielkości ~ 200 MB z 10^6 rzędami i 10 kolumnami (n = 50). Stosunek pozostaje mniej więcej taki sam dla większych i mniejszych plików (od 10 MB do 8 GB).
CP: shutil: csv_reader 1:10:55
to za pomocą wbudowanego cp
funkcji jest około 55 razy większa niż przy użyciu modułu Pythona csv
.
Komputer:
- regularne HDD
- Python 3.5.2 64-bit
- Ubuntu 16.04
- i7-3770
import csv
import random
import shutil
import time
import subprocess
rows = 1 * 10**3
cols = 10
repeats = 50
shell_script = '/tmp/csv.sh'
input_csv = '/tmp/temp.csv'
output_csv = '/tmp/huge_output.csv'
col_titles = ['titles_' + str(i) for i in range(cols)]
with open(shell_script, 'w') as f:
f.write("#!/bin/bash\necho '{0}' > {1}\ncat {2} >> {1}".format(','.join(col_titles), output_csv, input_csv))
with open(shell_script, 'w') as f:
f.write("echo '{0}' > {1}\ncat {2} >> {1}".format(','.join(col_titles), output_csv, input_csv))
subprocess.call(['chmod', '+x', shell_script])
run_times = dict([
('csv_writer', list()),
('external', list()),
('shutil', list())
])
def random_csv():
with open(input_csv, 'w') as csvfile:
csv_writer = csv.writer(csvfile, delimiter=',')
for i in range(rows):
csv_writer.writerow([str(random.random()) for i in range(cols)])
with open(output_csv, 'w'):
pass
for r in range(repeats):
random_csv()
#http://stackoverflow.com/a/41982368/2776376
start_time = time.time()
with open(input_csv) as fr, open(output_csv, "w", newline='') as fw:
cr = csv.reader(fr)
cw = csv.writer(fw)
cw.writerow(col_titles)
cw.writerows(cr)
run_times['csv_writer'].append(time.time() - start_time)
random_csv()
#http://stackoverflow.com/a/41982383/2776376
start_time = time.time()
subprocess.call(['bash', shell_script])
run_times['external'].append(time.time() - start_time)
random_csv()
#http://stackoverflow.com/a/41982383/2776376
start_time = time.time()
with open('header.txt', 'w') as header_file:
header_file.write(','.join(col_titles))
with open(output_csv, 'w') as new_file:
with open('header.txt', 'r') as header_file, open(input_csv, 'r') as main_file:
shutil.copyfileobj(header_file, new_file)
shutil.copyfileobj(main_file, new_file)
run_times['shutil'].append(time.time() - start_time)
print('#'*20)
for key in run_times:
print('{0}: {1:.2f} seconds'.format(key, run_times[key][-1]))
print('#'*20)
print('Averages')
for key in run_times:
print('{0}: {1:.2f} seconds'.format(key, sum(run_times[key])/len(run_times[key])))
Jeśli naprawdę chcesz to zrobić w Pythonie, można utworzyć plik nagłówka, a następnie połączyć je z 2 pliku poprzez shutil.copyfileobj
.
import shutil
with open('header.txt', 'w') as header_file:
header_file.write('col1;col2;col3')
with open('new_file.csv', 'w') as new_file:
with open('header.txt', 'r') as header_file, open('main.csv', 'r') as main_file:
shutil.copyfileobj(header_file, new_file)
shutil.copyfileobj(main_file, new_file)
Czy wypróbowałeś metodę zapisu w DictWriter? (https://docs.python.org/3/library/csv.html) Nie znam dokładnej odpowiedzi, po prostu pomysł: Jeśli otworzysz plik z "a" i spróbujesz użyć nagłówka, działa? Byłoby dobrze, gdybyś zrobił to próbując z kopią oryginalnego pliku, jeśli coś jest nie tak. – Kroy
Zobacz zaktualizowaną odpowiedź, aby porównać szybkość trzech metod. –