【问题标题】:Python - Extract and reformat field names from rows of dataPython - 从数据行中提取和重新格式化字段名称
【发布时间】:2013-05-02 21:51:51
【问题描述】:

我有一个平面文本文件 (infile),我想对其进行重组。它有几个制表符分隔的列,看起来像这样:

 Person1    HEIGHT=60;WEIGHT=100;AGE=22
 Person2    HEIGHT=62;WEIGHT=101;AGE=25
 Person3    HEIGHT=64;WEIGHT=110;AGE=29

我希望它看起来像这样:

 PERSON    HEIGHT    WEIGHT    AGE
 1         60        100       22
 2         62        101       25
 3         64        110       29

您可以看到第二列实际上包含几个以分号分隔的标题/值字段,我想将它们重组为典型的列标题行。

现在我有:

for line in infile:
        line = line.split("\t")
        line_meta = line[1].split(";")
        print line_meta

我认为最好的解决方案是现在循环 line_meta 变量,使用正则表达式来检测标题名称(检测以多个大写字母开头并以“=”结尾的字符串),将每个标题添加到字典作为键,然后将字符串的其余部分存储为值。然后,对于下一行,如果检测到相同的标题,只需附加到现有字典。

谁能提供有关此代码的帮助或提供有关如何进行的反馈?

谢谢

编辑:感谢您的回复。我为这个例子简化了我的数据,但这里是实际元列之一的样子(仍然是 ; 分隔,但值类型是混合的):

       P=0.9626;IPU=.$.+1T.+1T.+;IRF=ncRNA;IUC=UTR3;IGN=NCRNA00115;IGI=NCRNA00115,RP11-206L10.16-001;IET=0;IEO=0;IEN=.;IHT=0;IHVC=0;IHD=.;IHI=.;IHN=.;IDI=.;IDN=.;ITMAF=.;ITAMR=.;ITASN=.;ITAFR=.;ITEUR=.;ITNRB=+A;ISF=.;ISD=.;ISM=.;ISX=.;

【问题讨论】:

    标签: python regex text dictionary header


    【解决方案1】:

    您可以只使用一个正则表达式来拆分键=值对:

    import re
    
    key_value = re.compile('(?P<key>[A-Z]+)=(?P<value>\[^\s=;]+)(?:(?=;)|$)')
    

    此表达式使用命名组,但如果您觉得更易于阅读,则可以不使用它们:

    key_value = re.compile('([A-Z]+)=([^\s=;])(?:(?=;)|$)')
    

    (?:..) 组是非捕获组;它在这里仅用于区分| 或符号适用于什么。该模式匹配= 符号之前的大写字符,以及任何 空格、=; 字符,前提是存在; 或 em> 值之后的字符串结尾。

    这会为每一行拆分键和值:

    >>> key_value = re.compile('(?P<key>[A-Z]+)=(?P<value>[^\s=;]+)(?:(?=;)|$)')
    >>> key_value.findall('Person1\tHEIGHT=60;WEIGHT=100;AGE=22')
    [('HEIGHT', '60'), ('WEIGHT', '100'), ('AGE', '22')]
    

    这可以很容易地变成字典:

    >>> dict(key_value.findall('Person1\tHEIGHT=60;WEIGHT=100;AGE=22'))
    {'AGE': '22', 'WEIGHT': '100', 'HEIGHT': '60'}
    

    然后您可以使用例如csv.DictWriter() 来编写这些:

    import csv
    import re
    
    key_value = re.compile('(?P<key>[A-Z]+)=(?P<value>[^\s=;]+)(?:(?=;)|$)')
    
    with open(inputfilename) as infile, open(outputfilename, 'wb') as outfile:
        writer = csv.DictWriter(outfile, ('PERSON', 'HEIGHT', 'WEIGHT', 'AGE'), delimiter='\t')
        writer.writeheader()
    
        for line in infile:
            person = line.split('\t', 1)[0]
            row = dict(key_value.findall(line))
            row['PERSON'] = person
            writer.writerow(row)
    

    基于您的真实数据样本的演示:

    >>> dict(key_value.findall('       P=0.9626;IPU=.$.+1T.+1T.+;IRF=ncRNA;IUC=UTR3;IGN=NCRNA00115;IGI=NCRNA00115,RP11-206L10.16-001;IET=0;IEO=0;IEN=.;IHT=0;IHVC=0;IHD=.;IHI=.;IHN=.;IDI=.;IDN=.;ITMAF=.;ITAMR=.;ITASN=.;ITAFR=.;ITEUR=.;ITNRB=+A;ISF=.;ISD=.;ISM=.;ISX=.;\n'))
    {'ISX': '.', 'ITAMR': '.', 'IDN': '.', 'ISM': '.', 'IDI': '.', 'ISF': '.', 'ISD': '.', 'ITMAF': '.', 'IUC': 'UTR3', 'IGI': 'NCRNA00115,RP11-206L10.16-001', 'ITNRB': '+A', 'IHVC': '0', 'IET': '0', 'ITASN': '.', 'ITEUR': '.', 'ITAFR': '.', 'IEO': '0', 'IEN': '.', 'IGN': 'NCRNA00115', 'IRF': 'ncRNA', 'P': '0.9626', 'IHT': '0', 'IHI': '.', 'IHN': '.', 'IPU': '.$.+1T.+1T.+', 'IHD': '.'}
    

    【讨论】:

    • 感谢您的回复。您能否建议如何编辑 key_value 以容纳不仅仅是数字的值?您的正则表达式对于我的理解水平来说非常复杂,但我尝试将其修改为 '(?P&lt;key&gt;[A-Z]+)=(?P&lt;value&gt;\S+)(?:(?=;)|$)' 并没有正确确定字段
    • @alexhli:你可以使用 \w+ 来表示单词字符,所以 a-z 加上 0-9。
    • @alexhli:啊,我错过了您提供的实际示例。我已将正则表达式更新为更宽松,并为您发布了一个演示。
    • 您的新演示效果很好!最后一个问题(我希望),由于某种原因,它现在正在最后一个字段周围打印“”,知道为什么吗?所以它看起来像Person1 60 100 "22 "
    • 包含空格;我们可以在字符类中添加不允许的空格:[^\s;=]+。 CSV 文件在值周围使用引号来表明22 之后的额外空格仍然是值的一部分。
    【解决方案2】:

    你可以试试这个

    data = open('testfile.dat').read().split('\n')
    
    def newcmp(x,y): 
        rv = cmp(len(x[1]), len(y[1]))
        if rv: return rv
        else: return cmp(x[0], y[0]) # alphabetical 
    
    persons = {}
    attributes = {}
    nAttrs = 0
    for l in data:
        pname , pvals = line.split('\t')[:2]
        for atName, atVal in (x.split('=') for x in pvals.psplit(';'))
            try:
                persons[pName][attributes[atName]] = atVal
            except KeyError:
                attributes[aName] = nAttrs
                persons[pName][attributes[atName]] = atVal
                nAttr += 1
    
    headers = ['NAME'] + range(nAttrs)
    for x in attributes.keys(): headers[attributes[x]+1] = x
    values = []
    for pName, pVals in sorted(persons.items(), cmp=newcmp)
        if len(pVals) < nAttrs: pVals += [0 for x in xrange(nAttrs - len(pVals))]
        values.append('\t'.join(('%d'%x for x in pVals)))
    
    outfh = open('outputfile.dat', 'w')
    outfh.write('%s\n%s\n'%('\t'.join(headers), '\n'.join(values)))
    outfh.close()
    

    【讨论】:

      猜你喜欢
      • 2016-10-15
      • 1970-01-01
      • 1970-01-01
      • 2018-03-12
      • 1970-01-01
      • 1970-01-01
      • 2017-12-31
      • 1970-01-01
      • 2013-06-18
      相关资源
      最近更新 更多