Class: Daru::DateTimeIndex

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

Constant Summary collapse

Helper =
DateTimeIndexHelper

Instance Attribute Summary collapse

Attributes inherited from Index

#relation_hash

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Index

#&, __new__, #add, #at, coerce, #conform, inherited, #key, new, #reorder, #|

Constructor Details

#initialize(data, opts = {freq: nil}) ⇒ 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]>

Options Hash (opts):

  • freq (Symbol, NilClass, String, Daru::DateOffset, Daru::Offsets::*)

    Option for specifying the frequency of data, if applicable. If :infer is passed to this option, daru will try to infer the frequency of the data by itself.

Parameters:

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

  • (defaults to: {freq: nil})

    Hash of options for configuring index.



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

def initialize data, opts={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.each_with_index.to_a.sort_by(&:first)

  @periods = data.size
end

Instance Attribute Details

#frequencyObject (readonly)

Returns the value of attribute frequency.



229
230
231
# File 'lib/daru/date_time/index.rb', line 229

def frequency
  @frequency
end

#offsetObject (readonly)

Returns the value of attribute offset.



229
230
231
# File 'lib/daru/date_time/index.rb', line 229

def offset
  @offset
end

#periodsObject (readonly)

Returns the value of attribute periods.



229
230
231
# File 'lib/daru/date_time/index.rb', line 229

def periods
  @periods
end

Class Method Details

._load(data) ⇒ Object



474
475
476
477
478
# File 'lib/daru/date_time/index.rb', line 474

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]>

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 (Integer)

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

Parameters:

  • (defaults to: {})

    Options hash to create the date range with

Returns:

  • DateTimeIndex object of the specified parameters.



334
335
336
337
338
339
340
341
342
# File 'lib/daru/date_time/index.rb', line 334

def self.date_range opts={}
  start  = Helper.coerce_date opts[:start]
  en     = Helper.coerce_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

.try_create(source) ⇒ Object



217
218
219
220
221
222
223
# File 'lib/daru/date_time/index.rb', line 217

def self.try_create(source)
  if source && ArrayHelper.array_of?(source, ::DateTime)
    new(source, freq: :infer)
  else
    nil
  end
end

Instance Method Details

#==(other) ⇒ Object



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

def == other
  to_a == other.to_a
end

#[](*key) ⇒ Object

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

Parameters:

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



348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/daru/date_time/index.rb', line 348

def [] *key
  return slice(*key) if key.size != 1
  key = key[0]
  case key
  when Numeric
    key
  when DateTime
    Helper.find_index_of_date(@data, key)
  when Range
    # FIXME: get_by_range is suspiciously close to just #slice,
    #   but one of specs fails when replacing it with just slice
    get_by_range(key.first, key.last)
  else
    raise ArgumentError, "Key #{key} is out of bounds" if
      Helper.key_out_of_bounds?(key, @data)

    slice(*Helper.find_date_string_bounds(key))
  end
end

#_dump(_depth) ⇒ Object

:nocov:



470
471
472
# File 'lib/daru/date_time/index.rb', line 470

def _dump(_depth)
  Marshal.dump(data: to_a, freq: @offset)
end

#dayArray<Integer>

Returns Array containing day of each index.

Returns:

  • Array containing day of each index.



493
494
495
496
497
498
499
# File 'lib/daru/date_time/index.rb', line 493

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

#dupObject

Custom dup method for DateTimeIndex



272
273
274
# File 'lib/daru/date_time/index.rb', line 272

def dup
  Daru::DateTimeIndex.new(@data.transpose[0], freq: @offset)
end

#each(&block) ⇒ Object



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

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

#empty?Boolean

Return true if the DateTimeIndex is empty.

Returns:



516
517
518
# File 'lib/daru/date_time/index.rb', line 516

def empty?
  @data.empty?
end

#hourArray<Integer>

Returns Array containing hour of each index.

Returns:

  • Array containing hour of each index.



493
494
495
496
497
498
499
# File 'lib/daru/date_time/index.rb', line 493

[:year, :month, :day, :hour, :min, :sec].each do |meth|
  define_method(meth) do
    each_with_object([]) do |d, arr|
      arr << d.send(meth)
    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:



503
504
505
506
507
508
509
510
511
512
513
# File 'lib/daru/date_time/index.rb', line 503

def include? date_time
  return false unless date_time.is_a?(String) || date_time.is_a?(DateTime)

  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 && result == date_time
end

#inspectObject



420
421
422
423
424
# File 'lib/daru/date_time/index.rb', line 420

def inspect
  meta = [@periods, @frequency ? "frequency=#{@frequency}" : nil].compact.join(', ')
  "#<#{self.class}(#{meta}) " \
     "#{@data.first[0]}...#{@data.last[0]}>"
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:

  • Integer 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:

  • A new lagged DateTimeIndex object.



462
463
464
465
466
467
# File 'lib/daru/date_time/index.rb', line 462

def lag distance
  distance.is_a?(Integer) && distance < 0 and
    raise IndexError, "Distance #{distance} cannot be negative"

  _shift(-distance)
end

#minArray<Integer>

Returns Array containing minutes of each index.

Returns:

  • Array containing minutes of each index.



493
494
495
496
497
498
499
# File 'lib/daru/date_time/index.rb', line 493

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

#monthArray<Integer>

Returns Array containing month of each index.

Returns:

  • Array containing month of each index.



493
494
495
496
497
498
499
# File 'lib/daru/date_time/index.rb', line 493

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

#pos(*args) ⇒ Object



368
369
370
371
372
373
# File 'lib/daru/date_time/index.rb', line 368

def pos *args
  # to filled
  out = self[*args]
  return out if out.is_a? Numeric
  out.map { |date| self[date] }
end

#secArray<Integer>

Returns Array containing seconds of each index.

Returns:

  • Array containing seconds of each index.



493
494
495
496
497
498
499
# File 'lib/daru/date_time/index.rb', line 493

[:year, :month, :day, :hour, :min, :sec].each do |meth|
  define_method(meth) do
    each_with_object([]) do |d, arr|
      arr << d.send(meth)
    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 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:

  • Returns a new, shifted DateTimeIndex object.



446
447
448
449
450
451
# File 'lib/daru/date_time/index.rb', line 446

def shift distance
  distance.is_a?(Integer) && distance < 0 and
    raise IndexError, "Distance #{distance} cannot be negative"

  _shift(distance)
end

#sizeObject

Size of index.



412
413
414
# File 'lib/daru/date_time/index.rb', line 412

def size
  @periods
end

#slice(first, last) ⇒ Object

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

Parameters:

  • Start of the slice as a string or DateTime.

  • End of the slice as a string or DateTime.



390
391
392
393
394
395
396
397
398
399
# File 'lib/daru/date_time/index.rb', line 390

def slice first, last
  if first.is_a?(Integer) && last.is_a?(Integer)
    DateTimeIndex.new(to_a[first..last], freq: @offset)
  else
    first = Helper.find_date_string_bounds(first)[0] if first.is_a?(String)
    last  = Helper.find_date_string_bounds(last)[1] if last.is_a?(String)

    slice_between_dates first, last
  end
end

#subset(*args) ⇒ Object



375
376
377
# File 'lib/daru/date_time/index.rb', line 375

def subset *args
  self[*args]
end

#to_aArray<DateTime>

Return the DateTimeIndex as an Array of DateTime objects.

Returns:

  • Array of containing DateTimes.



403
404
405
406
407
408
409
# File 'lib/daru/date_time/index.rb', line 403

def to_a
  if @offset
    @data
  else
    @data.sort_by(&:last)
  end.transpose.first
end

#valid?(*args) ⇒ Boolean

Returns:



379
380
381
382
383
384
# File 'lib/daru/date_time/index.rb', line 379

def valid? *args
  self[*args]
  true
rescue IndexError
  false
end

#yearArray<Integer>

Returns Array containing year of each index.

Returns:

  • Array containing year of each index.



493
494
495
496
497
498
499
# File 'lib/daru/date_time/index.rb', line 493

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