Как разделить чтение большого CSV файла на куски одинакового размера в Python?
В основном у меня был следующий процесс.
import csv
reader = csv.reader(open('huge_file.csv', 'rb'))
for line in reader:
process_line(line)
Смотрите этот question. Я хочу отправить технологическую линию каждые 100 строк, чтобы реализовать пакетное очертание.
Проблема с реализацией связанного ответа заключается в том, что объект csv не подлежит подписке и не может использовать len.
>>> import csv
>>> reader = csv.reader(open('dataimport/tests/financial_sample.csv', 'rb'))
>>> len(reader)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type '_csv.reader' has no len()
>>> reader[10:]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '_csv.reader' object is unsubscriptable
>>> reader[10]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '_csv.reader' object is unsubscriptable
Как я могу это решить?
Ответы
Ответ 1
Просто сделайте ваш reader
подписанным, обернув его в list
. Очевидно, что это повредит действительно большие файлы (см. альтернативы в Обновлениях ниже):
>>> reader = csv.reader(open('big.csv', 'rb'))
>>> lines = list(reader)
>>> print lines[:100]
...
Дополнительная информация: Как разделить список на куски одинакового размера в Python?
Обновление 1 (список версий). Другой возможный способ - просто обработать каждый патрон, поскольку он поступает во время итерации по строкам:
#!/usr/bin/env python
import csv
reader = csv.reader(open('4956984.csv', 'rb'))
chunk, chunksize = [], 100
def process_chunk(chuck):
print len(chuck)
# do something useful ...
for i, line in enumerate(reader):
if (i % chunksize == 0 and i > 0):
process_chunk(chunk)
del chunk[:]
chunk.append(line)
# process the remainder
process_chunk(chunk)
Обновление 2 (версия генератора): я не тестировал его, но, возможно, вы можете повысить производительность с помощью генератора чанков:
#!/usr/bin/env python
import csv
reader = csv.reader(open('4956984.csv', 'rb'))
def gen_chunks(reader, chunksize=100):
"""
Chunk generator. Take a CSV 'reader' and yield
'chunksize' sized slices.
"""
chunk = []
for i, line in enumerate(reader):
if (i % chunksize == 0 and i > 0):
yield chunk
del chunk[:] # or: chunk = []
chunk.append(line)
yield chunk
for chunk in gen_chunks(reader):
print chunk # process chunk
# test gen_chunk on some dummy sequence:
for chunk in gen_chunks(range(10), chunksize=3):
print chunk # process chunk
# => yields
# [0, 1, 2]
# [3, 4, 5]
# [6, 7, 8]
# [9]
Есть небольшая ошибка, как указывает @totalhack points out:
Имейте в виду, что это дает один и тот же объект снова и снова с различным содержанием. Это прекрасно работает, если вы планируете делать все, что вам нужно, с чанком между каждой итерацией.
Ответ 2
Нет хорошего способа сделать это для всех файлов .csv
. Вы должны разделить файл на куски, используя file.seek
, чтобы пропустить раздел файла. Затем вам нужно сканировать один байт за раз, чтобы найти конец строки. Вы можете самостоятельно обрабатывать два куска. Что-то вроде следующего (непроверенного) кода должно начать вас.
file_one = open('foo.csv')
file_two = open('foo.csv')
file_two.seek(0, 2) # seek to the end of the file
sz = file_two.tell() # fetch the offset
file_two.seek(sz / 2) # seek back to the middle
chr = ''
while chr != '\n':
chr = file_two.read(1)
# file_two is now positioned at the start of a record
segment_one = csv.reader(file_one)
segment_two = csv.reader(file_two)
Я не уверен, как вы можете сказать, что вы закончили прохождение segment_one
. Если у вас есть столбец в CSV, который является идентификатором строки, вы можете остановить обработку segment_one
, когда вы встретите идентификатор строки из первой строки в segment_two
.
Ответ 3
Мы можем использовать модуль Pandas для обработки этих больших CSV файлов.
df = pd.DataFrame()
temp = pd.read_csv('BIG_File.csv', iterator=True, chunksize=1000)
df = pd.concat(temp, ignore_index=True)