하나의 열을 기준으로 3개 파일의 공통 행을 추출하는 방법이 있나요?

하나의 열을 기준으로 3개 파일의 공통 행을 추출하는 방법이 있나요?

공백으로 구분된 3개의 파일이 있으며 약 340만 개의 행이 있습니다(그러나 행 수가 정확히 같지 않고 "태그" 열을 기준으로 정렬되어 있습니다). 그것들은 다음과 같습니다:

  • head neu1
       Chr        BP          Marker      MAF A1 A2 Direction   pValue    N
       1 100000012 1:100000012:G:T 0.229925  T  G         + 0.650403 1594
       1 100000827 1:100000827:C:T 0.287014  T  C         + 0.955449 1594
       1 100002713 1:100002713:C:T 0.097867  T  C         - 0.290455 1594
       1 100002882 1:100002882:T:G 0.287014  G  T         + 0.955449 1594
       1 100002991 1:100002991:G:A 0.097867  A  G         - 0.290455 1594
       1 100004726 1:100004725:G:A 0.132058  A  G         + 0.115005 1594
    
  • head nep1
       Chr        BP          Marker       MAF A1 A2 Direction    pValue    N
       1 100000012 1:100000012:G:T 0.2300430  T  G         - 0.1420030 1641
       1 100000827 1:100000827:C:T 0.2867150  T  C         - 0.2045580 1641
       1 100002713 1:100002713:C:T 0.0975015  T  C         - 0.0555507 1641
       1 100002882 1:100002882:T:G 0.2867150  G  T         - 0.2045580 1641
       1 100002991 1:100002994:G:A 0.0975015  A  G         - 0.0555507 1641
       1 100004726 1:100004726:G:A 0.1325410  A  G         - 0.8725660 1641
    
  • head ret1
       Chr        BP          Marker       MAF A1 A2 Direction   pValue    N
       1 100000012 1:100000012:G:T 0.2322760  T  G         - 0.230383 1608
       1 100000827 1:100000827:C:T 0.2882460  T  C         - 0.120356 1608
       1 100002713 1:100002713:C:T 0.0982587  T  C         - 0.272936 1608
       1 100002882 1:100002883:T:G 0.2882460  G  T         - 0.120356 1608
       1 100002991 1:100002991:G:A 0.0982587  A  G         - 0.272936 1608
       1 100004726 1:100004726:G:A 0.1340170  A  G         - 0.594538 1608
    

"표시" 열(세 번째 열)의 값 이 3개의 원본 파일 모두에 대해 동일한 행만 포함하는 neu23 개의 파일을 더 생성하는 방법이 있습니까 ?nep2ret2

출력 파일에는 초기 파일의 9개 열이 모두 포함되어야 한다는 점을 언급해야 합니다.

예상되는 출력은 다음 3개 파일입니다.

  • head neu2
       Chr        BP          Marker      MAF A1 A2 Direction   pValue    N
       1 100000012 1:100000012:G:T 0.229925  T  G         + 0.650403 1594
       1 100000827 1:100000827:C:T 0.287014  T  C         + 0.955449 1594
       1 100002713 1:100002713:C:T 0.097867  T  C         - 0.290455 1594
    
  • head nep1
       Chr        BP          Marker       MAF A1 A2 Direction    pValue    N
       1 100000012 1:100000012:G:T 0.2300430  T  G         - 0.1420030 1641
       1 100000827 1:100000827:C:T 0.2867150  T  C         - 0.2045580 1641
       1 100002713 1:100002713:C:T 0.0975015  T  C         - 0.0555507 1641
    
  • head ret1
       Chr        BP          Marker       MAF A1 A2 Direction   pValue    N
       1 100000012 1:100000012:G:T 0.2322760  T  G         - 0.230383 1608
       1 100000827 1:100000827:C:T 0.2882460  T  C         - 0.120356 1608
       1 100002713 1:100002713:C:T 0.0982587  T  C         - 0.272936 1608
    

3개 파일 각각의 헤더는 초기 파일과 결과 파일에 각각 9개씩 있습니다.

Chr  BP  Marker MAF  A1  A2    Direction   pValue    N

답변1

이는 빠르고 효율적이어야 합니다. 그것은 join(의 일부를 사용합니다GNU 핵심 도구) 및 field(의 일부탱고 도구):

# This finds the matching lines and makes it into a big file with 25 columns
join -1 3 -2 3 neu1 nep1 | join -1 1 -2 3 - ret1 |
  # Now split the right columns
  # field is https://gitlab.com/ole.tange/tangetools/-/tree/master/field
  tee > >(field 2-3,1,4-9 > neu2) >(field 10-11,1,12-17 > nep2) >(field 18-19,1,20-25 > ret2)

답변2

$ cat ../tst.awk
ARGIND < 4 {
    if ( FNR == 1 ) {
        ARGV[ARGC] = FILENAME
        ARGC++
    }
    if ( !seen[ARGIND,$3]++ ) {
        cnt[$3]++
    }
    next
}
FNR == 1 {
    close(out)
    out = FILENAME
    sub(/1/,"2",out)
}
cnt[$3] == 3 {
    print $0 " > " out
}

.

$ awk -f ../tst.awk neu1 nep1 ret1
Chr        BP          Marker      MAF A1 A2 Direction   pValue    N > neu2
1 100000012 1:100000012:G:T 0.229925  T  G         + 0.650403 1594 > neu2
1 100000827 1:100000827:C:T 0.287014  T  C         + 0.955449 1594 > neu2
1 100002713 1:100002713:C:T 0.097867  T  C         - 0.290455 1594 > neu2
Chr        BP          Marker       MAF A1 A2 Direction    pValue    N > nep2
1 100000012 1:100000012:G:T 0.2300430  T  G         - 0.1420030 1641 > nep2
1 100000827 1:100000827:C:T 0.2867150  T  C         - 0.2045580 1641 > nep2
1 100002713 1:100002713:C:T 0.0975015  T  C         - 0.0555507 1641 > nep2
Chr        BP          Marker       MAF A1 A2 Direction   pValue    N > ret2
1 100000012 1:100000012:G:T 0.2322760  T  G         - 0.230383 1608 > ret2
1 100000827 1:100000827:C:T 0.2882460  T  C         - 0.120356 1608 > ret2
1 100002713 1:100002713:C:T 0.0982587  T  C         - 0.272936 1608 > ret2

테스트 완료 후 실제로 필요한 출력 파일을 생성 print $0 " > " out하도록 변경합니다 . print > out위의 내용은 에 대해 GNU awk를 사용합니다 ARGIND. GNU awk가 없으면 FNR==1 { ARGIND++ }스크립트의 첫 번째 줄에 추가하면 됩니다.

답변3

이것은 참으로 좋은 질문입니다. 파일이 세 번째 열을 기준으로 오름차순으로 정렬되어 있다고 확신하는 경우 SQL World의 병합 조인에 유사한 접근 방식을 사용하는 것이 좋습니다. 파일 정렬은 bash에서 문자열을 비교하는 데 사용되는 것과 동일한 정렬 기능을 사용하여 수행되어야 합니다. 아래 스크립트에는 bash가 필요합니다. 다음을 통해 호출해야 합니다.

$ script file1 file2 file3

3개의 출력 파일(file1.out, file2.out, file3.out)이 생성됩니다. 여기에는 모든 입력 파일에 있는 세 번째 열에 값이 있는 해당 소스 파일의 행이 포함되어 있습니다. 해당 값을 가진 모든 행은 ".out" 파일에 있습니다.

#!/bin/bash
Fil1="$1"
Fil2="$2"
Fil3="$3"
# Opening file descriptors  for reading
exec 10<"$Fil1"
exec 20<"$Fil2"
exec 30<"$Fil3"
# Open output files
exec 11>"$Fil1".out
exec 21>"$Fil2".out
exec 31>"$Fil3".out
# First line is copied without changes.
IFS= read -r -u 10 Line1
printf "%s\n" "$Line1" >&11 
IFS= read -r -u 20 Line2
printf "%s\n" "$Line2" >&21 
IFS= read -r -u 30 Line3
printf "%s\n" "$Line3" >&31 
# Prepare to walk through files, searching for identical values in the third column.
IFS= read -r -u 10 Line1
if [ $? -eq 0 ] ; then RC1=0; else RC1=1; fi
read -r F11 F12 F13 F1 <<<  "$Line1" 

IFS= read -r -u 20 Line2
if [ $? -eq 0 ] ; then RC2=0; else RC2=1; fi
read -r F21 F22 F23 F2 <<<  "$Line2" 

IFS= read -r -u 30 Line3
if [ $? -eq 0 ] ; then RC3=0; else RC3=1; fi
read -r F31 F32 F33 F3 <<<  "$Line3" 

while [ $RC1 -eq 0 ] && [ $RC2 -eq 0 ] && [ $RC3 -eq 0 ]
do
  while [ "$F23" \< "$F13" ]
  do
    IFS= read -r -u 20 Line2
    if [ $? -eq 0 ] ; then RC2=0; else RC2=1; break; fi
    read -r F21 F22 F23 F2 <<<  "$Line2" 
  done
  if [ $RC2 -ne 0 ]; then break; fi
  if [ "$F23" = "$F13" ]
  then
    FFF="$F23"  
    while [ "$F33" \< "$FFF" ]
    do
      IFS= read -r -u 30 Line3
      if [ $? -eq 0 ] ; then RC3=0; else RC3=1; break; fi
      read -r F31 F32 F33 F3 <<<  "$Line3" 
    done
    if [ $RC3 -ne 0 ]; then break; fi
    if [ "$F33" = "$FFF" ]
    then
      while [ "$F23" = "$FFF" ]
      do
        printf "%s\n" "$Line2" >&21 
        IFS= read -r -u 20 Line2
        if [ $? -eq 0 ] ; then RC2=0; else RC2=1; break; fi
        read -r F21 F22 F23 F2 <<<  "$Line2" 
      done
      while [ "$F13" = "$FFF" ]
      do
        printf "%s\n" "$Line1" >&11 
        IFS= read -r -u 10 Line1
        if [ $? -eq 0 ] ; then RC1=0; else RC1=1; break; fi
        read -r F11 F12 F13 F1 <<<  "$Line1" 
      done
      while [ "$F33" = "$FFF" ]
      do
        printf "%s\n" "$Line3" >&31 
        IFS= read -r -u 30 Line3
        if [ $? -eq 0 ] ; then RC3=0; else RC3=1; break; fi
        read -r F31 F32 F33 F3 <<<  "$Line3" 
      done
      if [ $RC1 -ne 0 ] || [ $RC2 -ne 0 ] || [ $RC3 -ne 0 ]; then break; fi
    fi
  fi  
  while [ "$F33" \> "$F23" ]
  do
    IFS= read -r -u 20 Line2
    if [ $? -eq 0 ] ; then RC2=0; else RC2=1; break; fi
    read -r F21 F22 F23 F2 <<<  "$Line2" 
  done
  while [ "$F23" \> "$F13" ]
  do
    IFS= read -r -u 10 Line1
    if [ $? -eq 0 ] ; then RC1=0; else RC1=1; break; fi
    read -r F11 F12 F13 F1 <<<  "$Line1" 
  done
done
# close files 
exec 10>&-
exec 11>&-
exec 20>&-
exec 21>&-

스크립트는 파일을 한 번만 읽고 앞으로 번갈아 가며 읽습니다. 따라서 모든 입력 파일을 동시에 열어야 합니다. 이는 bash에서 가능하지만 쉘은 이러한 종류의 처리에 적합한 도구가 아닙니다. 큰 입력 파일의 경우 C로 작성하면 더 나은 결과를 얻을 수 있습니다. 또한 코드에 더 나은 구조를 부여하고 프로세스를 완전히 제어하는 ​​것이 더 쉽습니다.

관련 정보