daru_lite 0.1
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 +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
|