동일한 데이터 파일의 다른 열에 있는 정보를 기반으로 한 열에 일부 값을 누적하는 방법은 무엇입니까?

동일한 데이터 파일의 다른 열에 있는 정보를 기반으로 한 열에 일부 값을 누적하는 방법은 무엇입니까?

다음과 같은 데이터 파일이 있습니다.

c1.11   SNP1    -6.73098    0.764833    Chr1:1
c1.21   SNP2    -4.871  0.00393002  Chr1:101
c1.12   SNP3    -0.766822   0.0891227   Chr1:201
c1.22   SNP4    11.7499 0.141861    Chr1:301
c2.11   SNP5    8.38008 0.741379    Chr1:401
c2.21   SNP6    -0.86974    0.00824037  Chr1:501
c2.12   SNP7    -0.181898   0.00494422  Chr1:601
c2.22   SNP8    -7.32856    0.245436    Chr1:701
c3.11   SNP9    -12.0418    0.369929    Chr1:801
c3.12   SNP10   11.2833 0.357378    Chr1:901
c3.22   SNP11   -0.0308993  0.270918    Chr1:1001
c4.121  SNP12   1.51586 0.0770791   Chr1:1101
c4.122  SNP13   0.118888    0.0742901   Chr1:1201

내가 하고 싶은 것은 첫 번째 열과 같은 그룹에 속한 세 번째 열에 값을 누적하는 것입니다. 예를 들어 처음 4개 값은 그룹 c1에 속하고 다음 4개 값은 그룹 c2에 속하며... 따라서 새 출력은 다음과 같아야 합니다.

 Output :
c1 -0.61778
c2  0.00845
c3 -0.7875
c4 1.6347

어떤 제안이 있으십니까? (실제 데이터가 방대하다는 점을 고려해주세요.

답변1

그리고GNU 데이터 혼합(마침표를 공백으로 대체한 후 사용 sed)

sed 's/\./ /' data | datamash -W groupby 1 sum 4
c1      -0.618902
c2      -0.000118
c3      -0.7893993
c4      1.634748

답변2

perl해결 방법은 입력 파일이 c1, c2 등으로 정렬되어 있다고 가정합니다. 따라서 해시/배열에 저장할 필요가 없습니다.

$ perl -lane '
$F[0] =~ s/\..*//;
if($F[0] ne $p && $. > 1)
{
    print "$p $sum";
    $sum = 0;
}
$sum += $F[2];
$p = $F[0];
END { print "$p $sum" }' ip.txt
c1 -0.618902
c2 -0.000118000000001395
c3 -0.7893993
c4 1.634748
  • -la입력에서 개행을 제거하고 인쇄할 때 추가하고, 공백에서 입력 줄을 분할하고 @F배열 에 저장합니다.
  • $F[0] =~ s/\..*//.첫 번째 필드의 모든 문자 제거
  • if($F[0] ne $p && $. > 1)입력 라인 번호가 첫 번째 라인이 아니고 첫 번째 필드가 이전 필드와 다른 경우
    • 필드 이름과 누적 합계를 인쇄하고 합계 변수를 지웁니다.
  • 마지막으로 마지막 항목을 설명하기 위해 다시 인쇄하십시오.


또 다른 방법은 입력 줄을 분할하지 않고 정규식을 사용하여 필요한 키와 값을 추출하는 것입니다.

$ perl -lne '
($k, $v) = /^([^.]+)(?:\S+\s+){2}(\S+)/;
if($k ne $p && $. > 1)
{
    print "$p $sum";
    $sum = 0;
}
$sum += $v;
$p = $k;
END { print "$p $sum" }' ip.txt
c1 -0.618902
c2 -0.000118000000001395
c3 -0.7893993
c4 1.634748

답변3

GNU 사용 awk:

awk '{grp = gensub("^([^.]+).*", "\\1", 1, $1); \
              arr[grp]+=$3} END {for (i in arr) print i, arr[i]}' file.txt
  • gensub("^([^.]+).*", "\\1", 1, $1)첫 번째 필드에서 첫 번째 부분 이전의 부분을 가져와 .변수로 저장합니다.grp

  • arr[grp]+=$3key 로 배열을 생성 grp하고 각 행의 세 번째 열부터 값을 누적합니다.

  • 세그먼트의 블록은 END배열 요소를 반복하고 원하는 형식으로 키 값을 인쇄합니다.

일관된 입력을 위해서는 POSIX를 사용하십시오 awk.

awk '{sub("\\..*", "", $1); arr[$1]+=$3} END {for (i in arr) print i, arr[i]}'
  • sub("\\..*", "", $1)첫 번째 필드를 수정하여 다음 부분을 자르고 .arr를 (수정된) 첫 번째 필드로 사용하여 배열을 만듭니다.

예:

% cat file.txt
c1.11   SNP1    -6.73098    0.764833    Chr1:1
c1.21   SNP2    -4.871  0.00393002  Chr1:101
c1.12   SNP3    -0.766822   0.0891227   Chr1:201
c1.22   SNP4    11.7499 0.141861    Chr1:301
c2.11   SNP5    8.38008 0.741379    Chr1:401
c2.21   SNP6    -0.86974    0.00824037  Chr1:501
c2.12   SNP7    -0.181898   0.00494422  Chr1:601
c2.22   SNP8    -7.32856    0.245436    Chr1:701
c3.11   SNP9    -12.0418    0.369929    Chr1:801
c3.12   SNP10   11.2833 0.357378    Chr1:901
c3.22   SNP11   -0.0308993  0.270918    Chr1:1001
c4.121  SNP12   1.51586 0.0770791   Chr1:1101
c4.122  SNP13   0.118888    0.0742901   Chr1:1201

% awk '{grp = gensub("^([^.]+).*", "\\1", 1, $1); arr[grp]+=$3} END {for (i in arr) print i, arr[i]}' file.txt
c1 -0.618902
c2 -0.000118
c3 -0.789399
c4 1.63475

% awk '{sub("\\..*", "", $1); arr[$1]+=$3} END {for (i in arr) print i, arr[i]}' file.txt
c1 -0.618902
c2 -0.000118
c3 -0.789399
c4 1.63475

답변4

여기 내 해결책이 있습니다. 시도해 보고 작동하는지 알려주세요.

#!/bin/bash


awk '
BEGIN{group="c1"
sum=0}
{
    if(substr($1,1,2)==group) 
    {sum+=$3
    print group " " sum} 
    else {
        group=substr($1,1,2)
        sum=$3
        print group " " sum}
    }'  file.txt > tmp.txt





awk 'BEGIN{group="c1"}
     $1!=group {print group " " sum
     group=$1} {sum=$2}
     END{print $1 " " $2}'  tmp.txt >finalResult.txt

rm tmp.txt

결과는 FinalResult.txt에 나타나야 합니다. 이것을 bash 스크립트에 복사하여 테스트할 수 있습니다.

관련 정보