1

我有一个非常大的 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
4

5 回答 5

2

您可以使用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)
于 2012-10-02T15:33:53.070 回答
1

您可以使用 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)

如果您需要经常执行此操作,您可以将列切割(任何一种方法,使用您喜欢的任何索引)抽象为一个函数。

您可能还想看看csvkit python library 和 command-line tools,特别是它的命令行工具csvcut,它似乎完全可以从命令行执行您想要的操作。

于 2012-10-02T16:29:03.243 回答
0

你在 Linux 上吗?然后保存 hazzle 并csvtool从 shell 使用:

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

还可以设置分隔符等,只需输入csvtool --help. 很容易使用。

于 2012-10-02T16:40:54.207 回答
0

使用 2 GB 或更大的 RAM,应该可以将数据集加载到内存中,删除所需的列,并将内容写入文件。这可以很容易地在 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 代码。

于 2012-10-02T15:15:01.500 回答
0

您可以动态构建输出行:

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 )

也许不是最快的执行,但肯定更容易编写。

于 2012-10-02T15:22:42.653 回答