Verwenden Sie awk, um aus der CSV-Datei nur Zeilen mit einem Feld auszuwählen, das kleiner als der Schwellenwert ist

Verwenden Sie awk, um aus der CSV-Datei nur Zeilen mit einem Feld auszuwählen, das kleiner als der Schwellenwert ist

Umgang mit der Nachbearbeitung einer mehrspaltigen CSV-Datei mit vielen (über 10.000) Zeilen:

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

Was ich bisher getan habe

Ich verwende folgenden awkCode, integriert in eine bashFunktion, die 1% (oberste Zeilen) aus der CSV nimmt und als neue CSV (enthalten, also mit reduzierter Zeilenanzahl) speichert:

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
}

Was ich jetzt tun möchte

Wie kann ich meinen Code ändern, awkum einen selektiveren Filter auf die ursprüngliche CSV-Datei anzuwenden? Beispielsweise um die Daten basierend auf dem Wert (Gleitkommazahl) der 4. Spalte (in dG(rescored)) zu filtern?

Beispielsweise muss ich den niedrigsten Wert (der immer in der zweiten Zeile steht, minForth = 0.421573) als Referenz verwenden und alle Zeilen aus der CSV-Datei drucken, die $4kleiner als ein ausgewählter Schwellenwert sind (sagen wir 20 % über minForth):

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

Antwort1

Wenn Sie einfach alle Zeilen filtern möchten, bei denen das 4. Feld unter einem Schwellenwert liegt, awkwürde der folgende Befehl funktionieren:

awk -F',' -v margin=0.2 'FNR==2 {min=$4} FNR>1&&($4<=(1+margin)*min)' input.csv

oder, wenn Sie den Header auch in der gefilterten Ausgabe haben möchten:

awk -F',' -v margin=0.2 'FNR==2 {min=$4} FNR==1||($4<=(1+margin)*min)' input.csv

Dadurch wird der Feldtrenner auf gesetzt ,(beachten Sie jedoch, dass es sich bei Ihrer Datei nicht um eine standardmäßige CSV-Datei handelt, da die Felder durch zusätzliche Leerzeichen getrennt sind) und eine Variable marginmit dem Wert 0.2in das awkProgramm importiert.

minInnerhalb des Programms wird der Variablenwert auf den Wert in der 4. Spalte gesetzt, wenn wir uns in Zeile 2 befinden ( FNR==2). Die aktuelle Zeile wird dann nur gedruckt, wenn wir uns entweder in Zeile 1 befinden (der Kopfzeile – falls gewünscht) oder wenn wir uns im Datenteil der Datei befinden und das 4. Feld kleiner als 1+marginmal dem Mindestwert ist.

Antwort2

Dies ist ein ziemlich ausführliches Skript. Verwenden Sie keine Abkürzungen und drucken Sie keine Informationen aus stderr. Was den sh-Teil betrifft, könnten Sie normalerweise Optionen hinzufügen, um die Werte „Globals“ oben festzulegen, sodass Sie zusätzlich zu den Argumenten auch mit Optionen aufrufen können. Beispiel:

my_script --max-factor 0.15 -p 20 --out-file foo.csv *.csv

Filtern Sie also nach rescoredund nach den Prozentsatz der Zeilen. Die ausführlichen Teile können offensichtlich gelöscht werden.

#!/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

Befehlszeilenoptionen

Gemäß Anfrage in den Kommentaren.

Um die Optionen zu erhalten, gibt es zahlreiche Möglichkeiten. Am einfachsten wären Positionsargumente. Zum Beispiel:

Usage: script percent margin <files ...>

Im Skript würde man dann sagen:

percent=$1
margin=$2
shift
shift
... loop files ...

Wenn man es etwas ausgefallener/flexibler haben möchte, könnte man so etwas machen;

Schreiben Sie zuerst eine helpFunktion. Es könnte etwa so aussehen. (Die Verwendung von basenameund $0kann wahrscheinlich besprochen werden):

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"
}

Wobei man es normalerweise mit „as“ aufruft, print_help >&2um auf stderr und nicht auf stdout zu drucken.

Oben helpwird eine halbstandardmäßige Methode verwendet. Es werden keine -abcoder verwendet --foo=123, aber jede Option und jedes Argument muss durch Leerzeichen getrennt sein.

Optional, kein Wortspiel beabsichtigt, schauen Sie sich Beiträge wie an

Eine einfache Möglichkeit für den Rest des Skripts mit einigen naiven Fehlerprüfungen könnte dann sein:


# 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

Man könnte mehr Validierung der Optionen implementieren, also sicherstellen, dass es sich um Zahlen usw. handelt.

verwandte Informationen