1

I have a file called Type1.txt, that looks like this:

$ 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...

I want to write a bash script to count the instances of each ID and export it into another file that provides a summary, something like this:

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

So far the script is messy and unusable.

For the above I have the following:

#!/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.

I have over a thousand ID.XXX, making this code unusable since it's not plausible to add individual ID.XXX for each search. Thank you for the help!

4

5 回答 5

0

这是 awk 一个班轮:

$ 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
于 2013-08-31T05:23:53.273 回答
0

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

作品?

于 2013-08-31T02:55:41.373 回答
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打印其输入中的每一行,并以计数为前缀
  • 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))

结果完全相同。

于 2013-08-31T03:17:15.317 回答
0

TL;博士

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

GNU awk

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

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

例如:

$ 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 对象),你可以在一个功能更全面的程序中操作它。

于 2013-08-31T03:01:49.640 回答
-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
于 2013-08-31T02:59:41.440 回答