当不同列中的连续单元格相等时,如何使用 shell 脚本计算列中的单词数!

当不同列中的连续单元格相等时,如何使用 shell 脚本计算列中的单词数!

当第 2 列、第 3 列和第 1 列中的连续单元格相同时,我尝试计算第 9 列中C_R和的数量。S_R该文件采用床格式(制表符分隔格式)。原始文件很大,第一列定义了染色体编号。文件的前几行看起来像这样,

chr1    10200   10300   8   10000   10214   100 214 S_R
chr1    10200   10300   8   10009   10233   100 224 S_R
chr1    10200   10300   8   10014   10220   100 206 S_R
chr1    10200   10300   8   10045   10215   100 170 S_R
chr1    10200   10300   8   10068   10209   100 141 S_R
chr1    10200   10300   8   10074   10300   100 226 C_R
chr1    10200   10300   8   10182   10283   100 101 S_R
chr1    10200   10300   8   10182   10387   100 205 C_R
chr1    10300   10400   4   10182   10387   100 205 S_R
chr1    10300   10400   4   10331   10467   100 136 S_R
chr1    10300   10400   4   10346   10461   100 115 S_R
chr1    10300   10400   4   10352   10468   100 116 S_R
chr1    10400   10500   3   10331   10467   100 136 S_R
chr1    10400   10500   3   10346   10461   100 115 S_R
chr1    10400   10500   3   10352   10468   100 116 S_R
chr1    11000   11100   2   11024   11163   100 139 S_R
chr1    11000   11100   2   11024   11188   100 164 S_R
chr1    11100   11200   3   11024   11163   100 139 S_R
chr1    11100   11200   3   11024   11188   100 164 S_R
chr1    11100   11200   3   11127   11296   100 169 S_R
chr1    11200   11300   1   11127   11296   100 169 S_R
chr1    11400   11500   2   11412   11561   100 149 S_R
chr1    11400   11500   2   11457   11608   100 151 S_R
chr1    11500   11600   3   11412   11561   100 149 S_R
chr1    11500   11600   3   11457   11608   100 151 C_R
chr1    11500   11600   3   11574   11744   100 170 S_R
chr1    11600   11700   3   11457   11608   100 151 S_R
chr1    11600   11700   3   11574   11744   100 170 C_R
chr1    11600   11700   3   11640   11815   100 175 S_R
chr1    11700   11800   4   11574   11744   100 170 S_R
chr1    11700   11800   4   11640   11815   100 175 C_R
chr1    11700   11800   4   11784   11963   100 179 S_R
chr1    11700   11800   4   11791   11936   100 145 S_R

在上表的前 8 行中,第 1、2、3 列相同,因此暂定输出文件如下所示

chr1    10200   10300   2   6
chr1    10300   10400   0   4
chr1    10400   10500   0   3
chr1    11000   11100   0   2
chr1    11100   11200   0   3
chr1    11200   11300   0   1
chr1    11400   11500   0   2
chr1    11500   11600   1   2
chr1    11600   11700   1   2
chr1    11700   11800   1   3

在输出文件中,第 4 列C_R和第 5 列是S_R

答案1

awk这可以通过以下方式完成:

awk 'function report() {
       if (n) print last_key, 0+count["C_R"], 0+count["S_R"]
     }
     {key = $1 OFS $2 OFS $3}
     key != last_key {report(); n = 0; split("", count); last_key = key}
     {count[$9]++; n++}
     END {report()}' <your-file

如果输入字段是用制表符分隔的,您可能需要添加-F '\t' -v OFS='\t'以指定输入和输出文件分隔符。默认情况下FS(由 设置的输入字段分隔符-F)是一个空格字符,它具有特殊含义:任何空格序列分隔字段以及前导和尾随空格都将被丢弃,或者 IOW 字段是非空白字符序列,因此不能任何空白字段; andOFS也是一个空格,简单的意思是输出上的字段用一个空格分隔。

答案2

这是 GNU awk(大多数 Linux 系统上的gawk默认设置)方法。awk请注意,我假设此处的列是制表符分隔的,但由于这看起来像 bed 文件或 bedpe 文件,两者都需要制表符,因此我有理由确定这是正确的。

$ gawk -F'\t' -v OFS='\t' -v type1="C_R" -v type2="S_R" '
  { 
    a[$1 OFS $2 OFS $3][$NF]++; 
  } 
  END{ 
    for (i in a){  
      print i,0+a[i][type1],0+a[i][type2]
    }
}' file.bed 
chr1    11200   11300   0   1
chr1    11700   11800   1   3
chr1    10300   10400   0   4
chr1    11000   11100   0   2
chr1    10400   10500   0   3
chr1    11500   11600   1   2
chr1    11400   11500   0   2
chr1    11100   11200   0   3
chr1    11600   11700   1   2
chr1    10200   10300   2   6

解释

-F'\t'将输入字段分隔符设置为制表符,并将-v OFS='\t'输出字段分隔符设置为制表符。接下来,我们设置另外两个变量type1adtype2以在脚本中使用。

脚本的主体只是a[$1 OFS $2 OFS $3][$NF]++;。这使用由输出字段分隔符(在我们的例子中是制表符)分隔的前三个字段OFS作为二维数组的第一个键a。第二个键是最后一个字段 ( $NF),我们只需将值加一。因此,a数组会计算每个字符、开始位置和结束位置与每个可能的最后字段一起找到的次数。

完成后,在END{ }块中,我们迭代数组的所有键a(字符、开始和结束位置),然后打印出两个可能的最后一个字段中的每一个,type1以及type2字符、开始和结束位置( 的值i),然后是每个可能的最后一个字段与前三个字段一起出现的次数 ( 0+a[i][type1],0+a[i][type2])。我们需要0+那里,这样如果在这些字符、开始和结束位置上没有看到特定的最后一个字段,我们将得到 a0而不是空字段或错误。

答案3

使用 TSV 感知实用程序 Miller ( mlr) 将输入读取为无标头 TSV 文件,仅挑选出必要的列(使用cut),将第 9 列的值收集/折叠到;每个列组合的 - 分隔列表中1、2 和 3(使用),然后计算为每个剩余记录收集的和字符串nest的数量(使用表达式)。第 9 列中折叠的值最终被删除(带有)。C_RS_Rputcut -x

mlr --from=file --tsv -N \
    cut -f 1,2,3,9 then \
    nest --ivar ';' -f 9 then \
    put '
        a = splita($9,";");
        for (k in ["C_R","S_R"]) {
            $[k] = count(select(a, func(e) { return e == k }))
        }' then \
    cut -x -f 9

给定问题中的数据,这将生成以下制表符分隔的输出:

chr1    10200   10300   2       6
chr1    10300   10400   0       4
chr1    10400   10500   0       3
chr1    11000   11100   0       2
chr1    11100   11200   0       3
chr1    11200   11300   0       1
chr1    11400   11500   0       2
chr1    11500   11600   1       2
chr1    11600   11700   1       2
chr1    11700   11800   1       3

答案4

使用(以前称为 Perl_6)

~$ raku -e 'my @a = lines.map: *.split(/\s+/);  \
            my %hash.push:  [Z=>] @a.map(*.[0..2]), @a.map(*.[*-1]);  \
            put join "\t", .key, (.value.map: * eq "C_R")>>.Int.sum,  \
                (.value.map: * eq "S_R")>>.Int.sum  for %hash.sort;'  file

这是用 Raku(Perl 编程语言家族的成员)编写的答案。上面lines是在空白处map插入的。split结果保存到@a数组中。%hash声明A ,并[Z=>] 使用 Raku 的归约元运算符将push键/值对添加到其上,.[0..2]前三列为钥匙最后一.[*-1]列为价值。在最后一个语句中,value检查 seqC_R和的字符串是否有效S_R;每个布尔结果都转换为Intsummed 和 output作为列。

输入示例:

chr1    10200   10300   8   10000   10214   100 214 S_R
chr1    10200   10300   8   10009   10233   100 224 S_R
chr1    10200   10300   8   10014   10220   100 206 S_R
chr1    10200   10300   8   10045   10215   100 170 S_R
chr1    10200   10300   8   10068   10209   100 141 S_R
chr1    10200   10300   8   10074   10300   100 226 C_R
chr1    10200   10300   8   10182   10283   100 101 S_R
chr1    10200   10300   8   10182   10387   100 205 C_R
chr1    10300   10400   4   10182   10387   100 205 S_R
chr1    10300   10400   4   10331   10467   100 136 S_R
chr1    10300   10400   4   10346   10461   100 115 S_R
chr1    10300   10400   4   10352   10468   100 116 S_R
chr1    10400   10500   3   10331   10467   100 136 S_R
chr1    10400   10500   3   10346   10461   100 115 S_R
chr1    10400   10500   3   10352   10468   100 116 S_R
chr1    11000   11100   2   11024   11163   100 139 S_R
chr1    11000   11100   2   11024   11188   100 164 S_R
chr1    11100   11200   3   11024   11163   100 139 S_R
chr1    11100   11200   3   11024   11188   100 164 S_R
chr1    11100   11200   3   11127   11296   100 169 S_R
chr1    11200   11300   1   11127   11296   100 169 S_R
chr1    11400   11500   2   11412   11561   100 149 S_R
chr1    11400   11500   2   11457   11608   100 151 S_R
chr1    11500   11600   3   11412   11561   100 149 S_R
chr1    11500   11600   3   11457   11608   100 151 C_R
chr1    11500   11600   3   11574   11744   100 170 S_R
chr1    11600   11700   3   11457   11608   100 151 S_R
chr1    11600   11700   3   11574   11744   100 170 C_R
chr1    11600   11700   3   11640   11815   100 175 S_R
chr1    11700   11800   4   11574   11744   100 170 S_R
chr1    11700   11800   4   11640   11815   100 175 C_R
chr1    11700   11800   4   11784   11963   100 179 S_R
chr1    11700   11800   4   11791   11936   100 145 S_R

示例输出:

chr1 10200 10300    2   6
chr1 10300 10400    0   4
chr1 10400 10500    0   3
chr1 11000 11100    0   2
chr1 11100 11200    0   3
chr1 11200 11300    0   1
chr1 11400 11500    0   2
chr1 11500 11600    1   2
chr1 11600 11700    1   2
chr1 11700 11800    1   3

[在最后一个put语句中,如果您希望所有列都以制表符分隔,则替换.key为]。.key.split(" ").join("\t")


请注意,查看中间步骤通常非常有启发性,因此这是 Raku 在.raku最终 2 列sum挖掘之前的数据 ( ) 内部表示:

~$ raku -e 'my @a = lines.map: *.split(/\s+/); my %hash.push:  [Z=>] @a.map(*.[0..2]), @a.map(*.[*-1]); .raku.say for %hash.sort;'  file
"chr1 10200 10300" => $["S_R", "S_R", "S_R", "S_R", "S_R", "C_R", "S_R", "C_R"]
"chr1 10300 10400" => $["S_R", "S_R", "S_R", "S_R"]
"chr1 10400 10500" => $["S_R", "S_R", "S_R"]
"chr1 11000 11100" => $["S_R", "S_R"]
"chr1 11100 11200" => $["S_R", "S_R", "S_R"]
"chr1 11200 11300" => "S_R"
"chr1 11400 11500" => $["S_R", "S_R"]
"chr1 11500 11600" => $["S_R", "C_R", "S_R"]
"chr1 11600 11700" => $["S_R", "C_R", "S_R"]
"chr1 11700 11800" => $["S_R", "C_R", "S_R", "S_R"]

https://docs.raku.org/language/hashmap
https://raku.org

相关内容