我有一个数据文件,如下所示:
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)
如果输入行号不是第一行并且第一个字段与前一个字段不同- 打印字段名和累计和,清除sum变量
- 最后,再次打印以说明最后的条目
另一种方法是不拆分输入行并使用正则表达式提取所需的键和值:
$ 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
生成键为 的数组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 脚本中并进行测试。