使用python(或perl等)从500 MB tsv文件中删除多列

使用python(或perl等)从500 MB tsv文件中删除多列,python,r,Python,R,我有一个非常大的tsv文件,需要删除几个列。我找到了CSV模块,下面是一个类似问题的答案(见下面的脚本)。但是我需要删除大量的列,并且不想键入要删除的每一列的每个索引。Ie从一个包含689513列的文件中,我想删除628715到650181列,并删除653321到689513列。(如果很难删除两个集合,我可以只删除最后一个集合,即653321到689613,或者相当于653321到文件末尾)。对不起,基本的问题;我不熟悉脚本编写和迷路。。。CSV模块页面没有详细介绍如何删除列范围。我试着在R中这

我有一个非常大的tsv文件,需要删除几个列。我找到了CSV模块,下面是一个类似问题的答案(见下面的脚本)。但是我需要删除大量的列,并且不想键入要删除的每一列的每个索引。Ie从一个包含689513列的文件中,我想删除628715到650181列,并删除653321到689513列。(如果很难删除两个集合,我可以只删除最后一个集合,即653321到689613,或者相当于653321到文件末尾)。对不起,基本的问题;我不熟悉脚本编写和迷路。。。CSV模块页面没有详细介绍如何删除列范围。我试着在R中这样做,但是第一个单元格条目是空的(参见下面代码的示例列表)。我的文件是一个tsv制表符分隔的文件,但我收集的数据可以使用命令将分隔符设置为\t进行更正。非常感谢您的帮助!!!(注意:不幸的是,我需要在我的列的名称中使用冒号,即2L:1274是一个列的名称)


对于2GB或更大的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代码。

您可以动态构建输出行:

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 )

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

您可以使用
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)
如果经常需要,您可以将列剪切(任何一种方法,使用您熟悉的任何索引)抽象为一个函数


您可能还想看看,尤其是它的命令行工具,它似乎完全可以从命令行执行您想要的操作。

您在Linux上吗?然后保存hazzle并使用shell中的
csvtool

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

您还可以设置分隔符等等,只需键入
csvtool--help
。非常容易使用。

有一个软件包就是为了实现这一点而设计的,但目前我还没有意识到这一点。如果我能找到答案,我会贴出来。我认为R的
read.table
中的
colClasses
参数可以让你跳过某些列的阅读。@AriB.Friedman正在考虑“colbycol”包。我将阅读中的第一行,在该字符向量前加上第一个列名,然后在BlueMagister建议的要转储的位置使用
colClasses
with
rep(“NULL,…)
。您需要设置
check.names=FALSE
以保留冒号。值得注意的是,在Python 2.x上,
范围(0628714)
将非常慢,应该使用
xrange
来代替。同样值得注意的是:
outrow.extend(r[:628714]);outrow.extend(r[650181:653320])
可能是表达此操作的更快、更简洁的方法。使用此方法(动态构建输出行)或下面带有多拼接的帖子,我收到错误消息:_csv.Error:field大于field limit(131072)。“我在使用R时遇到问题,因为我的第一个单元格是空的(我想我需要它在后续的Python脚本中保持为空),这似乎会导致R将所有内容移动一次,并使标题/内容不对齐——这就是我尝试Python的原因。还有什么建议吗?
 csvtool col 1-500,502-1000 input.csv > output.csv