値がリストにない場合は、CSVから値を削除します。

値がリストにない場合は、CSVから値を削除します。

100行と数値列を含むCSVテーブルがあります。テーブルのいくつかの数値を含む1列ファイルである別のリストがあります。リストにないCSVのすべての値を削除する方法はありますか?

これをリストファイルとして書き込むことができると思いましたがgrep -f、削除したい値の一部が保持したい値と同じ行に悩んでいます。

例えば

CSVテーブル:

11,12,13 
11,10,12,13 

リストファイル:

13
11

出力:

11,,13 
11,,,,13 

または代替的に

11,13 
11,13 

ベストアンサー1

次のawk手順では、CSV フィールドに先行または末尾の空白がないと仮定します。

awk 'BEGIN {FS=OFS=","}
     NR==FNR{valid[$1];next}
     {for (i=1;i<=NF;i++) {if (!($i in valid)) {$i=""}}} 1' validvalues.txt input.csv 

validvalues.txt有効な値を含むファイルを最初に処理し、次に実際のCSVファイルを処理します。

  • BEGINセクションでは、入力と出力のフィールド区切り文字がに設定されます,
  • 最初のファイル(ファイルごとのNRラインカウンタと同じグローバルラインFNRカウンタとして表示されます)を処理するときに許容される値を配列のインデックスとして記録しvalid、それ以外の場合は次の入力ラインに処理をスキップします。
  • 2番目のファイルを処理するときは、すべてのフィールドを繰り返し、フィールドの内容が「配列インデックス」の一部であることを確認してくださいvalid。それ以外の場合は、フィールド値を空のフィールドに設定します。
  • 1これまでのすべての修正を含む、現在の行の見かけ上は汚れて見える印刷です。

重要なのは、($i in valid)テストが文字列ベースの比較であるため、有効な値ファイルの列エントリまたはCSVファイルのフィールドに先行/末尾のスペースが含まれている場合、比較のために同じスペースが予期しない動作を引き起こす可能性があります。

@glenn jackmanが述べたように手順は次のように単純化できます。

awk 'BEGIN {FS=OFS=","}
     NR==FNR{valid[$1]=$1;next}
     {for (i=1;i<=NF;i++) {$i=valid[$i]}} 1' validvalues.txt input.csv 

ここでは、実際に有効な値を「配列値」としても登録します。無効な値は、項目がないためvalid自動的valid[$i]に空の文字列として評価され、有効な値の場合は値自体を返すという考えです。

ただし、「フィールド値自体」を不必要に置き換えてより多くのメモリを必要とするため、パフォーマンスが少し遅くなるため、「有効な値」ファイルが大きい場合は問題になる可能性があります。

おすすめ記事