Finding the average of a table - ruby

I need to find the averages for all columns in the same row of a particular value of the first column. So for a table like the following:
0 11 12 40
1 22 24 92
0 12 13 45
1 24 26 90
2 33 36 138
1 22 24 80
2 36 39 135
0 11 12 46
2 33 36 120
I want the resulting table of averages:
0 11.33333333 12.33333333 43.66666667
1 22.66666667 24.66666667 87.33333333
2 34 37 131
So for example for the value 0..
I take the averages of (11 + 12 + 11)/3 on the second column, (12 + 13 + 12)/3 on the second, and (46 + 45 + 40)/3 on the third column

a = [
[0, 11, 12, 40],
[1, 22, 24, 92],
[0, 12, 13, 45],
[1, 24, 26, 90],
[2, 33, 36, 138],
...
]
def average array, index
array = array.select{|l| l[0] == index}
n = array.length.to_f
array.transpose.drop(1).map {|values| values.inject(0){|s, v| s += v}/n}
end
average(a, 0) # => [11.33333333, 12.33333333, 43.66666667]
To have all of them at once:
array.group_by{|l| l[0]}.map do |k, array|
n = array.length.to_f
[k, array.transpose.drop(1).map {|values| values.inject(0){|s, v| s += v}/n}]
end
# =>
[
[0, [11.33333333, 12.33333333, 43.66666667]],
[1, [22.66666667, 24.66666667, 87.33333333]]
[2, [34.0, 37.0, 131.0]]
]

data = "0 11 12 40
1 22 24 92
0 12 13 45
1 24 26 90
2 33 36 138
1 22 24 80
2 36 39 135
0 11 12 46
2 33 36 120"
avgs = data.split(/\n/).map{|d| d.split(/\t/)}.group_by{|d| d[0] }.each{|a,b| b.each(&:shift) }.inject({}){|avg, (k, ar)| avg[k] = ar.inject([0,0,0]){|av,(a,b,c)| av[0]+=a.to_f; av[1]+=b.to_f; av[2]+=c.to_f; av}.map{|e| e/ar.size}; avg}
#=> {"0"=>[11.333333333333332, 12.333333333333332, 43.66666666666667], "1"=>[22.666666666666664, 24.666666666666664, 87.33333333333334], "2"=>[34.0, 37.0, 131.0]}
to print it:
avgs.each{|k, arr| puts [k,*arr].join("\t") }
#=> 0 11.333333333333332 12.333333333333332 43.66666666666667
#=> 1 22.666666666666664 24.666666666666664 87.33333333333334
#=> 2 34.0 37.0 131.0
UPD
I've cleaned my method a little:
avgs = data.split(/\n/).
map{|d| d.split(/\t/).map(&:to_f)}.
group_by(&:first).
inject({}){|avg, (k, ar)| avg[k] = ar.transpose[1..-1].map{|av| av.inject(:+)/av.size}; avg}

say you have a table table = [ [0, 11, 12, 40] , [1, 22, 24, 92] .... [2, 33, 36, 120] ]
i'm presuming the table has a fixed number of elements on a line
first_elements_array = table.map { |line| line[0] }.uniq! #get [0,1,2] from your table
avgs = [] #will contain your [ [0, 11.333, 12.3333, 43.66667], ... ]
for element in first_elements_array
#i need an array with 4 zeros - the number of elements on a line
temp_sum = [0,0,0,0]
count = 0 #number of lines that start with 0, or 1, or 2 etc
#read the lines that start with 0, then 1, then 2 etc
for line in table
if line[0] == element
count += 1
#add in temp_sum the new line found, element by element
(0...line.length).each do |i|
temp_sum[i] += line[i]
end
end
end
line_avg = []
#so, temp_sum contains the sum for one line that starts with 0 or 1 or 2 etc. now calculate the average
for sum in temp_sum
line_avg << sum/count
end
#... and push it in an array
avgs << line_avg
end
this could probably be done more elegant so feel free to adapt it
also, haven't had any time to test it, let me know if it works

Related

Ruby program to print first n Fibonacci Seqeunce

Newbie here! Trying to implement a program to print the first 20 Fibonacci numbers in Ruby. I've managed to create a program which generates the nth number, but I want to produce all from 0 through to 20.
Is there a simple way to do this or do I need to rewrite the whole program?
CURRENT CODE
def fib(n)
if n < 1
return 0
elsif n == 1
return 1
else fib(n-2) + fib(n-1)
end
end
puts fib(20)
CURRENT OUTPUT EXAMPLE
6765
DESIRED OUTCOME
0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
At the moment you only print the last value returned by your method (fib(20)) but not the result of all intermediate steps.
An easy way would be to cache all intermediate results in a hash data structure. This would also improve performance for big n because you do not need to recalculate many values over and over again.
Then you can just print out all results from 0 to n:
def cached_fib(n)
#cache ||= Hash.new do |cache, n|
#cache[n] = n < 2 ? n : cache[n-1] + cache[n-2]
end
#cache[n]
end
def fib(n)
0.upto(n) { |i| puts cached_fib(i) }
end
fib(20)
#=> 0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
Printing each value is easier with a button-up approach where you start at 0 and 1 and calculate each following value based on its predecessors, e.g.:
i, j = 0, 1
puts i
puts j
21.times do
k = i + j
puts k
i, j = j, k
end
You could turn the above into an Enumerator:
fib = Enumerator.new do |y|
i, j = 0, 1
y << i
y << j
loop do
k = i + j
y << k
i, j = j, k
end
end
Which will generate the sequence:
fib.take(21)
#=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,
# 233, 377, 610, 987, 1597, 2584, 4181, 6765]

Trying to write a simple countdown loop in pairs with Ruby

This instructions are as follow:
NUMBER CRUNCHER
Write a method that takes a number as an argument
If the number is greater than 20
count down from the number by 2's
If the number is less than 20
count down from the number by 1's
Display the numbers as they count down to 0.
I have written this, but it's not doing what it's supposed. Any help?
def num_cruncher(num)
count = num
until count == 0 do
if num > 20
puts count - 2
else
puts "#{count}"
end
count -= 1
end
end
Here's your code, with as few changes as possible :
def num_cruncher(num)
count = num
until count < 0 do
puts count
if num > 20
count -= 2
else
count -= 1
end
end
end
num_cruncher(10)
# 10
# 9
# 8
# 7
# 6
# 5
# 4
# 3
# 2
# 1
# 0
num_cruncher(21)
# 21
# 19
# 17
# 15
# 13
# 11
# 9
# 7
# 5
# 3
# 1
By extracting the if-statement outside of the loop, the code becomes a bit shorter :
def num_cruncher(num)
if num > 20
step = 2
else
step = 1
end
until num < 0 do
puts num
num -= step
end
end
You can use Numeric#step here. Something like this:
def num_cruncher n
s = n > 20 ? -2 : -1
n.step(by: s, to: 0).entries
end
num_cruncher 23
#=> [23, 21, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1]

How to return the same random numbers in this BINGO program, ruby?

I am trying to continue returning the same random numbers generated initially, then for the iteration to continue looking for matching numbers as long as a "BINGO" is not found vertically, horizontally or diagonally.
class BingoBoard
def initialize
#bingo_board = Array.new(5) {Array(5.times.map{rand(1..100)})} #creates array with 5 sub arrays containing randomly generated numbers.
#bingo = {'B'=>0,'I'=>1,'N'=>2,'G'=>3,'O'=>4}
#num = rand(1..100) #random number
#letter_index = #bingo.values.sample #randomly generated index to target the elements in the matching index of the arrays sub-arrays.
end
def generator #generates the display
if #letter_index == 0
puts"B #{#num}"
elsif #letter_index == 1
puts"I #{#num}"
elsif #letter_index == 2
puts"N #{#num}"
elsif #letter_index == 3
puts"G #{#num}"
elsif #letter_index == 4
puts"O #{#num}"
end
end
def play #stamps X if random number is found inside sub-array of targeted index.
if #bingo_board[#letter_index].include?(#num)
#bingo_board[#letter_index].map! {|el| el == #num ? "X" : el}
puts ('%3s ' * 5) % 'BINGO'.split('')
5.times do |y|
5.times { |x| print "%3s " % #bingo_board[x][y] } #print the sub arrays vertically, with each index side by side, starting with 0 on the left.
puts ""
end
else #prints the bingo_board.
puts ('%3s ' * 5) % 'BINGO'.split('')
5.times do |y|
5.times { |x| print "%3s " % #bingo_board[x][y] }
puts " "
end
end
end
def run # one method to rule them all!
generator
play
end
end
#=> B 83
# B I N G O
# X 18 75 83 8
# 71 22 47 71 86
# 3 96 50 92 96
# 31 69 58 53 47
# 15 78 15 94 47
#=> I 22
# B I N G O
# X 18 75 83 8
# 71 X 47 71 86
# 3 96 50 92 96
# 31 69 58 53 47
# 15 78 15 94 47
#=> N 50
# B I N G O
# X 18 75 83 8
# 71 X 47 71 86
# 3 96 X 92 96
# 31 69 58 53 47
# 15 78 15 94 47
#=> G 53
# B I N G O
# X 18 75 83 8
# 71 X 47 71 86
# 3 96 X 92 96
# 31 69 58 X 47
# 15 78 15 94 47
#=> O 47
# B I N G O
# X 18 75 83 8
# 71 X 47 71 86
# 3 96 X 92 96
# 31 69 58 X 47
# 15 78 15 94 X
#=> "BINGO!!"
In order to generate the same "random" number in ruby, you need to provide a seed.
So, instead of using rand, you first need to create a random number generator with a seed:
my_random_number_generator = Random.new(1234)
And then use that:
#bingo_board = Array.new(5) {Array(5.times.map{my_random_number_generator.rand(1..100)})}
That will always generate the same numbers in the same sequence.

Why does these two injects give the same output in ruby?

Why is the output the same?
First inject:
puts (3...10).inject(0) { |sum, x| (x % 3 == 0 || x % 5 == 0) ? sum + x : sum }
# => 23
Second inject:
puts (3...10).inject { |sum, x| (x % 3 == 0 || x % 5 == 0) ? sum + x : sum }
# => 23
# Why not 26?
I thought if there is no argument passed to it, inject uses the first element of the collection as initial value.
So the second inject should return the same value as this one:
puts (3...10).inject(3) { |sum, x| (x % 3 == 0 || x % 5 == 0) ? sum + x : sum }
# => 26
Why does these two injects give the same output in ruby?
... Because they're supposed to. They only differ by the addition of a 0.
I thought if there is no argument passed to it, inject uses the first element of the collection as initial value.
It does. But it doesn't duplicate it.
Your first example receives these numbers:
0, 3, 4, 5, 6, 7, 8, 9
Your second example receives these numbers:
3, 4, 5, 6, ...
Adding 0 to the beginning doesn't affect the result, they're both 23, not 26 as you claim.
Your 3rd example returns 26 because it receives these numbers:
3, 3, 4, 5, 6, ...
#inject() with an argument:
result = (3...10).inject(0) do |sum, x|
puts "#{sum} <-- #{x}: sum = #{sum+x}"
sum + x
end
--output:--
0 <-- 3: sum = 3
3 <-- 4: sum = 7
7 <-- 5: sum = 12
12 <-- 6: sum = 18
18 <-- 7: sum = 25
25 <-- 8: sum = 33
33 <-- 9: sum = 42
...
#inject without an argument:
result = (3...10).inject() do |sum, x|
puts "#{sum} <-- #{x}: sum = #{sum+x}"
sum + x
end
--output:--
3 <-- 4: sum = 7
7 <-- 5: sum = 12
12 <-- 6: sum = 18
18 <-- 7: sum = 25
25 <-- 8: sum = 33
33 <-- 9: sum = 42
I always thought it takes the first element of the collection as
initial value and still performs the first iteration
The first iteration uses arr[0] as the sum and arr[1] as the first x. When you don't provide an argument for inject(), it's equivalent to doing this:
data = (3...10).to_a
initial_sum = data.shift
data.inject(initial_sum) do |sum, x|
puts "#{sum} <-- #{x}: sum = #{sum+x}"
sum + x
end
--output:--
3 <-- 4: sum = 7
7 <-- 5: sum = 12
12 <-- 6: sum = 18
18 <-- 7: sum = 25
25 <-- 8: sum = 33
33 <-- 9: sum = 42

Sum of even Fibonacci numbers?

Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...
By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.
a = [1,2]
upto = 4_000_000
while a[-2] + a[-1] < upto
a << a[-2] + a[-1]
end
sum = 0
a.each { |x| sum += x if x.even? }
puts "The result is #{sum}"
How does this work? What is a[-2]? What does that even mean? The negative second index of a or a minus 2? Thanks for the explanation.
This is negative index. a[-1] and a[-2] are the last two elements of the array a.
If you watch closely, you'll see the following sequence:
1 1 2 3 5 8 13 21 34 55 89 144 ...
The formula for mapping the Fibonacci sequence is:
And you only want the sum of the even numbers such as:
1 1 2 3 5 8 13 21 34 55 89 144 ...
So you can map a new formula such as:
And you will obtain the following sequence:
2 8 34 144 ...
Code example (Go):
package main
import "fmt"
func fibonacci() func() int {
first, second := 0, 2
return func() int {
ret := first
first, second = second, first+(4*second)
return ret
}
}
func main() {
sum := 0
f := fibonacci()
for i := 0; sum < 4000000; i++ {
sum += f()
}
fmt.Println(sum)
}
In that case you will not need the if conditionals.
Hope that helped you! Cheers!
def fibonacci(array)
new_fibs = []
new_fibs << array[0]
new_fibs << array[1]
sum = 0
i = 0
while new_fibs.last < 4_000_000
sum = new_fibs[i] + new_fibs[i+1]
new_fibs << sum
i += 1
end
total_even = 0
new_fibs.each do |fibs|
if fibs%2 == 0
total_even = total_even + fibs
end
end
p total_even
end
fibonacci([1,2])
Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...
By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.

Resources