1つの列に基づいて3つのファイルの共通行を抽出する方法はありますか?

1つの列に基づいて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番目の列)の値が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

これは本当に良い質問です。ファイルが3番目の列に基づいて昇順にソートされていると確信している場合は、SQL Worldのマージ結合に同様のアプローチを使用することをお勧めします。ファイルのソートは、bashで文字列を比較するために使用されるのと同じソート機能を使用して実行する必要があります。以下のスクリプトにはbashが必要です。以下を介して呼び出す必要があります。

$ script file1 file2 file3

3つの出力ファイル(file1.out、file2.out、file3.out)が生成されます。これには、すべての入力ファイルの3番目の列に値を持つ対応するソースファイルの行が含まれています。その値を持つすべての行は「.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で書くとより良い結果が得られます。また、コードにより良い構造を与え、プロセスを完全に制御する方が簡単です。

関連情報