How do I create an average from a Ruby array?
Try this:
arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5
Note the .to_f
, which you'll want for avoiding any problems from integer division. You can also do:
arr = [5, 6, 7, 8]
arr.inject(0.0) { |sum, el| sum + el } / arr.size
=> 6.5
You can define it as part of Array
as another commenter has suggested, but you need to avoid integer division or your results will be wrong. Also, this isn't generally applicable to every possible element type (obviously, an average only makes sense for things that can be averaged). But if you want to go that route, use this:
class Array
def sum
inject(0.0) { |result, el| result + el }
end
def mean
sum / size
end
end
If you haven't seen inject
before, it's not as magical as it might appear. It iterates over each element and then applies an accumulator value to it. The accumulator is then handed to the next element. In this case, our accumulator is simply an integer that reflects the sum of all the previous elements.
Edit: Commenter Dave Ray proposed a nice improvement.
Edit: Commenter Glenn Jackman's proposal, using arr.inject(:+).to_f
, is nice too but perhaps a bit too clever if you don't know what's going on. The :+
is a symbol; when passed to inject, it applies the method named by the symbol (in this case, the addition operation) to each element against the accumulator value.
Array Average Rails
average = temp.sum / temp.size.to_f
How to calculate averages of items in array
You can use Enumerable#each_cons
:
array = [ 1, 3, 5, 1, -10]
array.each_cons(2).map { |a| a.inject(:+) / 2.0 }
#=> [2.0, 4.0, 3.0, -4.5]
Summarizing it into a method:
def each_cost_avg(array, size = 2)
array.each_cons(size).map { |a| a.inject(:+) / size.to_f }
end
each_cost_avg(array, 2) #=> [2.0, 4.0, 3.0, -4.5]
each_cost_avg(array, 3) #=> [3.0, 3.0, -1.3333333333333333]
each_cost_avg(array, 4) #=> [2.5, -0.25]
Get the average of numbers in an array which is the values of an hash
hash_1 = {"Luke"=> [2,3,4], "Mark"=>[3,5], "Jack"=>[2]}
You don't need another hash for the given below code.
p hash_1.transform_values!{|x| x.sum/x.count}
Result
{"Luke"=>3, "Mark"=>4, "Jack"=>2}
Ruby: Average array of times
So you need do define a function that can calculate the average of times formatted as strings. Convert the data to minutes, avg the total minutes and then back to a time.
I would do it something like this:
a = ['18:35', '19:07', '23:09']
def avg_of_times(array_of_time)
size = array_of_time.size
avg_minutes = array_of_time.map do |x|
hour, minute = x.split(':')
total_minutes = hour.to_i * 60 + minute.to_i
end.inject(:+)/size
"#{avg_minutes/60}:#{avg_minutes%60}"
end
p avg_of_times(a) # = > "20:17"
Then when you call you function you check if any/all items in your array is formatted as a time. Maybe using regexp.
Sum and Average Array of Array
output = [
{"name"=>"aaa", "job"=>"a", "pay"=> 2 },
{"name"=>"zzz", "job"=>"a", "pay"=> 4 },
{"name"=>"xxx", "job"=>"a", "pay"=> 6 },
{"name"=>"yyy", "job"=>"a", "pay"=> 8 },
{"name"=>"aaa", "job"=>"b", "pay"=> 2 },
{"name"=>"zzz", "job"=>"b", "pay"=> 4 },
{"name"=>"xxx", "job"=>"b", "pay"=> 6 },
{"name"=>"yyy", "job"=>"b", "pay"=> 10 },
]
output.group_by { |obj| obj['job'] }.map do |key, list|
[key, list.map { |obj| obj['pay'] }.reduce(:+) / list.size.to_f]
end
The group_by
method will transform your list into a hash with the following structure:
{"a"=>[{"name"=>"aaa", "job"=>"a", "pay"=>2}, ...], "b"=>[{"name"=>"aaa", "job"=>"b", ...]}
After that, for each pair of that hash, we want to calculate the mean of its 'pay'
values, and return a pair [key, mean]
. We use a map for that, returning a pair with:
- They key itself (
"a"
or"b"
). - The mean of the values. Note that the values list has the form of a list of hashes. To retrieve the values, we need to extract the last element of each pair; that's what
list.map { |obj| obj['pay'] }
is used for. Finally, calculate the mean by suming all elements with.reduce(:+)
and dividing them by the list size as a float.
Not the most efficient solution, but it's practical.
Comparing the answer with @EricDuminil's, here's a benchmark with a list of size 8.000.000
:
def Wikiti(output)
output.group_by { |obj| obj['job'] }.map do |key, list|
[key, list.map { |obj| obj['pay'] }.reduce(:+) / list.size.to_f]
end
end
def EricDuminil(output)
count_and_sum = output.each_with_object(Hash.new([0, 0])) do |hash, mem|
job = hash['job']
count, sum = mem[job]
mem[job] = count + 1, sum + hash['pay']
end
result = count_and_sum.map do |job, (count, sum)|
[job, sum / count.to_f]
end
end
require 'benchmark'
Benchmark.bm do |x|
x.report('Wikiti') { Wikiti(output) }
x.report('EricDuminil') { EricDuminil(output) }
end
user system total real
Wikiti 4.100000 0.020000 4.120000 ( 4.130373)
EricDuminil 4.250000 0.000000 4.250000 ( 4.272685)
How to calculate average in a multidimensional array conditionally if their first values are the same
With a bit of debugging, the following should help get much faster results:
Answer.
joins(:question, :feedbacks). # assuming that answer has_many feedbacks
group(["questions.id", "feedbacks.week"]). # assuming week is integer column
average("CAST(answers.name AS INT)"). # assuming that name is string-y column
each_with_object({}) do |(keys, average), hash|
question_id, week = keys
hash[question_id] ||= []
hash[question_id][week] = average
end
If you want to keep things the way they are (not advised), then one working (albeit hard-to-follow) solution is this:
arr = [
[0, [[22, 3], [23, 5]]],
[0, [[22, 1], [23, 2]]],
[1, [[22, 4], [23, 4]]],
[1, [[22, 2], [23, 4]]]
]
arr.each_with_object({}) do |(a, b), hash|
c, d, e, f = b.flatten
# for first row this will be c, d, e, f = 22, 3, 23, 5
hash[c] ||= []
hash[c][a] ||= []
hash[c][a] << d
hash[e] ||= []
hash[e][a] ||= []
hash[e][a] << f
end.each_with_object({}) do |(k, v), hash|
# k are your 'keys' like 22, 23
# v is an array of arrays that you want to find out the averages of
hash[k] = \
v.map do |array|
array.reduce(:+).fdiv(array.size)
end
end
Related Topics
How to Calculate Number of Chars Common to Two Strings
What Is the Ruby ≪=≫ (Spaceship) Operator
Is 'Eval' Supposed to Be Nasty
Connecting Rails 3.1 With Multiple Databases
Eventsource/Server-Sent Events Through Nginx
When to Use Symbols Instead of Strings in Ruby
Reducing N+1 Queries Using the Bullet and Rspec Gems
Understanding [Classone, Classtwo].Each(&:My_Method)
Why Am I Getting Objects Printed Twice
Cannot Load Such File - Script/Rails: Getting This Error While Remote Debugging Through Rubymine
Case Statement With Multiple Values in Each 'When' Block
Ruby Class Instance Variable Vs. Class Variable
Difference Between Include and Extend in Ruby
How to Find and Return a Duplicate Value in Array
What's the Difference Between Uri.Escape and Cgi.Escape
How to Split (Chunk) a Ruby Array into Parts of X Elements