daru 0.1.3.1 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|