2014-12-17 2 views
-1

테이블이 포함 된 두 개의 파일을 병합 할 수있는 스크립트를 찾고 있습니다. 열은 박테리아의 이름을 포함하는 반면 열은 개별 샘플의 세균 수입니다. 일부 박테리아는 하나의 파일에만 있지만 다른 하나의 파일에는 나타나지 않기 때문에 나는 그들을 정렬하고 병합 할 수 없습니다. 이 경우 행을 0으로 채우고 싶습니다. 여기 bash를 사용하여 누락 된 값이있는 두 데이터 테이블 병합

은 예입니다

파일 1

Header       S1 S2 S3 S4 
Acetobacterium submarinus  1350 1000 1541 1541 
Abiotrophia defectiva   100 110 112 166 
Acetobacterium tundrae   2  1  0  0 

파일 2

Header       S5 S6  S7 S8 
Acholeplasma cavigenitalium 100 90 88 120 
Acetobacterium woodii   2  3  4  0 
Acetobacterium submarinus  500 600 400 480 

그리고 결과 파일은

Header       S1 S2 S3 S4 S5 S6  S7 S8 
Abiotrophia defectiva   100 110 112 166  0  0  0  0 
Acetobacterium submarinus  1350 1000 1541 1541 500 600 400 480 
Acetobacterium tundrae   2  1  0  0  0  0  0  0 
Acetobacterium woodii   0  0  0  0  2  3  4  0 
Acholeplasma cavigenitalium 0  0  0  0 100 90 88 120 

어떤 아이디어 (알파벳 순으로 정렬)한다 ?

저는 붙여 넣기 기능이 첫 번째 열에 의해 파일을 병합 할 수 있지만 누락 된 종을 처리하는 방법을 잘 모르겠습니다.

업데이트 다음은 두 가지 샘플 데이터 집합입니다. 열 번호는 원래 데이터 집합과 동일하며 행 수를 줄였습니다. 당신은 -a 1 2, -e '0'-o '0,1.2,1.3,1.4,1.5,2.2,2.3,2.4,2.5' 옵션 join를 사용해야합니다

https://www.dropbox.com/s/h46nwjwwfdyzwqr/Class_Level_Aggregate_Counts-1.csv?dl=0 https://www.dropbox.com/s/x8wtdxl45bej729/Class_Level_Aggregate_Counts-2.csv?dl=0

답변

0

: 당신은 헤더 라인 상단에 있어야 할

join -a 1 -a 2 -e '0' -1 1 -2 1 -o '0,1.2,1.3,1.4,1.5,2.2,2.3,2.4,2.5' -t $'\t' file1 file2 > joinedfile 

join 이후 요구가 입력을 정렬하고, 당신이 이 첫 번째 줄을 제외하고 다음을 정렬하십시오.

sed -n '2,$p' file1unsorted | sort >file1 
sed -n '2,$p' file2unsorted | sort >file2 

그런 다음 정렬 된 파일에 대해 위의 join 명령을 실행하십시오 (열 구분 기호를 지정하는 -t에 주목하십시오 - 사용자가 Tab - 분리 된 파일이라고 가정합니다).

cat headerfile joinedfile >resulfile 

업데이트 :

으로

head -1 file1unsorted | join -1 1 -2 1 -o '0,1.2,1.3,1.4,1.5,2.2,2.3,2.4,2.5' -t $'\t' - <(head -1 file2unsorted) >headerfile 

그리고는 최종 파일 (파일의 나머지 부분에 새로운 헤더를 추가) "조립"

는 별도로 헤더 가입 열 개수에 대한 join의 의존성 (파일에 더 많은 열이있는 경우) : 예, 어느 정도 종속성이 있습니다. 정확하게 말하면, 열 번호는 -1-2 옵션에서 사용됩니다 (두 값은 합치는 각각의 파일에있는 열의 수인 1입니다.) 분명히 총 수에 의존하지 않습니다. 첫 번째 열에 합류하는 한 열). 열 번호는 출력 형식을 지정하는 -o 옵션에도 사용됩니다 (즉, 출력 할 열과 순서는 "file # .column #"이고 형식은 모두 1부터 시작하고 조인에 사용 된 열은 "0"의 특수 구문).이 예에서 지정한 형식은 실제로 기본 형식입니다 (먼저 열에 가입 한 다음 첫 번째 파일의 나머지 열 모두를 따르고 두 번째 파일의 다른 모든 열을 뒤 따름). 그러나 불행히도 우리는 여전히 생략 할 수 없습니다 이 옵션을 사용하려면 -e 옵션이 필요합니다. 귀하의 버전이 join이 아니므로 -o 부분을 생략하고 어떻게되는지보십시오.

+0

설명은 확장 토론이 아닙니다. 이 대화는 [채팅으로 이동되었습니다] (http://chat.stackoverflow.com/rooms/67279/discussion-on-answer-by-striving-coder-merging-two-data-tables-with-missing-valu) . – Taryn

+0

@bluefeet : 시도했지만 OP에서 채팅 할 수있는 담당자가 충분하지 않았습니다. –

0

가끔 무차별 방식은 데이터를 단일 함수로 구부리기가 어려운 경우에 사용됩니다. 다음 Bash 스크립트는 두 개의 데이터 파일을 읽고, 정렬 된 tmp 파일 (/ tmp)로 점진적으로 조작 한 다음 값을 배열로 읽어 들이고 마지막으로 각 고유 이름에 대해 데이터가 두 파일에 모두 존재하면 S1 - S8 값을 결합합니다. 누락 된 값은 0으로 채워집니다. trap 함수는 종료시 임시 파일을 제거합니다. 이 파일은 논리적 인 설명을 돕기 위해 잘 설명되어 있습니다. 참고 이것은 (주로 substing 테스트 연산자로 인한) Bash 솔루션이지만 다른 쉘 환경에도 쉽게 적용 할 수 있습니다. 당신은 질문이 있으면 알려 주시기 :

#!/bin/bash 

## simple error/usage function 
function usage { 
    errno=${2:-0} 
    if test -n "$1" ; then 
     printf "\n %s\n" "$1" 
    fi 
cat >&2 <<TAG 

    Merge two bacterial count data files zeroing non-common columns. 

    Usage: ./${0//*\//} file_1 file_2 

TAG 
    exit $((errno)) 
} 

## semi-random tmp file timestamp 'mmdd????' 
function tstamp { 
    local rd=$(date +%N) 
    printf "%s" "$(date +%m%d)${rd:4:4}" 
} 

## trap function - cleanup temp files 
function cleanup { 
    rm "$tfn1" 
    rm "$tfn2" 
    rm "$tfnall" 
    rm "$tfnuniq" 
} 

## respond to help 
test "$1" = "-h" -o "$1" = "--help" && usage 

## validate input files 
test -z "$1" && usage "error: insufficient input." 1 
test -z "$2" && usage "error: insufficient input." 1 
test -r "$1" || usage "error: invalid input, file not readable '$1'" 1 
test -r "$2" || usage "error: invalid input, file not readable '$2'" 1 

## assign temp file names 
tfn1="/tmp/bmrg_$(tstamp).tmp"  # temp file_1 (sorted w/o header) 
tfn2="/tmp/bmrg_$(tstamp).tmp"  # temp file_2 (sorted w/o header) 
tfnall="/tmp/bmrg_$(tstamp).tmp" # concatenated $tfn1 $tfn2 
tfnuniq="/tmp/bmrg_$(tstamp).tmp" # uniq records $tfn1 $tfn2 

## create $tfn1 $tfn2 & validate 
tail -n+2 "$1" | sort > "$tfn1" 
tail -n+2 "$2" | sort > "$tfn2" 

test -f "$tfn1" || usage "error: failed to create tmp file '${tfn1}'" 1 
test -f "$tfn2" || usage "error: failed to create tmp file '${tfn2}'" 1 

## set trap for cleanup on exit 
trap cleanup EXIT 

## read names from $tfn1 
while read -r name || test -n "$name" ; do 
    name1+=("$name") 
done <<<"$(cut -c -30 "$tfn1")" 
unset name 

## read names from $tfn2 
while read -r name || test -n "$name" ; do 
    name2+=("$name") 
done <<<"$(cut -c -30 "$tfn2")" 
unset name 

## concatenate $tfn1 $tfn2 
printf "%s\n" "${name1[@]}" > "$tfnall" 
printf "%s\n" "${name2[@]}" >> "$tfnall" 

## get unique names 
sort -u "$tfnall" > "$tfnuniq" 

## read $tfn1 values into separate arrays 
while read -r v1 v2 v3 v4 || test -n "$v4" ; do 
    s1+=("$v1") 
    s2+=("$v2") 
    s3+=("$v3") 
    s4+=("$v4") 
done <<<"$(cut -c 31- "$tfn1")" 

## read $tfn2 values into separate arrays 
while read -r v5 v6 v7 v8 || test -n "$v8" ; do 
    s5+=("$v5") 
    s6+=("$v6") 
    s7+=("$v7") 
    s8+=("$v8") 
done <<<"$(cut -c 31- "$tfn2")" 

printf "Header       S1 S2 S3 S4 S5 S6 S7 S8\n" 

## for each unique name in $tfnuniq 
while read -r name || test -n "$name" ; do 

    ## test if found in name1, print values by index, else print 0's 
    found=0 
    for ((i=0; i < ${#name1[@]}; i++)); do 
     test "${name1[i]}" = "$name" && { found=1; break; } 
    done 
    if test "$found" -eq 1 ; then 
     printf "%-30s%-6s%-6s%-6s%-6s" "$name" "${s1[i]}" "${s2[i]}" "${s3[i]}" "${s4[i]}" 
    else 
     printf "%-30s%-6s%-6s%-6s%-6s" "$name" "0" "0" "0" "0" 
    fi 

    ## test if found in name2, print values by index, else print 0's 
    found=0 
    for ((i=0; i < ${#name2[@]}; i++)); do 
     test "${name2[i]}" = "$name" && { found=1; break; } 
    done 
    if test "$found" -eq 1 ; then 
     printf "%-6s%-6s%-6s%-6s\n" "${s5[i]}" "${s6[i]}" "${s7[i]}" "${s8[i]}" 
    else 
     printf "%-6s%-6s%-6s%-6s\n" "0" "0" "0" "0" 
    fi 

done <"$tfnuniq" 

exit 0 

출력 :

$ bash bactmerge.sh dat/bact1.txt dat/bact2.txt 
Header       S1 S2 S3 S4 S5 S6 S7 S8 
Abiotrophia defectiva   100 110 112 166 0  0  0  0 
Acetobacterium submarinus  1350 1000 1541 1541 500 600 400 480 
Acetobacterium tundrae  2  1  0  0  0  0  0  0 
Acetobacterium woodii   0  0  0  0  2  3  4  0 
Acholeplasma cavigenitalium 0  0  0  0  100 90 88 120 

참고 : 스크립트가 귀하의 질문에 제공되는 데이터 파일의 간격에 의존한다. 데이터 파일이 게시 된 것과 다른 경우 (예 : 탭/공백 변환으로 인해) 위의 cut 명령에 제공된 값을 조정할 수 있습니다.

+0

제안 해 주셔서 감사합니다. 나는 대본을 보았습니다. 그러나 출력에서 ​​다른 솔루션과 동일한 문제가 발생합니다. 개별 박테리아가 복제됩니다. 나는. 각 박테리아에는 2 개의 행이 있으며, 개수는 입력 파일이 아니라 다른 분할에 의해 행 사이에 나뉘어집니다. 또한 파일에서 헤더를 읽을 수 있습니까? 위에서 제시 한 내용은 간단한 예일뿐 실제 파일에는 각각 약 100 개의 열이 포함되어 있으며 샘플 이름은 간단하지 않습니다. –

+0

오. 그것은 약간 변경됩니다. 샘플 파일은 비교적 간단했습니다. 실제 데이터 파일 (또는 전체 열이있는 최소 20 줄)에 대한 링크를 게시 할 수 있다면 스크립트를 수정할 수 있습니다. 열쇠는 이름의 길이가 될 것입니다. 일관성이 유지되는 한 무제한의 값을 읽도록 스크립트를 수정하는 것은 어렵지 않습니다. 저는 당신의 예에서 'S1-S8'이라고 생각했습니다. 샘플 데이터를 기준으로 실행하는지 확인할 수 있습니다 - 광고 된대로 작동합니다. ** bash 미묘함이 많이 포함되어 있습니다. 배우면, 소화하기 좋은 것입니다. –

+0

죄송합니다, 제가 언급 했어야합니다. 몇 가지 예제 데이터 파일에 대한 링크를 추가하겠습니다.하지만 약간 잘라내 야합니다. –

관련 문제