结合多个grep搜索并使我的脚本更有效率

时间:2013-08-31 02:51:15

标签: bash grep counter

我有一个名为Type1.txt的文件,如下所示:

$ cat Type1.txt
ID.580.G3C0
TTTTTTTTTTT
ID.580.G3C8
ATTATATC-AAA
ID.580.GXC16
ATTATTTC-ACG-TTTTTCCTA
ID.694.G9C3
ATTATATC-ACG-AAATCCTA
ID.694.G9C3
etc...

我想编写一个bash脚本来计算每个ID的实例,并将其导出到另一个提供摘要的文件中,如下所示:

ID.580 = 3
ID.694 = 1
etc...

到目前为止,脚本很乱并且无法使用。

对于上述内容,我有以下内容:

#!/bin/bash

for Count in `grep -c "ID.580" Type1.txt; do
    echo $Count=ID.580
done > Result.txt  #Allows to count only for that single ID.

我有超过一千个ID.XXX,使得此代码无法使用,因为为每次搜索添加单独的ID.XXX是不合理的。感谢您的帮助!

5 个答案:

答案 0 :(得分:0)

grep '^ID.[0-9][0-9][0-9]' input_file | cut -c1-6 | sort | uniq -c

作品?

答案 1 :(得分:0)

TL; DR

根据您的特定语料库和分组策略,获得所需结果的方法不止一种。这里有两个替代解决方案,一个在awk中,另一个在Ruby中。

GNU awk

一种方法是使用GNU awk执行以下步骤:

  1. 仅匹配ID行
  2. 将匹配的输入行拆分为字段
  3. 选择并打印您需要的字段
  4. 对过滤结果中的行进行排序
  5. 计算相邻的重复项
  6. 对结果执行任何特殊格式设置
  7. 例如:

    $ awk '/^ID/ {split($0, a, "."); print a[1] "." a[2]}' /tmp/foo |
        sort | uniq --count | awk '{print $2 " = " $1}'
    ID.580 = 3
    ID.694 = 2
    

    使用您在问题中提供的语料库,我的系统平均需要8毫秒。当然,更大的语料库需要更长的时间,但除非你有一个非常庞大的数据集,否则对于大多数用途来说这应该足够快。

    红宝石

    Ruby提供了我认为更优雅的解决方案,但实际上速度较慢。这里的想法是将ID的相关部分存储为哈希键,并在每次遇到给定ID时递增计数器。例如,考虑这个Ruby单行:

    $ ruby -ne 'BEGIN { id = Hash.new(0) }
                id[$&] += 1 if /\AID\.\d+/
                END { id.each_pair do |k,v| puts "#{k} = #{v}" end }' /tmp/foo
    ID.580 = 3
    ID.694 = 2
    

    此解决方案需要大约45毫秒来处理相同的语料库,因此我不建议仅仅为了转换输出而在awk管道上。这样做的主要优点是你有一个实际的数据结构(例如Hash object),你可以在一个功能更全面的程序中操作。

答案 2 :(得分:0)

贝壳

下面的代码使用标准的UNIX实用程序,并不假设ID的第二部分正好是3个字符,但是会找到ID.1.123123123ID.1234.123123并正确地只取第一个点 - 划界的部分。因为它是

grep '^ID\.[0-9]' Type1.txt | cut -d . -f 1-2 | sort \
    | uniq -c | awk '{ print $2" = "$1 }'
  • grep仅过滤以ID.开头,后跟1位数字(至少)的行
  • cut使用.作为字段分隔符,仅输出字段1和2,从而删除 包括第二个.在内的所有内容。
  • sort对uniq的行进行排序
  • uniq打印前缀为count
  • 的每一行
  • awk部分会反转这些字段,并使用=分隔打印它们。

如果ID的第一部分也可以包含字母,请将正则表达式的结尾更改为[0-9][0-9A-Z]。例如

管道输出

ID.580 = 3
ID.694 = 2

的Python

由于Python在生物学家中很受欢迎,你可能想要磨练你的python技能:

from collections import Counter

counter = Counter()
with open('Type1.txt') as f:
    for line in f:
        if line.startswith('ID.'):
            top_id = '.'.join(line.split('.', 2)[:2])
            counter[top_id] += 1

for top_id, count in sorted(counter.items()):
    print("%s = %d" % (top_id, count))

结果完全相同。

答案 3 :(得分:0)

这是awk one liner:

$ awk -F. '$1=="ID"{a[$2,$3]++}END{for (i in a) {split(i,ind,SUBSEP); r[ind[1]]++}for (i in r)  print "ID."i" = "r[i]}' file
ID.694 = 1
ID.580 = 3

这是一个纯粹的bash解决方案:

#!/bin/bash
while IFS=. read -r pre id code rest 
do
    [[ $pre == ID ]] || continue
    [[ ${a[$id]} =~ \."$code"\. ]] || {
        a[$id]="${a[$id]}.$code."
        ((count[$id]++));
    }
done < file
for i in "${!count[@]}"
do
    echo "ID.$i = ${count[$i]}"
done

$ ./script.sh 
ID.580 = 3
ID.694 = 1

答案 4 :(得分:-1)

awk也可能有用......

awk '/ID.580/{x++}END{print x}' test.txt

你可以把它放在for循环中

for i in ID.580 ID.694
do
  awk '/'$i'/{x++}END{print x}' test.txt
done