Ошибка модуля Python csv: индекс вне диапазона

У меня есть файл CSV, и я хочу извлечь из него столбцы, но только из некоторых строк. Это выглядит так:

gene_id, ENSDARG00000104632, gene_version, 2, gene_name, RERG

gene_id, ENSDARG00000104632, gene_version, 2, transcript_id, ENSDART00000166186

gene_id, ENSDARG00000104632, gene_version, 2, transcript_id, ENSDART00000166186

gene_id, ENSDARG00000104632, gene_version, 2, transcript_id, ENSDART00000166186

gene_id, ENSDARG00000104632, gene_version, 2, transcript_id, ENSDART00000166186

По сути, мне нужны 2-й и 6-й столбцы, но только из строк, в которых есть «gene_name» в 5-м столбце. Итак, я хочу извлечь:

ENSDARG00000104632, RERG

(Оттуда идет много тысяч строк)

Вот что я написал:

import csv


with open('filename.csv', 'rb') as infh:
        reader = csv.reader(infh)
        for row in reader:
                if row[4] == 'gene_name':
                        print row[1, 5]

Однако это дает мне эту ошибку:

Файл "./gene_name_grabber.sh", строка 10, в if row[4] == 'gene_name': IndexError: индекс списка вне допустимого диапазона

Я понимаю, что эта ошибка означает, что я попросил посмотреть номер индекса, превышающий количество индексов в строках... но в каждой строке явно более 4 индексов. Помогите, пожалуйста?

Спасибо!


person David Tatarakis    schedule 21.09.2017    source источник
comment
Вы уверены, что все ваши строки имеют одинаковое количество столбцов? Можете ли вы добавить оператор печати прямо перед условием if, чтобы мы могли видеть строку, которая выдает эту ошибку?   -  person Antimony    schedule 22.09.2017
comment
Я изменил его на это: импортируйте csv с open('zebrafish_gtf_IDs_and_names.csv', 'rb') как infh: reader = csv.reader(infh) для строки в читателе: напечатайте строку, если row[4] == 'gene_name': напечатать строку [1, 5], но все равно выдает эту ошибку: Файл ./gene_name_grabber.sh, строка 11, в строке if [4] == 'gene_name': IndexError: индекс списка вне диапазона   -  person David Tatarakis    schedule 22.09.2017
comment
Какую строку он печатает последней?   -  person Antimony    schedule 22.09.2017
comment
Он не печатает строки   -  person David Tatarakis    schedule 22.09.2017
comment
Только ошибка и ничего больше   -  person David Tatarakis    schedule 22.09.2017
comment
Ждать. Он печатает это: ['', ' '], а затем сообщение об ошибке. Я понятия не имею, что это такое.   -  person David Tatarakis    schedule 22.09.2017
comment
Похоже, ваши первые несколько строк могут не иметь 5 столбцов: S   -  person Antimony    schedule 22.09.2017


Ответы (3)


Очевидно, что некоторые строки не содержат достаточного количества столбцов. Попробуй это:

import csv

with open('input.csv', 'r') as f:

    reader = csv.reader(f)

    for row in reader:
        try:
            if 'gene_name' in row[4]:
                print('%s, %s' % (row[1].strip(), row[5].strip()))
        except IndexError:
            continue

...выход:

ЭНСДАРГ00000104632, РЕРГ

person adder    schedule 21.09.2017
comment
Это сработало, хотя элементы в каждом столбце были окружены (' Чтобы они выглядели так: ('ENSDARG00000104632', 'RERG'). Есть ли способ сделать так, чтобы вывод был простым: ENSDARG00000104632, RERG ? - person David Tatarakis; 22.09.2017
comment
А, понятно. Большое спасибо, это очень помогает! - person David Tatarakis; 22.09.2017

Мне нужны 2-й и 6-й столбцы, но только из строк, в которых есть «gene_name» в 5-м столбце.

Я люблю питона. Но наиболее естественно это выражается как

awk '$5 ~ /gene_name/ {print $2, $6}'

Вернемся к питону. Это не то, что вы хотели написать:

                    print row[1, 5]

Вместо этого сформулируйте это как print(row[1], row[5]).

Некоторые из ваших строк имеют лишь небольшое количество столбцов. Итак, вы захотите обернуть разыменования, например. row[4] или row[5] в операторе if, который проверяет, что это достаточно длинная строка:

    if len(row) > 5:
        ...
person J_H    schedule 21.09.2017
comment
Я пытался использовать команду awk, но, похоже, она вообще ничего не делала. Я написал это так: cat имя_файла.csv | awk '$5 ~ /^gene_name$/ {print $2, $6}' › newfile.csv Это неверно? - person David Tatarakis; 22.09.2017
comment
Извините, я не должен был ставить якорь. Я пересмотрю ответ, чтобы удалить ^ и $, поскольку они не нужны в контексте awk. - person J_H; 22.09.2017

Как заметил Антимони, похоже, что в ваших данных есть случайные пропущенные значения, с которыми csv не может легко справиться из коробки. Я бы предложил использовать такую ​​библиотеку, как pandas, которая имеет функцию read_csv и может обрабатывать отсутствующие значения. Используя эти данные в качестве примера:

gene_id, ENSDARG00000104632, gene_version, 2, gene_name, RERG
gene_id, ENSDARG00000104632, gene_version, 2, transcript_id, ENSDART00000166186
gene_id, ENSDARG00000104632, gene_version, 2, transcript_id, ENSDART00000166186
gene_id, ENSDARG00000104632, gene_version, 2, transcript_id, ENSDART00000166186
gene_id, ENSDARG00000104632, gene_version, 2, transcript_id, ENSDART00000166186
gene_id, ENSDARG00000104632, gene_version, 2, transcript_id, ENSDART00000166186
gene_id, ENSDARG00000104632, gene_version, 2, transcript_id, ENSDART00000166186
gene_id, ENSDARG00000104632, gene_version, 2, transcript_id,
gene_id, ENSDARG00000104632, gene_version, , transcript_id,
gene_id, ENSDARG00000104632, gene_version, 2, transcript_id, ENSDART00000166186

его можно было бы прочитать следующим образом:

import pandas as pd

# Use the 2nd, 5th and 6th columns - i.e.column indices 1, 4 and 5 respectively
# And, we set the 'not available' data - i.e. `na_values` as 'N/A'.
data = pd.read_csv('test.dat', na_values='N/A', header=None, skipinitialspace=True, usecols=[1,4,5])

# now select only the rows without 'gene_version':
d = data.loc[data[4] != 'gene_name']

# and, now we only select columns with index 1 and 5:
selected_data = d[[1, 5]]

Урожайность:

                    1                   5
0  ENSDARG00000104632                RERG
1  ENSDARG00000104632  ENSDART00000166186
2  ENSDARG00000104632  ENSDART00000166186
3  ENSDARG00000104632  ENSDART00000166186
4  ENSDARG00000104632  ENSDART00000166186
5  ENSDARG00000104632  ENSDART00000166186
6  ENSDARG00000104632  ENSDART00000166186
7  ENSDARG00000104632                 NaN
8  ENSDARG00000104632                 NaN
9  ENSDARG00000104632  ENSDART00000166186

По желанию.

Однако, если данные отсутствуют, как в этом примере, все, что вам нужно сделать, это удалить такие строки, как:

selected_data.dropna()

Что выводит:

                    1                   5
1  ENSDARG00000104632  ENSDART00000166186
2  ENSDARG00000104632  ENSDART00000166186
3  ENSDARG00000104632  ENSDART00000166186
4  ENSDARG00000104632  ENSDART00000166186
5  ENSDARG00000104632  ENSDART00000166186
6  ENSDARG00000104632  ENSDART00000166186
9  ENSDARG00000104632  ENSDART00000166186

(Однако это может быть не то, что вам нужно.)

СПРАВКА

https://pandas.pydata.org/pandas-docs/stable/generated/pandas.read_csv.html

person jrd1    schedule 21.09.2017
comment
Вы сказали usecols=[1, 5], но я думаю, вам нужно usecols=[1, 4, 5]. Таким образом вы сохраните достаточно информации, чтобы замаскировать любые строки, в которых элемент 4 не является геном_имя. - person J_H; 22.09.2017
comment
@JH Ах! Хороший улов!! Спасибо! Отредактировано. - person jrd1; 22.09.2017