범용 IDS를 사용하여 여러 파일 병합

범용 IDS를 사용하여 여러 파일 병합

파일이 많고 1열의 공통 ID를 사용하여 파일을 병합하고 싶습니다.

파일 1:

MYORGANISM_I_05140.t1   Atypical/PIKK/FRAP
MYORGANISM_I_06518.t1   CAMK/MLCK
MYORGANISM_I_00854.t1   TK-assoc/SH2/SH2-R
MYORGANISM_I_12755.t1   TK-assoc/SH2/Unique

파일 2:

MYORGANISM_I_05140.t1   VALUES to be taken
MYORGANISM_I_12766.t1   what

파일 3:

MYORGANISM_I_16941.t1   OK
MYORGANISM_I_93484.t1   LET IT BE

많은 파일을 병합하고 값이 누락된 경우 "-NA-"를 추가하고 싶습니다. 원하는 출력은 다음과 같습니다.

MYORGANISM_I_05140.t1   Atypical/PIKK/FRAP  VALUES to be taken  -NA-
MYORGANISM_I_06518.t1   CAMK/MLCK   -NA-    -NA-
MYORGANISM_I_00854.t1   TK-assoc/SH2/SH2-R  -NA-    -NA-
MYORGANISM_I_12755.t1   TK-assoc/SH2/Unique -NA-    -NA-
MYORGANISM_I_12766.t1   -NA-    what    -NA-
MYORGANISM_I_16941.t1   -NA-    -NA-    OK
MYORGANISM_I_93484.t1   -NA-    -NA-    LET IT BE

답변1

단일 도구로는 불가능할 수도 있습니다. 이는 sort두 개의 임시 외부 파일 호출과 관련된 스크립트 기반 제안입니다 .

#!/bin/bash

# The number of columns is equal to the number of input files, which is
# equal to the number of command-line arguments.
NUMCOLS=$#


# Use associative container to record all "IDs" and associated fields
declare -A entries
col=0


# Read the fields from all files and store them so that the field values can be
# associated with the file they came from (= the column they belong).
for FILE in "$@"
do
    while read id value
    do
        SORTKEY="$id"__"$col"
        entries[$SORTKEY]="$value"
        echo "$id" >> "tmp.ids"
    done < $FILE
    let col=$col+1
done

# Sort the IDs
sort -u "tmp.ids" > "tmp.ids.sorted"


# Read the sorted IDs back in and generate output lines, where the
# column fields are taken from the associative container "entries" and
# tab-separated.
# If "entries" doesn't contain a value for a given key, output "-NA-" instead.

while read id
do
    LINE="$id"
    for (( col=0; col<NUMCOLS; col++ ))
    do
        SORTKEY="$id"__"$col"
        if [[ -z "${entries[$SORTKEY]}" ]]
        then
            LINE=$(printf "%s\t-NA-" "$LINE")
        else
            LINE=$(printf "%s\t%s" "$LINE" "${entries[$SORTKEY]}")
        fi
    done
    echo "$LINE" >> "outfile.txt"
done < "tmp.ids.sorted"

rm tmp.ids tmp.ids.sorted

이것을 호출할 수 있습니다 ./sortscript.sh <file1> <file2> ... <fileN>.

그러면 연관 컨테이너가 생성되고 entries"ID" 필드 및 열 번호에서 생성된 키 아래에 입력 파일에서 읽은 모든 필드가 저장됩니다. ID는 정렬이 가능하도록 외부 파일에 기록되는데 tmp.ids, 이는 원하는 대로 보입니다.

정렬 후 ID를 다시 읽습니다. 그런 다음 각 ID에 대해 컨테이너에서 해당 키에 속하는 사용 가능한 모든 필드를 읽고 entries출력 행(변수 LINE)에 배치합니다. 특정 ID/열 조합에 사용할 수 있는 값이 없으면 -NA-대신 작성하세요.

그런 다음 출력 행이 파일에 기록됩니다 outfile.txt.

답변2

join유틸리티를 두 번 사용하여 세 개의 파일에 대해 두 개의 "외부 조인"을 생성할 수 있습니다. 세 파일이 모두 탭으로 구분되어 있다고 가정하고 처음 두 파일부터 시작합니다.

$ join -a 1 -a 2 -o 0,1.2,2.2 -e '-NA-' -t $'\t' <( sort File1 ) <( sort File2 )
MYORGANISM_I_05140.t1   Atypical/PIKK/FRAP      VALUES to be taken
MYORGANISM_I_06518.t1   CAMK/MLCK       -NA-
MYORGANISM_I_00854.t1   TK-assoc/SH2/SH2-R      -NA-
MYORGANISM_I_12755.t1   TK-assoc/SH2/Unique     -NA-
MYORGANISM_I_12766.t1   -NA-    what

이를 위해서는 join유틸리티가 첫 번째 필드(기본값)에서 정렬된 파일을 결합해야 합니다. 우리는 -a 1 -a2일치하지 않더라도 두 파일에서 모든 행을 명시적으로 가져오기를 원하며 -o 0,1.2,2.2출력에 각 파일의 조인 필드(첫 번째 열)와 두 번째 열이 포함되도록 요청합니다. 이 -e '-NA-'옵션은 빈 필드를 채우는 데 사용할 문자열을 지정합니다.

위의 내용은 세 번째 파일에 대한 두 번째 연결에서 사용할 수 있는 새로운 데이터 세트를 제공합니다. 단순화를 위해 위의 결과를 tmpdata사용할 수 있다고 가정합니다(리디렉션 후).

$ join -a 1 -a 2 -o 0,1.2,1.3,2.2 -e '-NA-' -t $'\t' tmpdata <( sort FILE3 )
MYORGANISM_I_00854.t1   TK-assoc/SH2/SH2-R      -NA-    -NA-
MYORGANISM_I_05140.t1   Atypical/PIKK/FRAP      VALUES to be taken      -NA-
MYORGANISM_I_06518.t1   CAMK/MLCK       -NA-    -NA-
MYORGANISM_I_12755.t1   TK-assoc/SH2/Unique     -NA-    -NA-
MYORGANISM_I_12766.t1   -NA-    what    -NA-
MYORGANISM_I_16941.t1   -NA-    -NA-    OK
MYORGANISM_I_93484.t1   -NA-    -NA-    LET IT BE

이는 이전의 "외부 조인"을 어느 정도 반복하지만 -o옵션이 있는 추가 열도 추가합니다.

답변3

셸(/bin/sh 또는 /bin/bash)에서 실행되는 코드 줄:

FILES="File1 File2 FILE3"; LIST=$(for F in ${FILES}; do cat ${F}|awk '{print $1}'; done|sort|uniq|xargs); for i in ${LIST}; do echo -n "$i"; for F in ${FILES}; do L=$(grep "^${i}\s" ${F}|head -1|sed 's/\t/ /'|cut -d' ' -f 2-|sed 's/^\s*//g'); [ -z "${L}" ] && echo -n " -NA-" || echo -n " ${L}" ; done; echo; done|sort

산출:

MYORGANISM_I_00854.t1 TK-assoc/SH2/SH2-R -NA- -NA-
MYORGANISM_I_05140.t1 Atypical/PIKK/FRAP VALUES to be taken -NA-
MYORGANISM_I_06518.t1 CAMK/MLCK -NA- -NA-
MYORGANISM_I_12755.t1 TK-assoc/SH2/Unique -NA- -NA-
MYORGANISM_I_12766.t1 -NA- what -NA-
MYORGANISM_I_16941.t1 -NA- -NA- OK
MYORGANISM_I_93484.t1 -NA- -NA- LET IT BE

설명하다:

# create list of files
# it can be created based on search like
# find . -type f -name filename.txt
# or something different
FILES="File1 File2 FILE3";
# create a list if unique first lines from all files from the list FILES
LIST=$(for F in ${FILES}; do cat ${F}|awk '{print $1}'; done|sort|uniq|xargs);
# take one by one each first line
# and go through all the files find corresponding lines endings
# and put them together
# or take '-NA-' for non-existing
for i in ${LIST}; do
    echo -n "$i";
    for F in ${FILES}; do
        #
        # old version of line commented out
        # L=$(grep "^${i}\s" ${F}|head -1|cut -d' ' -f 2-|sed 's/^\s*//g');
        # new version of line to make tab separator working
        L=$(grep "^${i}\s" ${F}|head -1|sed 's/\t/ /'|cut -d' ' -f 2-|sed 's/^\s*//g');
        #
        [ -z "${L}" ] && echo -n " -NA-" || echo -n " ${L}" ;
    done;
    echo;
done|sort
# sorted results printed

관련 정보