使用 awk 從 CSV 檔案中僅選擇一個欄位小於閾值的行

使用 awk 從 CSV 檔案中僅選擇一個欄位小於閾值的行

處理包含許多(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
}

我現在想做的事

如何修改awk程式碼以對原始 CSV 應用更具選擇性的過濾器?例如,根據第四列(在 中)的值(浮點數)過濾資料dG(rescored)

例如,我需要使用最低值(始終位於第二行minForth = 0.421573)作為參考,並列印 CSV 中$4小於選定閾值的所有行(例如,高於 20% minForth):

$4<=(1+0.2)*min))'

答案1

如果您只想過濾第四個欄位低於閾值的所有行,則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.2awk

在程式內部,min如果我們位於第 2 行 ( FNR==2),它將把變數值設為第 4 列中的值。如果我們位於第 1 行(標題 - 如果您需要的話),或者我們位於文件的資料部分並且第四個欄位小於1+margin最小值的倍,那麼它只會列印目前行。

答案2

這是一個相當冗長的腳本 - 不要使用任何快捷方式並將資訊列印到stderr.至於 sh 部分,您通常可以新增選項來在頂部設定值“Globals”,以便除了參數之外還可以使用選項進行呼叫。 IE:

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函數。它可能是類似的東西。 (可以討論basenameand的使用):$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 >&2as 呼叫它來印到 stderr 而不是 stdout。

以上help一種使用半標準方式。它不接受-abcor --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

人們可以對選項進行更多驗證,即確保它是數字等。

相關內容