daru 0.1.3.1 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.rspec +2 -1
- data/.rspec_formatter.rb +33 -0
- data/.rubocop.yml +26 -2
- data/History.md +38 -0
- data/README.md +22 -13
- data/Rakefile +50 -2
- data/benchmarks/csv_reading.rb +22 -0
- data/daru.gemspec +9 -2
- data/lib/daru.rb +36 -4
- data/lib/daru/accessors/array_wrapper.rb +6 -1
- data/lib/daru/accessors/dataframe_by_row.rb +10 -2
- data/lib/daru/accessors/gsl_wrapper.rb +1 -3
- data/lib/daru/accessors/nmatrix_wrapper.rb +9 -0
- data/lib/daru/category.rb +935 -0
- data/lib/daru/core/group_by.rb +29 -38
- data/lib/daru/core/merge.rb +186 -145
- data/lib/daru/core/query.rb +22 -11
- data/lib/daru/dataframe.rb +976 -885
- data/lib/daru/date_time/index.rb +166 -166
- data/lib/daru/date_time/offsets.rb +66 -77
- data/lib/daru/formatters/table.rb +54 -0
- data/lib/daru/helpers/array.rb +40 -0
- data/lib/daru/index.rb +476 -73
- data/lib/daru/io/io.rb +66 -45
- data/lib/daru/io/sql_data_source.rb +33 -62
- data/lib/daru/iruby/helpers.rb +38 -0
- data/lib/daru/iruby/templates/dataframe.html.erb +52 -0
- data/lib/daru/iruby/templates/dataframe_mi.html.erb +58 -0
- data/lib/daru/iruby/templates/multi_index.html.erb +12 -0
- data/lib/daru/iruby/templates/vector.html.erb +27 -0
- data/lib/daru/iruby/templates/vector_mi.html.erb +36 -0
- data/lib/daru/maths/arithmetic/dataframe.rb +16 -18
- data/lib/daru/maths/arithmetic/vector.rb +4 -6
- data/lib/daru/maths/statistics/dataframe.rb +8 -15
- data/lib/daru/maths/statistics/vector.rb +120 -98
- data/lib/daru/monkeys.rb +12 -40
- data/lib/daru/plotting/gruff.rb +3 -0
- data/lib/daru/plotting/gruff/category.rb +49 -0
- data/lib/daru/plotting/gruff/dataframe.rb +91 -0
- data/lib/daru/plotting/gruff/vector.rb +57 -0
- data/lib/daru/plotting/nyaplot.rb +3 -0
- data/lib/daru/plotting/nyaplot/category.rb +34 -0
- data/lib/daru/plotting/nyaplot/dataframe.rb +187 -0
- data/lib/daru/plotting/nyaplot/vector.rb +46 -0
- data/lib/daru/vector.rb +694 -421
- data/lib/daru/version.rb +1 -1
- data/profile/_base.rb +23 -0
- data/profile/df_to_a.rb +10 -0
- data/profile/filter.rb +13 -0
- data/profile/joining.rb +13 -0
- data/profile/sorting.rb +12 -0
- data/profile/vector_each_with_index.rb +9 -0
- data/spec/accessors/wrappers_spec.rb +2 -4
- data/spec/categorical_spec.rb +1734 -0
- data/spec/core/group_by_spec.rb +52 -2
- data/spec/core/merge_spec.rb +63 -2
- data/spec/core/query_spec.rb +236 -80
- data/spec/dataframe_spec.rb +1373 -79
- data/spec/date_time/data_spec.rb +3 -5
- data/spec/date_time/index_spec.rb +154 -17
- data/spec/date_time/offsets_spec.rb +3 -4
- data/spec/fixtures/empties.dat +2 -0
- data/spec/fixtures/strings.dat +2 -0
- data/spec/formatters/table_formatter_spec.rb +99 -0
- data/spec/helpers_spec.rb +8 -0
- data/spec/index/categorical_index_spec.rb +168 -0
- data/spec/index/index_spec.rb +283 -0
- data/spec/index/multi_index_spec.rb +570 -0
- data/spec/io/io_spec.rb +31 -4
- data/spec/io/sql_data_source_spec.rb +0 -1
- data/spec/iruby/dataframe_spec.rb +172 -0
- data/spec/iruby/helpers_spec.rb +49 -0
- data/spec/iruby/multi_index_spec.rb +37 -0
- data/spec/iruby/vector_spec.rb +107 -0
- data/spec/math/arithmetic/dataframe_spec.rb +71 -13
- data/spec/math/arithmetic/vector_spec.rb +8 -10
- data/spec/math/statistics/dataframe_spec.rb +3 -5
- data/spec/math/statistics/vector_spec.rb +45 -55
- data/spec/monkeys_spec.rb +32 -9
- data/spec/plotting/dataframe_spec.rb +386 -0
- data/spec/plotting/vector_spec.rb +230 -0
- data/spec/shared/vector_display_spec.rb +215 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/vector_spec.rb +905 -138
- metadata +143 -11
- data/.rubocop_todo.yml +0 -44
- data/lib/daru/plotting/dataframe.rb +0 -104
- data/lib/daru/plotting/vector.rb +0 -38
- data/spec/daru_spec.rb +0 -58
- data/spec/index_spec.rb +0 -375
data/lib/daru/date_time/index.rb
CHANGED
@@ -24,55 +24,44 @@ module Daru
|
|
24
24
|
Rational(1,86_400) => Daru::Offsets::Second
|
25
25
|
}.freeze
|
26
26
|
|
27
|
+
DOW_REGEXP = Regexp.new(Daru::DAYS_OF_WEEK.keys.join('|'))
|
28
|
+
FREQUENCY_PATTERN = /^
|
29
|
+
(?<multiplier>[0-9]+)?
|
30
|
+
(
|
31
|
+
(?<offset>MONTH|YEAR|S|H|MB|ME|M|D|YB|YE) |
|
32
|
+
(?<offset>W)(-(?<weekday>#{DOW_REGEXP}))?
|
33
|
+
)$/x
|
34
|
+
|
27
35
|
# Generates a Daru::DateOffset object for generic offsets or one of the
|
28
36
|
# specialized classed within Daru::Offsets depending on the 'frequency'
|
29
37
|
# string.
|
30
38
|
def offset_from_frequency frequency
|
31
|
-
frequency = 'D' if frequency.nil?
|
32
39
|
return frequency if frequency.is_a?(Daru::DateOffset)
|
40
|
+
frequency ||= 'D'
|
33
41
|
|
34
|
-
matched =
|
35
|
-
|
36
|
-
"Invalid frequency string #{frequency}" if matched.nil?
|
37
|
-
|
38
|
-
n = matched[1] == '' ? 1 : matched[1].to_i
|
39
|
-
offset_string = matched[2]
|
40
|
-
offset_klass = OFFSETS_HASH[offset_string]
|
42
|
+
matched = FREQUENCY_PATTERN.match(frequency) or
|
43
|
+
raise ArgumentError, "Invalid frequency string #{frequency}"
|
41
44
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
day = Regexp.new(Daru::DAYS_OF_WEEK.keys.join('|')).match(frequency).to_s
|
47
|
-
return offset_klass.new(n, weekday: Daru::DAYS_OF_WEEK[day])
|
48
|
-
end
|
45
|
+
n = (matched[:multiplier] || 1).to_i
|
46
|
+
offset_string = matched[:offset]
|
47
|
+
offset_klass = OFFSETS_HASH[offset_string] or
|
48
|
+
raise ArgumentError, "Cannont interpret offset #{offset_string}"
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
def start_date start
|
54
|
-
if start.is_a?(String)
|
55
|
-
date_time_from(start, determine_date_precision_of(start))
|
50
|
+
if offset_string == 'W'
|
51
|
+
offset_klass.new(n, weekday: Daru::DAYS_OF_WEEK[matched[:weekday]])
|
56
52
|
else
|
57
|
-
|
53
|
+
offset_klass.new(n)
|
58
54
|
end
|
59
55
|
end
|
60
56
|
|
61
|
-
def
|
62
|
-
|
63
|
-
|
64
|
-
else
|
65
|
-
en
|
66
|
-
end
|
57
|
+
def coerce_date date
|
58
|
+
return date unless date.is_a?(String)
|
59
|
+
date_time_from(date, determine_date_precision_of(date))
|
67
60
|
end
|
68
61
|
|
69
62
|
def begin_from_offset? offset, start
|
70
|
-
|
71
|
-
|
72
|
-
true
|
73
|
-
else
|
74
|
-
false
|
75
|
-
end
|
63
|
+
offset.is_a?(Daru::Offsets::Tick) ||
|
64
|
+
offset.respond_to?(:on_offset?) && offset.on_offset?(start)
|
76
65
|
end
|
77
66
|
|
78
67
|
def generate_data start, en, offset, periods
|
@@ -103,17 +92,10 @@ module Daru
|
|
103
92
|
end
|
104
93
|
|
105
94
|
def infer_offset data
|
106
|
-
|
107
|
-
inferred = true
|
108
|
-
data.each_cons(2) do |d|
|
109
|
-
if d[1] - d[0] != possible_freq
|
110
|
-
inferred = false
|
111
|
-
break
|
112
|
-
end
|
113
|
-
end
|
95
|
+
diffs = data.each_cons(2).map { |d1, d2| d2 - d1 }
|
114
96
|
|
115
|
-
if
|
116
|
-
TIME_INTERVALS[
|
97
|
+
if diffs.uniq.count == 1
|
98
|
+
TIME_INTERVALS[diffs.first].new
|
117
99
|
else
|
118
100
|
nil
|
119
101
|
end
|
@@ -146,26 +128,17 @@ module Daru
|
|
146
128
|
end
|
147
129
|
end
|
148
130
|
|
131
|
+
DATE_PRECISION_REGEXP = /^(\d\d\d\d)(-\d{1,2}(-\d{1,2}( \d{1,2}(:\d{1,2}(:\d{1,2})?)?)?)?)?$/
|
132
|
+
DATE_PRECISIONS = [nil, :year, :month, :day, :hour, :min, :sec].freeze
|
133
|
+
|
149
134
|
def determine_date_precision_of date_string
|
150
|
-
|
151
|
-
|
152
|
-
:sec
|
153
|
-
when /\d\d\d\d\-\d?\d\-\d?\d \d?\d:\d?\d/
|
154
|
-
:min
|
155
|
-
when /\d\d\d\d\-\d?\d\-\d?\d \d?\d/
|
156
|
-
:hour
|
157
|
-
when /\d\d\d\d\-\d?\d\-\d?\d/
|
158
|
-
:day
|
159
|
-
when /\d\d\d\d\-\d?\d/
|
160
|
-
:month
|
161
|
-
when /\d\d\d\d/
|
162
|
-
:year
|
163
|
-
else
|
135
|
+
components = date_string.scan(DATE_PRECISION_REGEXP).flatten.compact
|
136
|
+
DATE_PRECISIONS[components.count] or
|
164
137
|
raise ArgumentError, "Unacceptable date string #{date_string}"
|
165
|
-
end
|
166
138
|
end
|
167
139
|
|
168
|
-
def generate_bounds date_time, date_precision
|
140
|
+
def generate_bounds date_time, date_precision # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
141
|
+
# FIXME: about that ^ disable: I'd like to use my zverok/time_boots here, which will simplify things
|
169
142
|
case date_precision
|
170
143
|
when :year
|
171
144
|
[
|
@@ -209,21 +182,46 @@ module Daru
|
|
209
182
|
end
|
210
183
|
|
211
184
|
def key_out_of_bounds? key, data
|
185
|
+
dates = data.transpose.first
|
186
|
+
|
212
187
|
precision = determine_date_precision_of key
|
213
188
|
date_time = date_time_from key, precision
|
189
|
+
|
190
|
+
# FIXME: I'm pretty suspicious about logic here:
|
191
|
+
# why only year & month? - zverok 2016-05-16
|
192
|
+
|
214
193
|
case precision
|
215
194
|
when :year
|
216
|
-
date_time
|
195
|
+
year_out_of_bounds?(date_time, dates)
|
217
196
|
when :month
|
218
|
-
(date_time
|
219
|
-
(date_time.year > data[-1][0].year and date_time.month > data[-1][0].month)
|
197
|
+
year_month_out_of_bounds?(date_time, dates)
|
220
198
|
end
|
221
199
|
end
|
200
|
+
|
201
|
+
private
|
202
|
+
|
203
|
+
def year_out_of_bounds?(date_time, dates)
|
204
|
+
date_time.year < dates.first.year || date_time.year > dates.last.year
|
205
|
+
end
|
206
|
+
|
207
|
+
def year_month_out_of_bounds?(date_time, dates)
|
208
|
+
date_time.year < dates.first.year && date_time.month < dates.first.month ||
|
209
|
+
date_time.year > dates.last.year && date_time.month > dates.last.month
|
210
|
+
end
|
222
211
|
end
|
223
212
|
end
|
224
213
|
|
225
214
|
class DateTimeIndex < Index
|
226
215
|
include Enumerable
|
216
|
+
Helper = DateTimeIndexHelper
|
217
|
+
|
218
|
+
def self.try_create(source)
|
219
|
+
if source && ArrayHelper.array_of?(source, DateTime)
|
220
|
+
new(source, freq: :infer)
|
221
|
+
else
|
222
|
+
nil
|
223
|
+
end
|
224
|
+
end
|
227
225
|
|
228
226
|
def each(&block)
|
229
227
|
to_a.each(&block)
|
@@ -255,24 +253,18 @@ module Daru
|
|
255
253
|
# DateTime.new(2012,4,8), DateTime.new(2012,4,9), DateTime.new(2012,4,10),
|
256
254
|
# DateTime.new(2012,4,11), DateTime.new(2012,4,12)], freq: :infer)
|
257
255
|
# #=>#<DateTimeIndex:84198340 offset=D periods=8 data=[2012-04-05T00:00:00+00:00...2012-04-12T00:00:00+00:00]>
|
258
|
-
def initialize
|
259
|
-
|
260
|
-
|
261
|
-
data = args[0]
|
262
|
-
opts = args[1] || {freq: nil}
|
263
|
-
|
264
|
-
helper.possibly_convert_to_date_time data
|
256
|
+
def initialize data, opts={freq: nil}
|
257
|
+
Helper.possibly_convert_to_date_time data
|
265
258
|
|
266
259
|
@offset =
|
267
260
|
case opts[:freq]
|
268
|
-
when :infer then
|
261
|
+
when :infer then Helper.infer_offset(data)
|
269
262
|
when nil then nil
|
270
|
-
else
|
263
|
+
else Helper.offset_from_frequency(opts[:freq])
|
271
264
|
end
|
272
265
|
|
273
266
|
@frequency = @offset ? @offset.freq_string : nil
|
274
|
-
@data = data.
|
275
|
-
@data.sort_by! { |d| d[0] } if @offset.nil?
|
267
|
+
@data = data.each_with_index.to_a.sort_by(&:first)
|
276
268
|
|
277
269
|
@periods = data.size
|
278
270
|
end
|
@@ -341,13 +333,11 @@ module Daru
|
|
341
333
|
# :start => '2012-5-2', :periods => 50, :freq => 'ME')
|
342
334
|
# #=> #<DateTimeIndex:83549940 offset=ME periods=50 data=[2012-05-31T00:00:00+00:00...2016-06-30T00:00:00+00:00]>
|
343
335
|
def self.date_range opts={}
|
344
|
-
|
345
|
-
|
346
|
-
start
|
347
|
-
|
348
|
-
|
349
|
-
offset = helper.offset_from_frequency opts[:freq]
|
350
|
-
data = helper.generate_data start, en, offset, opts[:periods]
|
336
|
+
start = Helper.coerce_date opts[:start]
|
337
|
+
en = Helper.coerce_date opts[:end]
|
338
|
+
Helper.verify_start_and_end(start, en) unless en.nil?
|
339
|
+
offset = Helper.offset_from_frequency opts[:freq]
|
340
|
+
data = Helper.generate_data start, en, offset, opts[:periods]
|
351
341
|
|
352
342
|
DateTimeIndex.new(data, freq: offset)
|
353
343
|
end
|
@@ -357,33 +347,41 @@ module Daru
|
|
357
347
|
# @param [String, DateTime] Specify a date partially (as a String) or
|
358
348
|
# completely to retrieve.
|
359
349
|
def [] *key
|
360
|
-
helper = DateTimeIndexHelper
|
361
|
-
|
362
350
|
return slice(*key) if key.size != 1
|
363
351
|
key = key[0]
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
helper.key_out_of_bounds?(first, @data) && helper.key_out_of_bounds?(last, @data)
|
374
|
-
|
375
|
-
slice_begin = helper.find_date_string_bounds(first)[0]
|
376
|
-
slice_end = helper.find_date_string_bounds(last)[1]
|
377
|
-
elsif key.is_a?(DateTime)
|
378
|
-
return helper.find_index_of_date(@data, key)
|
352
|
+
case key
|
353
|
+
when Numeric
|
354
|
+
key
|
355
|
+
when DateTime
|
356
|
+
Helper.find_index_of_date(@data, key)
|
357
|
+
when Range
|
358
|
+
# FIXME: get_by_range is suspiciously close to just #slice,
|
359
|
+
# but one of specs fails when replacing it with just slice
|
360
|
+
get_by_range(key.first, key.last)
|
379
361
|
else
|
380
362
|
raise ArgumentError, "Key #{key} is out of bounds" if
|
381
|
-
|
363
|
+
Helper.key_out_of_bounds?(key, @data)
|
382
364
|
|
383
|
-
|
365
|
+
slice(*Helper.find_date_string_bounds(key))
|
384
366
|
end
|
367
|
+
end
|
368
|
+
|
369
|
+
def pos *args
|
370
|
+
# to filled
|
371
|
+
out = self[*args]
|
372
|
+
return out if out.is_a? Numeric
|
373
|
+
out.map { |date| self[date] }
|
374
|
+
end
|
375
|
+
|
376
|
+
def subset *args
|
377
|
+
self[*args]
|
378
|
+
end
|
385
379
|
|
386
|
-
|
380
|
+
def valid? *args
|
381
|
+
self[*args]
|
382
|
+
true
|
383
|
+
rescue IndexError
|
384
|
+
false
|
387
385
|
end
|
388
386
|
|
389
387
|
# Retrive a slice of the index by specifying first and last members of the slice.
|
@@ -391,51 +389,24 @@ module Daru
|
|
391
389
|
# @param [String, DateTime] first Start of the slice as a string or DateTime.
|
392
390
|
# @param [String, DateTime] last End of the slice as a string or DateTime.
|
393
391
|
def slice first, last
|
394
|
-
|
395
|
-
|
396
|
-
if first.is_a?(String) && last.is_a?(String)
|
397
|
-
self[first..last]
|
398
|
-
elsif first.is_a?(Fixnum) && last.is_a?(Fixnum)
|
392
|
+
if first.is_a?(Fixnum) && last.is_a?(Fixnum)
|
399
393
|
DateTimeIndex.new(to_a[first..last], freq: @offset)
|
400
394
|
else
|
401
|
-
|
402
|
-
|
403
|
-
helper.find_date_string_bounds(first)[0]
|
404
|
-
else
|
405
|
-
first
|
406
|
-
end
|
407
|
-
|
408
|
-
last_dt =
|
409
|
-
if last.is_a?(String)
|
410
|
-
helper.find_date_string_bounds(last)[1]
|
411
|
-
else
|
412
|
-
last
|
413
|
-
end
|
414
|
-
|
415
|
-
start = @data.bsearch { |d| d[0] >= first_dt }
|
416
|
-
after_en = @data.bsearch { |d| d[0] > last_dt }
|
417
|
-
|
418
|
-
result =
|
419
|
-
if @offset
|
420
|
-
en = after_en ? @data[after_en[1] - 1] : @data.last
|
421
|
-
return start[1] if start == en
|
422
|
-
DateTimeIndex.date_range start: start[0], end: en[0], freq: @offset
|
423
|
-
else
|
424
|
-
st = @data.index(start)
|
425
|
-
en = after_en ? @data.index(after_en) - 1 : helper.last_date(@data)[1]
|
426
|
-
return start[1] if st == en
|
427
|
-
DateTimeIndex.new(@data[st..en].transpose[0])
|
428
|
-
end
|
395
|
+
first = Helper.find_date_string_bounds(first)[0] if first.is_a?(String)
|
396
|
+
last = Helper.find_date_string_bounds(last)[1] if last.is_a?(String)
|
429
397
|
|
430
|
-
|
398
|
+
slice_between_dates first, last
|
431
399
|
end
|
432
400
|
end
|
433
401
|
|
434
402
|
# Return the DateTimeIndex as an Array of DateTime objects.
|
435
403
|
# @return [Array<DateTime>] Array of containing DateTimes.
|
436
404
|
def to_a
|
437
|
-
|
438
|
-
|
405
|
+
if @offset
|
406
|
+
@data
|
407
|
+
else
|
408
|
+
@data.sort_by(&:last)
|
409
|
+
end.transpose.first
|
439
410
|
end
|
440
411
|
|
441
412
|
# Size of index.
|
@@ -448,11 +419,9 @@ module Daru
|
|
448
419
|
end
|
449
420
|
|
450
421
|
def inspect
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
string
|
422
|
+
meta = [@periods, @frequency ? "frequency=#{@frequency}" : nil].compact.join(', ')
|
423
|
+
"#<#{self.class}(#{meta}) " \
|
424
|
+
"#{@data.first[0]}...#{@data.last[0]}>"
|
456
425
|
end
|
457
426
|
|
458
427
|
# Shift all dates in the index by a positive number in the future. The dates
|
@@ -476,16 +445,10 @@ module Daru
|
|
476
445
|
# index.shift(4)
|
477
446
|
# #=>#<DateTimeIndex:83979630 offset=YEAR periods=10 data=[2016-01-01T00:00:00+00:00...2025-01-01T00:00:00+00:00]>
|
478
447
|
def shift distance
|
479
|
-
|
480
|
-
raise IndexError, "Distance #{distance} cannot be negative"
|
481
|
-
raise IndexError, 'To shift non-freq date time index pass an offset.' unless @offset
|
448
|
+
distance.is_a?(Fixnum) && distance < 0 and
|
449
|
+
raise IndexError, "Distance #{distance} cannot be negative"
|
482
450
|
|
483
|
-
|
484
|
-
distance.times { start = @offset + start }
|
485
|
-
DateTimeIndex.date_range(start: start, periods: @periods, freq: @offset)
|
486
|
-
else # its a Daru::Offset/DateOffset
|
487
|
-
DateTimeIndex.new(to_a.map { |e| distance + e }, freq: :infer)
|
488
|
-
end
|
451
|
+
_shift(distance)
|
489
452
|
end
|
490
453
|
|
491
454
|
# Shift all dates in the index to the past. The dates are shifted by the same
|
@@ -498,18 +461,13 @@ module Daru
|
|
498
461
|
# that it was created with.
|
499
462
|
# @return [DateTimeIndex] A new lagged DateTimeIndex object.
|
500
463
|
def lag distance
|
501
|
-
|
502
|
-
raise IndexError, "Distance #{distance} cannot be negative"
|
503
|
-
raise IndexError, 'To lag non-freq date time index pass an offset.' unless @offset
|
464
|
+
distance.is_a?(Fixnum) && distance < 0 and
|
465
|
+
raise IndexError, "Distance #{distance} cannot be negative"
|
504
466
|
|
505
|
-
|
506
|
-
distance.times { start = @offset - start }
|
507
|
-
DateTimeIndex.date_range(start: start, periods: @periods, freq: @offset)
|
508
|
-
else
|
509
|
-
DateTimeIndex.new(to_a.map { |e| distance - e }, freq: :infer)
|
510
|
-
end
|
467
|
+
_shift(-distance)
|
511
468
|
end
|
512
469
|
|
470
|
+
# :nocov:
|
513
471
|
def _dump(_depth)
|
514
472
|
Marshal.dump(data: to_a, freq: @offset)
|
515
473
|
end
|
@@ -519,6 +477,7 @@ module Daru
|
|
519
477
|
|
520
478
|
Daru::DateTimeIndex.new(h[:data], freq: h[:freq])
|
521
479
|
end
|
480
|
+
# :nocov:
|
522
481
|
|
523
482
|
# @!method year
|
524
483
|
# @return [Array<Fixnum>] Array containing year of each index.
|
@@ -544,20 +503,61 @@ module Daru
|
|
544
503
|
# you pass a string. Recommened specifying the full date as a DateTime object.
|
545
504
|
def include? date_time
|
546
505
|
return false unless date_time.is_a?(String) || date_time.is_a?(DateTime)
|
547
|
-
|
506
|
+
|
548
507
|
if date_time.is_a?(String)
|
549
|
-
date_precision =
|
550
|
-
date_time =
|
508
|
+
date_precision = Helper.determine_date_precision_of date_time
|
509
|
+
date_time = Helper.date_time_from date_time, date_precision
|
551
510
|
end
|
552
511
|
|
553
|
-
result = @data.bsearch { |d| d[0] >= date_time }
|
554
|
-
|
555
|
-
result[0] == date_time
|
512
|
+
result, = @data.bsearch { |d| d[0] >= date_time }
|
513
|
+
result && result == date_time
|
556
514
|
end
|
557
515
|
|
558
516
|
# Return true if the DateTimeIndex is empty.
|
559
517
|
def empty?
|
560
518
|
@data.empty?
|
561
519
|
end
|
520
|
+
|
521
|
+
private
|
522
|
+
|
523
|
+
def get_by_range first, last
|
524
|
+
return slice(first, last) if first.is_a?(Fixnum) && last.is_a?(Fixnum)
|
525
|
+
|
526
|
+
raise ArgumentError, "Keys #{first} and #{last} are out of bounds" if
|
527
|
+
Helper.key_out_of_bounds?(first, @data) && Helper.key_out_of_bounds?(last, @data)
|
528
|
+
|
529
|
+
slice first, last
|
530
|
+
end
|
531
|
+
|
532
|
+
def slice_between_dates first, last # rubocop:disable Metrics/AbcSize
|
533
|
+
# about that ^ disable: I'm waiting for cleaner understanding
|
534
|
+
# of offsets logic. Reference: https://github.com/v0dro/daru/commit/7e1c34aec9516a9ba33037b4a1daaaaf1de0726a#diff-a95ef410a8e1f4ea3cc48d231bb880faR250
|
535
|
+
start = @data.bsearch { |d| d[0] >= first }
|
536
|
+
after_en = @data.bsearch { |d| d[0] > last }
|
537
|
+
|
538
|
+
if @offset
|
539
|
+
en = after_en ? @data[after_en[1] - 1] : @data.last
|
540
|
+
return start[1] if start == en
|
541
|
+
DateTimeIndex.date_range start: start[0], end: en[0], freq: @offset
|
542
|
+
else
|
543
|
+
st = @data.index(start)
|
544
|
+
en = after_en ? @data.index(after_en) - 1 : Helper.last_date(@data)[1]
|
545
|
+
return start[1] if st == en
|
546
|
+
DateTimeIndex.new(@data[st..en].transpose[0])
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
550
|
+
def _shift distance
|
551
|
+
if distance.is_a?(Fixnum)
|
552
|
+
raise IndexError, 'To lag non-freq date time index pass an offset.' unless @offset
|
553
|
+
|
554
|
+
start = @data[0][0]
|
555
|
+
off = distance > 0 ? @offset : -@offset
|
556
|
+
distance.abs.times { start = off + start }
|
557
|
+
DateTimeIndex.date_range(start: start, periods: @periods, freq: @offset)
|
558
|
+
else
|
559
|
+
DateTimeIndex.new(to_a.map { |e| distance + e }, freq: :infer)
|
560
|
+
end
|
561
|
+
end
|
562
562
|
end
|
563
563
|
end
|