From: Phrogz Date: 2007-11-14T10:50:02+09:00 Subject: [SOLUTION] Vehicle Counters (#146) Attached is my solution (in progress). Before I get onto the analysis reports, I'm still trying to simplify the data post parsing. But, at least it's getting there. :) As you can see from the results, I'm only properly pulling data from the first day. This is why I enjoy quizzes; I'm sure I tried to fix this same problem at some point. #!/usr/bin/env ruby WHEELBASE_AVG = 100 # inches INCHES_PER_MILE = 63_360 SECONDS_PER_HOUR = 3600.0 INCHES_PER_SECOND_TO_MPH = 17.6 USAGE = < 60, #:data_file => 'vehicle_counter.data' } UNFLAGGED_ARGS = [ :data_file ] next_arg = UNFLAGGED_ARGS.first ARGV.each{ |arg| case arg when '-t','--time' next_arg = :time_segment when '-a','--average' ARGS[:average] = true else if next_arg if next_arg==:time_segment arg = arg.to_i end ARGS[next_arg] = arg UNFLAGGED_ARGS.delete( next_arg ) end next_arg = UNFLAGGED_ARGS.first end } if !ARGS[:data_file] || !ARGS[:time_segment] puts USAGE exit end class Record SECONDS_PER_DAY = 3600 * 24 attr_reader :time, :direction, :ms attr_accessor :speed def initialize( str, day_offset ) _, @direction, @ms = /([AB])(\d+)/.match( str ).to_a @ms = @ms.to_i @time = Time.gm( 2007 ) + ( @ms.to_i / 1000.0 ) + ( day_offset * SECONDS_PER_DAY ) end end # Prepare data raw_data = IO.readlines( ARGS[:data_file] ) day_changes = 0 records = raw_data.inject([]){ |records,line| record = Record.new( line, ARGS[:average] ? 0 : day_changes ) if (last_record = records.last) && (record.ms < last_record.ms) day_changes += 1 end records << record } # Convert axle pairs to speed last_record = {} records.each{ |record| if last_axle = last_record[ record.direction ] last_axle.speed = WHEELBASE_AVG / ( record.time - last_axle.time ) last_axle.speed /= INCHES_PER_SECOND_TO_MPH last_record[ record.direction ] = nil else last_record[ record.direction ] = record end } records.delete_if{ |r| r.speed.nil? } # Figure out which direction gets double hits possible_directions = records.map{ |r| r.direction }.uniq double_hit_direction = possible_directions.map{ |dir| [ records.select{ |r| r.direction == dir }.length , dir ] }.sort.last.last # Remove extraneous records require 'enumerator' records.each_cons(2){ |r1,r2| if (r1.direction == double_hit_direction) && (r2.direction != double_hit_direction) && # 0.02 seconds @ 50mph is ~18 inches # If the times are this close, it must be a double hit (r1.time - r2.time).abs < 0.02 r1.speed = nil end } records.delete_if{ |r| r.speed.nil? } t1 = Time.gm(0) ms_trigger = 0 slot_count = nil ms_per_slot = ARGS[ :time_segment ] * 60 * 1000 records << Record.new( 'B99999999', 0 ) records.each{ |r| if r.ms >= ms_trigger if slot_count t2 = t1 + ms_per_slot / 1000.0 print "#{t1.strftime('%H:%M')}..#{t2.strftime('%H:%M')} : " puts "%4i %s, %4i %s" % possible_directions.map{ |dir| [slot_count[dir],dir=="A" ? "left" : "right"] }.flatten t1 = t2 end slot_count = Hash[ *possible_directions.map{ |d| [d,0] }.flatten ] ms_trigger += ms_per_slot end slot_count[ r.direction ] += 1 } Slim2:Code phrogz$ ruby vehicle_counter.rb vehicle_counter.data -t 15 00:00..00:15 : 2 left, 1 right 00:15..00:30 : 1 left, 4 right 00:30..00:45 : 3 left, 2 right 00:45..01:00 : 1 left, 1 right 01:00..01:15 : 0 left, 2 right 01:15..01:30 : 0 left, 1 right 01:30..01:45 : 0 left, 3 right 01:45..02:00 : 1 left, 1 right 02:00..02:15 : 0 left, 1 right 02:15..02:30 : 1 left, 2 right 02:30..02:45 : 0 left, 2 right 02:45..03:00 : 1 left, 1 right 03:00..03:15 : 0 left, 3 right 03:15..03:30 : 0 left, 2 right 03:30..03:45 : 1 left, 1 right 03:45..04:00 : 1 left, 1 right 04:00..04:15 : 1 left, 3 right 04:15..04:30 : 0 left, 1 right 04:30..04:45 : 0 left, 1 right 04:45..05:00 : 1 left, 0 right 05:00..05:15 : 0 left, 5 right 05:15..05:30 : 0 left, 15 right 05:30..05:45 : 1 left, 4 right 05:45..06:00 : 2 left, 8 right 06:00..06:15 : 13 left, 8 right 06:15..06:30 : 18 left, 9 right 06:30..06:45 : 13 left, 9 right 06:45..07:00 : 8 left, 5 right 07:00..07:15 : 18 left, 64 right 07:15..07:30 : 16 left, 62 right 07:30..07:45 : 44 left, 66 right 07:45..08:00 : 44 left, 64 right 08:00..08:15 : 49 left, 59 right 08:15..08:30 : 45 left, 75 right 08:30..08:45 : 46 left, 151 right 08:45..09:00 : 44 left, 169 right 09:00..09:15 : 35 left, 25 right 09:15..09:30 : 32 left, 14 right 09:30..09:45 : 23 left, 16 right 09:45..10:00 : 26 left, 14 right 10:00..10:15 : 29 left, 16 right 10:15..10:30 : 33 left, 15 right 10:30..10:45 : 25 left, 14 right 10:45..11:00 : 23 left, 12 right 11:00..11:15 : 26 left, 32 right 11:15..11:30 : 16 left, 38 right 11:30..11:45 : 35 left, 26 right 11:45..12:00 : 26 left, 26 right 12:00..12:15 : 33 left, 29 right 12:15..12:30 : 25 left, 42 right 12:30..12:45 : 30 left, 27 right 12:45..13:00 : 31 left, 27 right 13:00..13:15 : 27 left, 41 right 13:15..13:30 : 29 left, 32 right 13:30..13:45 : 24 left, 38 right 13:45..14:00 : 31 left, 25 right 14:00..14:15 : 26 left, 41 right 14:15..14:30 : 23 left, 44 right 14:30..14:45 : 28 left, 49 right 14:45..15:00 : 21 left, 46 right 15:00..15:15 : 42 left, 48 right 15:15..15:30 : 55 left, 46 right 15:30..15:45 : 44 left, 55 right 15:45..16:00 : 42 left, 48 right 16:00..16:15 : 41 left, 51 right 16:15..16:30 : 58 left, 41 right 16:30..16:45 : 93 left, 40 right 16:45..17:00 : 102 left, 35 right 17:00..17:15 : 80 left, 41 right 17:15..17:30 : 111 left, 53 right 17:30..17:45 : 114 left, 39 right 17:45..18:00 : 106 left, 39 right 18:00..18:15 : 19 left, 6 right 18:15..18:30 : 21 left, 11 right 18:30..18:45 : 27 left, 7 right 18:45..19:00 : 25 left, 6 right 19:00..19:15 : 9 left, 9 right 19:15..19:30 : 12 left, 7 right 19:30..19:45 : 11 left, 18 right 19:45..20:00 : 3 left, 10 right 20:00..20:15 : 7 left, 11 right 20:15..20:30 : 9 left, 12 right 20:30..20:45 : 9 left, 6 right 20:45..21:00 : 11 left, 8 right 21:00..21:15 : 8 left, 10 right 21:15..21:30 : 7 left, 4 right 21:30..21:45 : 12 left, 11 right 21:45..22:00 : 6 left, 16 right 22:00..22:15 : 10 left, 5 right 22:15..22:30 : 17 left, 5 right 22:30..22:45 : 7 left, 8 right 22:45..23:00 : 16 left, 2 right 23:00..23:15 : 9 left, 2 right 23:15..23:30 : 4 left, 4 right 23:30..23:45 : 4 left, 6 right 23:45..00:00 : 8916 left, 9061 right