100個のファイルがあり、csv
各ファイルには2つの列が含まれています。最初のものはでtaxonomy
、2番目はですcounts
。各ファイルには約10,000行があります。各ファイルのデータはtaxonomy
部分的にのみ共有され、合計約50,000の固有値があります。taxa
1つのファイルから欠落している項目に値が割り当てられたテーブルにマージする必要があります0
。結果は、50,000行と101列のテーブルでなければなりませんcsv
。tsv
単純化された例は次のとおりです。ファイル 1( R1.csv
):
A,1
B,20
C,30
ファイル 2( R2.csv
):
C,1
D,13
E,15
F,19
ファイル3(R3.csv
):
A,1
B,4
E,2
G,6
H,8
予想される結果:
Taxa,R1,R2,R3
A,1,0,1
B,20,0,4
C,30,1,0
D,0,13,0
E,0,15,2
F,0,19,0
G,0,0,6
H,0,0,8
bashスクリプトを使ってこれを行う方法を知っていますか?
答え1
私はawk
これを使用して複数のファイルを一度に処理します。
sed 's/,R[1-9]\+\.csv:/,/g' <(awk -v HEADER="$(printf ",%s:" R{1..3}.csv)" -F, '
{ seen[$1]=seen[$1]","FILENAME":"$2; }
END { print HEADER; for (x in seen) print x seen[x]}' R{1..3}.csv \
|awk -F, 'NR==1{split($0,arr,/,/);next} {SEP=""; fld=1;
for (x in arr){printf ($0 ~ arr[x])?SEP""$(fld++):",0";SEP=","};print ""}')
出力:
A,1,0,1
B,20,0,4
C,30,1,0
D,0,13,0
E,0,15,2
F,0,19,0
G,0,0,6
H,0,0,8
コード分析:
awk -F, '{ seen[$1]=seen[$1]","FILENAME":"$2; }
END{ print HEADER; for (x in seen) print x seen[x] }' R{1..3}.csv
コードの主要部分は、すべてのファイルの2番目の列をすべて1つに連結し、同じ最初の列を持つファイルに属する値を印刷します。以下は、キーが最初の列で値が追加モードの配列seen
名です。,FILENAME:$2
Inは、seen[$1]=seen[$1]","FILENAME":"$2;
カンマを印刷し、,
その後に現在処理されているFILENAME
ファイルを印刷することを意味します。アッ、コロン:
の後に2番目の列の値が続きます$2
(最初の列が同じ場合)。seen[$1]=...
同じキーインデックスに追加され、=seen[$1]...
同じキー値に保存されます。
このEND
声明は、アッこのブロックはすべてのレコード/行を読み取ると最終的に実行され、forループを使用して配列を繰り返します。ボン印刷して鍵最初とコア値次から。
結果は次のとおりです。
A,R1.csv:1,R3.csv:1
B,R1.csv:20,R3.csv:4
C,R1.csv:30,R2.csv:1
D,R2.csv:13
E,R2.csv:15,R3.csv:2
F,R2.csv:19
G,R3.csv:6
H,R3.csv:8
さて、既存の値がどのファイルから来たのか、どのファイルにこのデータがないのかがわかります。存在しないファイルをデータで埋めるために0
シェルコマンドを使用しました。すべてのファイル名を含むヘッダー行の生成そして配信アッ〜のようにHEADER
-V可変的な:
awk -v HEADER="$(printf ",%s:" R{1..3}.csv)" ...
HEADER
後でこの行を使用します0
。現在の入力形式は次のとおりです。
$ awk -v HEADER="$(printf ",%s:" R{1..3}.csv)" -F, '
{ seen[$1]=seen[$1]","FILENAME":"$2; }
END { print HEADER; for (x in seen) print x seen[x]}' R{1..3}.csv
,R1.csv:,R2.csv:,R3.csv:
A,R1.csv:1,R3.csv:1
B,R1.csv:20,R3.csv:4
C,R1.csv:30,R2.csv:1
D,R2.csv:13
E,R2.csv:15,R3.csv:2
F,R2.csv:19
G,R3.csv:6
H,R3.csv:8
次に、以下の他のものを使用しました。アッ終了しないファイルデータを埋めるスクリプト、0
質問に対する他の回答からこのデータをコピーしました。「列に基づいて書式設定し、不足しているデータを埋める」。
... |awk -F, 'NR==1{split($0,arr,/,/);next} {SEP=""; fld=1;
for (x in arr){printf ($0 ~ arr[x])?SEP""$(fld++):",0";SEP=","};print ""}'
最後に、sed 's/,R[1-9]\+\.csv:/,/g'
結果の既存のファイル名を単一のコンマに変更するために使用します,
。
答え2
見事に使うミラー、あなたはできます
mlr --csv --implicit-csv-header put '$f=FILENAME;$f=sub($f,"\..+","")' then \
label Taxa then \
reshape -s f,2 then \
unsparsify then \
fill-empty -v 0 then \
sort-within-records then \
reorder -f Taxa *.csv
得るために
Taxa,R1,R2,R3
A,1,0,1
B,20,0,4
C,30,1,0
D,0,13,0
E,0,15,2
F,0,19,0
G,0,0,6
H,0,0,8
答え3
これは痛いです。
join -t, -j1 -a1 -e0 -o auto r1.csv r2.csv > r12a.csv
join -t, -j1 -a2 -e0 -o auto r1.csv r2.csv > r12b.csv
sort -u r12?.csv > r12.csv
join -t, -j1 -a1 -e0 -o auto r12.csv r3.csv > r123a.csv
join -t, -j1 -a2 -e0 -o auto r12.csv r3.csv > r123b.csv
sort -u r123{a,b}.csv
- 最初の接続は、ファイルx()にペアのない値を出力します。
-ax
デフォルトは(-e0
)-o auto
で、接続にゼロを印刷するように指示します。 sort -u
独自の記録を分類して維持します。
awk
コードが読みやすくなるかどうかはわかりません。
答え4
それを処理する方法はいくつかありますコマンドラインのCSV、またはArchemarの答え。しかし、あなたの要件のためにPythonを使用することをお勧めします。私はこのスクリプトをPython 3.5でテストしましたが、トリックを実行するか、少なくとも良い起動を提供する必要があります。
import os,re,argparse
import csv
parser = argparse.ArgumentParser(description='join csvs with rows of the \
form \w+,[1-9], inserting a zero for a row label if it does not \
exist.')
parser.add_argument('infiles', type=str, help='infile names', nargs='+')
args = parser.parse_args()
d = {}
file_idx = 0
for infile in args.infiles:
with open(infile, 'r') as f:
for line in f:
parsed_line = re.match('(\w+),([0-9]+)', line)
if not parsed_line:
print("line {} not parsed in file {}".format(line, infile))
continue
if parsed_line.group(1) in d:
d[parsed_line.group(1)].append(parsed_line.group(2))
else:
l = [0]*(file_idx)
l.append(parsed_line.group(2))
d[parsed_line.group(1)]=l
for k in d:
if (len(d[k]) == file_idx):
d[k].append(0)
if not(len(d[k]) == file_idx+1):
print("problem with file {}, dict {}, key {}".format(f,d,k))
file_idx = file_idx + 1
## output time
with open('results.csv','w') as csvfile:
cwriter = csv.writer(csvfile)
header = [os.path.splitext(x)[0] for x in args.infiles]
header.insert(0,'Taxa')
cwriter.writerow(header)
for k in sorted(d.keys()):
d[k].insert(0,k)
cwriter.writerow(d[k])