如何使用文件中的值作为 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
并跳过第一个块。
- 第二块再次读取输入文件,打开一个文件打印
输出,逐行读取输入数据,添加信息
比如第一遍得到的平均值。
我正在尝试计算每行的计数是否超过某个值,即总计数的 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
returnsTRUE
只读取第一个文件。 我们计算此块中count
字段的总和作为第一遍。 - 块末尾的
next
语句跳过了后面的代码。 - 如果第一个文件完成,脚本将读取第二个文件 当然和第一个文件一样。
- 读取第二个文件时,条件
NR==FNR
不再returnsTRUE
并跳过第一个块。 - 第二块再次读取输入文件,打开一个文件打印 输出,逐行读取输入数据,添加信息 比如第一遍得到的平均值。