2番目の列値に基づいてCSVファイルを分割する

2番目の列値に基づいてCSVファイルを分割する

私はUbuntuを使用しており、2番目の列(年齢)の値に基づいてcsvファイルを2つのcsvファイルに分割したいと思います。最初のファイルは60歳未満(< 60)の患者用で、2番目のファイルは60歳以上(> =)の患者用です。たとえば、次のような入力があるとします。

id,age
1,65
2,63
3,5
4,55
5,78

希望の出力は次のとおりです。

ファイルの下:

id,age
3,5
4,55

ファイル範囲:

id,age
1,65
2,63
5,78

次のコードを試しましたが、ヘッダー(列名)が削除されました。これを防ぐにはどうすればよいですか?

awk -F ',' '($2>=60){print}' file.csv > file_over.csv 

入力ファイルには約50,000行があります。

答え1

私は私のデータを文字行ではなく、テーブルの内容としてすでに理解している環境でこのすべてのフィルタリングを実行します。

フィルタリングを実行しているようなので、理想的には、環境にはテーブルのクエリに使用できる構造化言語の一種が必要です。

SQLを入力して、S構造化キューガラス患者データベースなどを処理するために設計された言語

データベースがディスクに存在しなくても、データベースにこれらのSQLインタフェースを提供するツールがあります。SQLite。 (ほとんどの場合、Ubuntuにすでにインストールされている可能性があります。そうしないと非常に小さく、を使用してインストールできますsudo apt install sqlite3。)

それでは、見てみましょう。

  1. 私たちはあなたのフィードバックを受け取りますallpeople.csv
  2. 実行すると、sqlite3 people.sqlite次のコマンドを作成できるきちんとした小さなシェルが提供されます。
  3. CREATE TABLE "people" ("id" INTEGER UNIQUE, "age" INTEGER);Enter、2つの列「id」と「age」を含む新しいテーブルを作成します。両方の列は整数です。 「id」は一意性も保証されます。同じ「id」を持つ2つの項目を持っていると、苦情が発生します。
  4. .import --csv "allpeople.csv" "people"Enter、CSV "allpeople"を読み取り、作成したばかりの "people"テーブルにロードします。

これでデータが準備されました。(データベースで何回を選択しても一度だけ行うことができます。)
ここから楽しみが始まります。

  1. .mode csvEnter、出力モードをCSVに設定します。
  2. .output oldpeople.csvEnter、これはsqliteに "oldpeople.csv"ファイル(要求されたヘッダーを含む)に出力を書き込むように指示します。
  3. SELECT * FROM "people" WHERE "age" >= 60;Enter、推測されましたが、60歳以上の人を含むすべての行を選択して結果をoldpeople.csv
  4. .output youngpeople.csvEnterSELECT * FROM "people" WHERE "age" < 60;Enterこれ以上の説明は必要ありません
  5. .quitEnterSQLiteを終了します。

もちろん、上記のコマンドをテキストファイル "commands.sql"に書いてくださいsqlite3 people.sqlite < commands.sql

"people.sqlite"には、"allpeople.csv"のデータベースよりも読みやすく、よりコンパクトで、より柔軟なデータベースが含まれています。私通常~を避けるどのCSVの統計、数学、または分析作業 - IMHOこれは正しい形式ではありません。 SQLは非常に便利で、特に複数のテーブルまたは2つ以上の列がある場合は、より興味深い作業を実行できます。

たとえば、データに「性別」の別の列と「体重」の列が1つある場合、SQLは1つのSELECT明確な文で18〜20歳の重度の太りすぎの男性の両方を簡単に選択できます。人のテーブルの「id」に診断をマッピングする他のテーブルがある場合は、深刻な糖尿病に罹っている18〜20歳の男性を具体的に見つけることもできます。 (あなたはできますおそらくawkで同じことをすると、ある時点で面倒で遅くなります。 )

私は実際にデータ交換形式でうまく動作するので、SQLiteが好きです。 CSVとは異なり、エンコード、区切り、引用、エスケープ、スペース、ヘッダーに同意するツールはほとんどありません。 SQLiteは実際に定義された記憶形式であり、CSVはおおよその概念に近いです。通常、カンマで区切っても問題ありません。

データ分析に使用される一般的なプログラミング言語には、通常、sqlite3インタフェースが組み込まれています。 python3の標準ライブラリにはこのモジュールがsqlite3含まれており、PerlにはこのモジュールがありDBD::SQLite、Rにはありlibrary(RSQLite)、C / C ++には基本インタフェースがあります。

答え2

awkの使い方は基本的にファイルにフィールドに引用されたカンマなどの高度なCSV機能が含まれていない場合。 1値が文字列1に解析されても、比較が語彙ではなく数値であることを確認するには、テストを$2+0<60andに変更する必要があります。$2+0>=60$2

どちらの場合も、ヘッダー行をエクスポートするには、最初のレコードに対してtrueを返すテストを追加する必要があります。{print}これが基本的な操作なので、このコンテキストでは完全に省略できます。だから

$ awk -F ',' 'NR==1 || $2+0<60' file.csv
id,age
3,5
4,55

そして

$ awk -F ',' 'NR==1 || $2+0>=60' file.csv
id,age
1,65
2,63
5,78

ファイルが単純なCSV標準に準拠していない場合は、csvsqlPythonベースのcsvkitで他のオプションを使用できます。

$ csvsql --query 'SELECT * FROM file WHERE age >= 60' file.csv
id,age
1,65
2,63
5,78

またはミラー:

$ mlr --csv filter '$age >= 60' file.csv
id,age
1,65
2,63
5,78

csvkitどちらのパッケージもmillerUbuntuで簡単に利用できます。宇宙リポジトリ。


  1. たとえば、C ロケールではアルファベット順に次aより大きいです。6($2>=60){print}($2<60){print}

答え3

使用するawk組み込み出力リダイレクト一度にファイルを分割する:

$ awk -F, -v over=file_over.csv \
          -v under=file_under.csv \
    'NR==1 { print > over; print > under ; next };
    $2 < 60 { print > under ; next };
    { print > over }' file.csv

リダイレクトはawkシェルのリダイレクトと同様に機能します。主な違いは、awkは>スクリプトが最初に作成されたときにのみ出力ファイルを切り取ることです(したがって、>>スクリプトを既存のファイルに追加しない限り、後続の出力行は必要ありません)。 。

上記のコード行を使用すると、awk変数overunder。一般的なエラーの原因であるため、頻繁に再利用される値には変数または定数を使用する方が一般的です。

$ awk -F, 'NR==1 {
    print > "file_over.csv";
    print > "file_under.csv" ;
    next
  };
  $2 < 60 { print > "file_under.csv" ; next };
  { print > "file_over.csv" }' file.csv```

またはBEGINブロックに設定します。

$ awk -F, '
    BEGIN {
      over  = "file_over.csv";
      under = "file_under.csv";
    };
    NR==1 { print > over; print > under ; next };
    $2 < 60 { print > under ; next };
    { print > over }' file.csv

入出力ファイル:

$ head file*.csv
==> file.csv <==
id,age
1,65
2,63
3,5
4,55
5,78

==> file_over.csv <==
id,age
1,65
2,63
5,78

==> file_under.csv <==
id,age
3,5
4,55

答え4

awkを使用してください。

$ awk -F',' '
    NR==1 { print > "file_under"; print > "file_over"; next }
    { print > ( "file_" ($2 < 60 ? "under" : "over") ) }
' file

$ head file_under file_over
==> file_under <==
id,age
3,5
4,55

==> file_over <==
id,age
1,65
2,63
5,78

あるいは、必要に応じてコードで出力ファイル名を繰り返すことなく同じ出力を生成します。

awk -F',' '
    BEGIN { split("file_over,file_under",out) }
    NR==1 { for (i in out) print > out[i]; next }
    { print > out[($2 < 60)+1] }
' file

関連情報