web-dev-qa-db-ja.com

CSVをいくつかの共有列と連結します

私は2つの大きなファイル〜9GBを持っています。 CSVファイル1には列がありますA, B, C, D, EおよびCSVファイル2には列B, C, F, G。必要な出力はA, B, C, D, E, F, G。私が見つけたのは、類似した列で結合し、同じ列と連結することだけですが、ここでは一致するものと一致しないものがあります。サンプル出力は、次のようになります。

A   B   C   D   E   F   G

1   2   3   4   5   6   7

NaN 1   2   NaN 1   2   1

したがって、その列の値が存在しない場合(存在しない場合など)は、NaN値を指定するだけです。問題を十分に説明できたと思います。ありがとう!

編集:通常はPythonでこれを行いますが、これらの大規模なファイルを使用すると、チャンクを繰り返し処理して最後に連結するのがかなり面倒になります。私が知らないbashを使用するより簡単な方法があるようです。ありがとう!

2
gold_cy

これは、次の事実に基づいて機能します。
(a)すべてのフィールドは厳密にタブで区切られています
(b)両方のファイル(BとC)の共通の列の値が同じ

_$ join --nocheck-order -eNaN -13 -22 -t$'\t' -o 1.1 1.2 1.3 1.4 1.5 2.3 2.4 b.txt c.txt
A   B   C   D   E   F   G
1   2   3   4   5   6   7
NaN 1   2   NaN 1   2   1
_

ファイルサンプル:

_$ cat b.txt
A   B   C   D   E
1   2   3   4   5
    1   2       1
$ cat c.txt
B   C   F   G
2   3   6   7
1   2   2   1
_

参加オプション:
-13 -22:file1 column3(C)= file2 column2(C)に基づいて結合
-t $ '\ t':入力と出力のタブ区切り文字
-o:出力フォーマット。 1.1は、file1、column1などを意味します。
-e:空の値をNaNで埋める
詳細については、_man join_およびさらに良い_info join_を参照してください。

AWKを使用した代替ソリューション
PS:awkで我慢してください、私はawkの新しい学習者です。

_$ awk -F"\t" '{a[1]="";{for (i=1;i<=NF;i++) if (i==6 ||i==7) continue;else \
if ($i!="") a[1]=a[1]FS$i;else a[1]=a[1]FS"NaN";print a[1]}}' <(paste b.txt c.txt)
_

コンマ区切りの入力フィールドの更新
コメントでアドバイスされているように、csvファイルはコンマで区切られているため、このソリューションでは入力フィールドがコンマで区切られ、タブを使用して結果が出力され、読みやすくなります。

_awk 'BEGIN {FS=",";OFS="\t"}{a[1]="";{for (i=1;i<=NF;i++) if (i==6 ||i==7) continue;else \
if ($i!="") a[1]=a[1]OFS$i;else a[1]=a[1]OFS"NaN";print a[1]}}' <(paste b.txt c.txt)
_

出力もコンマで印刷する必要がある場合は、begineセクションを_{FS=OFS=","}_に置き換えてください。

ただし、共通の列/異なる値で何をしようとしているのかはまだ不明です。

パーツif (i==6 ||i==7) continue;elseを削除して、結果がニーズに合っているかどうかを確認できます。これまで、ファイル2の2つの列はファイル1の列と同一であると見なされていたため、この条件チェックは実際にはフィールド6(file2のB列)とフィールド7(file2のC列)をスキップします。

結合ソリューションの場合:
カンマ区切りのフィールドを読み取るには、_-t$'\t'_を_-t','_に置き換えます

一般的な列の場合、次の出力形式で遊ぶことができます。

_join --nocheck-order -eNaN -13 -22 -t',' -o 1.1 1.2 2.1 1.3 2.2 1.4 1.5 2.3 2.4 b.txt c.txt
_
2
George Vasiliou