열 값이 동일하지 않은 경우 해당 행만 인쇄하는 방법은 무엇입니까?

열 값이 동일하지 않은 경우 해당 행만 인쇄하는 방법은 무엇입니까?

유전자형 계산을 위한 대용량 입력 파일이 있습니다. 처음 몇 줄은 다음과 같습니다.

LocusID f nAlleles x y
2L:8347 1 2 44.3166 -12.2373
2L:8347 1 2 39.2667 -6.8333
2L:31184 1 2 39.2667 -6.8333
2L:31184 1 2 39.2667 -6.8333
2L:42788 1 2 39.2667 -6.8333
2L:42788 1 2 39.2667 -6.8333
2L:42887 1 2 39.2667 -6.8333
2L:42887 1 2 39.2667 -6.8333

첫 번째 열은 사이트 ID이고 각 사이트마다 동일한 사이트 ID를 가진 두 개의 행이 있습니다. x 열과 y 열이 각 궤적에 대해 자격이 없는 열만 유지하고 싶습니다.

이것은 위의 예에서 내가 원하는 결과입니다.

out
2L:8347 1 2 44.3166 -12.2373
2L:8347 1 2 39.2667 -6.8333

어떻게 할 수 있는지 아시나요?

답변1

$ cat tst.awk
NR == 1 {
    for (i=1; i<=NF; i++) {
        f[$i] = i
    }
    next
}
NR % 2 {
    split(prev,p)
    if ( ( p[f["x"]] != $(f["x"]) ) || ( p[f["y"]] != $(f["y"]) ) ) {
        print prev
        print
    }
}
{ prev = $0 }

$ awk -f tst.awk file
2L:8347 1 2 44.3166 -12.2373
2L:8347 1 2 39.2667 -6.8333

답변2

테스트되었습니다. 열 헤더가 파일에 없으면 BEGIN 줄을 삭제합니다.

#! /bin/bash

AWK='''
BEGIN { getline; }
{
    R1 = $0; getline R2;
    split (R1, V1); split (R2, V2);
    if (V1[4] != V2[4] || V1[5] != V2[5]) {
        print R1; print R2;
    }
}
'''

    awk "${AWK}" myFile

답변3

아마도 Ed Morton에 대한 리뷰이지만 너무 방대하고 아마도 더 일반적인 관심을 끌 것입니다.

나는 두 개의 도움이 되지 않는 댓글을 보고 작업을 시작할 수 없는 OP를 위해 간단한 5분 작업을 작성했습니다. 다음과 같이 구토를 유발하는 대안이 포함된 귀하의 참고자료에는 그다지 관심이 없습니다.

awk 'c&&!--c;/3/{c=5}/4/{print "Eureka!"}' file

나는 더 나은 기술이 가능하다는 점을 지적하고 코드를 게시하고 테스트했습니다.

물론 여기서는 getline이 필요하지 않지만 작업에 어느 정도 대칭성을 제공합니다. awk 루프에 의존하는 경우 FNR % 2 == 1과 같은 것을 사용하여 엄격하게 교대하는 라인을 처리하게 됩니다. 이것은 짜증나므로 일반성을 위해 OP "두 행"을 무시하고 행을 LocusID별로 그룹화하는 것이 좋습니다.

나는 awk 스크립트가 더 잘 읽기 때문에 쉘 변수에 게시함으로써 그 스크립트를 품위있게 만듭니다. 나는 명령줄에 많은 awk 코드가 있고 이상한 곳에 접혀 있는 것을 싫어합니다. 또한 이는 전체 awk가 ps -ef에 표시되고 출력을 손상시키기 때문에 시스템 관리자에게 영향을 미칩니다(SunOS에서는 최대 행 길이가 고정되어 있기 때문에 ps를 충돌시키는 데 사용됨).

Bash에서 이를 방지하기 위한 나의 일반적인 관용구는 다음과 같습니다.

awk -f <( echo "${AWK}" )

ps는 이를 awk -f /dev/fd/63으로 나열하여 호기심으로부터 내 독점 코드를 편리하게 숨깁니다.

나는 awk에서 쉘 명령을 분리하지 않습니다. 나는 .awk 파일을 사용하지 않습니다. 두 가지 나쁜 일 중 하나가 발생합니다.

(a) .awk 파일을 제공하고 사용자가 awk 명령(아마도 "-F|" 또는 다른 옵션 포함)을 입력할 것으로 예상하면 오류가 발생합니다.

(b) 사용자에게 .sh 및 .awk를 제공했지만, 파일 간에 불일치가 발생하는 업데이트 문제가 있습니다.

마찬가지로 매뉴얼 페이지를 제공해야 하는 경우 여기에 문서가 포함된 "Usage" 및 "Help"라는 함수로 이를 스크립트 자체에 포함시킬 것입니다.

나는 '''...'''가 필요하지 않다는 것을 잘 알고 있습니다. 그러나 저는 작은 따옴표 솔루션을 게시하는 것에 지쳤습니다. 사람들이 "거기 빠진 인용문이 있으니 수정하겠습니다"라고 생각하게 하고 테스트 게시물을 망친 다음 작동하지 않는다고 불평하게 만드는 것입니다. 몇 개의 빈 문자열로 난독화하면 사람들이 함부로 다루는 것을 방지하고 가시성이 향상됩니다.

40년 동안 Unix를 사용해 보니 쉘과 awk의 예약 변수에 대해 알게 되었습니다. 나는 좋은 명명 규칙을 가지고 있지만 작은 질문의 경우 OP가 직관적으로 사용할 수 있는 용어를 사용하는 것을 선호합니다. 두 개의 선, 두 개의 벡터. 나는 가시성을 높이고 키워드와 구별하기 위해 많은 쉘 변수에 대문자를 사용합니다. 누구나 키워드를 작성할 수 있습니다. 거의 모든 오류는 코더가 데이터를 보지 못하고 변수의 역할을 강조하지 않기 때문에 발생합니다.

Getline은 무엇을 기대해야 할지 알고 있기 때문에 예상치 못한 방식으로 실패하지 않습니다. 내가 사용하는 관용구가 마음에 들지 않을 수도 있지만 그것이 틀렸다는 의미는 아닙니다. 그것은 제가 수년에 걸쳐 보아온 문제에 대한 해결책입니다.

답변4

내 제안: 공백을 쉼표로 바꾸어 CSV를 만든 다음데이터베이스에 로드합니다.

psql다음 과 같이 Postgres와 제공한 파일을 사용합니다 temp.csv.

postgres=> create temp table d (locusid text, f int, n_alleles int, x float, y float);
CREATE TABLE
postgres=> \copy d from program 'tr " " , < temp.csv' with (format csv, header true)
COPY 8
postgres=> table d;
 locusid  | f | n_alleles |    x    |    y     
----------+---+-----------+---------+----------
 2L:8347  | 1 |         2 | 44.3166 | -12.2373
 2L:8347  | 1 |         2 | 39.2667 |  -6.8333
 2L:31184 | 1 |         2 | 39.2667 |  -6.8333
 2L:31184 | 1 |         2 | 39.2667 |  -6.8333
 2L:42788 | 1 |         2 | 39.2667 |  -6.8333
 2L:42788 | 1 |         2 | 39.2667 |  -6.8333
 2L:42887 | 1 |         2 | 39.2667 |  -6.8333
 2L:42887 | 1 |         2 | 39.2667 |  -6.8333
(8 rows)

postgres=> select d.* from d join d as d2 on d.locusid = d2.locusid and (d.x != d2.x or d.y != d2.y);
 locusid | f | n_alleles |    x    |    y     
---------+---+-----------+---------+----------
 2L:8347 | 1 |         2 | 44.3166 | -12.2373
 2L:8347 | 1 |         2 | 39.2667 |  -6.8333
(2 rows)

관련 정보