Ruby Array group and average by hour - ruby

We get our data from a sensor which records and stores data like hashes.
At any time it measures a few stuff like that:
{:temperature => 30, :pression => 100, :recorded_at => 14:34:23}
{:temperature => 30, :pression => 101, :recorded_at => 14:34:53}
{:temperature => 31, :pression => 102, :recorded_at => 14:34:24}
{:temperature => 30, :pression => 101, :recorded_at => 14:34:55}
{:temperature => 30, :pression => 102, :recorded_at => 14:34:25}
{:temperature => 31, :pression => 101, :recorded_at => 14:34:56}
We need to be able to export that data on a JSON format, but we have way too much data (the sensor records about every 30 seconds) and we need to remove some of the data. Ideally we'd want to export 1 measure per hour in the last 24 hours so we have something like
{0 => {:temperature => 30, :pression => 100}, 1 => {:temperature => 30, :pression => 100}, 2 => {:temperature => 30, :pression => 100}, 3 => {:temperature => 30, :pression => 100}, 4 => {:temperature => 30, :pression => 100}}
For each hour, the temperature is the average of all temperatures measured within that hour.
Also, if for any reason some data is missing for 1hour, I'd like to to extrapolate it by being the mean between the previous and next hour. Anybody can help?

More functional version (with simple interpolation of missing values)
probs = [{:temperature => .. }] # array of measurings
def average(list, key)
list.reduce(0){|acc,el| acc+el[key]} / list.length unless list.empty
end
prob_groups = probs.group_by{|prob| prob[:recorded_at][0,2].to_i}
average_groups = prob_groups.map do |hour,prob_group|
{ hour => {
:temperature => average(prob_group, :temperature),
:pression => average(prob_group, :pression)
}}
end.reduce{|acc,el| acc.merge(el)}
def interpolate(p, n, key)
(p[key] + n[key])/2 unless p.nil? || n.nil? || p[key].nil? || n[key].nil?
end
resuls = (1..24).map do |hour|
if average_groups[hour]
{ hour => average_groups[hour] }
else
{ hour => {
:temperature => interpolate(average_groups[hour-1], average_groups[hour+1], :temperature),
:pression => interpolate(average_groups[hour-1], average_groups[hour+1], :pression)
}}
end
end.reduce{|acc,el| acc.merge(el)}
Hope it works

something like this
t = [....] - array of measurings
result = {}
(1..24).each do|hour|
# measurings of given hour
measurings = t.select{|measuring| measuring[:recorded_at][0, 2].to_i == hour}
# average temperature of hour
sum = measurings.inject(0){|sum, measuring| sum + measuring[:temperature].to_i}
average_temperature = (measurings.length == 0)? nil: sum/measurings.length.to_f
result[hour] = average_temperature
end

If you are not interested on the history but only on an approximation of actual value(s), consider to use a "moving metric" (http://en.wikipedia.org/wiki/Moving_average).

Related

Ruby count hash key

I have a hash like this:
{'yes' => 23,
'b' => 'travel',
'yesterday' => 34,
5 => '234',
:yesss => :fg,
try: 30,
key: 'some value',
'yesterday1' => 34,
'yesteryear' => 2014}
How can I count all keys which includes yes?
I suppose you meant:
your_hash.count { |k, _| k.to_s.include?('yes') }
#=> 5

How to count the number of objects created in Ruby

Is it possible to count the total number of objects created in a Ruby application? If so, how can I do it?
I know how to count the number of instances of a given class I create, as of in this post, but is there a way to get the number of objects created of any class in an application (including internal ones)?
You should use
ObjectSpace.count_objects
For example, this is what it outputs on a fresh IRB session:
{
:TOTAL => 30161,
:FREE => 378,
:T_OBJECT => 152,
:T_CLASS => 884,
:T_MODULE => 30,
:T_FLOAT => 4,
:T_STRING => 11517,
:T_REGEXP => 165,
:T_ARRAY => 3395,
:T_HASH => 180,
:T_STRUCT => 2,
:T_BIGNUM => 2,
:T_FILE => 15,
:T_DATA => 1680,
:T_MATCH => 99,
:T_COMPLEX => 1,
:T_NODE => 11620,
:T_ICLASS => 37
}

How do I return an array of days and hours from a range?

How do I return an array of days and hours from a range? So far I have tried:
(48.hours.ago..Time.now.utc).map { |time| { :hour => time.hour } }.uniq
Returns:
[{:hour=>1}, {:hour=>2}, {:hour=>3}, {:hour=>4}, {:hour=>5}, {:hour=>6}, {:hour=>7}, {:hour=>8}, {:hour=>9}, {:hour=>10}, {:hour=>11}, {:hour=>12}, {:hour=>13}, {:hour=>14}, {:hour=>15}, {:hour=>16}, {:hour=>17}, {:hour=>18}, {:hour=>19}, {:hour=>20}, {:hour=>21}, {:hour=>22}, {:hour=>23}, {:hour=>0}]
Not ideal, since its iterates over every second. Which takes a long time. And I get several warning messages that say:
/Users/Chris/.rvm/gems/ruby-1.9.2-p290/gems/activesupport-3.2.2/lib/active_support/time_with_zone.rb:328: warning: Time#succ is obsolete; use time + 1
I am trying to return something like:
[{:day => 25, :hour=>1}, {:day => 25, :hour=>2}, {:day => 25, :hour=>3}, {:day => 25, :hour=>4} ... {:day => 26, :hour=>1}, {:day => 26, :hour=>2}, {:day => 26, :hour=>3}, {:day => 26, :hour=>4}]
Use Range#step, but as a precaution, convert the dates to integers first (apparently ranges using integers have step() optimized—YMMV). As a matter of style, I also truncate to the hour first.
Here's some quick 'n' dirty code:
#!/usr/bin/env ruby
require 'active_support/all'
s=48.hours.ago
n=Time.now
st=Time.local(s.year, s.month, s.day, s.hour).to_i
en=Time.local(n.year, n.month, n.day, n.hour).to_i
result = (st..en).step(1.hour).map do |i|
t = Time.at(i)
{ day: t.day, hour: t.hour }
end
puts result.inspect
Yields:
[{:day=>25, :hour=>11}, {:day=>25, :hour=>12}, {:day=>25, :hour=>13},
{:day=>25, :hour=>14}, {:day=>25, :hour=>15}, {:day=>25, :hour=>16},
...
stime = 48.hours.ago
etime=Time.now.utc
h = []
while stime <= etime
h.push({ :day => stime.day, :hour => stime.hour })
stime += 1.hour
end

ANSI escape code with html tags in Ruby?

Interestingly there are built-in ansi escape code in Ruby.
There is also a more powerful version from a gem.
Unfortunately, these logs output to the console. My text is shown in the page so I need HTML tags to wrap around my text.
Would you guys have any idea how to go about it?
I guess what you want is to transform from escape characters to HTML.
I did it once by assuming the following code/colour hash for escape characters:
{ :reset => 0,
:bright => 1,
:dark => 2,
:underline => 4,
:blink => 5,
:negative => 7,
:black => 30,
:red => 31,
:green => 32,
:yellow => 33,
:blue => 34,
:magenta => 35,
:cyan => 36,
:white => 37,
:back_black => 40,
:back_red => 41,
:back_green => 42,
:back_yellow => 43,
:back_blue => 44,
:back_magenta => 45,
:back_cyan => 46,
:back_white => 47}
What I did was the following conversion (far away from being anyhow optimized):
def escape_to_html(data)
{ 1 => :nothing,
2 => :nothing,
4 => :nothing,
5 => :nothing,
7 => :nothing,
30 => :black,
31 => :red,
32 => :green,
33 => :yellow,
34 => :blue,
35 => :magenta,
36 => :cyan,
37 => :white,
40 => :nothing,
41 => :nothing,
43 => :nothing,
44 => :nothing,
45 => :nothing,
46 => :nothing,
47 => :nothing,
}.each do |key, value|
if value != :nothing
data.gsub!(/\e\[#{key}m/,"<span style=\"color:#{value}\">")
else
data.gsub!(/\e\[#{key}m/,"<span>")
end
end
data.gsub!(/\e\[0m/,'</span>')
return data
end
Well, you will need fill the gaps of the colours I am not considering or backgrounds. But I guess you can get the idea.
Hope it helps
Thank you for the link to a cool gem I had not seen. I think what you are looking for, however, is termed Cascading Style Sheets (CSS). Because that google search will bring up about every other page cached on the internet, here are a few links for you that should get you started:
http://www.w3schools.com/css/default.asp - start with the most basic stuff.
http://guides.rubyonrails.org/layouts_and_rendering.html
http://rorrocket.com/ - generalized tutorials
http://nubyonrails.com/articles/dynamic-css - this may or may not be useful but a brief glance might provide some more rails like info on css, including SASS*.
*SASS is a ruby-ized abstraction to CSS used very frequently with ruby/rails

How do I add values from two different arrays of hashes together?

I have two arrays of hashes. The keys for the hashes are different:
player_scores1 = [{:first_name=>"Bruce", :score => 43, :time => 50},
{:first_name=>"Clark", :score => 45, :minutes => 20}]
player_scores2 = [{:last_name=>"Wayne", :points => 13, :time => 40},
{:last_name=>"Kent", :points => 3, :minutes => 20}]
I'd like to create a new array of hashes which adds up :score and :points together and assign it to a key called :score. I'd also like to combine the :first_name and :last_name and assign it to a key called :full_name. I want to discard any other keys.
This would result in this array:
all_players = [{:full_name => "Bruce Wayne", :score => 56},
{:full_name => "Clark Kent", :score => 48}]
Is there an elegant way to do this?
Something like this:
player_scores1.zip(player_scores2).map { |a,b|
{
:full_name => a[:first_name]+' '+b[:last_name],
:score => a[:score]+b[:points]
}
}
The code you're looking for is:
final = []
player_scores1.each_index do |index|
entry_1 = player_scores1.values(index)
entry_2 = player_scores2.values(index)[:first_name]
score = entry_1[:score] + entry_2[:points]
final << {:full_name => "#{entry_1[:first_name]} #{entry_2[:last_name]}", :score => score }
end
Any suggestions on tightening this up would be much appreciated!
This works. I don't if that's elegant enough though.
player_scores1 = [{:first_name=>"Bruce", :score => 43, :time => 50},
{:first_name=>"Clark", :score => 45, :minutes => 20}]
player_scores2 = [{:last_name=>"Wayne", :points => 13, :time => 40},
{:last_name=>"Kent", :points => 3, :minutes => 20}]
p (0...[player_scores1.length, player_scores2.length].min).map {|i| {
:full_name => player_scores1[i][:first_name] + " " + player_scores2[i][:last_name],
:score => player_scores1[i][:score] + player_scores2[i][:points]
}}
This example on Codepad.
This uses zip with a block to loop over the hashes, joining the names and summarizing:
all_players = []
player_scores1.zip(player_scores2) { |a, b|
all_players << {
:full_name => a[:first_name] + ' ' + b[:last_name],
:score => a[:score] + b[:points]
}
}
all_players # => [{:full_name=>"Bruce Wayne", :score=>56}, {:full_name=>"Clark Kent", :score=>48}]

Resources