Bash

SSV/CSV 操作:計算比率

  • February 6, 2020

請注意,我知道datamash並且是一位經驗豐富的awk使用者。我正在尋找比awk. 假設我有以下內容:

// data_file
foo bar biz
10  100 1000
11  150 990
10  95  1010
9   99  950
// usage goal, in pseudo code
cat data_file | <tool> --ratio foo,bar --ratio foo,biz --ratio bar,biz
// desired output
foo bar biz foo_bar foo_biz bar_biz
10  100 1000 0.1    0.01    0.1  
11  150 990  0.073  0.011   0.1515
10  95  1010 0.105  0.0099  0.094
9   99  950  0.09   0.0095  0.1042

為了得到這個介面,我將用 C++ 建構一些微不足道的東西。

在此之前,在 Unix 中是否有一個解決方案?

使用幾個 bash 函式,如果你有一個文件可以使用paste,你可以非常簡單地到達那裡:bc``csvtool

div() {
 printf "%1.4f\n" $(bc -l <<<"1.0 * $1 / $2")
}
export -f div

ratio() {
 echo "$1"_"$2"
 csvtool -t ' ' namedcol $1,$2 data.ssv |
 tail -n+2                              |
 csvtool call div -
}

paste -d ' ' <(cat data.ssv) <(ratio foo bar) <(ratio foo biz) <(ratio bar biz) |
csvtool -t ' ' readable -

輸出:

foo bar biz  foo_bar foo_biz bar_biz 
10  100 1000 0.1000  0.0100  0.1000  
11  150 990  0.0733  0.0111  0.1515  
10  95  1010 0.1053  0.0099  0.0941  
9   99  950  0.0909  0.0095  0.1042 

如果您真的想以流媒體方式進行,您最好的選擇可能是awk,例如:

解析.awk

# Parse the requested column ratios into dividend[] and divisor[]
# by column name
BEGIN {
 split(ratios_str, ratios, / +/)
 for(r in ratios) {
   split(ratios[r], cols, /,/)
   dividend[++i] = cols[1] 
   divisor[i]    = cols[2]
 }
}

# Sort out the header
NR == 1 { 
 # Create the ColumnName-to-ColumnNumber hash
 split($0, a); for(k in a) c2n[a[k]]=k

 # Print the header line
 printf "%s ", $0
 for(i=1; i<=length(dividend); i++)
   printf "%s_%s ", dividend[i], divisor[i]
 printf "\n"
}

NR > 1 {
 printf "%s ", $0
 for(i=1; i<=length(dividend); i++)
   printf "%1.4f ", $(c2n[dividend[i]]) / $(c2n[divisor[i]])
 printf "\n"
}

像這樣執行它:

<data.ssv awk -f parse.awk -v ratios_str='foo,bar foo,biz bar,biz' | column -t

輸出:

foo  bar  biz   foo_bar  foo_biz  bar_biz
10   100  1000  0.1000   0.0100   0.1000
11   150  990   0.0733   0.0111   0.1515
10   95   1010  0.1053   0.0099   0.0941
9    99   950   0.0909   0.0095   0.1042

使用米勒(https://github.com/johnkerl/miller)並執行

mlr --pprint put '$foo_bar=$foo/$bar;$foo_biz=$foo/$biz;$bar_biz=$bar/$biz' input >output

你有

foo bar biz  foo_bar  foo_biz  bar_biz
10  100 1000 0.100000 0.010000 0.100000
11  150 990  0.073333 0.011111 0.151515
10  95  1010 0.105263 0.009901 0.094059
9   99  950  0.090909 0.009474 0.104211

引用自:https://unix.stackexchange.com/questions/565928