다음과 같은 데이터 파일이 있습니다.
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]+=$3
key 로 배열을 생성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 스크립트에 복사하여 테스트할 수 있습니다.