1열의 값을 기준으로 행을 병합하고 싶습니다. 내 데이터는 다음과 같습니다(탭으로 구분된 열).
OG FC_AG_NICO FC_AG_ZEA FC_AG_BRAS FC_MB_NICO FC_MB_ZEA FC_MB_BRAS FC_TN_NICO FC_TN_ZEA FC_TN_BRAS FC_SL_NICO FC_SL_ZEA FC_SL_BRAS FC_SE_NICFC_SE_ZEA FC_SE_BRAS
OG0004400 -0.787302663 -0.710790578 0.663333543
OG0004400 -1.659046364 -1.019969932 0.588969542
OG0004400 -0.373838773 0.277055943 0.481626213
OG0004400 -0.360799687 -0.0958126 0.056722264
OG0004400 -1.77626686 -0.971114297 0.707963822
OG0004402 -0.304209641 -0.259080399 0.44366888
OG0004402 0.442748804 0.042958499 -0.316412832
OG0004402 -0.274550145 0.1933262 0.374095809
OG0004402 0.253000346 0.338511357 -0.121760564
따라서 동일한 OG 번호를 공유하는 행은 하나의 행으로 병합되어야 합니다. 열당 하나의 값만 있으므로 배정밀도에는 문제가 없습니다.
여러 다른 게시물에서 비슷한 문제를 다루고 있으며 이 답변이 매우 유용하다고 생각하여 약간 편집했습니다. 그러나 여기서는 모든 값이 서로 바로 뒤에 기록됩니다. 다만, 컬럼 값은 같은 위치에 유지되는 것이 중요합니다.
awk '{if(NR!=1){a[$1]=$2"\t"a[$1]} else print $0} END {n = asorti(a, b); for (n in b) {print b[n],a[b[n]]}}'
누구든지 위 코드를 편집하는 데 도움을 줄 수 있나요?
답변1
awk 'BEGIN{FS="\t"} NR==1{print; next} {a[$1]=$1; for(i=2; i<=NF; i++){if($i!="") {f[$1,i]=$i; if(i>last[$1]){last[$1]=i}}} } END{for(j in a){printf("%s", a[j]); for(k=2; k<=last[j]; k++){printf("%s%s", FS, f[j,k])} print ""}}' file
awk 'BEGIN{FS="\t"} # use tab as field separator
NR==1{print; next} # print header
{
a[$1]=$1 # save first column in current row
for(i=2; i<=NF; i++){ # loop with all columns but first
if($i!=""){ # if column not empty
f[$1,i]=$i # save content to array
if(i>last[$1]){
last[$1]=i # save number of last element in current row
}
}
}
}
END{
for(j in a){
printf("%s", a[j]) # print first element
for(k=2; k<=last[j]; k++){ # print second to last element
printf("%s%s", FS, f[j,k])
}
print ""
}
}' file
배열에는 a
첫 번째 열이 포함되어 있습니다. 배열에는 f
첫 번째 열이 없는 행이 포함되어 있습니다. 배열에는 last
현재 행의 마지막 요소 위치가 포함됩니다.
아직 테스트되지 않았습니다.
답변2
한 가지 방법은 다음과 같습니다.
$ awk -F"\t" '{if(NR==1){ cols=NF; print; } else{for(i=2;i<=NF;i++){if(length($i)>0){data[$1][i]=$i}}}}END{for(id in data){ printf "%s",id; for(i=2;i<=cols;i++){printf "\t%s", data[id][i]} print ""}}' file
OG FC_AG_NICO FC_AG_ZEA FC_AG_BRAS FC_MB_NICO FC_MB_ZEA FC_MB_BRAS FC_TN_NICO FC_TN_ZEA FC_TN_BRAS FC_SL_NICO FC_SL_ZEA FC_SL_BRAS FC_SE_NICFC_SE_ZEA FC_SE_BRAS
OG0004400 -0.787302663 -0.710790578 0.663333543 -0.360799687 -0.0958126 0.056722264 -1.77626686 -0.971114297 0.707963822 -0.373838773 0.277055943 0.481626213 -1.659046364 -1.019969932
OG0004402 -0.304209641 -0.259080399 0.44366888 0.253000346 0.338511357 -0.121760564 -0.274550145 0.1933262 0.374095809 0.442748804 0.042958499
또는 읽기가 더 쉽습니다.
awk -F"\t" '{
## Print the headers and store the number of columns.
if(NR==1){
cols=NF;
print;
}
else{
## Iterate over all columns, starting from the 2nd.
for(i=2;i<=NF;i++){
## If this one isn't empty, store it.
if(length($i)>0){
data[$1][i]=$i
}
}
}
}
## After reading everything, print.
END{
for(id in data){
printf "%s",id;
for(i=2;i<=cols;i++){
printf "\t%s", data[id][i]
}
print ""
}
}' file
이는 각 ID(첫 번째 필드)에 행의 각 열에 대한 값이 있고 한 행에만 값이 있다고 가정합니다. 빈 ID가 있는 열이 있을 수 있는 경우 약간 다른 접근 방식이 필요합니다.
답변3
마친 후에는 더 많은 내용이 있을 것입니다 awk
.
연관 배열을 반복하면 필드 출력이 엉망이 될지 궁금했지만 for (f in fields)
최대 20개 필드까지 일부 테스트를 실행한 후에는 그렇지 않은 것 같습니다.
헤더가 행 1에 있다고 가정하면 데이터가 게시물에 따라 정렬되고 동시에 메모리에 저장하고 싶지 않은 많은 양의 데이터가 있습니다.
awk 'BEGIN{getline; split($0,out,"\t"); old=$1}
old!=$1{for (o in out) printf "%s\t", out[o]; print""; delete out;old=$1}
{split($0,tmp,"\t"); for (t in tmp) out[t]=(t==1)?tmp[t]:out[t]+tmp[t]}
END{for (o in out) printf "%s\t", out[o];}' file
이 입력
head c1 c2 c3
H1 -0.71
H1 2
H1 3
H2 11 12
H2 13
주다
head c1 c2 c3
H1 -0.71 2 3
H2 11 12 13
송곳
첫 번째 행을 잡고 첫 번째 필드를 기억하세요.old
awk 'BEGIN{getline; split($0,out,"\t"); old=$1}
다음 레코드의 첫 번째 필드가 마지막 필드(새 헤더)와 다른 경우 마지막 집계가 완료된 것이므로 인쇄하고 집계 배열을 지우고 out
새 컬렉션에 있음을 확인하세요.old=$1
old!=$1{for (o in out) printf "%s\t", out[o]; print""; delete out;old=$1}
$0
배열로 분할하고 값만 가져올 때 헤더 열이 아닌 한 tmp
값을 추가하여 배열을 반복합니다.tmp
out
{split($0,tmp,"\t"); for (t in tmp) out[t]=(t==1)?tmp[t]:out[t]+tmp[t]}
다음에 저장된 마지막 out
기록 세트를 지웁니다.END
END{for (o in out) printf "%s\t", out[o];}' file