Skip to content

Instantly share code, notes, and snippets.

@mestachs
Created January 26, 2017 19:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mestachs/0b35e56762d22858f80f50f6092c1e4c to your computer and use it in GitHub Desktop.
Save mestachs/0b35e56762d22858f80f50f6092c1e4c to your computer and use it in GitHub Desktop.
punch card
require 'histogram/array'
require 'active_support'
require 'active_support/core_ext'
require 'active_support/core_ext/numeric'
timestamps = (1..355).map do |t|
[
Time.now + (t * 1).days + 3.hours + rand(60).minutes + rand(16).seconds,
Time.now + (t * 1).days - 1.hours + rand(60).minutes + rand(16).seconds
]
end.flatten
puts "**** dataset #{timestamps.size}"
puts timestamps
puts '** histogram'
[:hour, :wday, :day, :mday, :yday, :year, :month, :min, :sec].each do |by|
puts "by #{by} : #{timestamps.map(&by).histogram}"
end
class PunchCard
TICKS_SPARK = ' _▁▂▃▄▅▆▇█'.freeze
TICKS_DENSITY = ' ░▒▓█'.freeze
def initialize(timestamps, by_a, by_b, mode = :proportional, tick_theme = :sparks)
@punch_card = {}
@by_a = by_a
@by_b = by_b
@mode = mode
@ticks = tick_theme == :sparks ? TICKS_SPARK : TICKS_DENSITY
timestamps.each do |t|
key = [t.send(by_a), t.send(by_b)]
punch_card[key] ||= 0
punch_card[key] += 1
end
populate_ranges
fill_with_zero
end
def dump
puts "** Punch card (#{by_a}, #{by_b} #{mode})"
puts "\t" + range(by_b).map { |h| h / 100 }.join + "\t" + range(by_b).to_s
puts "\t" + range(by_b).map { |h| h / 10 }.join
puts "\t" + range(by_b).map { |h| h % 10 }.join
range(by_a).each do |d|
dvalues = range(by_b).map { |h| punch_card[[d, h]] }.reject(&:nil?)
sum = dvalues.inject(0, :+)
max = dvalues.max
dticks = dvalues.map do |v|
denominator = proportional? ? sum : max
percentage = denominator.zero? ? 0 : v.to_f / denominator
tick_index = percentage * (ticks.size - 1)
tick_index = v.zero? ? 0 : [tick_index, 1].max
ticks[tick_index]
end
puts d .to_s + "\t" + dticks.join + " #{sum} #{dvalues.reject(&:zero?).join(',')}"
end
end
private
attr_reader :punch_card, :by_a, :by_b, :mode, :ticks
def proportional?
mode == :proportional
end
def range(by)
@ranges[by]
end
def populate_ranges
@ranges = {}
popupate_range(by_a)
popupate_range(by_b)
end
def popupate_range(by)
index = by == by_a ? 0 : 1
min, max = punch_card.keys.map { |v| v[index] }.minmax
@ranges[by] = min..max
end
def fill_with_zero
range(by_a).each do |d|
range(by_b).map do |h|
key = [d, h]
punch_card[key] ||= 0
end
end
end
end
[:sparks, :density].each do |ticks|
[:max, :proportional].each do |mode|
[
[:hour, :wday],
[:wday, :hour],
[:day, :hour],
[:month, :day],
[:month, :hour],
[:year, :month]
].each do |by_a, by_b|
PunchCard.new(timestamps, by_a, by_b, mode, ticks).dump
end
end
end
class Item
attr_reader :duration
def initialize(duration)
@duration = duration
end
def nothing
0
end
end
items = (1..10000).map { |d| Item.new rand(20 + (rand(d) + d) % 50) }
[:sparks, :density].each do |ticks|
[:max, :proportional].each do |mode|
PunchCard.new(items, :nothing, :duration, mode, ticks).dump
#PunchCard.new(items, :duration, :nothing, mode, ticks).dump
end
end
puts "Ⓟ ⓤ ⓝ ⓒ ⓗ Ⓒ ⓐ ⓡ ⓓ show case completed"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment