我想计算 tab delim txt 文件第二列和第三列中的值/单词出现次数,其中第一列值相同
输入文件:
GeneA Pathogenic snp
GeneA Pathogenic snp
GeneA Benign indel
GeneA Likely_pathogenic snp
GeneA Pathogenic indel
GeneB Benign snp
GeneB Pathogenic snp
GeneB Benign indel
GeneC Benign snp
GeneC Likely_pathogenic snp
预期输出:(表格格式)
| Pathogenic | Benign | Likely_Pathogenic | snp | indel |
------------|--------|-------------------|-----|-------|
|GeneA| 3| 1| 1| 3| 2|
|GeneB| 1| 2| 0| 2| 1|
|GeneC| 0| 1| 1| 2| 0|
答案1
如下所示的脚本awk
将从给定文件中收集数据,并计算每个“名称”(第一列)中每个“类型”(第二列及以上)出现的次数。它输出一个简单的类似 CSV 的格式,假设所有输入数据均不包含嵌入的逗号,则该格式可以导入到其他程序中。
{
genes[$1] = 1
for (i = 2; i <= NF; ++i) {
types[$i] = 1
counts[$1,$i]++
}
}
END {
OFS = ","
$0 = "name"
for (t in types)
$(NF+1) = header[++n] = t
print
for (g in genes) {
$0 = g
for (i = 1; i <= n; ++i)
$(NF+1) = counts[g,header[i]]+0
print
}
}
genes
和数组types
是包含名称和类型作为键的关联数组。该counts
数组计算特定名称和类型在输入中作为对出现的次数。
该END
块创建并输出标题,然后迭代所有基因名称并输出每种类型收集的计数。
测试这个:
$ awk -f script file
name,indel,Benign,Likely_pathogenic,snp,Pathogenic
GeneA,2,1,1,3,3
GeneB,1,2,0,2,1
GeneC,0,1,1,2,0
$ awk -f script file | column -t -s,
name indel Benign Likely_pathogenic snp Pathogenic
GeneA 2 1 1 3 3
GeneB 1 2 0 2 1
GeneC 0 1 1 2 0
$ awk -f script file | csvlook
| name | indel | Benign | Likely_pathogenic | snp | Pathogenic |
| ----- | ----- | ------ | ----------------- | --- | ---------- |
| GeneA | 2 | 1 | True | 3 | 3 |
| GeneB | 1 | 2 | False | 2 | 1 |
| GeneC | 0 | 1 | True | 2 | 0 |
(如果你不想csvlook
要从数据推断类型,请使用它及其-I
选项。)
答案2
正如评论框中所说,Ruby 是允许的,我有一个独立的解决方案,它不需要管道不同的程序:
程序
data = <<~'EOF'
GeneA Pathogenic snp
GeneA Pathogenic snp
GeneA Benign indel
GeneA Likely_pathogenic snp
GeneA Pathogenic indel
GeneB Benign snp
GeneB Pathogenic snp
GeneB Benign indel
GeneC Benign snp
GeneC Likely_pathogenic snp
EOF
hash, header1, header2 = {}, [], []
data.each_line { |x|
fields = x.split("\s")
field = hash[fields[0].to_sym] ||= [{}, {}]
field[0].merge!(fields[1] => 1) { |key, val1, val2| val1 + val2 }
field[1].merge!(fields[2] => 1) { |key, val1, val2| val1 + val2 }
header1 << fields[1]
header2 << fields[2]
}
headers = header1.tap(&:uniq!).concat(header2.tap(&:uniq!))
header = '| ' + headers.join(' | ') + ' | '
puts header, ?- * header.length
hash.each { |k, v|
v0, v1 = v[0], v[1]
print "| #{k} |"
headers.each_with_index { |x, i|
out = "#{(v0[x] || v1[x]).to_i} |"
print i == 0 ? out.rjust(5) : out.rjust(x.length + 3)
}
puts
}
执行:
$ ruby thisFile.rb
输出:
| Pathogenic | Benign | Likely_pathogenic | snp | indel |
----------------------------------------------------------
| GeneA | 3 | 1 | 1 | 3 | 2 |
| GeneB | 1 | 2 | 0 | 2 | 1 |
| GeneC | 0 | 1 | 1 | 2 | 0 |
您可能会对这些大喊大叫rjust(5)
,rjust(x.length + 3)
因为它们是硬编码的。实际上这些是针对 的' | '
,它们也是硬编码的。如果再添加一个空格,则数字会加一。如果数据发生变化,它不会中断。
data <<~EOF...
您可以使用 来读取包含数据的文件,而不是使用data = (ARGV[0] && File.readable?(ARGV[0])) ? IO.read(ARGV[0]) : ''
- 这里的数据是从作为参数传递的文件名中读取的。应该适用于每个数据,但对齐是根据标题完成的。对该代码进行小的修改可以根据最大字段对齐数据。但现在我认为这会完成你的工作。