daru_lite 0.1

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