多数 (10000 行以上) の行を含む複数列の csv ファイルの後処理を処理します。
ID(Prot), ID(lig), ID(cluster), dG(rescored), dG(before), POP(before)
9000, lig662, 1, 0.421573, -7.8400, 153
10V2, lig807, 1, 0.42692, -8.0300, 149
3000, lig158, 1, 0.427342, -8.1900, 147
3001, lig158, 1, 0.427342, -8.1900, 147
10V2, lig342, 1, 0.432943, -9.4200, 137
10V1, lig807, 1, 0.434338, -8.0300, 147
4000, lig236, 1, 0.440377, -7.3200, 156
10V1, lig342, 1, 0.441205, -9.4200, 135
4000, lig497, 1, 0.442088, -7.7900, 148
9000, lig28, 1, 0.442239, -7.5200, 152
3001, lig296, 1, 0.444512, -7.8900, 146
10V2, lig166, 1, 0.447681, -7.1500, 157
....
4000, lig612, 1, 0.452904, -7.0200, 158
9000, lig123, 1, 0.461601, -6.8000, 160
10V1, lig166, 1, 0.463963, -7.1500, 152
10V1, lig369, 1, 0.465029, -7.3600, 148
これまでやってきたこと
私は関数awk
に統合された次のコードを使用していますbash
。このコードは、CSV から 1% (上部の行) を取得し、それを新しい CSV (行数が削減されたファイル) として保存します。
take_top44 () {
# Take the top lines from the initial CSV
awk -v lines="$(wc -l < original.csv)" '
BEGIN{
top=int(lines/100)
}
FNR>(top){exit}
1
' original.csv >> csv_with_top_lines.csv
}
今やりたいこと
元の CSV にさらに選択的なフィルターを適用するには、コードをどのように変更すればよいでしょうかawk
? たとえば、( ) の 4 番目の列の値 (浮動小数点数) に基づいてデータをフィルター処理するにはどうすればよいでしょうかdG(rescored)
?
たとえば、最小値 (常に 2 行目にある ) を参照として使用し、 が選択したしきい値 (たとえば、 の 20% 上)より小さいminForth = 0.421573
CSV のすべての行を印刷する必要があります。$4
minForth
$4<=(1+0.2)*min))'
答え1
4 番目のフィールドがしきい値を下回るすべての行をフィルター処理するだけの場合は、次のawk
コマンドが機能します。
awk -F',' -v margin=0.2 'FNR==2 {min=$4} FNR>1&&($4<=(1+margin)*min)' input.csv
または、フィルタリングされた出力にヘッダーも含める場合は、次のようにします。
awk -F',' -v margin=0.2 'FNR==2 {min=$4} FNR==1||($4<=(1+margin)*min)' input.csv
これにより、フィールド区切り文字が に設定され,
(ただし、フィールドを区切る追加のスペースがあるため、ファイルは非標準の CSV であることに注意してください)、margin
の値を持つ変数がプログラム0.2
にインポートされますawk
。
プログラム内では、min
2 行目 ( ) の場合、変数の値が 4 列目の値に設定されます。その後、1 行目 (必要な場合はヘッダー) の場合、またはファイルのデータ部分にあり、4 番目のフィールドが最小値の倍FNR==2
よりも小さい場合にのみ、現在の行が印刷されます。1+margin
答え2
これはかなり冗長なスクリプトです。ショートカットを使用せず、情報を に出力してくださいstderr
。sh 部分については、通常はオプションを追加して先頭に「Globals」の値を設定し、引数に加えてオプションを指定して呼び出すことができます。つまり、次のようになります。
my_script --max-factor 0.15 -p 20 --out-file foo.csv *.csv
このようにrescored
行のパーセンテージをフィルタリングすることで、冗長な部分は明らかに削除できます。
#!/bin/sh
# Globals with defaults set
num_lines=0
max_percent_lines=10
max_factor=0.2
fn_in=""
# Default out. Optionally set to another file.
fn_out=/dev/stdout
# As /dev/null to quiet information
fn_err=/dev/stderr
get_num_lines()
{
num_lines=$(wc -l< "$1")
}
print_filtered()
{
awk \
-v num_lines="$num_lines" \
-v max_percent_lines="$max_percent_lines" \
-v max_factor="$max_factor" \
-v fn_err="$fn_err" \
'
BEGIN {
FS=", "
# Exclude header
max_line = (1 + num_lines / 100 * max_percent_lines)
# Truncate
max_line -= max_line % 1
printf "Lines : %d\n",
num_lines - 1 >>fn_err
printf "Line Max : %d (%d%%)\n",
max_line, max_percent_lines >>fn_err
}
NR == 2 {
max_rescored = ($4 + $4 * max_factor)
printf "Rescored Max: %f\n", max_rescored >>fn_err
}
NR > 1 {
print $0
}
NR >= max_line {
printf "Max Line : %d (Exit)\n", max_line >>fn_err
exit
}
$4 >= max_rescored && NR > 2 {
printf "Max Rescored: %f (Exit)\n", $4 >>fn_err
exit
}
' "$fn_in" >>"$fn_out"
}
# Here one could loop multiple input files
コマンドラインオプション
コメントのリクエストに応じて。
オプションを取得するには、さまざまな方法があります。最も簡単なのは位置引数です。例:
Usage: script percent margin <files ...>
スクリプトでは次のように記述します。
percent=$1
margin=$2
shift
shift
... loop files ...
もう少し凝った/柔軟なものを好む場合は、次のようなこともできます。
まずhelp
関数を記述します。次のようなものになります。(basename
との使用について$0
はおそらく議論されるでしょう):
print_help() {
printf "Usage: %s [OPTIONS] <FILES ...>\n" "$(basename "$0")"
printf "\nSome description\n"
printf "\nOPTIONS\n"
printf " -p --percent-lines V Print percent of file. Default %s\n" "$max_percent_lines"
printf " -r --max-factor V Max rescored increase. Default %s\n" "$max_factor"
printf " -o --out-file V Output file. Default stdout\n"
printf " -q --quiet Silence information\n"
printf " -h --help This help\n"
printf " -- Everything after this is input files\n"
printf "\nEverything after first unrecognized option is treated as a file.\n"
}
通常print_help >&2
、stdout ではなく stderr に印刷するには、 as で呼び出します。
上記では、help
半標準的な方法を使用します。 または は使用しません-abc
が--foo=123
、各オプションと引数はスペースで区切る必要があります。
オプションとして、しゃれではありませんが、次のような投稿をチェックしてください
残りのスクリプトでは、単純なエラー チェックを使用して次の簡単な方法を実行できます。
# While not empty
while ! [ -z "$1" ]; do
case "$1" in
-h|--help)
print_help >&2
exit 1
;;
-p|--percent-lines)
shift
max_percent_lines="$1"
;;
-r|--max-factor)
shift
max_factor="$1"
;;
-o|--out-file)
shift
fn_out="$1"
;;
-q|--quiet)
fn_err="/dev/null"
;;
--)
break
;;
*)
break
;;
esac
# Next argument
shift
done
if ! [ -r "$1" ]; then
printf "Unable to read file: \`%s'\n" "$1" >&2
exit 1
fi
# Print header from first file
head -n 1 "$1" >>"$fn_out"
for fn_in in "$@"; do
printf "Processing '%s'\n" "$fn_in" >>"$fn_err"
if ! [ -r "$1" ]; then
printf "Unable to read file: \`%s'\n" "$1" >&2
exit 1
fi
get_num_lines
print_filtered
done
オプションの検証をさらに実装して、それが数字であることを確認することもできます。