다음과 같이 다른 열이 있는 입력 파일이 있습니다.
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
-M
GNU 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
노트:
- Perl 배열은 1이 아닌 0에서 시작하며
$F[7]
각 입력 줄의 8번째 필드도 마찬가지입니다($8
awk와 동일). $.
이는NR
awk의 입력 행 번호와 동일합니다.- 스크립트에서 임의의 정밀도 부동 소수점 계산도 수행해야 한다면 아마도 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;'