Class: DaruLite::DateTimeIndex

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

Constant Summary collapse

Helper =
DateTimeIndexHelper

Instance Attribute Summary collapse

Attributes inherited from Index

#name, #relation_hash

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Index

#&, __new__, #add, #at, coerce, #conform, #delete_at, inherited, #is_values, #key, new, #reorder, #sort, #subset_slice, #to_df, #|

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 = DaruLite::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 = DaruLite::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) (defaults to: { freq: nil })

    Hash of options for configuring index.

Options Hash (opts):

  • freq (Symbol, NilClass, String, DaruLite::DateOffset, DaruLite::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.



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

def initialize(data, opts = { freq: nil })
  super(data)
  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&.freq_string
  @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.



230
231
232
# File 'lib/daru_lite/date_time/index.rb', line 230

def frequency
  @frequency
end

#keysObject (readonly)

Returns the value of attribute keys.



230
231
232
# File 'lib/daru_lite/date_time/index.rb', line 230

def keys
  @keys
end

#offsetObject (readonly)

Returns the value of attribute offset.



230
231
232
# File 'lib/daru_lite/date_time/index.rb', line 230

def offset
  @offset
end

#periodsObject (readonly)

Returns the value of attribute periods.



230
231
232
# File 'lib/daru_lite/date_time/index.rb', line 230

def periods
  @periods
end

Class Method Details

._load(data) ⇒ Object



480
481
482
483
484
# File 'lib/daru_lite/date_time/index.rb', line 480

def self._load(data)
  h = Marshal.load data

  DaruLite::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

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

DaruLite::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, DaruLite::DateOffset, DaruLite::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`.

Returns:

  • (DateTimeIndex)

    DateTimeIndex object of the specified parameters.



336
337
338
339
340
341
342
343
344
# File 'lib/daru_lite/date_time/index.rb', line 336

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



222
223
224
# File 'lib/daru_lite/date_time/index.rb', line 222

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

Instance Method Details

#==(other) ⇒ Object



420
421
422
# File 'lib/daru_lite/date_time/index.rb', line 420

def ==(other)
  to_a == other.to_a
end

#[](*key) ⇒ Object

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

Parameters:

  • key (String, DateTime)

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



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

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:



476
477
478
# File 'lib/daru_lite/date_time/index.rb', line 476

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

#dayArray<Integer>

Returns Array containing day of each index.

Returns:

  • (Array<Integer>)

    Array containing day of each index.



499
500
501
502
503
504
505
# File 'lib/daru_lite/date_time/index.rb', line 499

%i[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



274
275
276
# File 'lib/daru_lite/date_time/index.rb', line 274

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

#eachObject



226
227
228
# File 'lib/daru_lite/date_time/index.rb', line 226

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

#empty?Boolean

Return true if the DateTimeIndex is empty.

Returns:

  • (Boolean)


522
523
524
# File 'lib/daru_lite/date_time/index.rb', line 522

def empty?
  @data.empty?
end

#hourArray<Integer>

Returns Array containing hour of each index.

Returns:

  • (Array<Integer>)

    Array containing hour of each index.



499
500
501
502
503
504
505
# File 'lib/daru_lite/date_time/index.rb', line 499

%i[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:

  • (Boolean)


509
510
511
512
513
514
515
516
517
518
519
# File 'lib/daru_lite/date_time/index.rb', line 509

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



424
425
426
427
428
429
430
# File 'lib/daru_lite/date_time/index.rb', line 424

def inspect
  meta = [@periods, @frequency ? "frequency=#{@frequency}" : nil].compact.join(', ')
  return "#<#{self.class}(#{meta})>" if @data.empty?

  "#<#{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:

  • distance (Integer, DaruLite::DateOffset, DaruLite::Offsets::*)

    Integer or DaruLite::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:



468
469
470
471
472
473
# File 'lib/daru_lite/date_time/index.rb', line 468

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

  _shift(-distance)
end

#minArray<Integer>

Returns Array containing minutes of each index.

Returns:

  • (Array<Integer>)

    Array containing minutes of each index.



499
500
501
502
503
504
505
# File 'lib/daru_lite/date_time/index.rb', line 499

%i[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<Integer>)

    Array containing month of each index.



499
500
501
502
503
504
505
# File 'lib/daru_lite/date_time/index.rb', line 499

%i[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

#posObject



371
372
373
374
375
376
377
# File 'lib/daru_lite/date_time/index.rb', line 371

def pos(*)
  # to filled
  out = self[*]
  return out if out.is_a? Numeric

  out.map { |date| self[date] }
end

#secArray<Integer>

Returns Array containing seconds of each index.

Returns:

  • (Array<Integer>)

    Array containing seconds of each index.



499
500
501
502
503
504
505
# File 'lib/daru_lite/date_time/index.rb', line 499

%i[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 = DaruLite::DateTimeIndex.date_range(
  :start => '2012', :periods => 10, :freq => 'YEAR')

# Passing a offset to shift
index.shift(DaruLite::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 (Integer, DaruLite::DateOffset, DaruLite::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.



452
453
454
455
456
457
# File 'lib/daru_lite/date_time/index.rb', line 452

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

  _shift(distance)
end

#sizeObject

Size of index.



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

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.



394
395
396
397
398
399
400
401
402
403
# File 'lib/daru_lite/date_time/index.rb', line 394

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

#subsetObject



379
380
381
# File 'lib/daru_lite/date_time/index.rb', line 379

def subset(*)
  self[*]
end

#to_aArray<DateTime>

Return the DateTimeIndex as an Array of DateTime objects.

Returns:

  • (Array<DateTime>)

    Array of containing DateTimes.



407
408
409
410
411
412
413
# File 'lib/daru_lite/date_time/index.rb', line 407

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

#valid?Boolean

Returns:

  • (Boolean)


383
384
385
386
387
388
# File 'lib/daru_lite/date_time/index.rb', line 383

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

#yearArray<Integer>

Returns Array containing year of each index.

Returns:

  • (Array<Integer>)

    Array containing year of each index.



499
500
501
502
503
504
505
# File 'lib/daru_lite/date_time/index.rb', line 499

%i[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