Unix에서 임계값을 기준으로 파일에서 줄을 삭제하는 방법은 무엇입니까?

Unix에서 임계값을 기준으로 파일에서 줄을 삭제하는 방법은 무엇입니까?

다음과 같이 다른 열이 있는 입력 파일이 있습니다.

VARIANT,SNP,chr,pos,A1,A2,BETA,P_value           
7:106350628_G_A,rs6977865,7,106350628,G,A,-0.0808873,8.6E-309
7:106353698_T_C,rs74804152,7,106353698,T,C,-0.0808701,9.3E-309
20:57674276_T_A,rs6026699,20,57674276,T,A,-0.0945835,6.0E-314
1:10177_A_AC,rs367896724,1,10177,A,AC,0.000264372,9.3E-01
1:10642_G_A,rs558604819,1,10642,G,A,0.0425225,7.0E-01
2:31467079_G_A,rs2295471,2,31467079,G,A,-0.0830949,8.6E-320

이제 P 값이 2.23E-308보다 작은 행을 삭제하여 다음 출력 파일을 얻으려고 합니다.

VARIANT,SNP,chr,pos,A1,A2,BETA,P_value
1:10177_A_AC,rs367896724,1,10177,A,AC,0.000264372,9.3E-01
1:10642_G_A,rs558604819,1,10642,G,A,0.0425225,7.0E-01

Unix 셸에서 다음 명령을 실행했습니다.

awk -F, '$8!"<2.23E-308"' input.file > output.file

하지만 여전히 모든 줄이 포함된 첫 번째 입력 파일이 있습니다.

순서가 잘못됐나요? 설정된 임계값 인식에 문제가 있는 것은 아닐까요?

저는 리눅스를 사용하고 있습니다.

답변1

귀하의 표현이 정확하지 않습니다.

a >= b

또는 (원하는 경우)

!(a < b)

대신에 a!"<b".

그러나 특정 경우에는 더 미묘한 문제가 있습니다. 즉, 숫자 값이 배정밀도(64비트) 부동 소수점 숫자로 표현될 수 있는 가장 작은 값보다 작다는 것입니다.

gawk-MGNU MPFR/MP 라이브러리로 빌드된 GNU awk() 버전이 있는 경우 또는 --bignum명령줄 옵션을 통해 임의 정밀도 처리를 활성화해야 할 수도 있습니다 .

$ gawk -F, -M '$8 >= 2.23E-308' input.file
VARIANT,SNP,chr,pos,A1,A2,BETA,P_value
1:10177_A_AC,rs367896724,1,10177,A,AC,0.000264372,9.3E-01
1:10642_G_A,rs558604819,1,10642,G,A,0.0425225,7.0E-01

그렇지 않은 경우 가능한 해결 방법은 비교하기 전에 숫자 변환을 강제하는 것입니다.

$ mawk -F, '$8+0 >= 2.23E-308' input.file
1:10177_A_AC,rs367896724,1,10177,A,AC,0.000264372,9.3E-01
1:10642_G_A,rs558604819,1,10642,G,A,0.0425225,7.0E-01

$ awk -F, '$8+0 >= 2.23E-308' input.file
1:10177_A_AC,rs367896724,1,10177,A,AC,0.000264372,9.3E-01
1:10642_G_A,rs558604819,1,10642,G,A,0.0425225,7.0E-01

그러나 이렇게 하면 IEEE 이중 범위 밖의 값이 강제로 0으로 설정됩니다(처음에는 문자열로 변환되고 문자열의 숫자 값은 0이기 때문입니다).

헤더 행도 원하는 경우 별도의 논리 테스트로 추가하세요.

awk -F, 'NR==1 || $8+0 >= 2.23E-308' input.file
VARIANT,SNP,chr,pos,A1,A2,BETA,P_value
1:10177_A_AC,rs367896724,1,10177,A,AC,0.000264372,9.3E-01
1:10642_G_A,rs558604819,1,10642,G,A,0.0425225,7.0E-01

답변2

여기서는 과학적 표기법으로 숫자를 분리하고 지수와 가수를 각각 비교하여 임계값과의 비교를 도출합니다.

awk -F ',' -v threshold=2.23E-308 '
BEGIN {
  split(threshold, t, /[Ee]/)
   pwrThreshold = t[2]
   numThreshold = t[1]
}
NR>1 {
  num = $8 ~ /[Ee]/ ? $8   \
      : sprintf("%0.6E", $8)
  split(num, a, /[Ee]/)
  pwr = a[2]
  num = a[1]
  gr8 = pwr > pwrThreshold ? 1 \
      : pwr < pwrThreshold ? 0 \
      : num > numThreshold ? 1 \
      : 0;
}
gr8||NR==1
' file.csv

결과:-

VARIANT,SNP,chr,pos,A1,A2,BETA,P_value
1:10177_A_AC,rs367896724,1,10177,A,AC,0.000264372,9.3E-01
1:10642_G_A,rs558604819,1,10642,G,A,0.0425225,7.0E-01
2:31467079_G_A,rs2295471,2,31467079,G,A,-0.0830949,8.60

답변3

perl대신 사용하십시오 awk:

$ perl -F, -lane 'print if ($F[7] >= 2.23E-308 || $. == 1)' input.csv 
VARIANT,SNP,chr,pos,A1,A2,BETA,P_value
1:10177_A_AC,rs367896724,1,10177,A,AC,0.000264372,9.3E-01
1:10642_G_A,rs558604819,1,10642,G,A,0.0425225,7.0E-01

노트:

  1. Perl 배열은 1이 아닌 0에서 시작하며 $F[7]각 입력 줄의 8번째 필드도 마찬가지입니다( $8awk와 동일).
  2. $.이는 NRawk의 입력 행 번호와 동일합니다.
  3. 스크립트에서 임의의 정밀도 부동 소수점 계산도 수행해야 한다면 아마도 Perl의큰::부동기준 치수.

답변4

사용행복하다(이전 Perl_6)

raku -e 'put get; for lines() {.put if .split(",").[7] >= 2.23e-308};' 

get위는 헤더 줄을 가져와 put인쇄(인쇄)한 다음 호출을 통해 for lines()파일의 나머지 부분을 한 줄씩 처리하는 답변입니다 .

  • split연속된 각 줄은 쉼표로 구분됩니다.
  • 7인덱스가 0인 번째 필드를 꺼내고
  • 숫자 비교를 수행하여 if기준에 맞는 행을 확인하세요.

입력 예:

VARIANT,SNP,chr,pos,A1,A2,BETA,P_value           
7:106350628_G_A,rs6977865,7,106350628,G,A,-0.0808873,8.6E-309
7:106353698_T_C,rs74804152,7,106353698,T,C,-0.0808701,9.3E-309
20:57674276_T_A,rs6026699,20,57674276,T,A,-0.0945835,6.0E-314
1:10177_A_AC,rs367896724,1,10177,A,AC,0.000264372,9.3E-01
1:10642_G_A,rs558604819,1,10642,G,A,0.0425225,7.0E-01
2:31467079_G_A,rs2295471,2,31467079,G,A,-0.0830949,8.6E-320

예제 출력:

VARIANT,SNP,chr,pos,A1,A2,BETA,P_value           
1:10177_A_AC,rs367896724,1,10177,A,AC,0.000264372,9.3E-01
1:10642_G_A,rs558604819,1,10642,G,A,0.0425225,7.0E-01

Raku에서 원하는 결과를 얻는 다른 방법이 있습니다. 다음은 @cas가 게시한 Perl(5) 솔루션과 유사한 코드입니다( ||교대 요소는 오류를 방지하기 위해 역순입니다 Cannot convert string to number).

raku -ne 'state $i=0; ++$i; .put if ( $i == 1 || .split(",").[7] >= 2.23e-308 );' 

lines또 다른 방법은 Raku 와 루틴을 사용하는 것입니다 grep.

raku -e 'put get; .put for lines.grep( {.split(",").[7] >= 2.23E-308} );' 

마지막으로, "간단한" 구현을 원하는 경우 헤더를 수동으로 제거하고 다음 코드를 실행합니다. 원하는 데이터 행을 얻을 수 있습니다(아마도 헤더를 수동으로 추가할 수 있습니다).

raku -ne '.put if .split(",").[7] >= 2.23e-308;' 

https://raku.org

관련 정보