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.
Files changed (149) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE.md +18 -0
  3. data/.github/workflows/ci.yml +33 -0
  4. data/.gitignore +10 -0
  5. data/.rspec +2 -0
  6. data/.rubocop.yml +27 -0
  7. data/.rubocop_todo.yml +137 -0
  8. data/CONTRIBUTING.md +47 -0
  9. data/Gemfile +2 -0
  10. data/History.md +4 -0
  11. data/LICENSE +24 -0
  12. data/README.md +218 -0
  13. data/Rakefile +69 -0
  14. data/ReleasePolicy.md +20 -0
  15. data/benchmarks/TradeoffData.csv +65 -0
  16. data/benchmarks/csv_reading.rb +22 -0
  17. data/benchmarks/dataframe_creation.rb +39 -0
  18. data/benchmarks/db_loading.rb +34 -0
  19. data/benchmarks/duplicating.rb +45 -0
  20. data/benchmarks/group_by.rb +32 -0
  21. data/benchmarks/joining.rb +52 -0
  22. data/benchmarks/row_access.rb +41 -0
  23. data/benchmarks/row_assign.rb +36 -0
  24. data/benchmarks/sorting.rb +51 -0
  25. data/benchmarks/statistics.rb +28 -0
  26. data/benchmarks/vector_access.rb +31 -0
  27. data/benchmarks/vector_assign.rb +42 -0
  28. data/benchmarks/where_clause.rb +48 -0
  29. data/benchmarks/where_vs_filter.rb +28 -0
  30. data/daru_lite.gemspec +55 -0
  31. data/images/README.md +5 -0
  32. data/images/con0.png +0 -0
  33. data/images/con1.png +0 -0
  34. data/images/init0.png +0 -0
  35. data/images/init1.png +0 -0
  36. data/images/man0.png +0 -0
  37. data/images/man1.png +0 -0
  38. data/images/man2.png +0 -0
  39. data/images/man3.png +0 -0
  40. data/images/man4.png +0 -0
  41. data/images/man5.png +0 -0
  42. data/images/man6.png +0 -0
  43. data/lib/daru_lite/accessors/array_wrapper.rb +109 -0
  44. data/lib/daru_lite/accessors/dataframe_by_row.rb +25 -0
  45. data/lib/daru_lite/accessors/mdarray_wrapper.rb +7 -0
  46. data/lib/daru_lite/category.rb +929 -0
  47. data/lib/daru_lite/configuration.rb +34 -0
  48. data/lib/daru_lite/core/group_by.rb +403 -0
  49. data/lib/daru_lite/core/merge.rb +270 -0
  50. data/lib/daru_lite/core/query.rb +109 -0
  51. data/lib/daru_lite/dataframe.rb +3080 -0
  52. data/lib/daru_lite/date_time/index.rb +569 -0
  53. data/lib/daru_lite/date_time/offsets.rb +397 -0
  54. data/lib/daru_lite/exceptions.rb +2 -0
  55. data/lib/daru_lite/extensions/which_dsl.rb +53 -0
  56. data/lib/daru_lite/formatters/table.rb +52 -0
  57. data/lib/daru_lite/helpers/array.rb +53 -0
  58. data/lib/daru_lite/index/categorical_index.rb +201 -0
  59. data/lib/daru_lite/index/index.rb +374 -0
  60. data/lib/daru_lite/index/multi_index.rb +374 -0
  61. data/lib/daru_lite/io/csv/converters.rb +21 -0
  62. data/lib/daru_lite/io/io.rb +294 -0
  63. data/lib/daru_lite/io/sql_data_source.rb +97 -0
  64. data/lib/daru_lite/iruby/helpers.rb +38 -0
  65. data/lib/daru_lite/iruby/templates/dataframe.html.erb +5 -0
  66. data/lib/daru_lite/iruby/templates/dataframe_mi.html.erb +5 -0
  67. data/lib/daru_lite/iruby/templates/dataframe_mi_tbody.html.erb +35 -0
  68. data/lib/daru_lite/iruby/templates/dataframe_mi_thead.html.erb +21 -0
  69. data/lib/daru_lite/iruby/templates/dataframe_tbody.html.erb +28 -0
  70. data/lib/daru_lite/iruby/templates/dataframe_thead.html.erb +21 -0
  71. data/lib/daru_lite/iruby/templates/multi_index.html.erb +12 -0
  72. data/lib/daru_lite/iruby/templates/vector.html.erb +5 -0
  73. data/lib/daru_lite/iruby/templates/vector_mi.html.erb +5 -0
  74. data/lib/daru_lite/iruby/templates/vector_mi_tbody.html.erb +26 -0
  75. data/lib/daru_lite/iruby/templates/vector_mi_thead.html.erb +8 -0
  76. data/lib/daru_lite/iruby/templates/vector_tbody.html.erb +17 -0
  77. data/lib/daru_lite/iruby/templates/vector_thead.html.erb +8 -0
  78. data/lib/daru_lite/maths/arithmetic/dataframe.rb +91 -0
  79. data/lib/daru_lite/maths/arithmetic/vector.rb +117 -0
  80. data/lib/daru_lite/maths/statistics/dataframe.rb +202 -0
  81. data/lib/daru_lite/maths/statistics/vector.rb +1019 -0
  82. data/lib/daru_lite/monkeys.rb +56 -0
  83. data/lib/daru_lite/vector.rb +1678 -0
  84. data/lib/daru_lite/version.rb +3 -0
  85. data/lib/daru_lite.rb +99 -0
  86. data/profile/_base.rb +23 -0
  87. data/profile/df_to_a.rb +10 -0
  88. data/profile/filter.rb +13 -0
  89. data/profile/joining.rb +13 -0
  90. data/profile/sorting.rb +12 -0
  91. data/profile/vector_each_with_index.rb +9 -0
  92. data/profile/vector_new.rb +9 -0
  93. data/spec/accessors/array_wrapper_spec.rb +3 -0
  94. data/spec/category_spec.rb +1741 -0
  95. data/spec/core/group_by_spec.rb +655 -0
  96. data/spec/core/merge_spec.rb +179 -0
  97. data/spec/core/query_spec.rb +347 -0
  98. data/spec/daru_lite_spec.rb +22 -0
  99. data/spec/dataframe_spec.rb +4330 -0
  100. data/spec/date_time/data_spec.rb +197 -0
  101. data/spec/date_time/date_time_index_helper_spec.rb +72 -0
  102. data/spec/date_time/index_spec.rb +588 -0
  103. data/spec/date_time/offsets_spec.rb +465 -0
  104. data/spec/extensions/which_dsl_spec.rb +38 -0
  105. data/spec/fixtures/bank2.dat +200 -0
  106. data/spec/fixtures/boolean_converter_test.csv +5 -0
  107. data/spec/fixtures/countries.json +7794 -0
  108. data/spec/fixtures/duplicates.csv +32 -0
  109. data/spec/fixtures/eciresults.html +394 -0
  110. data/spec/fixtures/empties.dat +2 -0
  111. data/spec/fixtures/empty_rows_test.csv +17 -0
  112. data/spec/fixtures/macau.html +3691 -0
  113. data/spec/fixtures/macd_data.csv +150 -0
  114. data/spec/fixtures/matrix_test.csv +100 -0
  115. data/spec/fixtures/moneycontrol.html +6812 -0
  116. data/spec/fixtures/music_data.tsv +2501 -0
  117. data/spec/fixtures/repeated_fields.csv +7 -0
  118. data/spec/fixtures/sales-funnel.csv +18 -0
  119. data/spec/fixtures/scientific_notation.csv +4 -0
  120. data/spec/fixtures/string_converter_test.csv +5 -0
  121. data/spec/fixtures/strings.dat +2 -0
  122. data/spec/fixtures/test_xls.xls +0 -0
  123. data/spec/fixtures/test_xls_2.xls +0 -0
  124. data/spec/fixtures/url_test.txt~ +0 -0
  125. data/spec/fixtures/valid_markup.html +62 -0
  126. data/spec/fixtures/wiki_climate.html +1243 -0
  127. data/spec/fixtures/wiki_table_info.html +631 -0
  128. data/spec/formatters/table_formatter_spec.rb +137 -0
  129. data/spec/helpers_spec.rb +8 -0
  130. data/spec/index/categorical_index_spec.rb +170 -0
  131. data/spec/index/index_spec.rb +417 -0
  132. data/spec/index/multi_index_spec.rb +680 -0
  133. data/spec/io/io_spec.rb +373 -0
  134. data/spec/io/sql_data_source_spec.rb +56 -0
  135. data/spec/iruby/dataframe_spec.rb +170 -0
  136. data/spec/iruby/helpers_spec.rb +49 -0
  137. data/spec/iruby/multi_index_spec.rb +37 -0
  138. data/spec/iruby/vector_spec.rb +105 -0
  139. data/spec/maths/arithmetic/dataframe_spec.rb +148 -0
  140. data/spec/maths/arithmetic/vector_spec.rb +165 -0
  141. data/spec/maths/statistics/dataframe_spec.rb +178 -0
  142. data/spec/maths/statistics/vector_spec.rb +756 -0
  143. data/spec/monkeys_spec.rb +42 -0
  144. data/spec/shared/vector_display_spec.rb +213 -0
  145. data/spec/spec_helper.rb +87 -0
  146. data/spec/support/database_helper.rb +30 -0
  147. data/spec/support/matchers.rb +5 -0
  148. data/spec/vector_spec.rb +2293 -0
  149. 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,2 @@
1
+ class SizeError < StandardError
2
+ 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