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

내가 지금까지 무엇을 했는지

awkbashCSV에서 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에 보다 선택적인 필터를 적용하려면 코드를 어떻게 수정해야 합니까 ? 예를 들어, 네 번째 열(in)의 값(부동 소수점 수)을 기준으로 데이터를 필터링합니다 dG(rescored).

minForth = 0.421573예를 들어, 가장 낮은 값(항상 두 번째 행에 있음 )을 참조로 사용 하고 선택한 임계값(예: 20% 초과 ) $4보다 작은 CSV의 모든 행을 인쇄해야 합니다.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.2으로 가져옵니다.awk

프로그램 내에서 min2행( )에 있는 경우 FNR==2변수 값을 4열의 값으로 설정합니다. 라인 1(헤더 - 필요한 경우)에 있거나 파일의 데이터 섹션에 있고 네 번째 필드가 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 >&2stdout 대신 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

옵션에 대해 더 많은 유효성 검사를 수행할 수 있습니다. 즉, 숫자인지 확인하는 등의 작업을 수행할 수 있습니다.

관련 정보