データを列に変換

データを列に変換
 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

私のデータの列の1つであるキーと値は次のとおりです。選択したデータをヘッダーが列のキーと値である列に変換したいと思います。

すべての行に同じデータがあるわけではありません。一部の行には他の行に表示されるフィールドがありません。

希望の出力:

AC      AC_AFR    AC_AMR and so on
126     0         0
12683   4578      559

これを行う方法や開始位置がわかりません。

答え1

問題は、データが最初の行が列名で、残りの行が行ごとの列データである単純なCSVタイプファイルではないことです。

;これには、文字で区切られたcolumn_name = column_dataがあります。私の解決策は、Pythonのような言語を使用してファイルを1行ずつ読み込むことでした。各行にdict()を作成し、各フィールドにK:Vペアを作成します。次に、その辞書をすべての行のlist()に追加します。

これを行ったら、リストを操作できます。最初の行にある場合は列名を印刷し、値を印刷し、そうでない場合は値のみを印刷します。

どの言語を使用しても、アプローチは似ていると思いますが、確かに可能です。

以下は、「列」の順序を維持するためにOrderedDictsを使用するPythonの簡単な例です。

#!/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_nameのみを印刷するため、すべての行に同じ列があるとします。

答え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

これは入力ファイルを2回スキャンします。まず、キーをインポートしてから列形式を指定します。おそらくText::CSVandを使用する必要があるため、汚れていました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に読み込みます。ここでは、フィールド名とフィールド値を表すラベルが区別されます。各レコードのフィールドの順序は、データを読み取るために重要ではありません。出力はTSVです。mlr;;=

$ 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不足しているフィールドにnull値を割り当てます(フィールドは一部のレコードにはありますが、他のレコードには欠落しています)。

関連情報