daru_lite 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/ISSUE_TEMPLATE.md +18 -0
- data/.github/workflows/ci.yml +33 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.rubocop.yml +27 -0
- data/.rubocop_todo.yml +137 -0
- data/CONTRIBUTING.md +47 -0
- data/Gemfile +2 -0
- data/History.md +4 -0
- data/LICENSE +24 -0
- data/README.md +218 -0
- data/Rakefile +69 -0
- data/ReleasePolicy.md +20 -0
- data/benchmarks/TradeoffData.csv +65 -0
- data/benchmarks/csv_reading.rb +22 -0
- data/benchmarks/dataframe_creation.rb +39 -0
- data/benchmarks/db_loading.rb +34 -0
- data/benchmarks/duplicating.rb +45 -0
- data/benchmarks/group_by.rb +32 -0
- data/benchmarks/joining.rb +52 -0
- data/benchmarks/row_access.rb +41 -0
- data/benchmarks/row_assign.rb +36 -0
- data/benchmarks/sorting.rb +51 -0
- data/benchmarks/statistics.rb +28 -0
- data/benchmarks/vector_access.rb +31 -0
- data/benchmarks/vector_assign.rb +42 -0
- data/benchmarks/where_clause.rb +48 -0
- data/benchmarks/where_vs_filter.rb +28 -0
- data/daru_lite.gemspec +55 -0
- data/images/README.md +5 -0
- data/images/con0.png +0 -0
- data/images/con1.png +0 -0
- data/images/init0.png +0 -0
- data/images/init1.png +0 -0
- data/images/man0.png +0 -0
- data/images/man1.png +0 -0
- data/images/man2.png +0 -0
- data/images/man3.png +0 -0
- data/images/man4.png +0 -0
- data/images/man5.png +0 -0
- data/images/man6.png +0 -0
- data/lib/daru_lite/accessors/array_wrapper.rb +109 -0
- data/lib/daru_lite/accessors/dataframe_by_row.rb +25 -0
- data/lib/daru_lite/accessors/mdarray_wrapper.rb +7 -0
- data/lib/daru_lite/category.rb +929 -0
- data/lib/daru_lite/configuration.rb +34 -0
- data/lib/daru_lite/core/group_by.rb +403 -0
- data/lib/daru_lite/core/merge.rb +270 -0
- data/lib/daru_lite/core/query.rb +109 -0
- data/lib/daru_lite/dataframe.rb +3080 -0
- data/lib/daru_lite/date_time/index.rb +569 -0
- data/lib/daru_lite/date_time/offsets.rb +397 -0
- data/lib/daru_lite/exceptions.rb +2 -0
- data/lib/daru_lite/extensions/which_dsl.rb +53 -0
- data/lib/daru_lite/formatters/table.rb +52 -0
- data/lib/daru_lite/helpers/array.rb +53 -0
- data/lib/daru_lite/index/categorical_index.rb +201 -0
- data/lib/daru_lite/index/index.rb +374 -0
- data/lib/daru_lite/index/multi_index.rb +374 -0
- data/lib/daru_lite/io/csv/converters.rb +21 -0
- data/lib/daru_lite/io/io.rb +294 -0
- data/lib/daru_lite/io/sql_data_source.rb +97 -0
- data/lib/daru_lite/iruby/helpers.rb +38 -0
- data/lib/daru_lite/iruby/templates/dataframe.html.erb +5 -0
- data/lib/daru_lite/iruby/templates/dataframe_mi.html.erb +5 -0
- data/lib/daru_lite/iruby/templates/dataframe_mi_tbody.html.erb +35 -0
- data/lib/daru_lite/iruby/templates/dataframe_mi_thead.html.erb +21 -0
- data/lib/daru_lite/iruby/templates/dataframe_tbody.html.erb +28 -0
- data/lib/daru_lite/iruby/templates/dataframe_thead.html.erb +21 -0
- data/lib/daru_lite/iruby/templates/multi_index.html.erb +12 -0
- data/lib/daru_lite/iruby/templates/vector.html.erb +5 -0
- data/lib/daru_lite/iruby/templates/vector_mi.html.erb +5 -0
- data/lib/daru_lite/iruby/templates/vector_mi_tbody.html.erb +26 -0
- data/lib/daru_lite/iruby/templates/vector_mi_thead.html.erb +8 -0
- data/lib/daru_lite/iruby/templates/vector_tbody.html.erb +17 -0
- data/lib/daru_lite/iruby/templates/vector_thead.html.erb +8 -0
- data/lib/daru_lite/maths/arithmetic/dataframe.rb +91 -0
- data/lib/daru_lite/maths/arithmetic/vector.rb +117 -0
- data/lib/daru_lite/maths/statistics/dataframe.rb +202 -0
- data/lib/daru_lite/maths/statistics/vector.rb +1019 -0
- data/lib/daru_lite/monkeys.rb +56 -0
- data/lib/daru_lite/vector.rb +1678 -0
- data/lib/daru_lite/version.rb +3 -0
- data/lib/daru_lite.rb +99 -0
- 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/profile/vector_new.rb +9 -0
- data/spec/accessors/array_wrapper_spec.rb +3 -0
- data/spec/category_spec.rb +1741 -0
- data/spec/core/group_by_spec.rb +655 -0
- data/spec/core/merge_spec.rb +179 -0
- data/spec/core/query_spec.rb +347 -0
- data/spec/daru_lite_spec.rb +22 -0
- data/spec/dataframe_spec.rb +4330 -0
- data/spec/date_time/data_spec.rb +197 -0
- data/spec/date_time/date_time_index_helper_spec.rb +72 -0
- data/spec/date_time/index_spec.rb +588 -0
- data/spec/date_time/offsets_spec.rb +465 -0
- data/spec/extensions/which_dsl_spec.rb +38 -0
- data/spec/fixtures/bank2.dat +200 -0
- data/spec/fixtures/boolean_converter_test.csv +5 -0
- data/spec/fixtures/countries.json +7794 -0
- data/spec/fixtures/duplicates.csv +32 -0
- data/spec/fixtures/eciresults.html +394 -0
- data/spec/fixtures/empties.dat +2 -0
- data/spec/fixtures/empty_rows_test.csv +17 -0
- data/spec/fixtures/macau.html +3691 -0
- data/spec/fixtures/macd_data.csv +150 -0
- data/spec/fixtures/matrix_test.csv +100 -0
- data/spec/fixtures/moneycontrol.html +6812 -0
- data/spec/fixtures/music_data.tsv +2501 -0
- data/spec/fixtures/repeated_fields.csv +7 -0
- data/spec/fixtures/sales-funnel.csv +18 -0
- data/spec/fixtures/scientific_notation.csv +4 -0
- data/spec/fixtures/string_converter_test.csv +5 -0
- data/spec/fixtures/strings.dat +2 -0
- data/spec/fixtures/test_xls.xls +0 -0
- data/spec/fixtures/test_xls_2.xls +0 -0
- data/spec/fixtures/url_test.txt~ +0 -0
- data/spec/fixtures/valid_markup.html +62 -0
- data/spec/fixtures/wiki_climate.html +1243 -0
- data/spec/fixtures/wiki_table_info.html +631 -0
- data/spec/formatters/table_formatter_spec.rb +137 -0
- data/spec/helpers_spec.rb +8 -0
- data/spec/index/categorical_index_spec.rb +170 -0
- data/spec/index/index_spec.rb +417 -0
- data/spec/index/multi_index_spec.rb +680 -0
- data/spec/io/io_spec.rb +373 -0
- data/spec/io/sql_data_source_spec.rb +56 -0
- data/spec/iruby/dataframe_spec.rb +170 -0
- data/spec/iruby/helpers_spec.rb +49 -0
- data/spec/iruby/multi_index_spec.rb +37 -0
- data/spec/iruby/vector_spec.rb +105 -0
- data/spec/maths/arithmetic/dataframe_spec.rb +148 -0
- data/spec/maths/arithmetic/vector_spec.rb +165 -0
- data/spec/maths/statistics/dataframe_spec.rb +178 -0
- data/spec/maths/statistics/vector_spec.rb +756 -0
- data/spec/monkeys_spec.rb +42 -0
- data/spec/shared/vector_display_spec.rb +213 -0
- data/spec/spec_helper.rb +87 -0
- data/spec/support/database_helper.rb +30 -0
- data/spec/support/matchers.rb +5 -0
- data/spec/vector_spec.rb +2293 -0
- metadata +571 -0
@@ -0,0 +1,397 @@
|
|
1
|
+
module DaruLite
|
2
|
+
# Generic class for generating date offsets.
|
3
|
+
class DateOffset
|
4
|
+
# A DaruLite::DateOffset object is created by a passing certain options
|
5
|
+
# to the constructor, which determine the kind of offset the object
|
6
|
+
# will support.
|
7
|
+
#
|
8
|
+
# You can pass one of the following options followed by their number
|
9
|
+
# to the DateOffset constructor:
|
10
|
+
#
|
11
|
+
# * :secs - Create a seconds offset
|
12
|
+
# * :mins - Create a minutes offset
|
13
|
+
# * :hours - Create an hours offset
|
14
|
+
# * :days - Create a days offset
|
15
|
+
# * :weeks - Create a weeks offset
|
16
|
+
# * :months - Create a months offset
|
17
|
+
# * :years - Create a years offset
|
18
|
+
#
|
19
|
+
# Additionaly, passing the `:n` option will apply the offset that many times.
|
20
|
+
#
|
21
|
+
# @example Usage of DateOffset
|
22
|
+
# # Create an offset of 3 weeks.
|
23
|
+
# offset = DaruLite::DateOffset.new(weeks: 3)
|
24
|
+
# offset + DateTime.new(2012,5,3)
|
25
|
+
# #=> #<DateTime: 2012-05-24T00:00:00+00:00 ((2456072j,0s,0n),+0s,2299161j)>
|
26
|
+
#
|
27
|
+
# # Create an offset of 5 hours
|
28
|
+
# offset = DaruLite::DateOffset.new(hours: 5)
|
29
|
+
# offset + DateTime.new(2015,3,3,23,5,1)
|
30
|
+
# #=> #<DateTime: 2015-03-04T04:05:01+00:00 ((2457086j,14701s,0n),+0s,2299161j)>
|
31
|
+
#
|
32
|
+
# # Create an offset of 2 minutes, applied 5 times
|
33
|
+
# offset = DaruLite::DateOffset.new(mins: 2, n: 5)
|
34
|
+
# offset + DateTime.new(2011,5,3,3,5)
|
35
|
+
# #=> #<DateTime: 2011-05-03T03:15:00+00:00 ((2455685j,11700s,0n),+0s,2299161j)>
|
36
|
+
def initialize(opts = {})
|
37
|
+
n = opts[:n] || 1
|
38
|
+
Offsets::LIST.each do |key, klass|
|
39
|
+
if opts.key?(key)
|
40
|
+
@offset = klass.new(n * opts[key])
|
41
|
+
break
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
@offset = Offsets::Day.new(7 * n * opts[:weeks]) if opts[:weeks]
|
46
|
+
end
|
47
|
+
|
48
|
+
# Offset a DateTime forward.
|
49
|
+
#
|
50
|
+
# @param other [DateTime] A DateTime object which is to offset.
|
51
|
+
def +(other)
|
52
|
+
@offset + other
|
53
|
+
end
|
54
|
+
|
55
|
+
# Offset a DateTime backward.
|
56
|
+
#
|
57
|
+
# @param other [DateTime] A DateTime object which is to offset.
|
58
|
+
def -(other)
|
59
|
+
@offset - other
|
60
|
+
end
|
61
|
+
|
62
|
+
def -@
|
63
|
+
NegativeDateOffset.new(self)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class NegativeDateOffset
|
68
|
+
def initialize(offset)
|
69
|
+
@offset = offset
|
70
|
+
end
|
71
|
+
|
72
|
+
def +(other)
|
73
|
+
@offset - other
|
74
|
+
end
|
75
|
+
|
76
|
+
def -(other)
|
77
|
+
@offset + other
|
78
|
+
end
|
79
|
+
|
80
|
+
def -@
|
81
|
+
@offset
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
module Offsets
|
86
|
+
class DateOffsetType < DateOffset
|
87
|
+
# @!method initialize(n)
|
88
|
+
# Initialize one of the subclasses of DateOffsetType with the number of the times
|
89
|
+
# the offset should be applied, which is the supplied as the argument.
|
90
|
+
#
|
91
|
+
# @param n [Integer] The number of times an offset should be applied.
|
92
|
+
def initialize(n = 1)
|
93
|
+
@n = n
|
94
|
+
end
|
95
|
+
|
96
|
+
def freq_string
|
97
|
+
(@n == 1 ? '' : @n.to_s) + self.class::FREQ
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Private superclass for Offsets with equal inter-frequencies.
|
102
|
+
# @abstract
|
103
|
+
# @private
|
104
|
+
class Tick < DateOffsetType
|
105
|
+
def +(other)
|
106
|
+
other + (@n * multiplier)
|
107
|
+
end
|
108
|
+
|
109
|
+
def -(other)
|
110
|
+
other - (@n * multiplier)
|
111
|
+
end
|
112
|
+
|
113
|
+
def ==(other)
|
114
|
+
other.is_a?(Tick) && period == other.period
|
115
|
+
end
|
116
|
+
|
117
|
+
def period
|
118
|
+
@n * multiplier
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Create a seconds offset
|
123
|
+
#
|
124
|
+
# @param n [Integer] The number of times an offset should be applied.
|
125
|
+
# @example Create a Seconds offset
|
126
|
+
# offset = DaruLite::Offsets::Second.new(5)
|
127
|
+
# offset + DateTime.new(2012,5,1,4,3)
|
128
|
+
# #=> #<DateTime: 2012-05-01T04:03:05+00:00 ((2456049j,14585s,0n),+0s,2299161j)>
|
129
|
+
class Second < Tick
|
130
|
+
FREQ = 'S'.freeze
|
131
|
+
|
132
|
+
def multiplier
|
133
|
+
1.to_r / 24 / 60 / 60
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Create a minutes offset
|
138
|
+
#
|
139
|
+
# @param n [Integer] The number of times an offset should be applied.
|
140
|
+
# @example Create a Minutes offset
|
141
|
+
# offset = DaruLite::Offsets::Minute.new(8)
|
142
|
+
# offset + DateTime.new(2012,5,1,4,3)
|
143
|
+
# #=> #<DateTime: 2012-05-01T04:11:00+00:00 ((2456049j,15060s,0n),+0s,2299161j)>
|
144
|
+
class Minute < Tick
|
145
|
+
FREQ = 'M'.freeze
|
146
|
+
|
147
|
+
def multiplier
|
148
|
+
1.to_r / 24 / 60
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Create an hours offset
|
153
|
+
#
|
154
|
+
# @param n [Integer] The number of times an offset should be applied.
|
155
|
+
# @example Create a Hour offset
|
156
|
+
# offset = DaruLite::Offsets::Hour.new(8)
|
157
|
+
# offset + DateTime.new(2012,5,1,4,3)
|
158
|
+
# #=> #<DateTime: 2012-05-01T12:03:00+00:00 ((2456049j,43380s,0n),+0s,2299161j)>
|
159
|
+
class Hour < Tick
|
160
|
+
FREQ = 'H'.freeze
|
161
|
+
|
162
|
+
def multiplier
|
163
|
+
1.to_r / 24
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# Create an days offset
|
168
|
+
#
|
169
|
+
# @param n [Integer] The number of times an offset should be applied.
|
170
|
+
# @example Create a Day offset
|
171
|
+
# offset = DaruLite::Offsets::Day.new(2)
|
172
|
+
# offset + DateTime.new(2012,5,1,4,3)
|
173
|
+
# #=> #<DateTime: 2012-05-03T04:03:00+00:00 ((2456051j,14580s,0n),+0s,2299161j)>
|
174
|
+
class Day < Tick
|
175
|
+
FREQ = 'D'.freeze
|
176
|
+
|
177
|
+
def multiplier
|
178
|
+
1
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# Create an months offset
|
183
|
+
#
|
184
|
+
# @param n [Integer] The number of times an offset should be applied.
|
185
|
+
# @example Create a Month offset
|
186
|
+
# offset = DaruLite::Offsets::Month.new(5)
|
187
|
+
# offset + DateTime.new(2012,5,1,4,3)
|
188
|
+
# #=> #<DateTime: 2012-10-01T04:03:00+00:00 ((2456202j,14580s,0n),+0s,2299161j)>
|
189
|
+
class Month < Tick
|
190
|
+
FREQ = 'MONTH'.freeze
|
191
|
+
|
192
|
+
def +(other)
|
193
|
+
other >> @n
|
194
|
+
end
|
195
|
+
|
196
|
+
def -(other)
|
197
|
+
other << @n
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# Create a years offset
|
202
|
+
#
|
203
|
+
# @param n [Integer] The number of times an offset should be applied.
|
204
|
+
# @example Create a Year offset
|
205
|
+
# offset = DaruLite::Offsets::Year.new(2)
|
206
|
+
# offset + DateTime.new(2012,5,1,4,3)
|
207
|
+
# #=> #<DateTime: 2014-05-01T04:03:00+00:00 ((2456779j,14580s,0n),+0s,2299161j)>
|
208
|
+
class Year < Tick
|
209
|
+
FREQ = 'YEAR'.freeze
|
210
|
+
|
211
|
+
def +(other)
|
212
|
+
other >> (@n * 12)
|
213
|
+
end
|
214
|
+
|
215
|
+
def -(other)
|
216
|
+
other << (@n * 12)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
class Week < DateOffset
|
221
|
+
def initialize(*args)
|
222
|
+
@n = args[0].is_a?(Hash) ? 1 : args[0]
|
223
|
+
opts = args[-1]
|
224
|
+
@weekday = opts[:weekday] || 0
|
225
|
+
end
|
226
|
+
|
227
|
+
def +(other)
|
228
|
+
wday = other.wday
|
229
|
+
distance = (@weekday - wday).abs
|
230
|
+
if @weekday > wday
|
231
|
+
other + distance + (7 * (@n - 1))
|
232
|
+
else
|
233
|
+
other + (7 - distance) + (7 * (@n - 1))
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def -(other)
|
238
|
+
wday = other.wday
|
239
|
+
distance = (@weekday - wday).abs
|
240
|
+
if @weekday >= wday
|
241
|
+
other - ((7 - distance) + (7 * (@n - 1)))
|
242
|
+
else
|
243
|
+
other - (distance + (7 * (@n - 1)))
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
def on_offset?(date_time)
|
248
|
+
date_time.wday == @weekday
|
249
|
+
end
|
250
|
+
|
251
|
+
def freq_string
|
252
|
+
"#{@n == 1 ? '' : @n.to_s}W-#{DaruLite::DAYS_OF_WEEK.key(@weekday)}"
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
# Create a month begin offset
|
257
|
+
#
|
258
|
+
# @param n [Integer] The number of times an offset should be applied.
|
259
|
+
# @example Create a MonthBegin offset
|
260
|
+
# offset = DaruLite::Offsets::MonthBegin.new(2)
|
261
|
+
# offset + DateTime.new(2012,5,5)
|
262
|
+
# #=> #<DateTime: 2012-07-01T00:00:00+00:00 ((2456110j,0s,0n),+0s,2299161j)>
|
263
|
+
class MonthBegin < DateOffsetType
|
264
|
+
FREQ = 'MB'.freeze
|
265
|
+
|
266
|
+
def +(other)
|
267
|
+
@n.times do
|
268
|
+
days_in_month = DaruLite::MONTH_DAYS[other.month]
|
269
|
+
days_in_month += 1 if other.leap? && other.month == 2
|
270
|
+
other += (days_in_month - other.day + 1)
|
271
|
+
end
|
272
|
+
|
273
|
+
other
|
274
|
+
end
|
275
|
+
|
276
|
+
def -(other)
|
277
|
+
@n.times do
|
278
|
+
other <<= 1 if on_offset?(other)
|
279
|
+
other = DateTime.new(other.year, other.month, 1,
|
280
|
+
other.hour, other.min, other.sec)
|
281
|
+
end
|
282
|
+
|
283
|
+
other
|
284
|
+
end
|
285
|
+
|
286
|
+
def on_offset?(date_time)
|
287
|
+
date_time.day == 1
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
# Create a month end offset
|
292
|
+
#
|
293
|
+
# @param n [Integer] The number of times an offset should be applied.
|
294
|
+
# @example Create a MonthEnd offset
|
295
|
+
# offset = DaruLite::Offsets::MonthEnd.new
|
296
|
+
# offset + DateTime.new(2012,5,5)
|
297
|
+
# #=> #<DateTime: 2012-05-31T00:00:00+00:00 ((2456079j,0s,0n),+0s,2299161j)>
|
298
|
+
class MonthEnd < DateOffsetType
|
299
|
+
FREQ = 'ME'.freeze
|
300
|
+
|
301
|
+
def +(other)
|
302
|
+
@n.times do
|
303
|
+
other >>= 1 if on_offset?(other)
|
304
|
+
days_in_month = DaruLite::MONTH_DAYS[other.month]
|
305
|
+
days_in_month += 1 if other.leap? && other.month == 2
|
306
|
+
|
307
|
+
other += (days_in_month - other.day)
|
308
|
+
end
|
309
|
+
|
310
|
+
other
|
311
|
+
end
|
312
|
+
|
313
|
+
def -(other)
|
314
|
+
@n.times do
|
315
|
+
other <<= 1
|
316
|
+
days_in_month = DaruLite::MONTH_DAYS[other.month]
|
317
|
+
days_in_month += 1 if other.leap? && other.month == 2
|
318
|
+
|
319
|
+
other += (days_in_month - other.day)
|
320
|
+
end
|
321
|
+
|
322
|
+
other
|
323
|
+
end
|
324
|
+
|
325
|
+
def on_offset?(date_time)
|
326
|
+
(date_time + 1).day == 1
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
# Create a year begin offset
|
331
|
+
#
|
332
|
+
# @param n [Integer] The number of times an offset should be applied.
|
333
|
+
# @example Create a YearBegin offset
|
334
|
+
# offset = DaruLite::Offsets::YearBegin.new(3)
|
335
|
+
# offset + DateTime.new(2012,5,5)
|
336
|
+
# #=> #<DateTime: 2015-01-01T00:00:00+00:00 ((2457024j,0s,0n),+0s,2299161j)>
|
337
|
+
class YearBegin < DateOffsetType
|
338
|
+
FREQ = 'YB'.freeze
|
339
|
+
|
340
|
+
def +(other)
|
341
|
+
DateTime.new(other.year + @n, 1, 1,
|
342
|
+
other.hour, other.min, other.sec)
|
343
|
+
end
|
344
|
+
|
345
|
+
def -(other)
|
346
|
+
if on_offset?(other)
|
347
|
+
DateTime.new(other.year - @n, 1, 1,
|
348
|
+
other.hour, other.min, other.sec)
|
349
|
+
else
|
350
|
+
DateTime.new(other.year - (@n - 1), 1, 1)
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
def on_offset?(date_time)
|
355
|
+
date_time.month == 1 && date_time.day == 1
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
# Create a year end offset
|
360
|
+
#
|
361
|
+
# @param n [Integer] The number of times an offset should be applied.
|
362
|
+
# @example Create a YearEnd offset
|
363
|
+
# offset = DaruLite::Offsets::YearEnd.new
|
364
|
+
# offset + DateTime.new(2012,5,5)
|
365
|
+
# #=> #<DateTime: 2012-12-31T00:00:00+00:00 ((2456293j,0s,0n),+0s,2299161j)>
|
366
|
+
class YearEnd < DateOffsetType
|
367
|
+
FREQ = 'YE'.freeze
|
368
|
+
|
369
|
+
def +(other)
|
370
|
+
if on_offset?(other)
|
371
|
+
DateTime.new(other.year + @n, 12, 31,
|
372
|
+
other.hour, other.min, other.sec)
|
373
|
+
else
|
374
|
+
DateTime.new(other.year + (@n - 1), 12, 31,
|
375
|
+
other.hour, other.min, other.sec)
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
def -(other)
|
380
|
+
DateTime.new(other.year - 1, 12, 31)
|
381
|
+
end
|
382
|
+
|
383
|
+
def on_offset?(date_time)
|
384
|
+
date_time.month == 12 && date_time.day == 31
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
LIST = {
|
389
|
+
secs: Second,
|
390
|
+
mins: Minute,
|
391
|
+
hours: Hour,
|
392
|
+
days: Day,
|
393
|
+
months: Month,
|
394
|
+
years: Year
|
395
|
+
}.freeze
|
396
|
+
end
|
397
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Support for a simple query DSL for accessing where(), inspired by gem "squeel"
|
2
|
+
|
3
|
+
module DaruLite
|
4
|
+
class DataFrame
|
5
|
+
# a simple query DSL for accessing where(), inspired by gem "squeel"
|
6
|
+
# e.g.:
|
7
|
+
# df.which{ `FamilySize` == `FamilySize`.max }
|
8
|
+
# equals
|
9
|
+
# df.where( df['FamilySize'].eq( df['FamilySize'].max ) )
|
10
|
+
#
|
11
|
+
# e.g.:
|
12
|
+
# df.which{ (`NameTitle` == 'Dr') & (`Sex` == 'female') }
|
13
|
+
# equals
|
14
|
+
# df.where( df['NameTitle'].eq('Dr') & df['Sex'].eq('female') )
|
15
|
+
def which(&block)
|
16
|
+
WhichQuery.new(self, &block).exec
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class WhichQuery
|
21
|
+
def initialize(df, &condition)
|
22
|
+
@df = df
|
23
|
+
@condition = condition
|
24
|
+
end
|
25
|
+
|
26
|
+
# executes a block of DSL code
|
27
|
+
def exec
|
28
|
+
query = instance_eval(&@condition)
|
29
|
+
@df.where(query)
|
30
|
+
end
|
31
|
+
|
32
|
+
def `(vector_name)
|
33
|
+
vector_name = vector_name.to_sym if !@df.has_vector?(vector_name) && @df.has_vector?(vector_name.to_sym)
|
34
|
+
VectorWrapper.new(@df[vector_name])
|
35
|
+
end
|
36
|
+
|
37
|
+
class VectorWrapper < SimpleDelegator
|
38
|
+
{
|
39
|
+
:== => :eq,
|
40
|
+
:!= => :not_eq,
|
41
|
+
:< => :lt,
|
42
|
+
:<= => :lteq,
|
43
|
+
:> => :mt,
|
44
|
+
:>= => :mteq,
|
45
|
+
:=~ => :in
|
46
|
+
}.each do |opt, method|
|
47
|
+
define_method opt do |*args|
|
48
|
+
send(method, *args)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module DaruLite
|
2
|
+
module Formatters
|
3
|
+
class Table
|
4
|
+
def self.format(data, options = {})
|
5
|
+
new(data, options[:headers], options[:row_headers])
|
6
|
+
.format(options[:threshold], options[:spacing])
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(data, headers, row_headers)
|
10
|
+
@data = data || []
|
11
|
+
@headers = (headers || []).to_a
|
12
|
+
@row_headers = (row_headers || []).to_a
|
13
|
+
@row_headers = [''] * @data.to_a.size if @row_headers.empty?
|
14
|
+
end
|
15
|
+
|
16
|
+
def format(threshold = nil, spacing = nil)
|
17
|
+
rows = build_rows(threshold || DaruLite.max_rows)
|
18
|
+
|
19
|
+
formatter = construct_formatter rows, spacing || DaruLite.spacing
|
20
|
+
|
21
|
+
rows.map { |r| formatter % r }.join("\n")
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def build_rows(threshold)
|
27
|
+
@row_headers.first(threshold).zip(@data).map do |(r, datarow)|
|
28
|
+
[*[r].flatten.map(&:to_s), *(datarow || []).map { |v| pretty_to_s(v) }]
|
29
|
+
end.tap do |rows|
|
30
|
+
unless @headers.empty?
|
31
|
+
spaces_to_add = rows.empty? ? 0 : rows.first.size - @headers.size
|
32
|
+
rows.unshift ([''] * spaces_to_add) + @headers.map(&:to_s)
|
33
|
+
end
|
34
|
+
|
35
|
+
rows << (['...'] * rows.first.count) if @row_headers.count > threshold
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def construct_formatter(rows, spacing)
|
40
|
+
width = rows.flatten.map(&:size).max || 0
|
41
|
+
width = [3, width].max # not less than 'nil'
|
42
|
+
width = [width, spacing].min # not more than max width
|
43
|
+
|
44
|
+
" %#{width}.#{width}s" * rows.first.size if rows.first
|
45
|
+
end
|
46
|
+
|
47
|
+
def pretty_to_s(val)
|
48
|
+
val.nil? ? 'nil' : val.to_s
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module DaruLite
|
2
|
+
module ArrayHelper
|
3
|
+
module_function
|
4
|
+
|
5
|
+
# Recode repeated values on an array, adding the number of repetition
|
6
|
+
# at the end
|
7
|
+
# Example:
|
8
|
+
# a=%w{a b c c d d d e}
|
9
|
+
# a.recode_repeated
|
10
|
+
# => ["a","b","c_1","c_2","d_1","d_2","d_3","e"]
|
11
|
+
def recode_repeated(array)
|
12
|
+
return array if array.size == array.uniq.size
|
13
|
+
|
14
|
+
# create hash of { <name> => 0}
|
15
|
+
# for all names which are more than one time in array
|
16
|
+
counter = array
|
17
|
+
.group_by(&:itself)
|
18
|
+
.select { |_, g| g.size > 1 }
|
19
|
+
.keys
|
20
|
+
.to_h { |n| [n, 0] }
|
21
|
+
|
22
|
+
# ...and use this hash for actual recode
|
23
|
+
array.collect do |n|
|
24
|
+
if counter.key?(n)
|
25
|
+
counter[n] += 1
|
26
|
+
new_n = format('%<index>s_%<counter>d', index: n, counter: counter[n])
|
27
|
+
n.is_a?(Symbol) ? new_n.to_sym : new_n
|
28
|
+
else
|
29
|
+
n
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def array_of?(array, match)
|
35
|
+
array.is_a?(Array) &&
|
36
|
+
!array.empty? &&
|
37
|
+
array.all? { |el| match === el } # rubocop:disable Style/CaseEquality,Performance/RedundantEqualityComparisonBlock
|
38
|
+
end
|
39
|
+
|
40
|
+
def sort_composite_data(array)
|
41
|
+
array.sort
|
42
|
+
rescue ArgumentError, TypeError => e
|
43
|
+
case e.to_s
|
44
|
+
when /comparison of Symbol with String failed/,
|
45
|
+
/comparison of Symbol with \d+ failed/,
|
46
|
+
/comparison of String with :.* failed/,
|
47
|
+
/comparison of Integer with :.* failed/,
|
48
|
+
/no implicit conversion from nil to integer/
|
49
|
+
array.sort_by(&:to_s)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|