Удалить несколько столбцов из 500 МБ TSV-файла с Python (или Perl и т. Д.)

У меня очень большой файл tsv, и мне нужно удалить несколько столбцов. Я нашел модуль CSV и ответ, как показано ниже, на подобный вопрос (см. Сценарий ниже). Тем не менее мне нужно удалить большой диапазон столбцов и не хочу вводить каждый индекс каждого столбца для удаления. Т.е. из файла с 689513 столбцами я хотел бы удалить столбцы с 628715 по 650181, а также удалить столбцы с 653321 по 689513. (Если слишком сложно удалить оба набора, я могу просто удалить только последние, т. Е. С 653321 по 689613, или, что эквивалентно, 653321 до конца файла). Извините за основной вопрос; Я новичок в написании сценариев и заблудился... и страница модуля CSV не содержит подробностей об удалении диапазонов столбцов. Я попытался сделать это в R, но первая запись в ячейке пуста (см. Пример списка ниже кода). Мой файл является файлом с разделителями табуляции в формате tsv, но я понял, что это можно исправить, используя команду для установки разделителя как \t. Любая помощь с благодарностью! (Примечание: к сожалению, мне нужно иметь двоеточия в именах моих столбцов, т.е. 2L:1274 - это общее название для одного столбца).

import csv
with open("source","rb") as source:
rdr= csv.reader( source )
with open("result","wb") as result:
    wtr= csv.writer( result )
    for r in rdr:
        wtr.writerow( (r[0], r[1], r[3], r[4]) )

2L:1274 2L:2425 2L:2853 3L:4    3L:5    3L:7
indivBCsusceptiblePL7A10_TATAGT NA  NA  NA  NA  NA  NA
indivBCsusceptiblePL7A11_CCTGAA NA  5   NA  NA  NA  NA
indivBCsusceptiblePL7A12_CAATAT NA  NA  6   7   8   9
indivBCsusceptiblePL7A1_CCGAAT  NA  NA  NA  NA  NA  NA

5 ответов

Ты можешь использовать del удалить фрагменты списка.

with open('in.tsv', 'r') as fin, open('out.tsv', 'w') as fout:
    reader = csv.reader(fin, dialect='excel-tab')
    writer = csv.writer(fout, dialect='excel-tab')
    for row in reader:
        # delete indices in reverse order to avoid shifting earlier indices
        del row[653321:689513+1]
        del row[628715:650181+1]
        writer.writerow(row)

Вы можете сделать это с очень небольшим объемом памяти, используя Python.

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

class TsvDialect(csv.Dialect):
    delimiter = '\t'
    quoting = csv.QUOTE_NONE
    escapechar = None

# you can just pass this class around, or you can register it under a name
csv.register_dialect('tsv', TsvDialect)

Затем вы можете пройти через каждую строку и скопировать в новый TSV:

with open('source.tsv', 'rb') as src, open('result.tsv', 'wb') as res:
    csrc = csv.reader(src, dialect='tsv')
    cres = csv.writer(res, dialect='tsv')
    for row in csrc:
        cres.writerow(row)

Это делает простую копию. Поскольку вам нужны только некоторые строки, давайте скопируем их.

Списки Python имеют нулевую индексацию (первый столбец - это столбец 0, а не столбец 1); и нарезка индекса не включает последний элемент (wholelist[:2] такой же как [wholelist[0], wholelist[1]]). Имейте это в виду, чтобы избежать ошибок одного!

with open('source.tsv', 'rb') as src, open('result.tsv', 'wb') as res:
    csrc = csv.reader(src, dialect='tsv')
    cres = csv.writer(res, dialect='tsv')
    for row in csrc:
        # remove [628714:650181] and [653320:689512]
        newrow = row[:628714] # columns before 628714
        newrow.extend(row[650181:653320]) # columns between 650180 and 653320
        cres.writerow(newrow)

В качестве альтернативы, вместо копирования нужных столбцов в новую строку, вы можете сэкономить некоторую память за счет ясности кода, удалив ненужные столбцы:

    for row in csrc:
        # remove [628714:650181] and [653320:689512]
        # be sure to remove in reverse order!
        del row[653320:689512]
        del row[628714:650181]
        cres.writerow(row)

Вы можете абстрагировать вырезание столбца (любой метод, используя любое удобное для вас индексирование) в функцию, если вам нужно делать это очень часто.

Возможно, вы также захотите взглянуть на библиотеку Python csvkit и инструменты командной строки, в частности на инструмент командной строки csvcut, который, похоже, выполняет именно то, что вы хотите из командной строки.

При 2 ГБ ОЗУ или более необходимо иметь возможность загружать набор данных в память, удалять нужные столбцы и записывать содержимое в файл. Это может быть легко сделано в R или Python. Для R:

dat = read.table("spam.tsv", ...)
dat = dat[-c(1,5)] # delete row 1 and 5
write.csv(dat, ....)

Делать это кусками можно легко, используя apply петля или for петля. Я использую apply стиль:

read_chunk = function(chunk_index, chunk_size, fname) {
    dat = read.table(fname, nrow = chunk_size, skip = (chunk_id - 1) * chunk_size, ...)
    dat = dat[-c(1,5)] # delete row 1 and 5
    write.csv(dat, append = TRUE, ....)    
}

tot_no_lines = 10000 # for example
chunk_size = 1000
sapply(1:(tot_no_lines / chunk_size), read_chunk)

Обратите внимание, что это код в стиле R, полезный для вдохновения, а не рабочий код R.

Вы на Linux? Затем сохраните хазл и используйте csvtool из оболочки:

 csvtool col 1-500,502-1000 input.csv > output.csv

Вы также можете установить разделитель и так далее, просто введите csvtool --help, Довольно прост в использовании.

Вы можете построить выходную строку динамически:

for r in rdr:
    outrow = []
    for i in range(0, 628714):
       outrow.append(r[i])
    for i in range(650181, 653320):
       outrow.append(r[i])
    wtr.writerow( outrow )

Я полагаю, что вы можете сделать это еще более кратко с кусочками входной строки r, в соответствии с:

 outrow = r[0:628714)
 outrow.extend(r[650181:653320)
 wrt.writerow( outrow )

Возможно, не самый быстрый для выполнения, но, безусловно, легче писать.

Другие вопросы по тегам