我有一个类DNA的程序。该程序计算字符串中最常见的k-mer。因此,它正在寻找长度为k的字符串中最常见的子字符串。
一个例子是创建一个带有AACCAATCCG字符串的dna1对象。 count k-mer方法将查找长度为k的子字符串并输出最常见的答案。所以,如果我们设置k = 1那么' A'和' C'将是字符串中出现次数最多的一次,因为它出现了四次。见下面的例子:
dna1 = DNA.new('AACCAATCCG')
=> AACCAATCCG
>> dna1.count_kmer(1)
=> [#<Set: {"A", "C"}>, 4]
>> dna1.count_kmer(2)
=> [#<Set: {"AA", "CC"}>, 2]
这是我的DNA课程:
class DNA
def initialize (nucleotide)
@nucleotide = nucleotide
end
def length
@nucleotide.length
end
protected
attr_reader :nucleotide
end
这是我想要实现的计数kmer方法:
# I have k as my only parameter because I want to pass the nucleotide string in the method
def count_kmer(k)
# I created an array as it seems like a good way to split up the nucleotide string.
counts = []
#this tries to count how many kmers of length k there are
num_kmers = self.nucleotide.length- k + 1
#this should try and look over the kmer start positions
for i in num_kmers
#Slice the string, so that way we can get the kmer
kmer = self.nucleotide.split('')
end
#add kmer if its not present
if !kmer = counts
counts[kmer] = 0
#increment the count for kmer
counts[kmer] +=1
end
#return the final count
return counts
end
#end dna class
end
我不确定我的方法出错了。
答案 0 :(得分:3)
这样的东西?
require 'set'
def count_kmer(k)
max_kmers = kmers(k)
.each_with_object(Hash.new(0)) { |value, count| count[value] += 1 }
.group_by { |_,v| v }
.max
[Set.new(max_kmers[1].map { |e| e[0] }), max_kmers[0]]
end
def kmers(k)
nucleotide.chars.each_cons(k).map(&:join)
end
编辑:这是该课程的全文:
require 'set'
class DNA
def initialize (nucleotide)
@nucleotide = nucleotide
end
def length
@nucleotide.length
end
def count_kmer(k)
max_kmers = kmers(k)
.each_with_object(Hash.new(0)) { |value, count| count[value] += 1 }
.group_by { |_,v| v }
.max
[Set.new(max_kmers[1].map { |e| e[0] }), max_kmers[0]]
end
def kmers(k)
nucleotide.chars.each_cons(k).map(&:join)
end
protected
attr_reader :nucleotide
end
使用您指定的类和方法,使用Ruby 2.2.1生成以下输出:
>> dna1 = DNA.new('AACCAATCCG')
=> #<DNA:0x007fe15205bc30 @nucleotide="AACCAATCCG">
>> dna1.count_kmer(1)
=> [#<Set: {"A", "C"}>, 4]
>> dna1.count_kmer(2)
=> [#<Set: {"AA", "CC"}>, 2]
作为奖励,你也可以这样做:
>> dna1.kmers(2)
=> ["AA", "AC", "CC", "CA", "AA", "AT", "TC", "CC", "CG"]
答案 1 :(得分:2)
<强>代码强>
def most_frequent_substrings(str, k)
(0..str.size-k).each_with_object({}) do |i,h|
b = []
str[i..-1].scan(Regexp.new str[i,k]) { b << Regexp.last_match.begin(0) + i }
(h[b.size] ||= []) << b
end.max_by(&:first).last.each_with_object({}) { |a,h| h[str[a.first,k]] = a }
end
示例强>
str = "ABBABABBABCATSABBABB"
most_frequent_substrings(str, 4)
#=> {"ABBA"=>[0, 5, 14], "BBAB"=>[1, 6, 15]}
这表明str
最频繁出现的4个字符的子字符串出现了3次。有两个这样的子串:&#34; ABBA&#34;和&#34; BBAB&#34;。 &#34; ABBA&#34;从抵消开始(进入str
)0,5和14,&#34; BBAB&#34;子串从偏移量1,6和15开始。
<强>解释强>
对于上面的例子,步骤如下。
k = 4
n = str.size - k
#=> 20 - 4 => 16
e = (0..n).each_with_object([])
#<Enumerator: 0..16:each_with_object([])>
我们可以看到这个枚举器通过将它转换为数组而生成的值。
e.to_a
#=> [[0, []], [1, []], [2, []], [3, []], [4, []], [5, []], [6, []], [7, []], [8, []],
# [9, []], [10, []], [11, []], [12, []], [13, []], [14, []], [15, []], [16, []]]
请注意,在构建数组时,将修改每个元素中包含的空数组。继续,e
的第一个元素被传递给块,块变量使用并行赋值分配:
i,a = e.next
#=> [0, []]
i #=> 0
a #=> []
我们现在正在考虑从str
偏移i #=> 0
开始的大小为4的子字符串,它被视为&#34; ABBA&#34;。现在执行块计算。
b = []
r = Regexp.new str[i,k]
#=> Regexp.new str[0,4]
#=> Regexp.new "ABBA"
#=> /ABAB/
str[i..-1].scan(r) { b << Regexp.last_match.begin(0) + i }
#=> "ABBABABBABCATSABBABB".scan(r) { b << Regexp.last_match.begin(0) + i }
b #=> [0, 5, 14]
我们接下来有
(h[b.size] ||= []) << b
成为
(h[b.size] = h[b.size] || []) << b
#=> (h[3] = h[3] || []) << [0, 5, 14]
由于h
没有键3
,右侧的h[3]
等于nil
。接着,
#=> (h[3] = nil || []) << [0, 5, 14]
#=> (h[3] = []) << [0, 5, 14]
h #=> { 3=>[[0, 5, 14]] }
请注意,我们会丢弃scan
的返回值。我们所需要的只是b
这告诉我们&#34; ABBA&#34;在str
出现三次,从偏移0,5和14开始。
现在观察
e.to_a
#=> [[0, [[0, 5, 14]]], [1, [[0, 5, 14]]], [2, [[0, 5, 14]]],
# ...
# [16, [[0, 5, 14]]]]
在e
的所有元素都传递给块之后,块返回
h #=> {3=>[[0, 5, 14], [1, 6, 15]],
# 1=>[[2], [3], [7], [8], [9], [10], [11], [12], [13], [14], [15], [16]],
# 2=>[[4, 16], [5, 14], [6, 15]]}
考虑只出现一次的子字符串:h[1]
。其中之一是[2]
。这适用于从str
偏移2
开始的4字符子字符串:
str[2,4]
#=> "BABA"
发现它是该子字符串的唯一实例。同样,出现两次的子字符串是str[4,4] = str[16,4] #=> "BABB", given by h[2][0] #=> [4, 16]
。
接下来,我们确定长度为4的子字符串的最大频率:
c = h.max_by(&:first)
#=> [3, [[0, 5, 14], [1, 6, 15]]]
(也可以写成c = h.max_by { |k,_| k }
)。
d = c.last
#=> [[0, 5, 14], [1, 6, 15]]
为方便起见,将d
转换为哈希:
d.each_with_object({}) { |a,h| h[str[a.first,k]] = a }
#=> {"ABBA"=>[0, 5, 14], "BBAB"=>[1, 6, 15]}
并从方法中返回该哈希值。
值得一提的是一个细节。 d
可能包含两个或多个引用相同子字符串的数组,在这种情况下,关联键(子字符串)的值将等于这些数组的最后一个。这是一个简单的例子。
str = "AAA"
k = 2
在这种情况下,上面的数组d
将等于
d = [[0], [1]]
这两个都引用str[0,2] #=> str[1,2] #=> "AA"
。在构建哈希时,第一个被第二个覆盖:
d.each_with_object({}) { |a,h| h[str[a.first,k]] = a }
#=> {"AA"=>[1]}