Class: Daru::DateTimeIndex

Inherits:
Index
  • Object
show all
Includes:
Enumerable
Defined in:
lib/daru/date_time/index.rb

Instance Attribute Summary collapse

Attributes inherited from Index

#relation_hash

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Index

#&, __new__, #dup, inherited, #key, #map, new, #|

Constructor Details

#initialize(*args) ⇒ DateTimeIndex

Create a DateTimeIndex with or without a frequency in data. The constructor should be used for creating DateTimeIndex by directly passing in DateTime objects or date-like strings, typically in cases where values with frequency are not needed.

Examples:

Usage of DateTimeIndex constructor

index = Daru::DateTimeIndex.new(
  [DateTime.new(2012,4,5), DateTime.new(2012,4,6), 
   DateTime.new(2012,4,7), DateTime.new(2012,4,8)])
#=>#<DateTimeIndex:84232240 offset=nil periods=4 data=[2012-04-05T00:00:00+00:00...2012-04-08T00:00:00+00:00]>

index = Daru::DateTimeIndex.new([
  DateTime.new(2012,4,5), DateTime.new(2012,4,6), DateTime.new(2012,4,7), 
  DateTime.new(2012,4,8), DateTime.new(2012,4,9), DateTime.new(2012,4,10), 
  DateTime.new(2012,4,11), DateTime.new(2012,4,12)], freq: :infer)
#=>#<DateTimeIndex:84198340 offset=D periods=8 data=[2012-04-05T00:00:00+00:00...2012-04-12T00:00:00+00:00]>

Parameters:

  • data (Array<String>, Array<DateTime>)

    Array of date-like Strings or actual DateTime objects for creating the DateTimeIndex.

  • opts (Hash)

    Hash of options for configuring index.



250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/daru/date_time/index.rb', line 250

def initialize *args
  helper = DateTimeIndexHelper

  data = args[0]
  opts = args[1] || {freq: nil}

  helper.possibly_convert_to_date_time data

  @offset = 
  case opts[:freq]
  when :infer then helper.infer_offset(data)
  when  nil    then nil
  else  helper.offset_from_frequency(opts[:freq])
  end

  @frequency = @offset ? @offset.freq_string : nil
  @data      = data.zip(Array.new(data.size) { |i| i })
  @data.sort_by! { |d| d[0] } if @offset.nil?
  @periods   = data.size
end

Instance Attribute Details

#frequencyObject (readonly)

Returns the value of attribute frequency.



224
225
226
# File 'lib/daru/date_time/index.rb', line 224

def frequency
  @frequency
end

#offsetObject (readonly)

Returns the value of attribute offset.



224
225
226
# File 'lib/daru/date_time/index.rb', line 224

def offset
  @offset
end

#periodsObject (readonly)

Returns the value of attribute periods.



224
225
226
# File 'lib/daru/date_time/index.rb', line 224

def periods
  @periods
end

Class Method Details

._load(data) ⇒ Object



504
505
506
507
508
# File 'lib/daru/date_time/index.rb', line 504

def self._load data
  h = Marshal.load data

  Daru::DateTimeIndex.new(h[:data], freq: h[:freq])
end

.date_range(opts = {}) ⇒ DateTimeIndex

Create a date range by specifying the start, end, periods and frequency of the data.

Notes

If you specify :start and :end options as strings, they can be complete or partial dates and daru will intelligently infer the date from the string directly. However, note that the date-like string must be in the format ‘YYYY-MM-DD HH:MM:SS`.

The string aliases supported by the :freq option are as follows:

  • ‘S’ - seconds

  • ‘M’ - minutes

  • ‘H’ - hours

  • ‘D’ - days

  • ‘W’ - Week (default) anchored on sunday

  • ‘W-SUN’ - Same as ‘W’

  • ‘W-MON’ - Week anchored on monday

  • ‘W-TUE’ - Week anchored on tuesday

  • ‘W-WED’ - Week anchored on wednesday

  • ‘W-THU’ - Week anchored on thursday

  • ‘W-FRI’ - Week anchored on friday

  • ‘W-SAT’ - Week anchored on saturday

  • ‘MONTH’ - Month

  • ‘YEAR’ - One year

  • ‘MB’ - month begin

  • ‘ME’ - month end

  • ‘YB’ - year begin

  • ‘YE’ - year end

Multiples of these can also be specified. For example ‘2S’ for 2 seconds or ‘2ME’ for two month end offsets.

Currently the precision of DateTimeIndex is upto seconds only, though this will improve in the future.

Examples:

Creating date ranges

Daru::DateTimeIndex.date_range(
  :start => DateTime.new(2014,5,1), 
  :end   => DateTime.new(2014,5,2), :freq => '6H')
#=>#<DateTimeIndex:83600130 offset=H periods=5 data=[2014-05-01T00:00:00+00:00...2014-05-02T00:00:00+00:00]>

Daru::DateTimeIndex.date_range(
  :start => '2012-5-2', :periods => 50, :freq => 'ME')
#=> #<DateTimeIndex:83549940 offset=ME periods=50 data=[2012-05-31T00:00:00+00:00...2016-06-30T00:00:00+00:00]>

Parameters:

  • opts (Hash) (defaults to: {})

    Options hash to create the date range with

Options Hash (opts):

  • :start (String, DateTime)

    A DateTime object or date-like string that defines the start of the date range.

  • :end (String, DateTime)

    A DateTime object or date-like string that defines the end of the date range.

  • :freq (String, Daru::DateOffset, Daru::Offsets::*) — default: 'D'

    The interval between each date in the index. This can either be a string specifying the frequency (i.e. one of the frequency aliases) or an offset object.

  • :periods (Fixnum)

    The number of periods that should go into this index. Takes precedence over ‘:end`.

Returns:

  • (DateTimeIndex)

    DateTimeIndex object of the specified parameters.



329
330
331
332
333
334
335
336
337
338
339
# File 'lib/daru/date_time/index.rb', line 329

def self.date_range opts={}
  helper = DateTimeIndexHelper

  start  = helper.start_date opts[:start]
  en     = helper.end_date opts[:end]
  helper.verify_start_and_end(start, en) unless en.nil?
  offset = helper.offset_from_frequency opts[:freq]
  data   = helper.generate_data start, en, offset, opts[:periods]

  DateTimeIndex.new(data, :freq => offset)
end

Instance Method Details

#==(other) ⇒ Object



427
428
429
# File 'lib/daru/date_time/index.rb', line 427

def == other
  self.to_a == other.to_a
end

#[](*key) ⇒ Object

Retreive a slice or a an individual index number from the index.

Parameters:

  • Specify (String, DateTime)

    a date partially (as a String) or completely to retrieve.



345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
# File 'lib/daru/date_time/index.rb', line 345

def [] *key
  helper = DateTimeIndexHelper
  if key.size == 1
    key = key[0]
    return key if key.is_a?(Numeric)
  else
    return slice(*key)
  end

  if key.is_a?(Range)
    first = key.first
    last = key.last
    return slice(first, last) if 
      first.is_a?(Fixnum) and last.is_a?(Fixnum)

    raise ArgumentError, "Keys #{first} and #{last} are out of bounds" if 
      helper.key_out_of_bounds?(first, @data) and helper.key_out_of_bounds?(last, @data)

    slice_begin = helper.find_date_string_bounds(first)[0]
    slice_end   = helper.find_date_string_bounds(last)[1]
  else
    if key.is_a?(DateTime)
      return helper.find_index_of_date(@data, key)
    else
      raise ArgumentError, "Key #{key} is out of bounds" if 
        helper.key_out_of_bounds?(key, @data)

      slice_begin, slice_end = helper.find_date_string_bounds key
    end
  end

  slice slice_begin, slice_end
end

#_dump(depth) ⇒ Object



500
501
502
# File 'lib/daru/date_time/index.rb', line 500

def _dump depth
  Marshal.dump({data: self.to_a, freq: @offset})
end

#dayArray<Fixnum>

Returns Array containing day of each index.

Returns:

  • (Array<Fixnum>)

    Array containing day of each index.



522
523
524
525
526
527
528
529
# File 'lib/daru/date_time/index.rb', line 522

[:year, :month, :day, :hour, :min, :sec].each do |meth|
  define_method(meth) do
    self.inject([]) do |arr, d|
      arr << d.send(meth)
      arr
    end
  end
end

#each(&block) ⇒ Object



220
221
222
# File 'lib/daru/date_time/index.rb', line 220

def each(&block)
  to_a.each(&block)
end

#empty?Boolean

Return true if the DateTimeIndex is empty.

Returns:

  • (Boolean)


546
547
548
# File 'lib/daru/date_time/index.rb', line 546

def empty?
  @data.empty?
end

#hourArray<Fixnum>

Returns Array containing hour of each index.

Returns:

  • (Array<Fixnum>)

    Array containing hour of each index.



522
523
524
525
526
527
528
529
# File 'lib/daru/date_time/index.rb', line 522

[:year, :month, :day, :hour, :min, :sec].each do |meth|
  define_method(meth) do
    self.inject([]) do |arr, d|
      arr << d.send(meth)
      arr
    end
  end
end

#include?(date_time) ⇒ Boolean

Check if a date exists in the index. Will be inferred from string in case you pass a string. Recommened specifying the full date as a DateTime object.

Returns:

  • (Boolean)


533
534
535
536
537
538
539
540
541
542
543
# File 'lib/daru/date_time/index.rb', line 533

def include? date_time
  return false if !(date_time.is_a?(String) or date_time.is_a?(DateTime))
  helper = DateTimeIndexHelper
  if date_time.is_a?(String)
    date_precision = helper.determine_date_precision_of date_time
    date_time = helper.date_time_from date_time, date_precision
  end

  result = @data.bsearch {|d| d[0] >= date_time }
  result[0] == date_time
end

#inspectObject



431
432
433
434
435
436
437
# File 'lib/daru/date_time/index.rb', line 431

def inspect
  string = "#<DateTimeIndex:" + self.object_id.to_s + " offset=" + 
    (@offset ? @offset.freq_string : 'nil') + ' periods=' + @periods.to_s + 
    " data=[" + @data.first[0].to_s + "..." + @data.last[0].to_s + ']'+ '>'

  string
end

#lag(distance) ⇒ DateTimeIndex

Shift all dates in the index to the past. The dates are shifted by the same amount as that specified in the offset.

Parameters:

  • distance (Fixnum, Daru::DateOffset, Daru::Offsets::*)

    Fixnum or Daru::DateOffset. Distance by which each date should be shifted. Passing an offset object to #lag will offset each data point by the offset value. Passing a positive integer will offset each data point by the same offset that it was created with.

Returns:



484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
# File 'lib/daru/date_time/index.rb', line 484

def lag distance
  if distance.is_a?(Fixnum)
    raise IndexError, "Distance #{distance} cannot be negative" if distance < 0
    if @offset
      start = @data[0][0]
      distance.times { start = @offset - start }
      return DateTimeIndex.date_range(
        :start => start, :periods => @periods, freq: @offset)
    else
      raise IndexError, "To lag non-freq date time index pass an offset."
    end
  else
    DateTimeIndex.new(self.to_a.map { |e| distance - e }, freq: :infer)
  end
end

#minArray<Fixnum>

Returns Array containing minutes of each index.

Returns:

  • (Array<Fixnum>)

    Array containing minutes of each index.



522
523
524
525
526
527
528
529
# File 'lib/daru/date_time/index.rb', line 522

[:year, :month, :day, :hour, :min, :sec].each do |meth|
  define_method(meth) do
    self.inject([]) do |arr, d|
      arr << d.send(meth)
      arr
    end
  end
end

#monthArray<Fixnum>

Returns Array containing month of each index.

Returns:

  • (Array<Fixnum>)

    Array containing month of each index.



522
523
524
525
526
527
528
529
# File 'lib/daru/date_time/index.rb', line 522

[:year, :month, :day, :hour, :min, :sec].each do |meth|
  define_method(meth) do
    self.inject([]) do |arr, d|
      arr << d.send(meth)
      arr
    end
  end
end

#secArray<Fixnum>

Returns Array containing seconds of each index.

Returns:

  • (Array<Fixnum>)

    Array containing seconds of each index.



522
523
524
525
526
527
528
529
# File 'lib/daru/date_time/index.rb', line 522

[:year, :month, :day, :hour, :min, :sec].each do |meth|
  define_method(meth) do
    self.inject([]) do |arr, d|
      arr << d.send(meth)
      arr
    end
  end
end

#shift(distance) ⇒ DateTimeIndex

Shift all dates in the index by a positive number in the future. The dates are shifted by the same amount as that specified in the offset.

Examples:

Using the shift method

index = Daru::DateTimeIndex.date_range(
  :start => '2012', :periods => 10, :freq => 'YEAR')

# Passing a offset to shift
index.shift(Daru::Offsets::Hour.new(3))
#=>#<DateTimeIndex:84038960 offset=nil periods=10 data=[2012-01-01T03:00:00+00:00...2021-01-01T03:00:00+00:00]>

# Pass an integer to shift
index.shift(4)
#=>#<DateTimeIndex:83979630 offset=YEAR periods=10 data=[2016-01-01T00:00:00+00:00...2025-01-01T00:00:00+00:00]>

Parameters:

  • distance (Fixnum, Daru::DateOffset, Daru::Offsets::*)

    Distance by which each date should be shifted. Passing an offset object to #shift will offset each data point by the offset value. Passing a positive integer will offset each data point by the same offset that it was created with.

Returns:

  • (DateTimeIndex)

    Returns a new, shifted DateTimeIndex object.



459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
# File 'lib/daru/date_time/index.rb', line 459

def shift distance
  if distance.is_a?(Fixnum)
    raise IndexError, "Distance #{distance} cannot be negative" if distance < 0
    if @offset
      start = @data[0][0]
      distance.times { start = @offset + start }
      return DateTimeIndex.date_range(
        :start => start, :periods => @periods, freq: @offset)
    else
      raise IndexError, "To shift non-freq date time index pass an offset."
    end
  else # its a Daru::Offset/DateOffset
    DateTimeIndex.new(self.to_a.map { |e| distance + e }, freq: :infer)
  end
end

#sizeObject

Size of index.



423
424
425
# File 'lib/daru/date_time/index.rb', line 423

def size
  @periods
end

#slice(first, last) ⇒ Object

Retrive a slice of the index by specifying first and last members of the slice.

Parameters:

  • first (String, DateTime)

    Start of the slice as a string or DateTime.

  • last (String, DateTime)

    End of the slice as a string or DateTime.



383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
# File 'lib/daru/date_time/index.rb', line 383

def slice first, last
  helper = DateTimeIndexHelper

  if first.is_a?(String) and last.is_a?(String)
    self[first..last]
  elsif first.is_a?(Fixnum) and last.is_a?(Fixnum)
    DateTimeIndex.new(self.to_a[first..last], freq: @offset)
  else
    first_dt = first.is_a?(String) ? 
      helper.find_date_string_bounds(first)[0] : first
    last_dt  = last.is_a?(String) ?
      helper.find_date_string_bounds(last)[1]  : last

    start    = @data.bsearch { |d| d[0] >= first_dt }
    after_en = @data.bsearch { |d| d[0] > last_dt }

    result =
    if @offset
      en = after_en ? @data[after_en[1] - 1] : @data.last
      return start[1] if start == en
      DateTimeIndex.date_range :start => start[0], :end => en[0], freq: @offset
    else
      st = @data.index(start)
      en = after_en ? @data.index(after_en) - 1 : helper.last_date(@data)[1]
      return start[1] if st == en
      DateTimeIndex.new(@data[st..en].transpose[0])
    end

    result
  end   
end

#to_aArray<DateTime>

Return the DateTimeIndex as an Array of DateTime objects.

Returns:

  • (Array<DateTime>)

    Array of containing DateTimes.



417
418
419
420
# File 'lib/daru/date_time/index.rb', line 417

def to_a
  return @data.sort_by { |d| d[1] }.transpose[0] unless @offset
  @data.transpose[0]
end

#yearArray<Fixnum>

Returns Array containing year of each index.

Returns:

  • (Array<Fixnum>)

    Array containing year of each index.



522
523
524
525
526
527
528
529
# File 'lib/daru/date_time/index.rb', line 522

[:year, :month, :day, :hour, :min, :sec].each do |meth|
  define_method(meth) do
    self.inject([]) do |arr, d|
      arr << d.send(meth)
      arr
    end
  end
end