Complex Queries + Basic Statistics in Rails

I inherited a rails codebase that was written about 5 years ago. The codebase is a web application for a nonprofit that does leadership training and assessments. Training participants perform a self-assessment and invite peers to evaluate their leadership. Each training participant receives a report that shows their self score in relation to peer scores, and other leaders who have taken the same assessment. The nonprofit’s database contains more than 80,000 rows of assessment scores. Not exactly “big data”, but not exactly small either.

Each assessment report contains roughly 20 – 30 assessment questions. Each question shows the self-score, peer scores, average peer score, and the quartile (compared against all previous assessments).

For each question, I looked up the peer evaluation scores for the participant:

def numeric_answers_for_q(question_id)
  Answer.joins(:evaluation, :question).where(:evaluations => {self_eval: false,
    participant_id: @participant.id,
    completed:true},
    :questions => {id: question_id, answer_type: 'numeric'}).
    where.not(numeric_response: 0).
    where.not(numeric_response: nil).
    pluck(:numeric_response)
end

In this case, I found all numeric answers, (there are also text answers), for completed peer evaluations for the given question and participant. Zero and nil answers are considered as not applicable, so we ignore these results.

Once I receive an the array of peer answers, I create a histogram with the array. The results of the histogram are later displayed.

def histogram_for_q(answers)
  histogram = Array.new(11) {|i| 0}
  answers.each do |a|
    histogram[a] += 1 unless a.nil? || a.zero?
  end
  return histogram
end

I also need the mean score.

def mean_score_for_q(answers)
  answers.sum.to_f/answers.length
end

If I only needed the mean score, I could’ve replace ‘pluck(:numeric_response)’ with ‘average(:numeric_response)’ in the numeric_answers_for_q method. I do this when I need the mean scores from previous assessments for comparison.

def self.peer_assessment_scores(question_id, participant_id)
    avgs_by_participant = Answer.joins(:evaluation, :question).where(:evaluations => {self_eval: false,
                                                     completed:true},
                                                     :questions => {answer_type: 'numeric', id: question_id}).
                                                     where.not(:evaluations => { participant_id: participant_id }).
                                                     where.not(numeric_response: 0).
                                                     where.not(numeric_response: nil).
                                                     group('evaluations.participant_id').
                                                     average(:numeric_response)
    avgs_by_participant.map {|k,v| v.to_f }
end

This last method I found the pretty helpful. Being able to find grouped averages based on complex conditions.

Code reviews are welcomed. Please post suggestions in the comments.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

About Hadiyah

Software Engineer, Entrepreneur, and Startup Aficionado. I co-founded a student venture firm for students at historically black colleges, HBCUvc, software development agency, Playpen Labs, and Black Founders nonprofit.