|
|
@ -127,7 +127,55 @@ class Node < ApplicationRecord |
|
|
end |
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def answer_percentages |
|
|
|
|
|
# Get all valid answers (non-nil values) |
|
|
|
|
|
valid_answers = answers.where.not(value: nil) |
|
|
|
|
|
|
|
|
|
|
|
# Count answers by value |
|
|
|
|
|
value_counts = valid_answers.group(:value).count |
|
|
|
|
|
|
|
|
|
|
|
# Calculate total number of answers |
|
|
|
|
|
total_answers = valid_answers.count |
|
|
|
|
|
|
|
|
|
|
|
# Initialize with 0 for both expected values |
|
|
|
|
|
percentages = {0 => 0, 1 => 0} |
|
|
|
|
|
|
|
|
|
|
|
# Return initial percentages if no answers |
|
|
|
|
|
return percentages if total_answers.zero? |
|
|
|
|
|
|
|
|
|
|
|
# Calculate exact percentages first (not rounded) |
|
|
|
|
|
exact_percentages = {} |
|
|
|
|
|
value_counts.each do |value, count| |
|
|
|
|
|
# Only consider values 0 and 1 |
|
|
|
|
|
next unless [0, 1].include?(value) |
|
|
|
|
|
exact_percentages[value] = count.to_f / total_answers * 100 |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
# First round down all percentages |
|
|
|
|
|
[0, 1].each do |value| |
|
|
|
|
|
percentages[value] = (exact_percentages[value] || 0).floor |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
# Calculate how much we're off by due to rounding |
|
|
|
|
|
remaining = 100 - percentages.values.sum |
|
|
|
|
|
|
|
|
|
|
|
# Distribute the remaining percentage points to values with the largest fractional parts |
|
|
|
|
|
if remaining > 0 |
|
|
|
|
|
# Filter exact percentages to only include 0 and 1 |
|
|
|
|
|
sorted_by_fraction = exact_percentages |
|
|
|
|
|
.select { |k, _| [0, 1].include?(k) } |
|
|
|
|
|
.sort_by { |_, p| p - p.floor } |
|
|
|
|
|
.reverse |
|
|
|
|
|
|
|
|
|
|
|
# Add 1 to the values with the largest fractional parts |
|
|
|
|
|
sorted_by_fraction.take(remaining).each do |value, _| |
|
|
|
|
|
percentages[value] += 1 |
|
|
|
|
|
end |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
# Return the percentages hash with keys 0 and 1 |
|
|
|
|
|
percentages |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
private |
|
|
private |
|
|
|
|
|
|
|
|
|