2013-12-02 2 views
-1

Я пытаюсь прочитать каждую строку в массиве и подсчитать количество раз, когда буквы появляются в каждой позиции (т.е. 1, 2, 3, 4). Как я не использую многомерный массив и + = оператор правильно?Рубиновое манипулирование - сканирование и подсчет

def scan_str(arr) 
    position = [[]] 
    x = 0 
    arr.select do |word| 
    word.length.times do |i| 
     if word.index('G') == x 
      position[x+1,0] += 1 
      x += 1 
     elsif word.index('A') == x 
      position[x+1,1] += 1 
      x += 1 
     elsif word.index('T') == x 
      position[x+1,2] += 1 
      x += 1 
     elsif word.index('C') == x 
      position[x+1,3] += 1 
      x += 1 
     else 
      x += 1 
     end 
    end 
end 

p position 


end 

input = ["CTAGATA","CCCGAT","AAATT","TTCAAATGA"] 
scan_str(input) 

Спасибо, это полезно. Но теперь, как я могу манипулировать массивом без сообщения об ошибке «` [] ': неявное преобразование из nil в integer (TypeError) »... Должно быть что-то, что я не получаю от индекса или позиции [] [] синтаксис.

def scan_str(arr) 
    position = [[]] 
    z=arr.count 
    x = 0 
    arr.select do |word| 
     if word.index('G') == x 
      position[y][0] += (countG =+ 1)/z 
      x += 1 
      y += 1 
     elsif word.index('A') == x 
      position[y][1] += (countA =+ 1)/z 
      x += 1 
      y += 1 
     elsif word.index('T') == x 
      position[y][2] += (countT =+ 1)/z 
      x += 1 
      y += 1 
     elsif word.index('C') == x 
      position[y][3] += (countC =+ 1)/z 
      x += 1 
      y += 1 
     else 
      x += 1 
      y += 1 
     end 
    end 

p position 


end 

input = ["CTAGATA","CCCGAT","AAATT","TTCAAATGA"] 
scan_str(input) 
+0

можно и использовать хэш вместо этого? –

+1

Вы можете разместить свой желаемый результат? – miah

+0

Синтаксис 'position [x + 1,0]' не ссылается на массив массивов, как вы думаете. У вас нет «многомерного массива» в вашем вопросе, у вас есть «Array», где первым элементом является «Array» –

ответ

0

AS они почти ответили на него в комментариях:

position[1,3] является 3 элемента из 2-й позиции, считая от 0.
Правильный синтаксис: position[1][3].

пс. Пример:

arr=[[1,2,3], [4,5,6]] 
arr[1][2] 
# 6 # 3rd element from 2nd array, counting from 0! 
0

Поскольку проблема с вашим кодом было разъяснено, я хотел бы предложить более "Рубин-подобный" подход:

TEST = ['G', 'A', 'T', 'C'] 

def scan_str(arr) 
    TEST.each_with_object({}) {|c,h| h[c] = arr.each_with_object(Hash.new(0)) {|line, hh| \ 
    line.chars.each_with_index {|s,i| hh[i] += 1 if s==c}}} 
end 

arr = ["CTAGATA","CCCGAT","AAATT","TTCAAATGA"] 
scan_str(arr) 
    # => {"G"=>{3=>2, 7=>1}, \ 
    # => "A"=>{2=>2, 4=>3, 6=>1, 0=>1, 1=>1, 3=>1, 5=>1, 8=>1}, \ 
    # => "T"=>{1=>2, 5=>2, 3=>1, 4=>1, 0=>1, 6=>1}, \ 
    # => "C"=>{0=>2, 1=>1, 2=>2}} 

Несколько пунктов:

  • Вероятно, наиболее удобно помещать результаты в хэш. Здесь у меня есть scan_str, возвращающий хэш, ключи которого являются элементами TEST. Значение каждой клавиши само по себе является хешем, причем каждая клавиша является позицией смещения линии, а связанное значение представляет собой количество раз, когда буква, указанная внешним ключом, находится в этом положении.
  • Я сначала перебираю элементы TEST, используя Enumerable#each_with_object, причем объектом по умолчанию является пустой хеш {}. Внутри блока хэш ссылается на h. Альтернативой было бы определить пустой (h = {}) в строке выше, а затем вместо этого использовать TEST.each {|c|.... Если бы я сделал это, было бы также необходимо добавить строку h в конце метода, чтобы хэш был возвращен.
  • Для каждого элемента c из TEST, я перебираю линии массива, снова используя each_with_object. Однако на этот раз значением по умолчанию для объекта является Hash.new(0), который создает хэш со значениями по умолчанию, равными нулю. При этом, когда hh[i] += 1 выполняется во внутреннем цикле, нам не нужно проверять, имеет ли hh ключ i; если это не так, Ruby сначала выполняет hh[i] = 0 (ноль является значением по умолчанию), затем hh[i] += 1 => 1.
  • Для каждой строки line.chars преобразует строку в массив символов. Затем я повторяю с Enumerable#each_with_index. Внутри блока символ (строка длины один) и смещение линии ссылаются на s и i соответственно.

Существует несколько способов получить желаемый результат. Первым и, вероятно, самым простым было бы просто изменить код, который я уже предложил. Я сделаю это позже сегодня. Во-вторых, использовать приведенный выше код как «вспомогательный метод».

Используйте вспомогательный метод»

Чтобы использовать код, который мы уже имеем, переименовать метод scan_str выше scan_str_helper и добавить:

def scan_str(arr) 
    h = scan_str_helper(arr) 
    posh = Hash[h.values.map(&:keys).flatten.uniq.map {|e| \ 
    [e,Hash[TEST.zip([0]*TEST.size)]]}] 
    h.each {|k,v| v.each {|kk,vv| posh[kk][k] += vv}} 
    posh.each_with_object({}) {|(k,v),hp| tot = 1.0 * v.values.reduce(&:+); \ 
    hp[k] = Hash[v.keys.zip(v.values.map {|e| e/tot})]} 
end 

scan_str(arr) 
    # {3=>{"G"=>0.5, "A"=>0.25, "T"=>0.25, "C"=>0.0}, 7=>{"G"=>1.0, "A"=>0.0, "T"=>0.0, "C"=>0.0}, 
    # 2=>{"G"=>0.0, "A"=>0.5, "T"=>0.0, "C"=>0.5}, 4=>{"G"=>0.0, "A"=>0.75, "T"=>0.25, "C"=>0.0}, 
    # 6=>{"G"=>0.0, "A"=>0.5, "T"=>0.5, "C"=>0.0}, 0=>{"G"=>0.0, "A"=>0.25, "T"=>0.25, "C"=>0.5}, 
    # 1=>{"G"=>0.0, "A"=>0.25, "T"=>0.5, "C"=>0.25}, 
    # 5=>{"G"=>0.0, "A"=>0.3333333333333333, "T"=>0.6666666666666666, "C"=>0.0}, 
    # 8=>{"G"=>0.0, "A"=>1.0, "T"=>0.0, "C"=>0.0}} 

Несколько дополнительных примечаний:

  • h.values.map(&:keys).flatten.uniq => [3, 7, 2, 4, 6, 0, 1, 5, 8] `просто создает массив смещений позиции, которые содержат один или несколько элементов TEST.
  • h.keys.zip([0]*TEST.size) => h.keys.zip([0, 0, 0, 0]) => Hash[["G",0], ["A",0], ["T",0], ["C",0]]] => {"G"=>0, "A"=>0, "T"=>0, "C"=>0}, поэтому для e = 3 (скажем), Hash[3, {"G"=>0, "A"=>0, "T"=>0, "C"=>0}] => {3=>{"G"=>0, "A"=>0, "T"=>0, "C"=>0}}.
  • Вместо h.keys.zip([0]*TEST.size) у вас может возникнуть соблазн написать a = [0]*TEST.size; TEST.zip(a). Это не работает. Я оставлю это вам, чтобы понять, почему.
  • h.each {|k,v| v.each {|kk,vv| posh[kk][k] += vv}} заполняет хэш posh => # {3=>{"G"=>2, "A"=>1, "T"=>1, "C"=>0}, 7=>{"G"=>1, "A"=>0, "T"=>0, "C"=>0}, # 2=>{"G"=>0, "A"=>2, "T"=>0, "C"=>2}, 4=>{"G"=>0, "A"=>3, "T"=>1, "C"=>0}, # 6=>{"G"=>0, "A"=>1, "T"=>1, "C"=>0}, 0=>{"G"=>0, "A"=>1, "T"=>1, "C"=>2}, # 1=>{"G"=>0, "A"=>1, "T"=>2, "C"=>1}, 5=>{"G"=>0, "A"=>1, "T"=>2, "C"=>0}, # 8=>{"G"=>0, "A"=>1, "T"=>0, "C"=>0}}.
  • Последняя строка просто преобразует числа вхождений в дроби. Например, 3=>{"G"=>2, "A"=>1, "T"=>1, "C"=>0} преобразуется в 3=>{"G"=>0.5, "A"=>0.25, "T"=>0.25, "C"=>0.0}

модификации исходного кода

def scan_str(arr) 
    a = Array.new(arr.map(&:size).max).map {|e| \ 
      Hash[TEST.zip(Array.new(TEST.size,0))]} 
    arr.each {|s| s.chars.each_with_index {|c,i| TEST.each \ 
     {|ss| a[i][ss] += 1 if c == ss}}} 
    Hash[a.map.with_index {|h,i| tot = 1.0 * h.values.reduce(&:+); tot > 0.0 ? \ 
     [i, Hash[h.keys.zip(h.values.map {|e| e/tot})]] : nil}.compact] 
    end 
  • Первый оператор создает массив a, г-й элемент, соответствующий характеру смещения я в каждой строке. Значение i-го элемента - это хеш, о котором говорится в следующей заметке, при этом все значения равны нулю.
  • Второе заявление заполняет массив a: # => [{"G"=>0, "A"=>1, "T"=>1, "C"=>2}, {"G"=>0, "A"=>1, "T"=>2, "C"=>1}, # => {"G"=>0, "A"=>2, "T"=>0, "C"=>2}, {"G"=>2, "A"=>1, "T"=>1, "C"=>0}, # => {"G"=>0, "A"=>3, "T"=>1, "C"=>0}, {"G"=>0, "A"=>1, "T"=>2, "C"=>0}, # => {"G"=>0, "A"=>1, "T"=>1, "C"=>0}, {"G"=>1, "A"=>0, "T"=>0, "C"=>0}, # => {"G"=>0, "A"=>1, "T"=>0, "C"=>0}].
  • Последнее утверждение преобразует каждый элемент a в хэш, если сумма значений положительна; еще до nil. compact удаляет все элементы, которые являются nil. Помещение Hash[ и начало и ] в конце преобразует массив в хэш, который возвращается scan_str.
  • Обратите внимание, что этот подход дает тот же результат, что и метод, который используется методом «помощник», хотя порядок элементов хэша отличается.
+0

Спасибо, что это определенно получилось - однако желаемый результат будет «Позиция 1 - {« C »: 50%,« A »: 25%,« T »: 25%,« G »: 0%} Позиция 2 - { «C»: 25%, «A»: 25%, «T»: 50%, «G»: 0%} »и т. Д. – Rabbitshoe

+0

OK. Это легко исправить. Я сделаю редактирование. –

+0

Спасибо, я думаю, что я понимаю ваши методы, но было бы любопытно, как вы меняете число букв на число символов, сохраняя при этом хэш. Я думаю, что я пропущу, как это возможно, так как новый случай экземпляров ключа/значения должен быть создан для каждой позиции символа. – Rabbitshoe

0

Это не очень красиво и, вероятно, следует разделить на несколько функций:

a = ["CTAGATA","CCCGAT","AAATT","TTCAAATGA"] 

p Hash[ 
a.map{|sub| sub.chars.with_index(1).to_a} 
    .flatten(1).group_by(&:last) 
    .map{|pos, values| 
    [pos, Hash[values.group_by{|char,|char}.map{|char,s|[char, s.size.to_f/values.length]}]] 
    } 
] #=> {1=>{"C"=>0.5, "A"=>0.25, "T"=>0.25}, 2=>{"T"=>0.5, "C"=>0.25, "A"=>0.25}, 3=> 
Смежные вопросы