将一列的值替换为该列值的平均值

将一列的值替换为该列值的平均值

我有以下名为“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 datamashsplit(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脚本中的数据文件。这与您所做的几乎相同,我只是让splitdatamash完成所有工作。

输入文件:

$ 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(行计数器)并添加到$7sum包含该输入行块中所有 $7 值的总和mod)。

没有临时文件,不会重复运行相同的数据,不会awk多次分叉 shell 循环。只需使用非常简单的算法简单地遍历输入文件即可。

注意:使用名为 的变量mod而不是硬编码 50 作为模数。如果未在命令行上指定,则默认为 50,例如-v mod=n.

相关内容