我有以下名为“snp_sol”的数据集,有 481974 行:
trait effect snp chr pos snp_effect weight variance_explained var_a_hat
1 2 1 1 54 0.2030156E-02 1.251482 0 0
1 2 2 1 689 -0.3726744E-03 0.9660012 0 0
1 2 3 1 1234 0.4801369E-03 0.9823542 0 0
1 2 4 1 1280 -0.1104844E-03 0.9272357 0 0
1 2 5 1 2610 -0.1296295E-02 1.115933 0 0
... ... ... ... ... ... ... ... ...
1 2 481971 26 4897157 -0.7846317E-04 0.9226092 0 0
1 2 481972 26 4898314 -0.3934468E-03 0.9691408 0 0
1 2 481973 26 4898376 -0.7204678E-03 1.019935 0 0
1 2 481974 26 4898606 -0.1522481E-03 0.9333048 0 0
我想获取第七列(权重)中每个 50 个值(窗口)的平均值,该平均值应出现在原始值的位置,如下所示:
trait effect snp chr pos snp_effect weight variance_explained var_a_hat
1 2 1 1 54 0.2030156E-02 mean of first 50 rows 0 0
... ... ... ... ... ... ... ... ...
1 2 50 1 4234 0.5801369E-03 mean of first 50 rows 0 0
1 2 51 1 5080 -0.5048544E-03 mean of second set of 50 rows 0 0
... ... ... ... ... ... ... ... ...
1 2 100 1 12050 -0.4854433E-03 mean of second set of 50 rows 0 0
1 2 101 1 14080 -0.3554433E-03 mean of third set of 50 rows 0 0
... ... ... ... ... ... ... ... ...
1 2 150 1 14080 -0.7894433E-03 mean of third set of 50 rows 0 0
and so on
1 2 481974 26 4898606 -0.1522481E-03 mean of last rows 0 0
请注意,不应有窗口重叠,并且最后一个窗口中不能有 50 行。
我正在尝试这段代码:
NR=$(wc -l "snp_sol" | awk '{print $1}') # Count the number rows
window=$((NR/50)) # Defining the number windows
int=${window%.*} # Converting to interger
it=$((2*int)) # Double the number of windows
for i in $(seq 0 50 $it) # for statement with a seq to count the windows
do
vi=$i # Variable to define the beginning of the window
vf=$((vi+50)) # Variable to define the end of window
awk -v vi="$vi" -v vf="$vf" '{ if(NR > vi && NR <= vf) # take each window
print } ' snp_sol > b.txt # new temporary file to receive the window
m=$(awk '{sum+=$7} END {m=sum/NR; print m}' b.txt) # Calculate the mean
awk -v mean="$m" '{print $1=$3,$2=mean}' b.txt > $i.temp # save a temporary file with the mean in second column
rm b.txt # Remove the file created to calculate the mean
done
cat *.temp > b.temp # join all temporary files in sequence
paste snp_sol b.temp > c.temp
awk '{print $1,$2,$3,$4,$5,$6,$7=$11,$8,$9=$10}' c.temp > snp_sol
rm *.temp
然而,这是行不通的。一定有另一种方法可以做到这一点,但我不知道该怎么做。
这种情况的解决方案最好是使用shell脚本。
拜托,你能帮我吗?
提前致谢。
答案1
使用 GNU datamash
、split
(GNU coreutils)和awk
:
#!/bin/bash
# remove header line and split `input_file` into n files `split00000`, `split00001`...
# with max. 4 lines each (use `-l50` for your data file)
split -d -a5 -l4 <(tail -n+2 input_file) split
{ head -n1 input_file # add header
for fsplit in split*; do
mean=$(datamash -W mean 7 < "$fsplit") # calculate mean value
awk -v mean="$mean" '{ print $1,$2,$3,$4,$5,$6,mean,$8,$9 }' "$fsplit"
done
} | column -t > output_file # format as table and write result
rm split* # cleanup
在此脚本中,我使用您的数据(已删除虚线)作为输入,并且仅使用 4 个值作为平均值。
替换-l4
为-l50
脚本中的数据文件。这与您所做的几乎相同,我只是让split
并datamash
完成所有工作。
输入文件:
$ cat input_file
trait effect snp chr pos snp_effect weight variance_explained var_a_hat
1 2 1 1 54 0.2030156E-02 1.251482 0 0
1 2 2 1 689 -0.3726744E-03 0.9660012 0 0
1 2 3 1 1234 0.4801369E-03 0.9823542 0 0
1 2 4 1 1280 -0.1104844E-03 0.9272357 0 0
1 2 5 1 2610 -0.1296295E-02 1.115933 0 0
1 2 481971 26 4897157 -0.7846317E-04 0.9226092 0 0
1 2 481972 26 4898314 -0.3934468E-03 0.9691408 0 0
1 2 481973 26 4898376 -0.7204678E-03 1.019935 0 0
1 2 481974 26 4898606 -0.1522481E-03 0.9333048 0 0
结果:
$ cat output_file
trait effect snp chr pos snp_effect weight variance_explained var_a_hat
1 2 1 1 54 0.2030156E-02 1.031768275 0 0
1 2 2 1 689 -0.3726744E-03 1.031768275 0 0
1 2 3 1 1234 0.4801369E-03 1.031768275 0 0
1 2 4 1 1280 -0.1104844E-03 1.031768275 0 0
1 2 5 1 2610 -0.1296295E-02 1.0069045 0 0
1 2 481971 26 4897157 -0.7846317E-04 1.0069045 0 0
1 2 481972 26 4898314 -0.3934468E-03 1.0069045 0 0
1 2 481973 26 4898376 -0.7204678E-03 1.0069045 0 0
1 2 481974 26 4898606 -0.1522481E-03 0.9333048 0 0
答案2
awk -v mod=50 '
BEGIN{ if(!mod) {mod=50} };
NR==1 {print;next};
(NR+1) % mod == 0 {
$7=sum/count;
print;
sum=count=0;
next;
};
{count++; sum+=$7}
END {
if (((NR+1) % mod)) != 0) {
$7=sum/count;
print;
};
}' snp_sol
这将打印未修改的标题行。然后,对于每 50 个输入行,它都会用计算出的平均值替换 $7 中的值并打印该行。它也在最终输入行上执行相同的操作当且仅当它以前没有印刷过。
对于所有其他输入行,它会递增count
(行计数器)并添加到$7
(sum
包含该输入行块中所有 $7 值的总和mod
)。
没有临时文件,不会重复运行相同的数据,不会awk
多次分叉 shell 循环。只需使用非常简单的算法简单地遍历输入文件即可。
注意:使用名为 的变量mod
而不是硬编码 50 作为模数。如果未在命令行上指定,则默认为 50,例如-v mod=n
.