私は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
。)
それでは、見てみましょう。
- 私たちはあなたのフィードバックを受け取ります
allpeople.csv
- 実行すると、
sqlite3 people.sqlite
次のコマンドを作成できるきちんとした小さなシェルが提供されます。 CREATE TABLE "people" ("id" INTEGER UNIQUE, "age" INTEGER);
Enter、2つの列「id」と「age」を含む新しいテーブルを作成します。両方の列は整数です。 「id」は一意性も保証されます。同じ「id」を持つ2つの項目を持っていると、苦情が発生します。.import --csv "allpeople.csv" "people"
Enter、CSV "allpeople"を読み取り、作成したばかりの "people"テーブルにロードします。
これでデータが準備されました。(データベースで何回を選択しても一度だけ行うことができます。)
ここから楽しみが始まります。
.mode csv
Enter、出力モードをCSVに設定します。.output oldpeople.csv
Enter、これはsqliteに "oldpeople.csv"ファイル(要求されたヘッダーを含む)に出力を書き込むように指示します。SELECT * FROM "people" WHERE "age" >= 60;
Enter、推測されましたが、60歳以上の人を含むすべての行を選択して結果をoldpeople.csv
.output youngpeople.csv
EnterSELECT * FROM "people" WHERE "age" < 60;
Enterこれ以上の説明は必要ありません.quit
EnterSQLiteを終了します。
もちろん、上記のコマンドをテキストファイル "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<60
andに変更する必要があります。$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標準に準拠していない場合は、csvsql
Pythonベースの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
どちらのパッケージもmiller
Ubuntuで簡単に利用できます。宇宙リポジトリ。
- たとえば、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変数over
とunder
。一般的なエラーの原因であるため、頻繁に再利用される値には変数または定数を使用する方が一般的です。
$ 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