一部のセルには複数の値を含む大容量CSVファイルがあります。最小値のみを含むようにこれらのセルをどのように変更できますか?
たとえば、次の入力が与えられた場合:
id,disease_1,disease_2
1001,2008;2009,2009;2010
ノート
- 列/フィールド区切り記号はコンマです。
,
- 各セルの値はセミコロンで区切られ、
;
昇順にソートされます。 - 列2でアルゴリズムを開始したいと思います。
希望の出力:
id,disease_1,disease_2
1001,2008,2009
答え1
サブコマンドでput
使用される「式」は次のとおりです。put
ミラー(mlr
;構造化データ操作用に特別に設計されたツール)は、;
各非フィールドから分離された値の最小値を計算しますid
。
for (key,value in mapexcept($*, "id")) {
value !=~ ";" { continue }
var minimum = "";
for (i,number in splitnv(value, ";")) {
minimum = min(minimum, number)
}
$[key] = minimum;
}
ここでは各レコードのフィールドを繰り返しますが、呼び出されるとフィールドはスキップされますid
。フィールド値をセミコロンに分割して生成された数値を繰り返しながら、各フィールドの最小値を追跡します。ループが終了すると、フィールド値は見つかった最小値で上書きされます。含まれていないフィールドは;
ループの開始時にスキップされます。
以下を使用して実行できます。
mlr --csv put -e script file.csv
...script
上記の短いスクリプトを保存するファイル名はどこにありますか?または、次のようにコマンドラインにスペルを入力できます。
mlr --csv put 'for (k,v in mapexcept($*,"id")) { v !=~ ";" { continue } var m=""; for (i,n in splitnv(v,";")) { m=min(m,n) } $[k]=m; }' file.csv
質問のデータを考慮すると、結果は次のようになります。
id,disease_1,disease_2
1001,2008,2009
最新バージョンのMiller(バージョン6+)を使用すると、コードを短縮できます。かなり新しいsort()
sumget_values()
関数を使用すると、次のようになります。
mlr --csv put 'for (k,v in mapexcept($*,"id")) { $[k] = sort(get_values(splitnv(v,";")))[1] }' file.csv
各フィールドの分離値のリストから最初の値を選択します;
。
(ありがとうございます。スチールドライバーこの巧妙な書き換えを悟りました。 )
値がすでにソートされている場合は、はるかに簡単で効率的です。
mlr --csv put 'for (k,v in mapexcept($*,"id")) { $[k] = sub(v,";.*","") }' file.csv
;
これにより、各フィールドの最初の文字が切り捨てられます。
答え2
答え3
単純なCSVの場合:
$ perl -MList::Util=min -F, -le 'print join ",", shift@F, map {min split /;/} @F' file.csv
id,disease_1,disease_2
1001,2008,2009
答え4
awkの場合、子供がまだ昇順にソートされていないと仮定します。
awk '
BEGIN{ FS=OFS="," }
function min(list) {
subNums=split(list, numbr, /;/)
min=numbr[1]
for(n=2; n<=subNums; n++)
if(numbr[n]<min)
min=numbr[n]
return min
}
{
for(fldNr=2; fldNr<=NF; fldNr++)
$fldNr=min($fldNr)
}' infile.csv
GNU awkを使用してください(asort()
機能配列も並べ替えます。PROCINFO["sorted_in"]
オプション)。
awk '
BEGIN{ FS=OFS=","; PROCINFO["sorted_in"]="@val_num_asc" }
{
for(fldNr=2; fldNr<=NF; fldNr++)
{
subNums=split(fldNr, numArr, /;/)
asort(numArr)
$fldNr=numArr[1]
}
}' infile.csv
BEGIN{ FS=OFS="," }
入力フィールドと出力フィールドの区切り文字を「,」に設定します。subNums=split(fldNr, numArr, /;/)
現在のフィールドを;
子区切り文字に分割し、結果の値を名前付き配列に格納します。シリアル番号。それから私たちは
asort(numArr)
並べ替えるシリアル番号値は昇順でソートされます。$fldNr=numArr[1]
ソート後に最初の(最小)値を割り当てます。シリアル番号現在のフィールド#の配列です。
最初の awk コマンドは同じことを行いますが、for ループを使用して子数を繰り返し、GNU awk 拡張を使用せずに各ペアを一度に 1 つずつ比較して最小値を見つけます。
ただし、各フィールドのサブ番号を昇順に並べ替えると、最初のサブ番号(最も小さい数字)を選択するだけで、コードがはるかに簡単になります。
awk '
BEGIN{ FS=OFS="," }
{
for(fldNr=2; fldNr<=NF; fldNr++)
{
split($fldNr, numbr, /;/)
$fldNr=numbr[1]
continue
}
}' infile.csv