解析一个带有缺失字段的制表符分隔文件

4

这是一个我正在尝试解析的复杂制表符分隔文件的示例

ENTRY   map0010\tNAME Glycolysis\tDESCRIPTION Glycolysis is the process of converting glucose into pyruvate\tCLASS   Metabolism\tDISEASE   H00071  Hereditary fructose intolerance\tH00072  Pyruvate dehydrogenase complex deficiency\tDBLINKS     GO: 0006096 0006094
ENTRY   map00020\tNAME  Citrate cycle (TCA cycle)\tCLASS   Metabolism; Carbohydrate Metabolism\tDISEASE   H00073  Pyruvate carboxylase deficiency\tDBLINKS     GO: 0006099\tREL_PATHWAY map00010  Glycolysis / Gluconeogenesis\tmap00053  Ascorbate and aldarate metabolism

我正在尝试获取仅包含某些字段的输出,例如:

ENTRY   map0010\tNAME Glycolysis\tCLASS   Metabolism\tDISEASE   H00071  Hereditary fructose intolerance H00072  Pyruvate dehydrogenase complex deficiency\tDBLINKS     GO: 0006096 0006094\tNA
ENTRY   map00020\tNAME  Citrate cycle (TCA cycle)\tCLASS   Metabolism; Carbohydrate Metabolism\tDISEASE   H00073  Pyruvate carboxylase deficiency\tDBLINKS     GO: 0006099\tREL_PATHWAY map00010  Glycolysis / Gluconeogenesis\tmap00053  Ascorbate and aldarate metabolism

主要问题在于,并不是所有的行都包含相同数量的字段,因此我需要删除例如包含字符串“DESCRIPTION”的字段,并且在字段“CLASS”不存在的行中添加一个空字段。
此外,对于某些字段,数据被分割成多个部分(例如,第1行后面紧跟着DISEASE的字段包含疾病数据!),我需要将它们合并。
我尝试了以下方法:
input = open('file', 'r')

dict = ["ENTRY", "NAME", "CLASS", "DISEASE", "DBLINKS", "REL_PATHWAY"]

split_tab = []
output = []

for line in input:
    split_tab.append(line.split('\t'))

for item in dict:
    for element in split_tab:
        if item in element:
            output.append(element)
        else:
            output.append('\tNA\t')

但它会保留所有内容,而不仅仅是字典中指定的元素。 请问我能为您做些什么呢?

那么,实际的格式/语法是什么? - Stuart Carnie
@Stuart,这是一个以制表符分隔的文件(也许我没有理解你的问题)。 - Sonny
似乎每一行都明确地命名了字段,用空格分隔,然后是值,用制表符分隔: - Stuart Carnie
3个回答

6
使用内置的csv库。这将使您的工作更加轻松。
以下是一些示例代码:
import csv
reader = csv.reader(open('myfile.csv', 'rb'), dialect='excel-tab')
fieldnames = ['Name','Class']
writer = csv.DictWriter(open('myfile.csv', 'rb'), fieldnames, restval='', extrasaction='ignore', dialect='excel-tab')

for row in reader:
    newrow = {}
    for field in row:
        key = field.split(' ', 1)[0]
        newrow[key] = field
    writer.writerow(newrow)

请特别注意DictWriter的设置。如果包括restval和extrasaction字段,使用起来将会更加容易。它们允许您传递一个字典,其中包含比写入器预期的值更多或更少的键值对。
只需正确设置字段名称,并设置读取器以使用正确的方言即可。这可能需要添加自己的方言,但csv链接提供了如何执行此操作的说明。 编辑 在Rob下面的评论后,我已经修改了这个内容,以考虑到csv方言并不像我想象的那么强大。

这样行不通。DictReader类希望给出字段名或数据的第一行给出字段名。Sonny的数据在每行中嵌入了字段名,并且任意字段可能会缺失(而不仅仅是空)。例如,第3列可能是“DESCRIPTION”,也可能是“CLASS”;您必须查看每行的字段内容才能知道它是哪个字段。如果某种方言可以处理此问题,则文档肯定没有解释如何做到这一点。 - rob mayoff
@robmayoff 我正在任意设置字段名称,使用包含的变量。其余部分将根据此处所述设置新方言。请注意,如果混合字段中嵌入了字段名称,则可能需要对方言变量进行子类化。 - Spencer Rathbun
我正在任意设置DictWriter上的字段名。您没有在DictReader上设置任何字段名。如此这里所解释的那样。该文档未说明如何使用方言来确定字段名。实际上,方言无法确定字段名。请检查源代码(csv.py _csv.c)。 - rob mayoff
@robmayoff 很抱歉,我误读了您的评论。我指的是DictWriter,它与此无关。我检查了源代码,以便提供一个示例,并发现它并不像我预期的那样有用。我已编辑我的答案来解决这个问题。 - Spencer Rathbun

3
requiredKeys = 'ENTRY NAME CLASS DISEASE DBLINKS REL_PATHWAY'.split(' ')

for line in open('file', 'r'):
    fields = line.split('\t')
    fieldMap = {}
    for field in fields:
        key = field.split(' ', 1)[0]
        fieldMap[key] = field
    print '\t'.join([fieldMap.get(key, 'NA') for key in requiredKeys])

2

你的这行代码

split_tab.append(line.split('\t'))

会导致问题,因为你在列表中再创建了一个列表。建议改成这样:

split_tab = line.split('\t')

这样做可以避免出现问题。


1
有人能解释一下为什么我的答案得到的赞很少吗?我发现了代码的问题 - 虽然另外两个答案也很好,但是它们提供了不同的解决方法。指出一个简单的错误就没有人喜欢吗?这是一个冷酷的世界。 - Rookie
你的答案是正确的,这就是为什么没有人对它进行负面评价。但是,他正在复制Python中已经存在的功能,因此最终会得到一个不太实用且更容易出错的代码库。 - Spencer Rathbun
1
谢谢,这让我很明白!(编辑:可能会被解释为讽刺,但那不是我的意图!) - Rookie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接