AC=126;AC_AFR=0;AC_AMR=0;AC_Adj=126;AC_EAS=120;AC_FIN=0;AC_Het=112;
AC=12683;AC_AFR=4578;AC_AMR=559;AC_Adj=12680;AC_EAS=2104;AC_FIN=501;AC_Het=91966
我的数据其中一列看起来像这样,即键和值。我想将选定的数据转换为列,标题为列中的键和值。
并非所有线路都具有相同的数据。有些行不会有其他行中出现的字段。
想要的输出:
AC AC_AFR AC_AMR and so on
126 0 0
12683 4578 559
不确定如何执行此操作或从哪里开始
答案1
这样做的挑战在于,数据不是简单的 CSV 类型文件,其中第一行是列名称,其余行是逐行的列数据。
这里有column_name=column_data,由;
字符分隔。我的解决方案是使用Python之类的语言逐行读取文件。我将从每一行创建 dict() ,并为每个字段创建一个 K:V 对。然后我将该字典附加到所有行的 list() 中。
一旦我有了这个,我就可以处理这个列表了。如果我在第一行,我将打印列名,然后打印值,否则我将只打印值。
我认为无论您使用什么语言,该方法都是相似的,但它绝对是可行的。
下面是 Python 中的一个简单示例,它使用 OrderedDicts 来保留“列”顺序:
#!/usr/bin/python
''' a quick example of a script to parse '=' delimited fields in
';' delimited columns of a text file.
prints tab delimited columnar data with headers to STDOUT
'''
from collections import OrderedDict
with open('data', 'rb') as infile:
FLINES = infile.read().split()
DATA = []
for line in FLINES:
fields = line.split(';')
d = OrderedDict()
for field in fields:
if '=' in field:
col, value = field.split('=')
d.update({col: value})
DATA.append(d)
L = 0
for D in DATA:
if L == 0:
print '\t'.join(D.keys())
print '\t'.join(D.values())
L += 1
- 此示例假设所有行都具有相同的列,因为它只会打印从列表中获取的第一个条目的 col_names。
答案2
快速而肮脏的解决方案perl
:
#!/usr/bin/env perl
use strict;
use warnings;
my %cache;
while (<>) {
chomp;
for my $pair ( split /;/ ) {
$pair =~ s/=.*//;
$cache{$pair} = 1;
}
}
continue {
last if eof;
}
my @keys = sort keys %cache;
print +( join "\t", @keys ), "\n";
while (<>) {
chomp;
my %h = map { m/([^=]+)=(\S+)/; ( $1, $2 ) } split /;/;
print +( join "\t", map { $h{$_} // '' } @keys ), "\n";
}
像这样使用它:
perl script.pl input.txt input.txt
这会扫描输入文件两次,首先获取密钥,然后格式化列。它很脏,因为它可能应该使用Text::CSV
and Array::Unique
。
答案3
使用 GNU awk
gawk -F '[=;]' '
{for (i=1; i<NF; i+=2) values[$i][NR] = $(i+1)}
END {
PROCINFO["sorted_in"] = "@ind_str_asc"
for (key in values) printf "%s\t", key
print ""
for (line=1; line<=NR; line++) {
for (key in values) printf "%s\t", value[key][line]
print ""
}
}
' filename
AC AC_AFR AC_AMR AC_Adj AC_EAS AC_FIN AC_Het
126 0 0 126 120 0 112
12683 4578 559 12680 2104 501 91966
我在这里使用 2 个字段分隔符,因此所有奇数字段都是键,所有偶数字段都是值。
答案4
以下管道使用sed
并mlr
删除任何输入行上的尾随;
(如果有),然后将这些行读取为带有;
- 分隔字段的记录,其中=
用于分隔指示字段名称和字段值的标签。每个记录中字段的顺序对于读取数据并不重要。输出为 TSV。
$ sed 's/;$//' file | mlr --ifs ';' --otsv unsparsify
AC AC_AFR AC_AMR AC_Adj AC_EAS AC_FIN AC_Het
126 0 0 126 120 0 112
12683 4578 559 12680 2104 501 91966
unsparsify
的子命令将为mlr
缺失字段分配空值(某些记录中存在字段,但其他记录中缺失字段)。