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,680 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe DaruLite::MultiIndex do
|
4
|
+
before(:each) do
|
5
|
+
@index_tuples = [
|
6
|
+
[:a,:one,:bar],
|
7
|
+
[:a,:one,:baz],
|
8
|
+
[:a,:two,:bar],
|
9
|
+
[:a,:two,:baz],
|
10
|
+
[:b,:one,:bar],
|
11
|
+
[:b,:two,:bar],
|
12
|
+
[:b,:two,:baz],
|
13
|
+
[:b,:one,:foo],
|
14
|
+
[:c,:one,:bar],
|
15
|
+
[:c,:one,:baz],
|
16
|
+
[:c,:two,:foo],
|
17
|
+
[:c,:two,:bar]
|
18
|
+
]
|
19
|
+
@multi_mi = DaruLite::MultiIndex.from_tuples(@index_tuples)
|
20
|
+
end
|
21
|
+
|
22
|
+
context ".initialize" do
|
23
|
+
it "accepts labels and levels as arguments" do
|
24
|
+
mi = DaruLite::MultiIndex.new(
|
25
|
+
levels: [[:a,:b,:c], [:one, :two]],
|
26
|
+
labels: [[0,0,1,1,2,2], [0,1,0,1,0,1]])
|
27
|
+
|
28
|
+
expect(mi[:a, :two]).to eq(1)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "raises error for wrong number of labels or levels" do
|
32
|
+
expect {
|
33
|
+
DaruLite::MultiIndex.new(
|
34
|
+
levels: [[:a,:a,:b,:b,:c,:c], [:one, :two]],
|
35
|
+
labels: [[0,0,1,1,2,2]])
|
36
|
+
}.to raise_error
|
37
|
+
end
|
38
|
+
|
39
|
+
context "create an MultiIndex with name" do
|
40
|
+
context 'if no name is set' do
|
41
|
+
subject { DaruLite::MultiIndex.new(
|
42
|
+
levels: [[:a,:b,:c], [:one, :two]],
|
43
|
+
labels: [[0,0,1,1,2,2], [0,1,0,1,0,1]]) }
|
44
|
+
its(:name) { is_expected.to be_nil }
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'correctly return the MultiIndex name' do
|
48
|
+
subject { DaruLite::MultiIndex.new(
|
49
|
+
levels: [[:a,:b,:c], [:one, :two]],
|
50
|
+
labels: [[0,0,1,1,2,2], [0,1,0,1,0,1]], name: ['n1', 'n2']) }
|
51
|
+
its(:name) { is_expected.to eq ['n1', 'n2'] }
|
52
|
+
end
|
53
|
+
|
54
|
+
context "set new MultiIndex name" do
|
55
|
+
subject {
|
56
|
+
DaruLite::MultiIndex.new(
|
57
|
+
levels: [[:a,:b,:c], [:one, :two]],
|
58
|
+
labels: [[0,0,1,1,2,2], [0,1,0,1,0,1]], name: ['n1', 'n2']) }
|
59
|
+
before(:each) { subject.name = ['k1', 'k2'] }
|
60
|
+
its(:name) { is_expected.to eq ['k1', 'k2'] }
|
61
|
+
end
|
62
|
+
|
63
|
+
context "set new MultiIndex name having empty string" do
|
64
|
+
subject {
|
65
|
+
DaruLite::MultiIndex.new(
|
66
|
+
levels: [[:a,:b,:c], [:one, :two]],
|
67
|
+
labels: [[0,0,1,1,2,2], [0,1,0,1,0,1]], name: ['n1', 'n2']) }
|
68
|
+
before { subject.name = ['k1', ''] }
|
69
|
+
its(:name) { is_expected.to eq ['k1', ''] }
|
70
|
+
end
|
71
|
+
|
72
|
+
it "raises SizeError for wrong number of name" do
|
73
|
+
error_msg = "\'names\' and \'levels\' should be of same size. Size of the \'name\' array is 2 and size of the MultiIndex \'levels\' and \'labels\' is 3.\nIf you do not want to set name for particular level (say level \'i\') then put empty string on index \'i\' of the \'name\' Array."
|
74
|
+
expect { @multi_mi.name = ['n1', 'n2'] }.to raise_error(SizeError, error_msg)
|
75
|
+
|
76
|
+
error_msg = "'names' and 'levels' should be of same size. Size of the 'name' array is 0 and size of the MultiIndex 'levels' and 'labels' is 3.\nIf you do not want to set name for particular level (say level 'i') then put empty string on index 'i' of the 'name' Array."
|
77
|
+
expect { @multi_mi.name = [ ] }.to raise_error(SizeError, error_msg)
|
78
|
+
|
79
|
+
error_msg = "'names' and 'levels' should be of same size. Size of the 'name' array is 1 and size of the MultiIndex 'levels' and 'labels' is 3.\nIf you do not want to set name for particular level (say level 'i') then put empty string on index 'i' of the 'name' Array."
|
80
|
+
expect { @multi_mi.name = [''] }.to raise_error(SizeError, error_msg)
|
81
|
+
|
82
|
+
error_msg = "'names' and 'levels' should be of same size. Size of the 'name' array is 4 and size of the MultiIndex 'levels' and 'labels' is 3."
|
83
|
+
expect { @multi_mi.name = ['n1', 'n2', 'n3', 'n4'] }.to raise_error(SizeError, error_msg)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context ".from_tuples" do
|
89
|
+
it "creates 2 layer MultiIndex from tuples" do
|
90
|
+
tuples = [
|
91
|
+
[:a, :one],
|
92
|
+
[:a, :two],
|
93
|
+
[:b, :one],
|
94
|
+
[:b, :two],
|
95
|
+
[:c, :one],
|
96
|
+
[:c, :two]
|
97
|
+
]
|
98
|
+
mi = DaruLite::MultiIndex.from_tuples(tuples)
|
99
|
+
expect(mi.levels).to eq([[:a, :b, :c], [:one,:two]])
|
100
|
+
expect(mi.labels).to eq([[0,0,1,1,2,2], [0,1,0,1,0,1]])
|
101
|
+
end
|
102
|
+
|
103
|
+
it "creates a triple layer MultiIndex from tuples" do
|
104
|
+
expect(@multi_mi.levels).to eq([[:a,:b,:c], [:one, :two],[:bar,:baz,:foo]])
|
105
|
+
expect(@multi_mi.labels).to eq([
|
106
|
+
[0,0,0,0,1,1,1,1,2,2,2,2],
|
107
|
+
[0,0,1,1,0,1,1,0,0,0,1,1],
|
108
|
+
[0,1,0,1,0,0,1,2,0,1,2,0]
|
109
|
+
])
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context '.try_from_tuples' do
|
114
|
+
it 'creates MultiIndex, if there are tuples' do
|
115
|
+
tuples = [
|
116
|
+
[:a, :one],
|
117
|
+
[:a, :two],
|
118
|
+
[:b, :one],
|
119
|
+
[:b, :two],
|
120
|
+
[:c, :one],
|
121
|
+
[:c, :two]
|
122
|
+
]
|
123
|
+
mi = DaruLite::MultiIndex.try_from_tuples(tuples)
|
124
|
+
expect(mi).to be_a DaruLite::MultiIndex
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'returns nil, if MultiIndex can not be created' do
|
128
|
+
mi = DaruLite::MultiIndex.try_from_tuples([:a, :b, :c])
|
129
|
+
expect(mi).to be_nil
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context "#size" do
|
134
|
+
it "returns size of MultiIndex" do
|
135
|
+
expect(@multi_mi.size).to eq(12)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context "#[]" do
|
140
|
+
it "returns the row number when specifying the complete tuple" do
|
141
|
+
expect(@multi_mi[:a, :one, :baz]).to eq(1)
|
142
|
+
end
|
143
|
+
|
144
|
+
it "returns MultiIndex when specifying incomplete tuple" do
|
145
|
+
expect(@multi_mi[:b]).to eq(DaruLite::MultiIndex.from_tuples([
|
146
|
+
[:b,:one,:bar],
|
147
|
+
[:b,:two,:bar],
|
148
|
+
[:b,:two,:baz],
|
149
|
+
[:b,:one,:foo]
|
150
|
+
]))
|
151
|
+
expect(@multi_mi[:b, :one]).to eq(DaruLite::MultiIndex.from_tuples([
|
152
|
+
[:b,:one,:bar],
|
153
|
+
[:b,:one,:foo]
|
154
|
+
]))
|
155
|
+
# TODO: Return DaruLite::Index if a single layer of indexes is present.
|
156
|
+
end
|
157
|
+
|
158
|
+
it "returns MultiIndex when specifying wholly numeric ranges" do
|
159
|
+
expect(@multi_mi[3..6]).to eq(DaruLite::MultiIndex.from_tuples([
|
160
|
+
[:a,:two,:baz],
|
161
|
+
[:b,:one,:bar],
|
162
|
+
[:b,:two,:bar],
|
163
|
+
[:b,:two,:baz]
|
164
|
+
]))
|
165
|
+
end
|
166
|
+
|
167
|
+
it "raises error when specifying invalid index" do
|
168
|
+
expect { @multi_mi[:a, :three] }.to raise_error IndexError
|
169
|
+
expect { @multi_mi[:a, :one, :xyz] }.to raise_error IndexError
|
170
|
+
expect { @multi_mi[:x] }.to raise_error IndexError
|
171
|
+
expect { @multi_mi[:x, :one] }.to raise_error IndexError
|
172
|
+
expect { @multi_mi[:x, :one, :bar] }.to raise_error IndexError
|
173
|
+
end
|
174
|
+
|
175
|
+
it "works with numerical first levels" do
|
176
|
+
mi = DaruLite::MultiIndex.from_tuples([
|
177
|
+
[2000, 'M'],
|
178
|
+
[2000, 'F'],
|
179
|
+
[2001, 'M'],
|
180
|
+
[2001, 'F']
|
181
|
+
])
|
182
|
+
|
183
|
+
expect(mi[2000]).to eq(DaruLite::MultiIndex.from_tuples([
|
184
|
+
[2000, 'M'],
|
185
|
+
[2000, 'F']
|
186
|
+
]))
|
187
|
+
|
188
|
+
expect(mi[2000,'M']).to eq(0)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
context "#include?" do
|
193
|
+
it "checks if a completely specified tuple exists" do
|
194
|
+
expect(@multi_mi.include?([:a,:one,:bar])).to eq(true)
|
195
|
+
end
|
196
|
+
|
197
|
+
it "checks if a top layer incomplete tuple exists" do
|
198
|
+
expect(@multi_mi.include?([:a])).to eq(true)
|
199
|
+
end
|
200
|
+
|
201
|
+
it "checks if a middle layer incomplete tuple exists" do
|
202
|
+
expect(@multi_mi.include?([:a, :one])).to eq(true)
|
203
|
+
end
|
204
|
+
|
205
|
+
it "checks for non-existence of completely specified tuple" do
|
206
|
+
expect(@multi_mi.include?([:b, :two, :foo])).to eq(false)
|
207
|
+
end
|
208
|
+
|
209
|
+
it "checks for non-existence of a top layer incomplete tuple" do
|
210
|
+
expect(@multi_mi.include?([:d])).to eq(false)
|
211
|
+
end
|
212
|
+
|
213
|
+
it "checks for non-existence of a middle layer incomplete tuple" do
|
214
|
+
expect(@multi_mi.include?([:c, :three])).to eq(false)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
context "#key" do
|
219
|
+
it "returns the tuple of the specified number" do
|
220
|
+
expect(@multi_mi.key(3)).to eq([:a,:two,:baz])
|
221
|
+
end
|
222
|
+
|
223
|
+
it "returns nil for non-existent pointer number" do
|
224
|
+
expect {
|
225
|
+
@multi_mi.key(100)
|
226
|
+
}.to raise_error ArgumentError
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
context "#to_a" do
|
231
|
+
it "returns tuples as an Array" do
|
232
|
+
expect(@multi_mi.to_a).to eq(@index_tuples)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
context "#dup" do
|
237
|
+
it "completely duplicates the object" do
|
238
|
+
duplicate = @multi_mi.dup
|
239
|
+
expect(duplicate) .to eq(@multi_mi)
|
240
|
+
expect(duplicate.object_id).to_not eq(@multi_mi.object_id)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
context "#inspect" do
|
245
|
+
context 'small index' do
|
246
|
+
subject {
|
247
|
+
DaruLite::MultiIndex.from_tuples [
|
248
|
+
[:a,:one,:bar],
|
249
|
+
[:a,:one,:baz],
|
250
|
+
[:a,:two,:bar],
|
251
|
+
[:a,:two,:baz],
|
252
|
+
[:b,:one,:bar],
|
253
|
+
[:b,:two,:bar],
|
254
|
+
[:b,:two,:baz],
|
255
|
+
[:b,:one,:foo],
|
256
|
+
[:c,:one,:bar],
|
257
|
+
[:c,:one,:baz],
|
258
|
+
[:c,:two,:foo],
|
259
|
+
[:c,:two,:bar]
|
260
|
+
]
|
261
|
+
}
|
262
|
+
|
263
|
+
its(:inspect) { is_expected.to eq %Q{
|
264
|
+
|#<DaruLite::MultiIndex(12x3)>
|
265
|
+
| a one bar
|
266
|
+
| baz
|
267
|
+
| two bar
|
268
|
+
| baz
|
269
|
+
| b one bar
|
270
|
+
| two bar
|
271
|
+
| baz
|
272
|
+
| one foo
|
273
|
+
| c one bar
|
274
|
+
| baz
|
275
|
+
| two foo
|
276
|
+
| bar
|
277
|
+
}.unindent
|
278
|
+
}
|
279
|
+
end
|
280
|
+
|
281
|
+
context 'large index' do
|
282
|
+
subject {
|
283
|
+
DaruLite::MultiIndex.from_tuples(
|
284
|
+
(1..100).map { |i| %w[a b c].map { |c| [i, c] } }.flatten(1)
|
285
|
+
)
|
286
|
+
}
|
287
|
+
|
288
|
+
its(:inspect) { is_expected.to eq %Q{
|
289
|
+
|#<DaruLite::MultiIndex(300x2)>
|
290
|
+
| 1 a
|
291
|
+
| b
|
292
|
+
| c
|
293
|
+
| 2 a
|
294
|
+
| b
|
295
|
+
| c
|
296
|
+
| 3 a
|
297
|
+
| b
|
298
|
+
| c
|
299
|
+
| 4 a
|
300
|
+
| b
|
301
|
+
| c
|
302
|
+
| 5 a
|
303
|
+
| b
|
304
|
+
| c
|
305
|
+
| 6 a
|
306
|
+
| b
|
307
|
+
| c
|
308
|
+
| 7 a
|
309
|
+
| b
|
310
|
+
| ... ...
|
311
|
+
}.unindent
|
312
|
+
}
|
313
|
+
end
|
314
|
+
|
315
|
+
context 'multi index with name' do
|
316
|
+
subject {
|
317
|
+
DaruLite::MultiIndex.new(
|
318
|
+
levels: [[:a,:b,:c],[:one,:two],[:bar, :baz, :foo]],
|
319
|
+
labels: [
|
320
|
+
[0,0,0,0,1,1,1,1,2,2,2,2],
|
321
|
+
[0,0,1,1,0,1,1,0,0,0,1,1],
|
322
|
+
[0,1,0,1,0,0,1,2,0,1,2,0]], name: ['n1', 'n2', 'n3'])
|
323
|
+
}
|
324
|
+
|
325
|
+
its(:inspect) { is_expected.to start_with %Q{
|
326
|
+
|#<DaruLite::MultiIndex(12x3)>
|
327
|
+
| n1 n2 n3
|
328
|
+
}.unindent
|
329
|
+
}
|
330
|
+
end
|
331
|
+
|
332
|
+
context 'multi index with name having empty string' do
|
333
|
+
subject {
|
334
|
+
mi= DaruLite::MultiIndex.new(
|
335
|
+
levels: [[:a,:b,:c],[:one,:two],[:bar, :baz, :foo]],
|
336
|
+
labels: [
|
337
|
+
[0,0,0,0,1,1,1,1,2,2,2,2],
|
338
|
+
[0,0,1,1,0,1,1,0,0,0,1,1],
|
339
|
+
[0,1,0,1,0,0,1,2,0,1,2,0]], name: ['n1', 'n2', 'n3'])
|
340
|
+
}
|
341
|
+
before { subject.name = ['n1', '', 'n3'] }
|
342
|
+
|
343
|
+
its(:inspect) { is_expected.to start_with %Q{
|
344
|
+
|#<DaruLite::MultiIndex(12x3)>
|
345
|
+
| n1 n3
|
346
|
+
}.unindent
|
347
|
+
}
|
348
|
+
end
|
349
|
+
|
350
|
+
end
|
351
|
+
|
352
|
+
context "#==" do
|
353
|
+
it "returns false for unequal MultiIndex comparisons" do
|
354
|
+
mi1 = DaruLite::MultiIndex.from_tuples([
|
355
|
+
[:a, :one, :bar],
|
356
|
+
[:a, :two, :baz],
|
357
|
+
[:b, :one, :foo],
|
358
|
+
[:b, :two, :bar]
|
359
|
+
])
|
360
|
+
mi2 = DaruLite::MultiIndex.from_tuples([
|
361
|
+
[:a, :two, :bar],
|
362
|
+
[:b, :one, :foo],
|
363
|
+
[:a, :one, :baz],
|
364
|
+
[:b, :two, :baz]
|
365
|
+
])
|
366
|
+
|
367
|
+
expect(mi1 == mi2).to eq(false)
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
context "#values" do
|
372
|
+
it "returns an array of indices in order" do
|
373
|
+
mi = DaruLite::MultiIndex.from_tuples([
|
374
|
+
[:a, :one, :bar],
|
375
|
+
[:a, :two, :baz],
|
376
|
+
[:b, :one, :foo],
|
377
|
+
[:b, :two, :bar]
|
378
|
+
])
|
379
|
+
|
380
|
+
expect(mi.values).to eq([0,1,2,3])
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
context "#|" do
|
385
|
+
before do
|
386
|
+
@mi1 = DaruLite::MultiIndex.from_tuples([
|
387
|
+
[:a, :one, :bar],
|
388
|
+
[:a, :two, :baz],
|
389
|
+
[:b, :one, :foo],
|
390
|
+
[:b, :two, :bar]
|
391
|
+
])
|
392
|
+
@mi2 = DaruLite::MultiIndex.from_tuples([
|
393
|
+
[:a, :two, :bar],
|
394
|
+
[:b, :one, :foo],
|
395
|
+
[:a, :one, :baz],
|
396
|
+
[:b, :two, :baz]
|
397
|
+
])
|
398
|
+
end
|
399
|
+
|
400
|
+
it "returns a union of two MultiIndex objects" do
|
401
|
+
expect(@mi1 | @mi2).to eq(DaruLite::MultiIndex.new(
|
402
|
+
levels: [[:a, :b], [:one, :two], [:bar, :baz, :foo]],
|
403
|
+
labels: [
|
404
|
+
[0, 0, 1, 1, 0, 0, 1],
|
405
|
+
[0, 1, 0, 1, 1, 0, 1],
|
406
|
+
[0, 1, 2, 0, 0, 1, 1]
|
407
|
+
])
|
408
|
+
)
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
context "#&" do
|
413
|
+
before do
|
414
|
+
@mi1 = DaruLite::MultiIndex.from_tuples([
|
415
|
+
[:a, :one],
|
416
|
+
[:a, :two],
|
417
|
+
[:b, :two]
|
418
|
+
])
|
419
|
+
@mi2 = DaruLite::MultiIndex.from_tuples([
|
420
|
+
[:a, :two],
|
421
|
+
[:b, :one],
|
422
|
+
[:b, :three]
|
423
|
+
])
|
424
|
+
end
|
425
|
+
|
426
|
+
it "returns the intersection of two MI objects" do
|
427
|
+
expect(@mi1 & @mi2).to eq(DaruLite::MultiIndex.from_tuples([
|
428
|
+
[:a, :two],
|
429
|
+
]))
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
context "#empty?" do
|
434
|
+
it "returns true if nothing present in MultiIndex" do
|
435
|
+
expect(DaruLite::MultiIndex.new(labels: [[]], levels: [[]]).empty?).to eq(true)
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
context "#drop_left_level" do
|
440
|
+
it "drops the leftmost level" do
|
441
|
+
expect(
|
442
|
+
DaruLite::MultiIndex.from_tuples([
|
443
|
+
[:c,:one,:bar],
|
444
|
+
[:c,:one,:baz],
|
445
|
+
[:c,:two,:foo],
|
446
|
+
[:c,:two,:bar]
|
447
|
+
]).drop_left_level).to eq(
|
448
|
+
DaruLite::MultiIndex.from_tuples([
|
449
|
+
[:one,:bar],
|
450
|
+
[:one,:baz],
|
451
|
+
[:two,:foo],
|
452
|
+
[:two,:bar]
|
453
|
+
])
|
454
|
+
)
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
context 'other forms of tuple list representation' do
|
459
|
+
let(:index) {
|
460
|
+
DaruLite::MultiIndex.from_tuples [
|
461
|
+
[:a,:one,:bar],
|
462
|
+
[:a,:one,:baz],
|
463
|
+
[:a,:two,:bar],
|
464
|
+
[:a,:two,:baz],
|
465
|
+
[:b,:one,:bar],
|
466
|
+
[:b,:two,:bar],
|
467
|
+
[:b,:two,:baz],
|
468
|
+
[:b,:one,:foo],
|
469
|
+
[:c,:one,:bar],
|
470
|
+
[:c,:one,:baz],
|
471
|
+
[:c,:two,:foo],
|
472
|
+
[:c,:two,:bar]
|
473
|
+
]
|
474
|
+
}
|
475
|
+
|
476
|
+
context '#sparse_tuples' do
|
477
|
+
subject { index.sparse_tuples }
|
478
|
+
|
479
|
+
it { is_expected.to eq [
|
480
|
+
[:a ,:one,:bar],
|
481
|
+
[nil, nil,:baz],
|
482
|
+
[nil,:two,:bar],
|
483
|
+
[nil, nil,:baz],
|
484
|
+
[:b ,:one,:bar],
|
485
|
+
[nil,:two,:bar],
|
486
|
+
[nil, nil,:baz],
|
487
|
+
[nil,:one,:foo],
|
488
|
+
[:c ,:one,:bar],
|
489
|
+
[nil, nil,:baz],
|
490
|
+
[nil,:two,:foo],
|
491
|
+
[nil, nil,:bar]
|
492
|
+
]}
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
context "#pos" do
|
497
|
+
let(:idx) do
|
498
|
+
described_class.from_tuples([
|
499
|
+
[:b,:one,:bar],
|
500
|
+
[:b,:two,:bar],
|
501
|
+
[:b,:two,:baz],
|
502
|
+
[:b,:one,:foo]
|
503
|
+
])
|
504
|
+
end
|
505
|
+
|
506
|
+
context "single index" do
|
507
|
+
it { expect(idx.pos :b, :one, :bar).to eq 0 }
|
508
|
+
end
|
509
|
+
|
510
|
+
context "multiple indexes" do
|
511
|
+
subject { idx.pos :b, :one }
|
512
|
+
|
513
|
+
it { is_expected.to be_a Array }
|
514
|
+
its(:size) { is_expected.to eq 2 }
|
515
|
+
it { is_expected.to eq [0, 3] }
|
516
|
+
end
|
517
|
+
|
518
|
+
context "single positional index" do
|
519
|
+
it { expect(idx.pos 0).to eq 0 }
|
520
|
+
end
|
521
|
+
|
522
|
+
context "multiple positional indexes" do
|
523
|
+
subject { idx.pos 0, 1 }
|
524
|
+
|
525
|
+
it { is_expected.to be_a Array }
|
526
|
+
its(:size) { is_expected.to eq 2 }
|
527
|
+
it { is_expected.to eq [0, 1] }
|
528
|
+
end
|
529
|
+
|
530
|
+
# TODO: Add specs for IndexError
|
531
|
+
end
|
532
|
+
|
533
|
+
context "#subset" do
|
534
|
+
let(:idx) do
|
535
|
+
described_class.from_tuples([
|
536
|
+
[:b, :one, :bar],
|
537
|
+
[:b, :two, :bar],
|
538
|
+
[:b, :two, :baz],
|
539
|
+
[:b, :one, :foo]
|
540
|
+
])
|
541
|
+
end
|
542
|
+
|
543
|
+
context "multiple indexes" do
|
544
|
+
subject { idx.subset :b, :one }
|
545
|
+
|
546
|
+
it { is_expected.to be_a described_class }
|
547
|
+
its(:size) { is_expected.to eq 2 }
|
548
|
+
its(:to_a) { is_expected.to eq [[:bar], [:foo]] }
|
549
|
+
end
|
550
|
+
|
551
|
+
context "multiple positional indexes" do
|
552
|
+
subject { idx.subset 0, 1 }
|
553
|
+
|
554
|
+
it { is_expected.to be_a described_class }
|
555
|
+
its(:size) { is_expected.to eq 2 }
|
556
|
+
its(:to_a) { is_expected.to eq [[:b, :one, :bar], [:b, :two, :bar]] }
|
557
|
+
end
|
558
|
+
|
559
|
+
# TODO: Checks for invalid indexes
|
560
|
+
end
|
561
|
+
|
562
|
+
context "at" do
|
563
|
+
let(:idx) do
|
564
|
+
described_class.from_tuples([
|
565
|
+
[:b, :one, :bar],
|
566
|
+
[:b, :two, :bar],
|
567
|
+
[:b, :two, :baz],
|
568
|
+
[:b, :one, :foo]
|
569
|
+
])
|
570
|
+
end
|
571
|
+
|
572
|
+
context "single position" do
|
573
|
+
it { expect(idx.at 2).to eq [:b, :two, :baz] }
|
574
|
+
end
|
575
|
+
|
576
|
+
context "multiple positions" do
|
577
|
+
subject { idx.at 1, 2 }
|
578
|
+
|
579
|
+
it { is_expected.to be_a described_class }
|
580
|
+
its(:size) { is_expected.to eq 2 }
|
581
|
+
its(:to_a) { is_expected.to eq [[:b, :two, :bar],
|
582
|
+
[:b, :two, :baz]] }
|
583
|
+
end
|
584
|
+
|
585
|
+
context "range" do
|
586
|
+
subject { idx.at 1..2 }
|
587
|
+
|
588
|
+
it { is_expected.to be_a described_class }
|
589
|
+
its(:size) { is_expected.to eq 2 }
|
590
|
+
its(:to_a) { is_expected.to eq [[:b, :two, :bar],
|
591
|
+
[:b, :two, :baz]] }
|
592
|
+
end
|
593
|
+
|
594
|
+
context "range with negative integers" do
|
595
|
+
subject { idx.at 1..-2 }
|
596
|
+
|
597
|
+
it { is_expected.to be_a described_class }
|
598
|
+
its(:size) { is_expected.to eq 2 }
|
599
|
+
its(:to_a) { is_expected.to eq [[:b, :two, :bar],
|
600
|
+
[:b, :two, :baz]] }
|
601
|
+
end
|
602
|
+
|
603
|
+
context "rangle with single element" do
|
604
|
+
subject { idx.at 1..1 }
|
605
|
+
|
606
|
+
it { is_expected.to be_a described_class }
|
607
|
+
its(:size) { is_expected.to eq 1 }
|
608
|
+
its(:to_a) { is_expected.to eq [[:b, :two, :bar]] }
|
609
|
+
end
|
610
|
+
|
611
|
+
context "invalid position" do
|
612
|
+
it { expect { idx.at 4 }.to raise_error IndexError }
|
613
|
+
end
|
614
|
+
|
615
|
+
context "invalid positions" do
|
616
|
+
it { expect { idx.at 2, 4 }.to raise_error IndexError }
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
620
|
+
context "#add" do
|
621
|
+
let(:idx) do
|
622
|
+
described_class.from_tuples [
|
623
|
+
[:a, :one, :bar],
|
624
|
+
[:a, :two, :bar],
|
625
|
+
[:b, :two, :baz],
|
626
|
+
[:b, :one, :foo]
|
627
|
+
]
|
628
|
+
end
|
629
|
+
|
630
|
+
context "single index" do
|
631
|
+
subject { idx.add :b, :two, :baz }
|
632
|
+
|
633
|
+
its(:to_a) { is_expected.to eq [
|
634
|
+
[:a, :one, :bar],
|
635
|
+
[:a, :two, :bar],
|
636
|
+
[:b, :two, :baz],
|
637
|
+
[:b, :one, :foo],
|
638
|
+
[:b, :two, :baz]] }
|
639
|
+
end
|
640
|
+
end
|
641
|
+
|
642
|
+
context "#valid?" do
|
643
|
+
let(:idx) do
|
644
|
+
described_class.from_tuples [
|
645
|
+
[:a, :one, :bar],
|
646
|
+
[:a, :two, :bar],
|
647
|
+
[:b, :two, :baz],
|
648
|
+
[:b, :one, :foo]
|
649
|
+
]
|
650
|
+
end
|
651
|
+
|
652
|
+
context "single index" do
|
653
|
+
it { expect(idx.valid? :a, :one, :bar).to eq true }
|
654
|
+
it { expect(idx.valid? :b, :two, :three).to eq false }
|
655
|
+
end
|
656
|
+
|
657
|
+
context "multiple indexes" do
|
658
|
+
it { expect(idx.valid? :a, :one).to eq true }
|
659
|
+
it { expect(idx.valid? :a, :three).to eq false }
|
660
|
+
end
|
661
|
+
end
|
662
|
+
|
663
|
+
context '#to_df' do
|
664
|
+
let(:idx) do
|
665
|
+
described_class.from_tuples([
|
666
|
+
%w[a one bar],
|
667
|
+
%w[a two bar],
|
668
|
+
%w[b two baz],
|
669
|
+
%w[b one foo]
|
670
|
+
]).tap { |idx| idx.name = %w[col1 col2 col3] }
|
671
|
+
end
|
672
|
+
|
673
|
+
subject { idx.to_df }
|
674
|
+
it { is_expected.to eq DaruLite::DataFrame.new(
|
675
|
+
'col1' => %w[a a b b],
|
676
|
+
'col2' => %w[one two two one],
|
677
|
+
'col3' => %w[bar bar baz foo]
|
678
|
+
)}
|
679
|
+
end
|
680
|
+
end
|