2016-11-21 9 views
2

У меня есть набор студентов, каждый выбранный определенное количество курсов, которые они хотят принять следующий семестр, представленный как массив хэшей:Построение матрицы пересечений в Рубине

[ 
{"student"=>"1", "English"=>true, "Algebra"=>true, "History"=>false}, 
{"student"=>"2", "English"=>false, "Algebra"=>false, "History"=>true}, 
{"student"=>"3", "English"=>false, "Algebra"=>true, "History"=>false}, 
{"student"=>"4", "English"=>true, "Algebra"=>false, "History"=>true} 
] 

Я хочу построить матрица, показывающая, сколько конфликтов существует между каждым курсом, конечный результат что-то вроде этого:

 English Algebra History 
English 2  1  1 
Algebra 1  2  - 
History 1  -  2 

Если число на пересечении является количество студентов, отобранных как курсы, то есть число на intersecti на (английском, английском) - 2 = общее количество студентов, выбравших английский. Число в (История, Алгебра) - «-», потому что никогда не было ученика, который выбрал оба этих курса.

Я попытался взглянуть на рубиновые документы для класса Matrix, и казалось, что это относится к более математическим матрицам - я не уверен, как его перенастроить для этой цели, или если это подходящий класс для этой проблемы.

Какой подход я мог бы попытаться исследовать/googling, чтобы эффективно построить такую ​​матрицу?

+0

ли ваши хэши хранятся в массиве? –

+0

Да! Они на самом деле читаются из csv как массив хэшей, где каждый хеш описывает конкретного ученика – zqe

+0

Возможно, вы захотите обновить свой вопрос, чтобы отразить это :) –

ответ

4

Следующие работы для любого количества школьных предметов и любого размера группировки (т. Е. Не только 2).

Код

def count_groupings(arr, group_size) 
    combos = (arr.flat_map { |h| h.keys }.uniq - ["student"]). 
    repeated_combination(group_size).to_a.product([0]).to_h 
    arr.each do |h| 
    keys = h.keys.select { |k| h[k] == true } 
    combos.keys.each { |k| combos[k] += 1 if (k-keys).empty? } 
    end 
    combos  
end 

Примеры

arr = [ 
    {"student"=>"1", "English"=>true, "Algebra"=>true, "History"=>false}, 
    {"student"=>"2", "English"=>false, "Algebra"=>false, "History"=>true}, 
    {"student"=>"3", "English"=>false, "Algebra"=>true, "History"=>false}, 
    {"student"=>"4", "English"=>true, "Algebra"=>false, "History"=>true} 
] 

count_groupings(arr, 1) 
    #=> {["English"]=>2, ["Algebra"]=>2, ["History"]=>2} 
count_groupings(arr, 2) 
    #=> {["English", "English"]=>2, ["English", "Algebra"]=>1, ["English", "History"]=>1, 
    # ["Algebra", "Algebra"]=>2, ["Algebra", "History"]=>0, ["History", "History"]=>2} 
count_groupings(arr, 3) 
    #=> {["English", "English", "English"]=>2, ["English", "English", "Algebra"]=>1, 
    # ["English", "English", "History"]=>1, ["English", "Algebra", "Algebra"]=>1, 
    # ["English", "Algebra", "History"]=>0, ["English", "History", "History"]=>1, 
    # ["Algebra", "Algebra", "Algebra"]=>2, ["Algebra", "Algebra", "History"]=>0, 
    # ["Algebra", "History", "History"]=>0, ["History", "History", "History"]=>2} 

Объяснение

См Array#repeated_combination.

Следующие шаги приведены для group_size #=> 2.

a = arr.flat_map { |h| h.keys } 
    #=> ["student", "English", "Algebra", "History", "student", "English", 
    # "Algebra", "History", "student", "English", "Algebra", "History", 
    # "student", "English", "Algebra", "History"] 
b = a.uniq 
    #=> ["student", "English", "Algebra", "History"] 
c = b - ["student"] 
    #=> ["English", "Algebra", "History"] 
d = c.repeated_combination(group_size) 
    #=> #<Enumerator: ["English", "Algebra", "History"]:repeated_combination(2) 
e = d.to_a 
    #=> [["English", "English"], ["English", "Algebra"], ["English", "History"], 
    # ["Algebra", "Algebra"], ["Algebra", "History"], ["History", "History"]] 
f = e.product([0]) 
    #=> [[["English", "English"], 0], [["English", "Algebra"], 0], 
    # [["English", "History"], 0], [["Algebra", "Algebra"], 0], 
    # [["Algebra", "History"], 0], [["History", "History"], 0]] 
combos = f.to_h 
    #=> {["English", "English"]=>0, ["English", "Algebra"]=>0, ["English", "History"]=>0, 
    # ["Algebra", "Algebra"]=>0, ["Algebra", "History"]=>0, ["History", "History"]=>0} 

g = arr.each 
    #=> #<Enumerator: [{"student"=>"1", "English"=>true, "Algebra"=>true, "History"=>false}, 
    # ... 
h = g.next 
    #=> {"student"=>"1", "English"=>true, "Algebra"=>true, "History"=>false} 
i = h.keys 
    #=> ["student", "English", "Algebra", "History"] 
keys = i.select { |k| h[k] == true } 
    #=> ["English", "Algebra"] 
j = combos.keys 
    #=> [["English", "English"], ["English", "Algebra"], ["English", "History"], 
    # ["Algebra", "Algebra"], ["Algebra", "History"], ["History", "History"]] 
m = j.each 
    #=> #<Enumerator: [["English", "English"], ["English", "Algebra"], 
    # ...]:each> 
k = m.next 
    #=> ["English", "English"] 
(k-keys).empty? 
    #=> (["English", "English"] - ["English", "Algebra"]).empty? 
    #=> [].empty?  
    #=> true 
combos[k] += 1 
combos 
    #=> {["English", "English"]=>1, ["English", "Algebra"]=>0, ["English", "History"]=>0, 
    # ["Algebra", "Algebra"]=>0, ["Algebra", "History"]=>0, ["History", "History"]=>0} 

k = m.next 
    #=> ["English", "Algebra"] 
(k-keys).empty? 
    #=> (["English", "Algebra"] - ["English", "Algebra"]).empty? 
    #=> [].empty? 
    #=> true 
combos[k] += 1 
combos 
    #=> {["English", "English"]=>1, ["English", "Algebra"]=>1, ["English", "History"]=>0, 
    # ["Algebra", "Algebra"]=>0, ["Algebra", "History"]=>0, ["History", "History"]=>0} 

Остальные расчеты аналогичны.При желании, можно написать

combos = (arr.flat_map { |h| h.keys }.uniq - ["student"]). 
    repeated_combination(group_size).map(&:uniq).product([0]).to_h 
    #=> {["English"]=>0, ["English", "Algebra"]=>0, ["English", "History"]=>0, 
    # ["Algebra"]=>0, ["Algebra", "History"]=>0, ["History"]=>0} 

Обратите внимание на инициализацию combos для group_size = 1:

combos = (arr.flat_map { |h| h.keys }.uniq - ["student"]). 
    repeated_combination(group_size).to_a.product([0]).to_h 
    #=> {["English"]=>0, ["Algebra"]=>0, ["History"]=>0} 

и для group_size = 3

combos = (arr.flat_map { |h| h.keys }.uniq - ["student"]). 
    repeated_combination(group_size).to_a.product([0]).to_h 
    #=> {["English", "English", "English"]=>0, ["English", "English", "Algebra"]=>0, 
    # ["English", "English", "History"]=>0, ["English", "Algebra", "Algebra"]=>0, 
    # ["English", "Algebra", "History"]=>0, ["English", "History", "History"]=>0, 
    # ["Algebra", "Algebra", "Algebra"]=>0, ["Algebra", "Algebra", "History"]=>0, 
    # ["Algebra", "History", "History"]=>0, ["History", "History", "History"]=>0} 

альтернативной структуры данных

Если есть выбор структуры данных, хэш может быть проще работать, чем массив хэшей, и нет необходимости включать курсы, которые не участвуют в этом классе (особенно, когда учатся сотни курсов).

courses_by_student = { 
    1 => %w| English Algebra|, 
    2 => %w| History |, 
    3 => %w| Algebra |, 
    4 => %w| English History | 
} 
+0

Вау! Спасибо, я собираюсь использовать документы, чтобы выяснить, как и почему эта комбинация методов работает. Похоже, я мог бы использовать один и тот же код с небольшим изменением, чтобы найти перекрестки между курсами троек, это правда ? Единственное, что этот ответ не делает, это выделить, когда есть нулевые конфликты между выборами курса, есть ли лучший способ, чем просто проверить, какие пары еще не представлены, и считая, что у них есть счет нуля? – zqe

+0

Да на троек (или больше). Просто измените 'repeat_combination (2)' на 'repeat_combination (n)', где 'n' является аргументом вашего метода. Я не знаю, что вы подразумеваете под «нулевыми конфликтами», но если в хеше значение равно нулю, это означает, что ни один из студентов не принимает пару курсов, представленных ключом для этого значения. –

+0

Большое спасибо за расширение вашего ответа! Извините, да, по конфликтам я имел в виду, когда количество студентов, запрашивающих эту пару, равно 0. Окончательный вывод хэша не отображается, чтобы перечислять пары, в которых 0 студентов запрашивают эту пару, например (History, Algebra) - эти пары (он показывает, что эти классы могут работать одновременно) - есть ли простой способ включить эти комбинации в вывод? Я прорабатываю свое подробное пошаговое объяснение, я так благодарен за вашу помощь, особенно за возможность работать и понимать, как вы ее решили. Спасибо! – zqe

1
data = [ 
{"student"=>"1", "English"=>true, "Algebra"=>true, "History"=>false}, 
{"student"=>"2", "English"=>false, "Algebra"=>false, "History"=>true}, 
{"student"=>"3", "English"=>false, "Algebra"=>true, "History"=>false}, 
{"student"=>"4", "English"=>true, "Algebra"=>false, "History"=>true} 
] 

classes = data.first.keys.reject { |class_name| class_name == "student" } 
print ' ' * 10 
classes.each { |class_name| print "#{class_name}".center(10) } 
puts 
classes.each_with_index do |row_class_name, idx| 
    print row_class_name.ljust(10) 

    classes.each do |col_class_name| 
    count = data.count do |hash| 
     hash[row_class_name] && hash[col_class_name] 
    end 
    print count.to_s.center(10) 
    end 
    puts 
end 

Это выведет

  English Algebra History 
English  2   1   1  
Algebra  1   2   0  
History  1   0   2  
+0

Ницца, и вы печатаете стол! Обратите внимание, что вы не используете 'idx'. Я предполагаю, что это реликвия предыдущего проекта. –

+0

Спасибо! Удивительно видеть разные подходы к одной и той же проблеме - я медленно прорабатываю это, чтобы полностью понять это – zqe

 Смежные вопросы

  • Нет связанных вопросов^_^