Ruby array inject - ruby

I am trying to log the average running time of 10 threads by using the inject method but it's giving me this error:
undefined method `+' for #<Thread:0x10b211590 dead> (NoMethodError)
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/open-uri.rb:301:in `inject'
from client_test.rb:13:in `each'
from client_test.rb:13:in `inject'
from client_test.rb:13
Here's the code:
require 'open-uri'
program_start_time = Time.now
threads = 10.times.map do
Thread.new do
time = Time.now
open('http://ca.yahoo.com/?p=us').read.length
Time.now-time
end
end
threads.map &:join
puts threads.inject() { |sum, e| sum + e.value}.to_f / threads.size
puts Time.now - program_start_time

You need to provide an initial value for inject in this case, since if you don't, the initial value is simply the first element in the array:
puts threads.inject(0) { |sum, e| sum + e.value}.to_f / threads.size

You didn't provide an initial value for sum in
threads.inject() { |sum, e| sum + e.value}.to_f / threads.size
Fix it with
threads.inject(0) { |sum, e| sum + e.value}.to_f / threads.size

Related

Find number of missing numbers to complete consecutive array

I am trying to write a function that gives you the number of numbers need to complete a consecutive array. For example if we have the array [3,5,7] the function should return 2 (i.e. 4,6). I have come up with the code below but it gives me the following error? Any ideas why? Thanks!
def consecutive(*arr)
sorted = arr.sort
current_count = 0
sorted.each_index do |i|
next if i == sorted.length
difference = arr[i+1] - arr[i] - 1
current_count += difference
end
current_count
end
And this is the error:
undefined method `-' for nil:NilClass
(repl):9:in `block in Consecutive'
(repl):6:in `each_index'
(repl):6:in `Consecutive'
(repl):16:in `<main>'
If arr is your array, you can do this:
arr = [3,1,5,7,8]
f,l = arr.minmax
#=> [1, 8]
l-f+1 - arr.size
#=> 3
arr[i + 1] is going to be nil on your final pass through each_index. You can use each_cons(2) instead and have it deal with the indices for you:
def Consecutive(arr)
sorted = arr.sort
current_count = 0
sorted.each_cons(2) do |a, b|
difference = b - a - 1
current_count += difference
end
current_count
end
Consecutive([3,5,7])
=> 2

Passing a splat parameter from one method to another method

I would like to create methods called 'add' and 'subtract' using splat parameter as below:
def add(*numbers)
numbers.inject(0) { |sum, n| sum + n }
end
def subtract(*numbers)
numbers[0] - add(numbers[1..-1])
end
But it didn't work.
What should I do to pass the splat parameter from one to another? (Especially for some specific range...)
Thank you!
When you call add function, add * before the array object to unpack array as multiple arguments.
def add(*numbers)
numbers.inject(0) { |sum, n| sum + n }
end
def subtract(*numbers)
numbers[0] - add(*numbers[1..-1])
# ^
end
subtract(9, 0, 1, 2) # => 6
subtract(9, 1) # => 8
Alternatively, you can accept numbers into the add function as an array.
def add(numbers)
numbers.inject(0) { |sum, n| sum + n }
end
def subtract(*numbers)
numbers[0] - add(numbers[1..-1])
end
A more compact way of doing this (by the way) would be
def subtract(*numbers)
numbers.shift - numbers.inject(:+)
end
def add *numbers
[*numbers].flatten.inject 0, :+
end
def subtract *numbers
numbers.shift - add(numbers)
end
subtract(9, 0, 1, 2) # ⇒ 6
subtract(9, 1) # ⇒ 8

Ruby fiber "dead fiber called"

Playing round with fibers and I know I'm doing something stupid here. But trying to create a simple enumerable type thing based on fibers (it's obviously trivial using Enumerable). When I run the following I get a dead fiber error after it prints out the first couple of iterations. Does anyone know why?
class SumPow
def initialize(a)
sum = a
pow = a
#fiber = Fiber.new do
Fiber.yield sum
pow = pow * a
sum = sum + pow
end
end
def next
#fiber.resume
end
def each
loop do
yield self.next
end
end
end
sp = SumPow.new(3)
sp.each do |sum|
puts sum
end
output:
3
12
tmp/fiber_enum.rb:96:in `resume': dead fiber called (FiberError)
from tmp/fiber_enum.rb:96:in `next'
from tmp/fiber_enum.rb:101:in `block in each'
from tmp/fiber_enum.rb:100:in `loop'
from tmp/fiber_enum.rb:100:in `each'
from tmp/fiber_enum.rb:108:in `<main>'
This code on the other hand works completely fine:
fib = Fiber.new do
x, y = 0, 1
loop do
Fiber.yield y
x, y = y, x + y
end
end
7.times { puts fib.resume }
output:
1
1
2
3
5
8
13
The code is calling resume regardless of state of the fiber.
Check whether fiber can resume by calling Fiber#alive?
def each
while #fiber.alive? do
yield self.next
end
end
NOTE You need to do require 'fiber' to use alive? method.
UPDATE accoridng to the question edit.
The original code yields only once. To iterate indefinitely, you need loop.
def initialize(a)
sum = a
pow = a
#fiber = Fiber.new do
loop do # <-------------
Fiber.yield sum
pow = pow * a
sum = sum + pow
end # <-----------------
end
end

ArgumentError in #new, Subclassing Enumerator

I'm subclassing Enumerator like this:
class CuadraticPrimeGenerator < Enumerator
def initialize(a=1,b=49)
super do |y|
x = 1
loop do
y << x**2 + a*x + b
x += 1
end
end
end
end
However...
> CuadraticPrimeGenerator.new(1,49)
0027.rb:41:in `initialize': 49 is not a symbol (TypeError)
from 0027.rb:41:in `initialize'
from 0027.rb:48:in `new'
from 0027.rb:48:in `<main>'
Thoughts?
What about:
class CuadraticPrimeGenerator < Enumerator
def initialize(a=1,b=49)
super() do |y|
count, x = 0, 1
loop { y.yield(x**2 + a*x + b) }
end
end
end

How can I do standard deviation in Ruby?

I have several records with a given attribute, and I want to find the standard deviation.
How do I do that?
module Enumerable
def sum
self.inject(0){|accum, i| accum + i }
end
def mean
self.sum/self.length.to_f
end
def sample_variance
m = self.mean
sum = self.inject(0){|accum, i| accum +(i-m)**2 }
sum/(self.length - 1).to_f
end
def standard_deviation
Math.sqrt(self.sample_variance)
end
end
Testing it:
a = [ 20, 23, 23, 24, 25, 22, 12, 21, 29 ]
a.standard_deviation
# => 4.594682917363407
01/17/2012:
fixing "sample_variance" thanks to Dave Sag
It appears that Angela may have been wanting an existing library. After playing with statsample, array-statisics, and a few others, I'd recommend the descriptive_statistics gem if you're trying to avoid reinventing the wheel.
gem install descriptive_statistics
$ irb
1.9.2 :001 > require 'descriptive_statistics'
=> true
1.9.2 :002 > samples = [1, 2, 2.2, 2.3, 4, 5]
=> [1, 2, 2.2, 2.3, 4, 5]
1.9.2p290 :003 > samples.sum
=> 16.5
1.9.2 :004 > samples.mean
=> 2.75
1.9.2 :005 > samples.variance
=> 1.7924999999999998
1.9.2 :006 > samples.standard_deviation
=> 1.3388427838995882
I can't speak to its statistical correctness, or your comfort with monkey-patching Enumerable; but it's easy to use and easy to contribute to.
The answer given above is elegant but has a slight error in it. Not being a stats head myself I sat up and read in detail a number of websites and found this one gave the most comprehensible explanation of how to derive a standard deviation. http://sonia.hubpages.com/hub/stddev
The error in the answer above is in the sample_variance method.
Here is my corrected version, along with a simple unit test that shows it works.
in ./lib/enumerable/standard_deviation.rb
#!usr/bin/ruby
module Enumerable
def sum
return self.inject(0){|accum, i| accum + i }
end
def mean
return self.sum / self.length.to_f
end
def sample_variance
m = self.mean
sum = self.inject(0){|accum, i| accum + (i - m) ** 2 }
return sum / (self.length - 1).to_f
end
def standard_deviation
return Math.sqrt(self.sample_variance)
end
end
in ./test using numbers derived from a simple spreadsheet.
#!usr/bin/ruby
require 'enumerable/standard_deviation'
class StandardDeviationTest < Test::Unit::TestCase
THE_NUMBERS = [1, 2, 2.2, 2.3, 4, 5]
def test_sum
expected = 16.5
result = THE_NUMBERS.sum
assert result == expected, "expected #{expected} but got #{result}"
end
def test_mean
expected = 2.75
result = THE_NUMBERS.mean
assert result == expected, "expected #{expected} but got #{result}"
end
def test_sample_variance
expected = 2.151
result = THE_NUMBERS.sample_variance
assert result == expected, "expected #{expected} but got #{result}"
end
def test_standard_deviation
expected = 1.4666287874
result = THE_NUMBERS.standard_deviation
assert result.round(10) == expected, "expected #{expected} but got #{result}"
end
end
I'm not a big fan of adding methods to Enumerable since there could be unwanted side effects. It also gives methods really specific to an array of numbers to any class inheriting from Enumerable, which doesn't make sense in most cases.
While this is fine for tests, scripts or small apps, it's risky for larger applications, so here's an alternative based on #tolitius' answer which was already perfect. This is more for reference than anything else:
module MyApp::Maths
def self.sum(a)
a.inject(0){ |accum, i| accum + i }
end
def self.mean(a)
sum(a) / a.length.to_f
end
def self.sample_variance(a)
m = mean(a)
sum = a.inject(0){ |accum, i| accum + (i - m) ** 2 }
sum / (a.length - 1).to_f
end
def self.standard_deviation(a)
Math.sqrt(sample_variance(a))
end
end
And then you use it as such:
2.0.0p353 > MyApp::Maths.standard_deviation([1,2,3,4,5])
=> 1.5811388300841898
2.0.0p353 :007 > a = [ 20, 23, 23, 24, 25, 22, 12, 21, 29 ]
=> [20, 23, 23, 24, 25, 22, 12, 21, 29]
2.0.0p353 :008 > MyApp::Maths.standard_deviation(a)
=> 4.594682917363407
2.0.0p353 :043 > MyApp::Maths.standard_deviation([1,2,2.2,2.3,4,5])
=> 1.466628787389638
The behavior is the same, but it avoids the overheads and risks of adding methods to Enumerable.
The presented computation are not very efficient because they require several (at least two, but often three because you usually want to present average in addition to std-dev) passes through the array.
I know Ruby is not the place to look for efficiency, but here is my implementation that computes average and standard deviation with a single pass over the list values:
module Enumerable
def avg_stddev
return nil unless count > 0
return [ first, 0 ] if count == 1
sx = sx2 = 0
each do |x|
sx2 += x**2
sx += x
end
[
sx.to_f / count,
Math.sqrt( # http://wijmo.com/docs/spreadjs/STDEV.html
(sx2 - sx**2.0/count)
/
(count - 1)
)
]
end
end
As a simple function, given a list of numbers:
def standard_deviation(list)
mean = list.inject(:+) / list.length.to_f
var_sum = list.map{|n| (n-mean)**2}.inject(:+).to_f
sample_variance = var_sum / (list.length - 1)
Math.sqrt(sample_variance)
end
If the records at hand are of type Integer or Rational, you may want to compute the variance using Rational instead of Float to avoid errors introduced by rounding.
For example:
def variance(list)
mean = list.reduce(:+)/list.length.to_r
sum_of_squared_differences = list.map { |i| (i - mean)**2 }.reduce(:+)
sum_of_squared_differences/list.length
end
(It would be prudent to add special-case handling for empty lists and other edge cases.)
Then the square root can be defined as:
def std_dev(list)
Math.sqrt(variance(list))
end
In case people are using postgres ... it provides aggregate functions for stddev_pop and stddev_samp - postgresql aggregate functions
stddev (equiv of stddev_samp) available since at least postgres 7.1, since 8.2 both samp and pop are provided.
Or how about:
class Stats
def initialize( a )
#avg = a.count > 0 ? a.sum / a.count.to_f : 0.0
#stdev = a.count > 0 ? ( a.reduce(0){ |sum, v| sum + (#avg - v) ** 2 } / a.count ) ** 0.5 : 0.0
end
end
You can place this as helper method and assess it everywhere.
def calc_standard_deviation(arr)
mean = arr.sum(0.0) / arr.size
sum = arr.sum(0.0) { |element| (element - mean) ** 2 }
variance = sum / (arr.size - 1)
standard_deviation = Math.sqrt(variance)
end

Resources