2013-10-06 2 views
0

Привет, ребята, я пишу программу для чтения csv-файла. Я искал объект-читатель и вызываю next(), он дает мне строку заголовка. Но когда я вызываю ее снова, дает ошибку StopIteration хотя есть строки в CSV file.i делает file.seek (0), то она работает fine.Anyone пожалуйста объясняет это me.a снимка кода приведен нижеОшибка StopIteration в pythoncode при чтении данных csv

with open(file,'r') as f: 
    reader = csv.reader(f) 
    header = next(reader) 
    result = [] 
    for colname in header[2:]: 
      col_index = header.index(colname)  
    #   f.seek(0) 
      next(reader) 
+0

Да это связано с os – maverick

+1

В качестве побочного примечания вы никогда не должны делать 'for foo в bar: index = bar.index (foo)'. Это медленный, сложный и потенциально ошибочный (что происходит, если два столбца имеют одинаковое имя?). Просто выполните 'for index, foo в enumerate (bar):'. – abarnert

ответ

2

You» re вызывает next один раз для каждого столбца (кроме первых двух). Итак, если у вас есть, скажем, 10 столбцов, он попытается прочитать 8 строк.

Если у вас есть 20 строк, это не вызовет исключения, но вы проигнорируете последние 12 строк, которые вы, вероятно, не хотите. С другой стороны, если у вас всего 5 строк, это будет повышаться при попытке прочитать 6-ю строку.

Причина, по которой исключение составляет f.seek(0), заключается в том, что он сбрасывает файл до начала перед каждым next, поэтому вы просто читаете строку заголовка снова и снова, игнорируя все остальное в файле. Он ничего не поднимает, но это не полезно.

То, что вы, вероятно, хотел что-то вроде этого:

with open(file,'r') as f: 
    reader = csv.reader(f) 
    header = next(reader) 
    result = [] 
    for row in reader: 
     for col_index, colname in enumerate(header)[2:]: 
      value = row[col_index] 
      result.append(do_something_with(value, colname)) 

Это читает каждую строку ровно один раз, и что-то делает с каждой колонки, но первые два из каждой строки.


Из комментария, что вы на самом деле хотите сделать, это найти максимальное значение для каждого столбца. Таким образом, вам нужно перебирать столбцы, а затем в каждом столбце вам нужно перебирать строки.

A csv.reader - это итератор, что означает, что вы можете перебирать его только один раз. Так что, если вы просто сделать это очевидным образом, он не будет работать:

maxes = {} 
with open(file) as f: 
    reader = csv.reader(f) 
    header = next(reader) 
    for col_index, colname in enumerate(header)[2:]: 
     maxes[colname] = max(reader, key=operator.itemgetter(col_index)) 

Первый столбец будет читать все, что осталось после прочтения заголовка, который хорошо. Следующий столбец прочитает все, что осталось после прочтения всего файла, что ничего.


Итак, как вы можете это исправить?

Один из способов, чтобы воссоздать Итератор каждый раз через внешний цикл:

maxes = {} 
with open(file) as f: 
    reader = csv.reader(f) 
    header = next(reader) 
for col_index, colname in enumerate(header)[2:]: 
    with open(file) as f: 
     reader = csv.reader(f) 
     next(reader) 
     maxes[colname] = max(reader, key=lambda row: float(row[col_index])) 

Проблема в том, что вы читаете весь файл N раз, и чтение файла от диска вероятно, самая медленная вещь, которую делает ваша программа.


То, что вы пытались сделать с f.seek(0) трюк, который зависит от того, как файловые объекты и объекты csv.reader работать. Хотя файловые объекты являются итераторами, они являются особыми, поскольку они имеют способ сбросить их в начало (или сохранить позицию и вернуться к ней позже). И объекты csv.reader в основном простые обертки вокруг файловых объектов, поэтому, если вы сбросите файл, вы также сбросите его. (Не ясно, что это гарантированно работает, но если вы знаете, как работает csv, вы, вероятно, можете убедить себя, что на практике это безопасно.) Таким образом:

maxes = {} 
with open(file) as f: 
    reader = csv.reader(f) 
    header = next(reader) 
    for col_index, colname in enumerate(header)[2:]: 
     f.seek(0) 
     next(reader) 
     maxes[colname] = max(reader, key=lambda row: float(row[col_index])) 

Это экономит затраты на закрытие и открытие файла каждый раз, но это не дорогая часть; вы все еще делаете чтение диска снова и снова. И теперь любой, кто читает ваш код, должен понимать трюк с использованием файловых объектов как итераторов, но их сброса или они не будут знать, как работает ваш код.


Итак, как вы можете этого избежать?

В общем, всякий раз, когда вам нужно сделать несколько проходов по итератору, есть два варианта. Самое простое решение для копирования итератора в многоразовую Iterable, как список:

maxes = {} 
with open(file) as f: 
    reader = csv.reader(f) 
    header = next(reader) 
    rows = list(reader) 
for col_index, colname in enumerate(header)[2:]: 
    maxes[colname] = max(rows, key=lambda row: float(row[col_index])) 

Это не только намного проще, чем ранее кода, это также намного быстрее. Если файл не огромен. Сохраняя все строки в списке, вы сразу читаете весь файл в памяти. Если он слишком велик, ваша программа потерпит неудачу. Или, что еще хуже, если он подходит, но только с помощью виртуальной памяти ваша программа будет обменивать часть ее в и из памяти каждый раз, когда вы проходите через цикл, обманывая ваш файл подкачки и делая все медленнее сканирование.


Другой альтернативой является реорганизация, поэтому вам нужно сделать только один проход. Это означает, что вам нужно поместить цикл поверх строк на внешней стороне и цикл над столбцами внутри. Это требует переосмыслений дизайна немного, и это означает, что вы не можете просто использовать простую max функции, но компромисс, вероятно, стоит:

with open(file) as f: 
    reader = csv.reader(f) 
    header = next(reader) 
    maxes = {colname: float('-inf') for colname in header[2:]} 
    for row in reader: 
     for col_index, colname in enumerate(header)[2:]: 
      maxes[colname] = max(maxes[colname], float(row[col_index])) 

Вы можете упростить еще дальше, например, использовать вместо Counter простой dict и DictReader вместо простой reader-но это уже простой, понятный и эффективный как есть.

+0

Привет, Abarnert, Спасибо за ур ответ. Что я пытаюсь сделать для каждого столбца, я должен найти максимальное значение. Так что я использовал выражение max() для каждого столбца. вы можете дать мне некоторый намек на код – maverick

+0

@maverick: Хорошо, я обновлю ответ. – abarnert

+0

@maverick: Между тем, в будущем, пожалуйста, дайте более полный пример, который показывает, что вы на самом деле пытались сделать, и достаточно ввода, чтобы мы могли воспроизвести проблему. – abarnert

-1

Почему вы не написали:

header = next(reader) 

В последней строке, а? Я не знаю, если это ваша проблема, но я бы начал там.

Смежные вопросы