如何使用文件中的值作为 awk 计算的输入 - 在 bash 中?

How to use the value in a file as input for a calculation in awk - in bash?

我正在尝试计算每行的计数是否超过某个值,即总计数的 30%。

在 for 循环中,我获得了 awk '=(/100)*30' ${i}_counts > ${i}_percentage-value 中的百分比,这是一个数字,输出仅包含该数字。

如何针对 ${i}_percentage-value${i}_counts 的每一行进行计算 "value is greater than"? 换句话说,如何使用文件中的数字作为数学运算的数值?

数据:

data.csv(节选)

SampleID    ASV    Count
1000A   ASV_1216    14
1000A   ASV_12580   150
1000A   ASV_12691   260
1000A   ASV_135     434
1000A   ASV_147     79
1000A   ASV_15      287
1000A   ASV_16      361
1000A   ASV_184     8
1000A   ASV_19      42

样本-ID-短

1000A
1000B
1000C

因此对于每个样本 ID,都有很多 ASV,数量可能会有很大差异,例如 1000A 有 50 个 ASV,1000B 有 120 个等等。每个 ASV_## 都有一个计数,我的代码是计算计数总和,然后找出每个样本的 30% 值,报告哪个 ASV_## 大于 30%。最终,它应该为 <30% 报告 0,为 >30% 报告 1。

到目前为止,这是我的代码:

    for i in $(cat samplesID-short)
    do
    grep ${i} data.csv | cut -d , -f3 - > ${i}_count_sample
    grep ${i} data.csv | cut -d , -f2 - > ${i}_ASV
    awk '{ sum += ; } END { print sum; }' ${i}_count_sample > ${i}_counts
    awk '=(/100)*30' ${i}_counts > ${i}_percentage-value

#I was thinking about replicate the numeric value for the entire column and make the comparison "greater than", but the repetition times depend on the ASV counts for each sample, and they are always different.

    wc -l ${i}_ASV > n
    for (( c=1; c<=n; c++)) ; do echo ${i}_percentage-value ; done

    paste <(sed 's/^[[:blank:]]*//' ${i}_ASV) ${i}_count_sample ${i}_percentage-value > ${i}_tmp; 
    awk 'BEGIN{OFS="\t"}{if( >= ) print }' ${i}_tmp > ${i}_is30;

#How the output should be:

    paste <(sed 's/^[[:blank:]]*//' ${i}_ASV) ${i}_count_sample ${i}_counts ${i}_percentage-value ${i}_is30 > ${i}_summary_nh
    echo -e "ASV_ID\tASV_in_sample\ttotal_ASVs_inSample\ttreshold_for_30%\tASV_over30%" | cat - ${i}_summary_nh > ${i}_summary
    rm ${i}_count_sample ${i}_counts ${i}_percentage-value ${i}_ASV ${i}_summary_nh ${i}_is30
    done &

您可以根据值过滤列,例如

$ awk '>300' data.csv
SampleID    ASV    Count
1000A   ASV_135     434
1000A   ASV_16      361

您可以使用 >= 表示大于或等于。

看来您的脚本过于复杂了。

这应该有效

$ awk 'NR==1 || >*3/10' file

SampleID    ASV    Count
1000A   ASV_135     434
1000A   ASV_16      361

或者,与指标列

$ awk 'NR==1{print [=11=], "Ind"} NR>1{print [=11=], (>*3/10)}' file | column -t

SampleID  ASV        Count  Ind
1000A     ASV_1216   14     0
1000A     ASV_12580  150    0
1000A     ASV_12691  260    0
1000A     ASV_135    434    1
1000A     ASV_147    79     0
1000A     ASV_15     287    0
1000A     ASV_16     361    1
1000A     ASV_184    8      0
1000A     ASV_19     42     0

请您尝试以下操作:

awk -v OFS="\t" '
    NR==FNR {   # this block is executed in the 1st pass only
        if (FNR > 1) sum[] += 
                # accumulate the "count" for each "SampleID"
        next
    }
                # the following block is executed in the 2nd pass only
    FNR > 1 {   # skip the header line
        if ( != prev_id) {
                # SampleID has changed. then update the output filename and print the header line
            if (outfile) close(outfile)
                # close previous outfile
            outfile =  "_summary"
            print "ASV_ID", "ASV_in_sample", "total_ASVs_inSample", "treshold_for_30%", "ASV_over30%" >> outfile
            prev_id = 
        }
        mark = ( > sum[] * 0.3) ? 1 : 0
                # set the mark to "1" if the "Count" exceeds 30% of sum
        print , , sum[], sum[] * 0.3, mark >> outfile
                # append the line to the summary file
    }
' data.csv data.csv

data.csv:

SampleID    ASV    Count
1000A   ASV_1216    14
1000A   ASV_12580   150
1000A   ASV_12691   260
1000A   ASV_135     434
1000A   ASV_147     79
1000A   ASV_15      287
1000A   ASV_16      361
1000A   ASV_184     8
1000A   ASV_19      42
1000B   ASV_1       90
1000B   ASV_2       90
1000B   ASV_3       20
1000C   ASV_4       100
1000C   ASV_5       10
1000C   ASV_6       10

在以下输出示例中,如果计数超过总和值的 30%,则最后一个字段 ASV_over30% 表示 1

1000A_summary:

ASV_ID  ASV_in_sample   total_ASVs_inSample     treshold_for_30%        ASV_over30%
ASV_1216        14      1635    490.5   0
ASV_12580       150     1635    490.5   0
ASV_12691       260     1635    490.5   0
ASV_135 434     1635    490.5   0
ASV_147 79      1635    490.5   0
ASV_15  287     1635    490.5   0
ASV_16  361     1635    490.5   0
ASV_184 8       1635    490.5   0
ASV_19  42      1635    490.5   0

1000B_summary:

ASV_ID  ASV_in_sample   total_ASVs_inSample     treshold_for_30%        ASV_over30%
ASV_1   90      200     60      1
ASV_2   90      200     60      1
ASV_3   20      200     60      0

1000C_summary:

ASV_ID  ASV_in_sample   total_ASVs_inSample     treshold_for_30%        ASV_over30%
ASV_4   100     120     36      1
ASV_5   10      120     36      0
ASV_6   10      120     36      0

[说明]

在计算输入数据的平均值时,需要经过直到 数据的结尾。如果我们要打印出输入记录和平均值 值(或基于平均值的其他信息)同时,我们需要 使用技巧:

  • 将整个输入记录存储在内存中。
  • 读取输入数据两次。

因为awk适合读取多个文件改变过程 根据文件的顺序,我选择了第二种方法。

  • 条件NR==FNR returns TRUE 只读取第一个文件。 我们计算此块中 count 字段的总和作为第一遍。
  • 块末尾的next语句跳过了后面的代码。
  • 如果第一个文件完成,脚本将读取第二个文件 当然和第一个文件一样。
  • 读取第二个文件时,条件NR==FNR不再returns TRUE 并跳过第一个块。
  • 第二块再次读取输入文件,打开一个文件打印 输出,逐行读取输入数据,添加信息 比如第一遍得到的平均值。