red_amber 0.2.2 → 0.3.0
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 +4 -4
- data/.rubocop.yml +114 -39
- data/CHANGELOG.md +203 -31
- data/Gemfile +5 -2
- data/README.md +62 -29
- data/benchmark/basic.yml +86 -0
- data/benchmark/combine.yml +62 -0
- data/benchmark/dataframe.yml +62 -0
- data/benchmark/drop_nil.yml +15 -3
- data/benchmark/group.yml +39 -0
- data/benchmark/reshape.yml +31 -0
- data/benchmark/{csv_load_penguins.yml → rover/csv_load_penguins.yml} +3 -3
- data/benchmark/rover/flights.yml +23 -0
- data/benchmark/rover/penguins.yml +23 -0
- data/benchmark/rover/planes.yml +23 -0
- data/benchmark/rover/weather.yml +23 -0
- data/benchmark/vector.yml +60 -0
- data/doc/DataFrame.md +335 -53
- data/doc/Vector.md +91 -0
- data/doc/image/dataframe/join.png +0 -0
- data/doc/image/dataframe/set_and_bind.png +0 -0
- data/doc/image/dataframe_model.png +0 -0
- data/lib/red_amber/data_frame.rb +167 -51
- data/lib/red_amber/data_frame_combinable.rb +486 -0
- data/lib/red_amber/data_frame_displayable.rb +6 -4
- data/lib/red_amber/data_frame_indexable.rb +2 -2
- data/lib/red_amber/data_frame_loadsave.rb +4 -1
- data/lib/red_amber/data_frame_reshaping.rb +35 -10
- data/lib/red_amber/data_frame_selectable.rb +221 -116
- data/lib/red_amber/data_frame_variable_operation.rb +146 -82
- data/lib/red_amber/group.rb +108 -18
- data/lib/red_amber/helper.rb +53 -43
- data/lib/red_amber/refinements.rb +199 -0
- data/lib/red_amber/vector.rb +56 -46
- data/lib/red_amber/vector_functions.rb +23 -83
- data/lib/red_amber/vector_selectable.rb +116 -69
- data/lib/red_amber/vector_updatable.rb +189 -65
- data/lib/red_amber/version.rb +1 -1
- data/lib/red_amber.rb +3 -0
- data/red_amber.gemspec +4 -3
- metadata +24 -10
@@ -3,71 +3,145 @@
|
|
3
3
|
module RedAmber
|
4
4
|
# mix-ins for the class DataFrame
|
5
5
|
module DataFrameVariableOperation
|
6
|
-
#
|
6
|
+
# Array is refined
|
7
|
+
using RefineArray
|
8
|
+
|
9
|
+
# Pick up variables (columns) to create a new DataFrame
|
10
|
+
#
|
11
|
+
# @note DataFrame#pick creates a DataFrame with single key.
|
12
|
+
# DataFrame#[] creates a Vector if single key is specified.
|
13
|
+
#
|
14
|
+
# @overload pick(keys)
|
15
|
+
# Pick variables by Symbols or Strings.
|
16
|
+
#
|
17
|
+
# @param keys [Symbol, String, <Symbol, String>]
|
18
|
+
# key name(s) of variables to pick.
|
19
|
+
# @return [DataFrame]
|
20
|
+
# Picked DataFrame.
|
21
|
+
#
|
22
|
+
# @overload pick(booleans)
|
23
|
+
# Pick variables by booleans.
|
24
|
+
#
|
25
|
+
# @param booleans [<true, false, nil>]
|
26
|
+
# boolean array to pick variables at true.
|
27
|
+
# @return [DataFrame]
|
28
|
+
# Picked DataFrame.
|
29
|
+
#
|
30
|
+
# @overload pick(indices)
|
31
|
+
# Pick variables by column indices.
|
32
|
+
#
|
33
|
+
# @param indices [Integer, Float, Range<Integer>, Vector, Arrow::Array]
|
34
|
+
# numeric array to pick variables by column index.
|
35
|
+
# @return [DataFrame]
|
36
|
+
# Picked DataFrame.
|
37
|
+
#
|
7
38
|
def pick(*args, &block)
|
8
|
-
picker = args
|
9
39
|
if block
|
10
|
-
|
40
|
+
unless args.empty?
|
41
|
+
raise DataFrameArgumentError, 'Must not specify both arguments and block.'
|
42
|
+
end
|
11
43
|
|
12
|
-
|
44
|
+
args = [instance_eval(&block)]
|
13
45
|
end
|
14
|
-
picker.flatten!
|
15
|
-
return DataFrame.new if picker.empty? || picker == [nil]
|
16
|
-
|
17
|
-
key_vector = Vector.new(keys)
|
18
|
-
vec = parse_to_vector(picker, vsize: n_keys)
|
19
|
-
|
20
|
-
ary =
|
21
|
-
if vec.boolean?
|
22
|
-
key_vector.filter(*vec).to_a
|
23
|
-
elsif vec.numeric?
|
24
|
-
key_vector.take(*vec).to_a
|
25
|
-
elsif vec.string? || vec.dictionary?
|
26
|
-
picker
|
27
|
-
else
|
28
|
-
raise DataFrameArgumentError, "Invalid argument #{args}"
|
29
|
-
end
|
30
46
|
|
31
|
-
|
32
|
-
|
33
|
-
|
47
|
+
case args
|
48
|
+
in [] | [nil]
|
49
|
+
return DataFrame.new
|
50
|
+
in [*] if args.symbols?
|
51
|
+
return DataFrame.create(@table.select_columns(*args))
|
52
|
+
in [*] if args.booleans?
|
53
|
+
picker = keys.select_by_booleans(args)
|
54
|
+
return DataFrame.create(@table.select_columns(*picker))
|
55
|
+
in [(Vector | Arrow::Array | Arrow::ChunkedArray) => a]
|
56
|
+
picker = a.to_a
|
57
|
+
else
|
58
|
+
picker = parse_args(args, n_keys)
|
59
|
+
end
|
60
|
+
|
61
|
+
return DataFrame.new if picker.compact.empty?
|
62
|
+
|
63
|
+
if picker.booleans?
|
64
|
+
picker = keys.select_by_booleans(picker)
|
65
|
+
return DataFrame.create(@table.select_columns(*picker))
|
66
|
+
end
|
67
|
+
picker.compact!
|
68
|
+
raise DataFrameArgumentError, "some keys are duplicated: #{args}" if picker.uniq!
|
69
|
+
|
70
|
+
DataFrame.create(@table.select_columns(*picker))
|
34
71
|
end
|
35
72
|
|
36
|
-
#
|
73
|
+
# Drop some variables (columns) to create a remainer DataFrame
|
74
|
+
#
|
75
|
+
# @note DataFrame#drop creates a DataFrame even if it is a single column.
|
76
|
+
#
|
77
|
+
# @overload drop(keys)
|
78
|
+
# Drop variables by Symbols or Strings.
|
79
|
+
#
|
80
|
+
# @param keys [Symbol, String, <Symbol, String>]
|
81
|
+
# key name(s) of variables to drop.
|
82
|
+
# @return [DataFrame]
|
83
|
+
# Remainer DataFrame.
|
84
|
+
#
|
85
|
+
# @overload drop(booleans)
|
86
|
+
# Drop variables by booleans.
|
87
|
+
#
|
88
|
+
# @param booleans [<true, false, nil>]
|
89
|
+
# boolean array of variables to drop at true.
|
90
|
+
# @return [DataFrame]
|
91
|
+
# Remainer DataFrame.
|
92
|
+
#
|
93
|
+
# @overload drop(indices)
|
94
|
+
# Pick variables by column indices.
|
95
|
+
#
|
96
|
+
# @param indices [Integer, Float, Range<Integer>, Vector, Arrow::Array]
|
97
|
+
# numeric array of variables to drop by column index.
|
98
|
+
# @return [DataFrame]
|
99
|
+
# Remainer DataFrame.
|
100
|
+
#
|
37
101
|
def drop(*args, &block)
|
38
|
-
dropper = args
|
39
102
|
if block
|
40
|
-
|
103
|
+
unless args.empty?
|
104
|
+
raise DataFrameArgumentError, 'Must not specify both arguments and block.'
|
105
|
+
end
|
41
106
|
|
42
|
-
|
107
|
+
args = [instance_eval(&block)]
|
43
108
|
end
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
keys - key_vector.take(*vec).each.map(&:to_sym) # Array
|
54
|
-
elsif vec.string? || vec.dictionary?
|
55
|
-
keys - dropper
|
109
|
+
return self if args.empty? || empty?
|
110
|
+
|
111
|
+
picker =
|
112
|
+
if args.symbols?
|
113
|
+
keys - args
|
114
|
+
elsif args.booleans?
|
115
|
+
keys.reject_by_booleans(args)
|
116
|
+
elsif args.integers?
|
117
|
+
keys.reject_by_indices(args)
|
56
118
|
else
|
57
|
-
|
119
|
+
dropper = parse_args(args, n_keys)
|
120
|
+
if dropper.booleans?
|
121
|
+
keys.reject_by_booleans(dropper)
|
122
|
+
elsif dropper.symbols?
|
123
|
+
keys - dropper
|
124
|
+
else
|
125
|
+
dropper.compact!
|
126
|
+
unless dropper.integers?
|
127
|
+
raise DataFrameArgumentError, "Invalid argument #{args}"
|
128
|
+
end
|
129
|
+
|
130
|
+
keys.reject_by_indices(dropper)
|
131
|
+
end
|
58
132
|
end
|
59
133
|
|
60
|
-
return DataFrame.new if
|
134
|
+
return DataFrame.new if picker.empty?
|
61
135
|
|
62
|
-
|
63
|
-
# DataFrame#drop creates a DataFrame with single key.
|
64
|
-
DataFrame.new(@table[ary])
|
136
|
+
DataFrame.create(@table.select_columns(*picker))
|
65
137
|
end
|
66
138
|
|
67
139
|
# rename variables to create a new DataFrame
|
68
140
|
def rename(*renamer, &block)
|
69
141
|
if block
|
70
|
-
|
142
|
+
unless renamer.empty?
|
143
|
+
raise DataFrameArgumentError, 'Must not specify both arguments and a block'
|
144
|
+
end
|
71
145
|
|
72
146
|
renamer = [instance_eval(&block)]
|
73
147
|
end
|
@@ -90,35 +164,23 @@ module RedAmber
|
|
90
164
|
|
91
165
|
# assign variables to create a new DataFrame
|
92
166
|
def assign(*assigner, &block)
|
93
|
-
|
94
|
-
return self if appender.is_a?(DataFrame)
|
95
|
-
|
96
|
-
append_to_fields_and_arrays(appender, fields, arrays, append_to_left: false) unless appender.empty?
|
97
|
-
|
98
|
-
DataFrame.new(Arrow::Table.new(Arrow::Schema.new(fields), arrays))
|
167
|
+
assign_update(*assigner, append_to_left: false, &block)
|
99
168
|
end
|
100
169
|
|
101
170
|
def assign_left(*assigner, &block)
|
102
|
-
|
103
|
-
return self if appender.is_a?(DataFrame)
|
104
|
-
|
105
|
-
append_to_fields_and_arrays(appender, fields, arrays, append_to_left: true) unless appender.empty?
|
106
|
-
|
107
|
-
DataFrame.new(Arrow::Table.new(Arrow::Schema.new(fields), arrays))
|
171
|
+
assign_update(*assigner, append_to_left: true, &block)
|
108
172
|
end
|
109
173
|
|
110
174
|
private
|
111
175
|
|
112
|
-
def assign_update(*assigner, &block)
|
176
|
+
def assign_update(*assigner, append_to_left: false, &block)
|
113
177
|
if block
|
114
178
|
assigner_from_block = instance_eval(&block)
|
115
179
|
assigner =
|
116
|
-
|
117
|
-
|
180
|
+
case assigner_from_block
|
181
|
+
in _ if assigner.empty? # block only
|
118
182
|
[assigner_from_block]
|
119
|
-
|
120
|
-
# assigner_from_block in [Array, *]
|
121
|
-
elsif multiple_assigner?(assigner_from_block)
|
183
|
+
in [Vector, *] | [Array, *] | [Arrow::Array, *]
|
122
184
|
assigner.zip(assigner_from_block)
|
123
185
|
else
|
124
186
|
assigner.zip([assigner_from_block])
|
@@ -128,10 +190,10 @@ module RedAmber
|
|
128
190
|
case assigner
|
129
191
|
in [] | [nil] | [{}] | [[]]
|
130
192
|
return self
|
131
|
-
in [Hash => key_array_pairs]
|
132
|
-
# noop
|
133
193
|
in [(Symbol | String) => key, (Vector | Array | Arrow::Array) => array]
|
134
194
|
key_array_pairs = { key => array }
|
195
|
+
in [Hash => key_array_pairs]
|
196
|
+
# noop
|
135
197
|
in [Array => array_in_array]
|
136
198
|
key_array_pairs = try_convert_to_hash(array_in_array)
|
137
199
|
in [Array, *] => array_in_array1
|
@@ -151,20 +213,27 @@ module RedAmber
|
|
151
213
|
appender[key] = array
|
152
214
|
end
|
153
215
|
end
|
154
|
-
|
216
|
+
fields, arrays = *update_fields_and_arrays(updater)
|
217
|
+
return self if appender.is_a?(DataFrame)
|
218
|
+
|
219
|
+
unless appender.empty?
|
220
|
+
append_to_fields_and_arrays(appender, fields, arrays, append_to_left)
|
221
|
+
end
|
222
|
+
|
223
|
+
DataFrame.create(Arrow::Table.new(Arrow::Schema.new(fields), arrays))
|
155
224
|
end
|
156
225
|
|
157
226
|
def try_convert_to_hash(array)
|
158
227
|
array.to_h
|
159
228
|
rescue TypeError
|
160
229
|
[array].to_h
|
161
|
-
rescue TypeError # rubocop:disable Lint/DuplicateRescueException
|
162
|
-
raise DataFrameArgumentError, "Invalid argument in Array #{array}"
|
163
230
|
end
|
164
231
|
|
165
232
|
def rename_by_hash(key_pairs)
|
166
233
|
not_existing_keys = key_pairs.keys - keys
|
167
|
-
|
234
|
+
unless not_existing_keys.empty?
|
235
|
+
raise DataFrameArgumentError, "Not existing: #{not_existing_keys}"
|
236
|
+
end
|
168
237
|
|
169
238
|
fields =
|
170
239
|
keys.map do |key|
|
@@ -175,7 +244,7 @@ module RedAmber
|
|
175
244
|
@table.schema[key]
|
176
245
|
end
|
177
246
|
end
|
178
|
-
DataFrame.
|
247
|
+
DataFrame.create(Arrow::Table.new(Arrow::Schema.new(fields), @table.columns))
|
179
248
|
end
|
180
249
|
|
181
250
|
def update_fields_and_arrays(updater)
|
@@ -185,7 +254,9 @@ module RedAmber
|
|
185
254
|
data = updater[key]
|
186
255
|
next unless data
|
187
256
|
|
188
|
-
|
257
|
+
if data.size != size
|
258
|
+
raise DataFrameArgumentError, "Data size mismatch (#{data.size} != #{size})"
|
259
|
+
end
|
189
260
|
|
190
261
|
a = Arrow::Array.new(data.is_a?(Vector) ? data.to_a : data)
|
191
262
|
fields[i] = Arrow::Field.new(key, a.value_data_type)
|
@@ -194,10 +265,12 @@ module RedAmber
|
|
194
265
|
[fields, arrays]
|
195
266
|
end
|
196
267
|
|
197
|
-
def append_to_fields_and_arrays(appender, fields, arrays, append_to_left
|
268
|
+
def append_to_fields_and_arrays(appender, fields, arrays, append_to_left)
|
198
269
|
enum = append_to_left ? appender.reverse_each : appender.each
|
199
270
|
enum.each do |key, data|
|
200
|
-
|
271
|
+
if data.size != size
|
272
|
+
raise DataFrameArgumentError, "Data size mismatch (#{data.size} != #{size})"
|
273
|
+
end
|
201
274
|
|
202
275
|
a = Arrow::Array.new(data.is_a?(Vector) ? data.to_a : data)
|
203
276
|
|
@@ -210,14 +283,5 @@ module RedAmber
|
|
210
283
|
end
|
211
284
|
end
|
212
285
|
end
|
213
|
-
|
214
|
-
def multiple_assigner?(assigner)
|
215
|
-
case assigner
|
216
|
-
in [Vector, *] | [Array, *] | [Arrow::Array, *]
|
217
|
-
true
|
218
|
-
else
|
219
|
-
false
|
220
|
-
end
|
221
|
-
end
|
222
286
|
end
|
223
287
|
end
|
data/lib/red_amber/group.rb
CHANGED
@@ -3,35 +3,88 @@
|
|
3
3
|
module RedAmber
|
4
4
|
# group class
|
5
5
|
class Group
|
6
|
+
include Enumerable # This feature is experimental
|
7
|
+
|
8
|
+
using RefineArrowTable
|
9
|
+
|
6
10
|
# Creates a new Group object.
|
7
11
|
#
|
8
12
|
# @param dataframe [DataFrame] dataframe to be grouped.
|
9
13
|
# @param group_keys [Array<>] keys for grouping.
|
10
14
|
def initialize(dataframe, *group_keys)
|
11
15
|
@dataframe = dataframe
|
12
|
-
@table = @dataframe.table
|
13
16
|
@group_keys = group_keys.flatten
|
14
17
|
|
15
|
-
raise GroupArgumentError, 'group_keys
|
18
|
+
raise GroupArgumentError, 'group_keys are empty.' if @group_keys.empty?
|
16
19
|
|
17
20
|
d = @group_keys - @dataframe.keys
|
18
21
|
raise GroupArgumentError, "#{d} is not a key of\n #{@dataframe}." unless d.empty?
|
19
22
|
|
20
|
-
@group = @table.group(*@group_keys)
|
23
|
+
@group = @dataframe.table.group(*@group_keys)
|
21
24
|
end
|
22
25
|
|
26
|
+
attr_reader :dataframe, :group_keys
|
27
|
+
|
23
28
|
functions = %i[count sum product mean min max stddev variance]
|
24
29
|
functions.each do |function|
|
25
30
|
define_method(function) do |*summary_keys|
|
26
|
-
|
31
|
+
summary_keys = Array(summary_keys).flatten
|
32
|
+
d = summary_keys - @dataframe.keys
|
33
|
+
unless summary_keys.empty? || d.empty?
|
34
|
+
raise GroupArgumentError, "#{d} is not a key of\n #{@dataframe}."
|
35
|
+
end
|
36
|
+
|
37
|
+
table = @group.aggregate(*build_aggregation_keys("hash_#{function}",
|
38
|
+
summary_keys))
|
39
|
+
g = @group_keys.map(&:to_s)
|
40
|
+
DataFrame.new(table[g + (table.keys - g)])
|
27
41
|
end
|
28
42
|
end
|
29
43
|
|
30
|
-
|
31
|
-
|
32
|
-
|
44
|
+
alias_method :__count, :count
|
45
|
+
private :__count
|
46
|
+
|
47
|
+
def count(*summary_keys)
|
48
|
+
df = __count(summary_keys)
|
49
|
+
# if counts are the same (and do not include NaN or nil), aggregate count columns.
|
50
|
+
if df.pick(@group_keys.size..).to_h.values.uniq.size == 1
|
51
|
+
df.pick(0..@group_keys.size).rename { [keys[-1], :count] }
|
52
|
+
else
|
53
|
+
df
|
33
54
|
end
|
34
|
-
|
55
|
+
end
|
56
|
+
|
57
|
+
def filters
|
58
|
+
@filters ||= begin
|
59
|
+
first, *others = @group_keys.map do |key|
|
60
|
+
vector = @dataframe[key]
|
61
|
+
vector.uniq.each.map { |u| u.nil? ? vector.is_nil : vector == u }
|
62
|
+
end
|
63
|
+
|
64
|
+
if others.empty?
|
65
|
+
first.select(&:any?)
|
66
|
+
else
|
67
|
+
first.product(*others).map { |a| a.reduce(&:&) }.select(&:any?)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def each
|
73
|
+
filters
|
74
|
+
return enum_for(:each) unless block_given?
|
75
|
+
|
76
|
+
@filters.each do |filter|
|
77
|
+
yield @dataframe[filter]
|
78
|
+
end
|
79
|
+
@filters.size
|
80
|
+
end
|
81
|
+
|
82
|
+
def group_count
|
83
|
+
DataFrame.create(add_columns_to_table(base_table, [:group_count], [group_counts]))
|
84
|
+
end
|
85
|
+
|
86
|
+
def inspect
|
87
|
+
"#<#{self.class} : #{format('0x%016x', object_id)}>\n#{group_count}"
|
35
88
|
end
|
36
89
|
|
37
90
|
def summarize(&block)
|
@@ -46,20 +99,57 @@ module RedAmber
|
|
46
99
|
end
|
47
100
|
end
|
48
101
|
|
102
|
+
# experimental
|
103
|
+
def agg_sum(*summary_keys)
|
104
|
+
call_aggregating_function(:sum, summary_keys, _options = nil)
|
105
|
+
end
|
106
|
+
|
49
107
|
private
|
50
108
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
|
109
|
+
def build_aggregation_keys(function_name, summary_keys)
|
110
|
+
if summary_keys.empty?
|
111
|
+
[function_name]
|
112
|
+
else
|
113
|
+
summary_keys.map { |key| "#{function_name}(#{key})" }
|
114
|
+
end
|
115
|
+
end
|
55
116
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
117
|
+
# @note `@group_counts.sum == @dataframe.size``
|
118
|
+
def group_counts
|
119
|
+
@group_counts ||= filters.map(&:sum)
|
120
|
+
end
|
121
|
+
|
122
|
+
def base_table
|
123
|
+
@base_table ||= begin
|
124
|
+
indexes = filters.map { |filter| filter.index(true) }
|
125
|
+
@dataframe.table[@group_keys].take(indexes)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def add_columns_to_table(table, keys, data_arrays)
|
130
|
+
fields = table.schema.fields
|
131
|
+
arrays = table.columns.map(&:data)
|
132
|
+
|
133
|
+
keys.zip(data_arrays).each do |key, array|
|
134
|
+
data = Arrow::ChunkedArray.new([array])
|
135
|
+
fields << Arrow::Field.new(key, data.value_data_type)
|
136
|
+
arrays << data
|
137
|
+
end
|
138
|
+
|
139
|
+
Arrow::Table.new(Arrow::Schema.new(fields), arrays)
|
140
|
+
end
|
141
|
+
|
142
|
+
# Call Vector aggregating function and return an array of arrays:
|
143
|
+
# [keys, data_arrays]
|
144
|
+
# (Experimental feature)
|
145
|
+
def call_aggregating_function(func, summary_keys, _options)
|
146
|
+
summary_keys.each.with_object([[], []]) do |key, (keys, arrays)|
|
147
|
+
vector = @dataframe[key]
|
148
|
+
arrays << filters.map { |filter| vector.filter(filter).send(func) }
|
149
|
+
keys << "#{func}(#{key})".to_sym
|
150
|
+
rescue Arrow::Error::NotImplemented
|
151
|
+
# next
|
61
152
|
end
|
62
|
-
df
|
63
153
|
end
|
64
154
|
end
|
65
155
|
end
|
data/lib/red_amber/helper.rb
CHANGED
@@ -5,58 +5,68 @@ module RedAmber
|
|
5
5
|
module Helper
|
6
6
|
private
|
7
7
|
|
8
|
+
# If num is larger than 1 return 's' to be plural.
|
9
|
+
#
|
10
|
+
# @param num [Numeric] some number.
|
11
|
+
# @return ['s', ''] return 's' if num is larger than 1.
|
12
|
+
# Otherwise return ''.
|
8
13
|
def pl(num)
|
9
14
|
num > 1 ? 's' : ''
|
10
15
|
end
|
11
16
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
17
|
+
# Parse the argments in an Array
|
18
|
+
# and returns a parsed Array.
|
19
|
+
#
|
20
|
+
# @param args
|
21
|
+
# [<Integer, Symbol, true, false, nil, Array, Range, Enumerator, String, Float>]
|
22
|
+
# arguments.
|
23
|
+
# @param array_size [Integer] size of target Array to use in a endless Range.
|
24
|
+
# @return [<Integer, Symbol, true, false, nil>] parsed flat Array.
|
25
|
+
# @note This method is recursively called to parse.
|
26
|
+
def parse_args(args, array_size)
|
27
|
+
args.flat_map do |elem|
|
28
|
+
case elem
|
29
|
+
when Integer, Symbol, NilClass, TrueClass, FalseClass
|
30
|
+
elem
|
31
|
+
when Array
|
32
|
+
parse_args(elem, array_size)
|
33
|
+
when Range
|
34
|
+
parse_range(elem, array_size)
|
35
|
+
when Enumerator
|
36
|
+
parse_args(Array(elem), array_size)
|
37
|
+
when String
|
38
|
+
elem.to_sym
|
39
|
+
when Float
|
40
|
+
elem.floor.to_i
|
41
|
+
else
|
42
|
+
Array(elem)
|
43
|
+
end
|
31
44
|
end
|
32
|
-
Vector.new(a)
|
33
45
|
end
|
34
46
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
Array(0...vsize)[elem]
|
51
|
-
elsif bg.nil? && en.nil?
|
52
|
-
Array(0...vsize)
|
53
|
-
else
|
54
|
-
Array[elem]
|
47
|
+
# Parse a Range to an Array
|
48
|
+
#
|
49
|
+
# @param range [Range] Range to parse.
|
50
|
+
# @param array_size [Integer] size of target Array to use in a endless Range.
|
51
|
+
# @return [Array<Integer, Symbol, String>] parsed Array.
|
52
|
+
def parse_range(range, array_size)
|
53
|
+
bg = range.begin
|
54
|
+
en = range.end
|
55
|
+
if [bg, en].any?(Integer)
|
56
|
+
bg += array_size if bg&.negative?
|
57
|
+
en += array_size if en&.negative?
|
58
|
+
en -= 1 if en.is_a?(Integer) && range.exclude_end?
|
59
|
+
if bg&.negative? || (en && en >= array_size)
|
60
|
+
raise IndexError, "Index out of range: #{range} for 0..#{array_size - 1}"
|
55
61
|
end
|
56
|
-
|
57
|
-
|
62
|
+
|
63
|
+
Array(0...array_size)[range]
|
64
|
+
elsif bg.nil?
|
65
|
+
raise DataFrameArgumentError, "Cannot use beginless Range: #{range}"
|
66
|
+
elsif en.nil?
|
67
|
+
raise DataFrameArgumentError, "Cannot use endless Range: #{range}"
|
58
68
|
else
|
59
|
-
Array
|
69
|
+
Array(range)
|
60
70
|
end
|
61
71
|
end
|
62
72
|
end
|