これは私の入力ファイルです。
0164318,001449,001452,001922
0164318,001456,001457,001922
0842179,002115,002118,001485
0846354,001512,001513,001590
0841422,001221,001224,001860
0841422,001227,001228,001860
私の結果は
0164318,001449,001457,001922
0842179,002115,002118,001485
0846354,001512,001513,001590
0841422,001221,001228,001860
col1でグループ化し、シェルスクリプトを介してmin(col2)とmax(col3)を見つけます。
答え1
使用csvkit
、
$ csvsql -H --query 'SELECT a,min(b),max(c),d FROM file GROUP BY a' file.csv
a,min(b),max(c),d
164318,1449,1457,1922
841422,1221,1228,1860
842179,2115,2118,1485
846354,1512,1513,1590
これはCSVデータを一時データベース(デフォルトではSQLiteと見なす)にロードし、与えられたSQLクエリをここに適用します。デフォルトでは、テーブルの名前は入力ファイルと同じ(サフィックスなし)、データに列ヘッダーがないため、デフォルトのフィールド名はアルファベット順に指定されます。
この-H
オプションは、csvsql
データに列見出しがないことを示します。
生成されたヘッダーを出力から削除するには、次のようなものを渡しますsed '1d'
。
0 で埋められた整数を取得するには、次のようにします。
$ csvsql -H --query 'SELECT printf("%07d,%06d,%06d,%06d",a,min(b),max(c),d) FROM file GROUP BY a' file.csv
"printf(""%07d,%06d,%06d,%06d"",a,min(b),max(c),d)"
"0164318,001449,001457,001922"
"0841422,001221,001228,001860"
"0842179,002115,002118,001485"
"0846354,001512,001513,001590"
ここでは実際には結果レコードごとに1つの出力フィールドしか要求しないため(カンマを含む)、この行を引用しました。より多くの入力が必要ですが、追加の二重引用符を生成しない別のアプローチは次のとおりです。
$ csvsql -H --query 'SELECT printf("%07d",a),printf("%06d",min(b)),printf("%06d",max(c)),printf("%06d",d) FROM file GROUP BY a' file.csv
"printf(""%07d"",a)","printf(""%06d"",min(b))","printf(""%06d"",max(c))","printf(""%06d"",d)"
0164318,001449,001457,001922
0841422,001221,001228,001860
0842179,002115,002118,001485
0846354,001512,001513,001590
同様に、結果をパイピングして出力ヘッダーを削除できますsed '1d'
。
答え2
使用csvkit
:
csvsql -H --query "select a,min(b),max(c),d from file group by a,d" file.csv
これにより先行ゼロが切り捨てられます。
出力:
a,min(b),max(c),d
164318,1449,1457,1922
841422,1221,1228,1860
842179,2115,2118,1485
846354,1512,1513,1590
答え3
ミラー(http://johnkerl.org/miller/doc)、使用
mlr --ocsv --quote-all --inidx --ifs , cat inputFile | \
mlr --ocsv --quote-none --icsvlite stats1 -g '"1"' -a min,max,min -f '"2","3","4"' \
then cut -f '"1","2"_min,"3"_max,"4"_min' \
then label id,col2,col3,col4 | sed 's/"//g'
あなたは
id,col2,col3,col4
0164318,001449,001457,001922
0842179,002115,002118,001485
0846354,001512,001513,001590
0841422,001221,001228,001860
答え4
SQLを基本的な手続き型操作に分割し、それをシェルスクリプトに複製できます。
SQLなどの宣言言語の利点の1つは、開発者が手続き型実装の冗長性と複雑さを隠してデータに集中できるという点では良い考えではありません。 (最適化は宣言言語の2番目の大きな利点であり、プロシージャルプログラムを使用して複製すると消えます。)
さらに、このアプローチには問題があります。シェルループでテキストを処理することは通常悪い習慣と見なされます。。
ただし、以下は多くのシステムにプリインストールされている標準ユーティリティを使用するシェルスクリプトの例です(配列設定を除外 - POSIXで指定されていないが、要求された後に広く利用可能で確実に機能するbash
)。
#!/bin/bash
# The input file will be passed as the first argument
file="$1"
# For each input line:
# We take only the values of the first field, sort them, remove duplicates
for i in $(cut -d ',' -f 1 "$file" | sort -n -u); do
# Resetting the array is not really needed; we do it for safety
out=()
# The first field of the output row is the key of the loop
out[0]="$i"
# We only consider the rows whose first field is equal
# to the current key (grep) and...
# ... we sort the values of the second field
# in ascending order and take only the first one
out[1]="$(grep "^${out[0]}" "$file" | cut -d ',' -f 2 | sort -n | head -n 1)"
# ... we sort the values of the third field in
# ascending order and take only the last one
out[2]="$(grep "^${out[0]}" "$file" | cut -d ',' -f 3 | sort -n | tail -n 1)"
# ... we sort the values of the fourth field in
# ascending order and take only the first one
out[3]="$(grep "^${out[0]}" "$file" | cut -d ',' -f 4 | sort -n | head -n 1)"
# Finally we print out the output, separating fields with ','
printf '%s,%s,%s,%s\n' "${out[@]}"
done
次のように呼び出す必要があります。
./script file
このスクリプトは次のとおりです。
SELECT col1, MIN(col2), MAX(col3), MIN(col4)
FROM text
GROUP BY col1
ORDER BY col1