daru 0.1.5 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE.md +18 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +21 -7
  5. data/.travis.yml +10 -5
  6. data/CONTRIBUTING.md +15 -10
  7. data/History.md +124 -2
  8. data/README.md +37 -9
  9. data/ReleasePolicy.md +20 -0
  10. data/benchmarks/db_loading.rb +34 -0
  11. data/benchmarks/statistics.rb +6 -6
  12. data/benchmarks/where_clause.rb +1 -1
  13. data/benchmarks/where_vs_filter.rb +1 -1
  14. data/daru.gemspec +17 -41
  15. data/lib/daru.rb +10 -13
  16. data/lib/daru/accessors/gsl_wrapper.rb +1 -1
  17. data/lib/daru/accessors/nmatrix_wrapper.rb +2 -0
  18. data/lib/daru/category.rb +29 -15
  19. data/lib/daru/configuration.rb +34 -0
  20. data/lib/daru/core/group_by.rb +158 -77
  21. data/lib/daru/core/merge.rb +12 -3
  22. data/lib/daru/core/query.rb +20 -4
  23. data/lib/daru/dataframe.rb +692 -118
  24. data/lib/daru/date_time/index.rb +14 -11
  25. data/lib/daru/date_time/offsets.rb +9 -1
  26. data/lib/daru/extensions/which_dsl.rb +55 -0
  27. data/lib/daru/formatters/table.rb +3 -5
  28. data/lib/daru/index/categorical_index.rb +4 -4
  29. data/lib/daru/index/index.rb +131 -42
  30. data/lib/daru/index/multi_index.rb +118 -10
  31. data/lib/daru/io/csv/converters.rb +21 -0
  32. data/lib/daru/io/io.rb +105 -33
  33. data/lib/daru/io/sql_data_source.rb +10 -0
  34. data/lib/daru/iruby/templates/dataframe.html.erb +4 -51
  35. data/lib/daru/iruby/templates/dataframe_mi.html.erb +3 -56
  36. data/lib/daru/iruby/templates/dataframe_mi_tbody.html.erb +35 -0
  37. data/lib/daru/iruby/templates/dataframe_mi_thead.html.erb +21 -0
  38. data/lib/daru/iruby/templates/dataframe_tbody.html.erb +28 -0
  39. data/lib/daru/iruby/templates/dataframe_thead.html.erb +21 -0
  40. data/lib/daru/iruby/templates/vector.html.erb +3 -25
  41. data/lib/daru/iruby/templates/vector_mi.html.erb +3 -34
  42. data/lib/daru/iruby/templates/vector_mi_tbody.html.erb +26 -0
  43. data/lib/daru/iruby/templates/vector_mi_thead.html.erb +8 -0
  44. data/lib/daru/iruby/templates/vector_tbody.html.erb +17 -0
  45. data/lib/daru/iruby/templates/vector_thead.html.erb +8 -0
  46. data/lib/daru/maths/arithmetic/vector.rb +38 -2
  47. data/lib/daru/maths/statistics/dataframe.rb +28 -30
  48. data/lib/daru/maths/statistics/vector.rb +295 -41
  49. data/lib/daru/plotting/gruff/dataframe.rb +13 -15
  50. data/lib/daru/plotting/nyaplot/category.rb +1 -1
  51. data/lib/daru/plotting/nyaplot/dataframe.rb +15 -4
  52. data/lib/daru/plotting/nyaplot/vector.rb +1 -2
  53. data/lib/daru/vector.rb +308 -96
  54. data/lib/daru/version.rb +1 -1
  55. data/profile/vector_new.rb +9 -0
  56. data/spec/accessors/gsl_wrapper_spec.rb +38 -35
  57. data/spec/accessors/nmatrix_wrapper_spec.rb +25 -22
  58. data/spec/category_spec.rb +24 -20
  59. data/spec/core/group_by_spec.rb +238 -4
  60. data/spec/core/merge_spec.rb +1 -1
  61. data/spec/core/query_spec.rb +65 -50
  62. data/spec/daru_spec.rb +22 -0
  63. data/spec/dataframe_spec.rb +473 -16
  64. data/spec/date_time/date_time_index_helper_spec.rb +72 -0
  65. data/spec/date_time/index_spec.rb +34 -16
  66. data/spec/date_time/offsets_spec.rb +14 -0
  67. data/spec/extensions/rserve_spec.rb +1 -1
  68. data/spec/extensions/which_dsl_spec.rb +38 -0
  69. data/spec/fixtures/boolean_converter_test.csv +5 -0
  70. data/spec/fixtures/duplicates.csv +32 -0
  71. data/spec/fixtures/eciresults.html +394 -0
  72. data/spec/fixtures/empty_rows_test.csv +17 -0
  73. data/spec/fixtures/macau.html +3691 -0
  74. data/spec/fixtures/macd_data.csv +150 -0
  75. data/spec/fixtures/matrix_test.csv +55 -55
  76. data/spec/fixtures/moneycontrol.html +6812 -0
  77. data/spec/fixtures/string_converter_test.csv +5 -0
  78. data/spec/fixtures/test_xls.xls +0 -0
  79. data/spec/fixtures/test_xls_2.xls +0 -0
  80. data/spec/fixtures/url_test.txt~ +0 -0
  81. data/spec/fixtures/valid_markup.html +62 -0
  82. data/spec/fixtures/wiki_climate.html +1243 -0
  83. data/spec/fixtures/wiki_table_info.html +631 -0
  84. data/spec/formatters/table_formatter_spec.rb +29 -0
  85. data/spec/index/categorical_index_spec.rb +33 -33
  86. data/spec/index/index_spec.rb +160 -41
  87. data/spec/index/multi_index_spec.rb +143 -33
  88. data/spec/io/io_spec.rb +246 -2
  89. data/spec/io/sql_data_source_spec.rb +31 -41
  90. data/spec/iruby/dataframe_spec.rb +17 -19
  91. data/spec/iruby/vector_spec.rb +26 -28
  92. data/spec/maths/arithmetic/dataframe_spec.rb +1 -1
  93. data/spec/maths/arithmetic/vector_spec.rb +18 -0
  94. data/spec/maths/statistics/vector_spec.rb +153 -15
  95. data/spec/plotting/gruff/category_spec.rb +3 -3
  96. data/spec/plotting/gruff/dataframe_spec.rb +14 -4
  97. data/spec/plotting/gruff/vector_spec.rb +9 -9
  98. data/spec/plotting/nyaplot/category_spec.rb +5 -9
  99. data/spec/plotting/nyaplot/dataframe_spec.rb +95 -47
  100. data/spec/plotting/nyaplot/vector_spec.rb +5 -11
  101. data/spec/shared/vector_display_spec.rb +12 -14
  102. data/spec/spec_helper.rb +30 -7
  103. data/spec/support/matchers.rb +5 -0
  104. data/spec/vector_spec.rb +306 -72
  105. metadata +96 -55
  106. data/spec/fixtures/stock_data.csv +0 -500
@@ -112,7 +112,7 @@ describe Daru::DataFrame do
112
112
  expect(@left.join(@right, how: :outer, on: [:name])).to eq(answer)
113
113
  end
114
114
 
115
- it "performs a left outer join", focus: true do
115
+ it "performs a left outer join" do
116
116
  answer = Daru::DataFrame.new({
117
117
  :id_1 => [2,3,1,4],
118
118
  :name => ["Monkey", "Ninja", "Pirate", "Spaghetti"],
@@ -43,79 +43,79 @@ describe "Arel-like syntax" do
43
43
  @comparator = Daru::Vector.new([45,22,1214,55,32,9])
44
44
  @klass = Daru::Core::Query::BoolArray
45
45
  end
46
-
46
+
47
47
  context "#eq" do
48
48
  it "accepts scalar value" do
49
49
  expect(@vector.eq(352)).to eq(
50
50
  @klass.new([false,false,false,true,false,false]))
51
51
  end
52
-
52
+
53
53
  it "accepts vector and compares corrensponding elements" do
54
54
  expect(@vector.eq(@comparator)).to eq(
55
55
  @klass.new([false,false,true,false,true,false]))
56
56
  end
57
57
  end
58
-
58
+
59
59
  context "#not_eq" do
60
60
  it "accepts scalar value" do
61
61
  expect(@vector.not_eq(51)).to eq(
62
62
  @klass.new([true, false, true, true, true, true]))
63
63
  end
64
-
64
+
65
65
  it "accepts vector and compares corrensponding elements" do
66
66
  expect(@vector.not_eq(@comparator)).to eq(
67
67
  @klass.new([true, true, false, true, false, true]))
68
68
  end
69
69
  end
70
-
70
+
71
71
  context "#lt" do
72
72
  it "accepts scalar value" do
73
73
  expect(@vector.lt(51)).to eq(
74
74
  @klass.new([true, false, false, false, true, true]))
75
75
  end
76
-
76
+
77
77
  it "accepts vector and compares corrensponding elements" do
78
78
  expect(@vector.lt(@comparator)).to eq(
79
79
  @klass.new([true,false,false,false,false,false]))
80
80
  end
81
81
  end
82
-
82
+
83
83
  context "#lteq" do
84
84
  it "accepts scalar value" do
85
85
  expect(@vector.lteq(51)).to eq(
86
86
  @klass.new([true, true, false, false, true, true]))
87
87
  end
88
-
88
+
89
89
  it "accepts vector and compares corrensponding elements" do
90
90
  expect(@vector.lteq(@comparator)).to eq(
91
91
  @klass.new([true,false,true,false,true,false]))
92
92
  end
93
93
  end
94
-
94
+
95
95
  context "#mt" do
96
96
  it "accepts scalar value" do
97
97
  expect(@vector.mt(51)).to eq(
98
98
  @klass.new([false, false, true, true, false, false]))
99
99
  end
100
-
100
+
101
101
  it "accepts vector and compares corrensponding elements" do
102
102
  expect(@vector.mt(@comparator)).to eq(
103
103
  @klass.new([false,true,false,true,false,true]))
104
104
  end
105
105
  end
106
-
106
+
107
107
  context "#mteq" do
108
108
  it "accepts scalar value" do
109
109
  expect(@vector.mteq(51)).to eq(
110
110
  @klass.new([false, true, true, true, false, false]))
111
111
  end
112
-
112
+
113
113
  it "accepts vector and compares corrensponding elements" do
114
114
  expect(@vector.mteq(@comparator)).to eq(
115
115
  @klass.new([false,true,true,true,true,true]))
116
116
  end
117
117
  end
118
-
118
+
119
119
  context "#in" do
120
120
  it "checks if any of elements in the arg are present in the vector" do
121
121
  expect(@vector.in([23,55,1,33,32])).to eq(
@@ -123,7 +123,7 @@ describe "Arel-like syntax" do
123
123
  end
124
124
  end
125
125
  end
126
-
126
+
127
127
  describe "categorical type" do
128
128
  let(:dv) { Daru::Vector.new ['e', 'd', 'd', 'x', 'x'],
129
129
  categories: ['a', 'x', 'c', 'd', 'e'], type: :category }
@@ -134,99 +134,99 @@ describe "Arel-like syntax" do
134
134
  context "#eq" do
135
135
  context "scalar" do
136
136
  subject { dv.eq 'd' }
137
-
137
+
138
138
  it { is_expected.to be_a query_bool_class }
139
139
  its(:to_a) { is_expected.to eq [false, true, true, false, false] }
140
140
  end
141
141
 
142
142
  context "vector" do
143
143
  subject { dv.eq comp }
144
-
144
+
145
145
  it { is_expected.to be_a query_bool_class }
146
146
  its(:to_a) { is_expected.to eq [false, true, false, false, true] }
147
147
  end
148
148
  end
149
-
149
+
150
150
  context "#not_eq" do
151
151
  context "scalar" do
152
152
  subject { dv.not_eq 'd' }
153
-
153
+
154
154
  it { is_expected.to be_a query_bool_class }
155
155
  its(:to_a) { is_expected.to eq [true, false, false, true, true] }
156
156
  end
157
-
157
+
158
158
  context "vector" do
159
159
  subject { dv.not_eq comp }
160
-
160
+
161
161
  it { is_expected.to be_a query_bool_class }
162
162
  its(:to_a) { is_expected.to eq [true, false, true, true, false] }
163
163
  end
164
164
  end
165
-
165
+
166
166
  context "#lt" do
167
167
  context "scalar" do
168
168
  subject { dv.lt 'd' }
169
-
169
+
170
170
  it { is_expected.to be_a query_bool_class }
171
171
  its(:to_a) { is_expected.to eq [false, false, false, true, true] }
172
172
  end
173
-
173
+
174
174
  context "vector" do
175
175
  subject { dv.lt comp }
176
-
176
+
177
177
  it { is_expected.to be_a query_bool_class }
178
178
  its(:to_a) { is_expected.to eq [false, false, false, true, false] }
179
179
  end
180
180
  end
181
-
181
+
182
182
  context "#lteq" do
183
183
  context "scalar" do
184
184
  subject { dv.lteq 'd' }
185
-
185
+
186
186
  it { is_expected.to be_a query_bool_class }
187
187
  its(:to_a) { is_expected.to eq [false, true, true, true, true] }
188
188
  end
189
-
189
+
190
190
  context "vector" do
191
191
  subject { dv.lteq comp }
192
-
192
+
193
193
  it { is_expected.to be_a query_bool_class }
194
194
  its(:to_a) { is_expected.to eq [false, true, false, true, true] }
195
195
  end
196
196
  end
197
-
197
+
198
198
  context "#mt" do
199
199
  context "scalar" do
200
200
  subject { dv.mt 'd' }
201
-
201
+
202
202
  it { is_expected.to be_a query_bool_class }
203
203
  its(:to_a) { is_expected.to eq [true, false, false, false, false] }
204
204
  end
205
-
205
+
206
206
  context "vector" do
207
207
  subject { dv.mt comp }
208
-
208
+
209
209
  it { is_expected.to be_a query_bool_class }
210
210
  its(:to_a) { is_expected.to eq [true, false, true, false, false] }
211
211
  end
212
212
  end
213
-
213
+
214
214
  context "#mteq" do
215
215
  context "scalar" do
216
216
  subject { dv.mteq 'd' }
217
-
217
+
218
218
  it { is_expected.to be_a query_bool_class }
219
219
  its(:to_a) { is_expected.to eq [true, true, true, false, false] }
220
220
  end
221
-
221
+
222
222
  context "vector" do
223
223
  subject { dv.mteq comp }
224
-
224
+
225
225
  it { is_expected.to be_a query_bool_class }
226
226
  its(:to_a) { is_expected.to eq [true, true, true, false, true] }
227
227
  end
228
228
  end
229
-
229
+
230
230
  # context "#in" do
231
231
  # subject { dv.in ['b', 'd'] }
232
232
  # it { is_expected.to be_a query_bool_class }
@@ -240,9 +240,9 @@ describe "Arel-like syntax" do
240
240
  context Daru::DataFrame do
241
241
  before do
242
242
  @df = Daru::DataFrame.new({
243
- number: [1,2,3,4,5,6],
244
- sym: [:one, :two, :three, :four, :five, :six],
245
- names: ['sameer', 'john', 'james', 'omisha', 'priyanka', 'shravan']
243
+ number: [1,2,3,4,5,6,Float::NAN],
244
+ sym: [:one, :two, :three, :four, :five, :six, :seven],
245
+ names: ['sameer', 'john', 'james', 'omisha', 'priyanka', 'shravan',nil]
246
246
  })
247
247
  end
248
248
 
@@ -267,6 +267,11 @@ describe "Arel-like syntax" do
267
267
  ).to eq(answer)
268
268
  end
269
269
 
270
+ let(:dv) { Daru::Vector.new([1,11,32,Float::NAN,nil]) }
271
+ it "handles empty data" do
272
+ expect(dv.where(dv.lt(14))).to eq(Daru::Vector.new([1,11]))
273
+ end
274
+
270
275
  it "does not give SystemStackError" do
271
276
  v = Daru::Vector.new [1]*300_000
272
277
  expect { v.where v.eq(1) }.not_to raise_error
@@ -276,45 +281,45 @@ describe "Arel-like syntax" do
276
281
  context Daru::Vector do
277
282
  context "non-categorical type" do
278
283
  before do
279
- @vector = Daru::Vector.new([2,5,1,22,51,4])
284
+ @vector = Daru::Vector.new([2,5,1,22,51,4,nil,Float::NAN])
280
285
  end
281
-
286
+
282
287
  it "accepts a simple single statement" do
283
288
  expect(@vector.where(@vector.lt(10))).to eq(
284
289
  Daru::Vector.new([2,5,1,4], index: Daru::Index.new([0,1,2,5])))
285
290
  end
286
-
291
+
287
292
  it "accepts somewhat complex operator chaining" do
288
293
  expect(@vector.where((@vector.lt(6) | @vector.eq(51)))).to eq(
289
294
  Daru::Vector.new([2,5,1,51,4], index: Daru::Index.new([0,1,2,4,5])))
290
295
  end
291
296
  end
292
-
297
+
293
298
  context "categorical type" do
294
299
  let(:dv) { Daru::Vector.new ['a', 'c', 'x', 'x', 'c'],
295
300
  categories: ['a', 'x', 'c'], type: :category }
296
-
301
+
297
302
  context "simple single statement" do
298
303
  subject { dv.where(dv.lt('x')) }
299
-
304
+
300
305
  it { is_expected.to be_a Daru::Vector }
301
306
  its(:type) { is_expected.to eq :category }
302
307
  its(:to_a) { is_expected.to eq ['a'] }
303
308
  its(:'index.to_a') { is_expected.to eq [0] }
304
309
  end
305
-
310
+
306
311
  context "complex operator chaining" do
307
312
  subject { dv.where((dv.lt('x') | dv.eq('c'))) }
308
-
313
+
309
314
  it { is_expected.to be_a Daru::Vector }
310
315
  its(:type) { is_expected.to eq :category }
311
316
  its(:to_a) { is_expected.to eq ['a', 'c', 'c'] }
312
317
  its(:'index.to_a') { is_expected.to eq [0, 1, 4] }
313
318
  end
314
-
319
+
315
320
  context "preserve categories" do
316
321
  subject { dv.where((dv.lt('x') | dv.eq('c'))) }
317
-
322
+
318
323
  it { is_expected.to be_a Daru::Vector }
319
324
  its(:type) { is_expected.to eq :category }
320
325
  its(:to_a) { is_expected.to eq ['a', 'c', 'c'] }
@@ -329,4 +334,14 @@ describe "Arel-like syntax" do
329
334
  end
330
335
  end
331
336
  end
337
+
338
+ describe "apply_where" do
339
+ context "matches regexp with block input" do
340
+ subject { dv.apply_where(dv.match /weeks/) { |x| "#{x.split.first.to_i * 7} days" } }
341
+
342
+ let(:dv) { Daru::Vector.new ['3 days', '5 weeks', '2 weeks'] }
343
+
344
+ it { is_expected.to eq(Daru::Vector.new ['3 days', '35 days', '14 days']) }
345
+ end
346
+ end
332
347
  end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe '#error' do
4
+ context 'by default' do
5
+ it { expect { Daru.error('test') }.to output("test\n").to_stderr_from_any_process }
6
+ end
7
+
8
+ context 'when set to nil' do
9
+ before { Daru.error_stream = nil }
10
+ it { expect { Daru.error('test') }.not_to output('test').to_stderr_from_any_process }
11
+ end
12
+
13
+ context 'when set to instance of custom class' do
14
+ let(:custom_stream) { double(puts: nil) }
15
+ before { Daru.error_stream = custom_stream }
16
+
17
+ it 'calls puts' do
18
+ expect { Daru.error('test') }.not_to output('test').to_stderr_from_any_process
19
+ expect(custom_stream).to have_received(:puts).with('test')
20
+ end
21
+ end
22
+ end
@@ -145,8 +145,15 @@ describe Daru::DataFrame do
145
145
  end
146
146
 
147
147
  context "#initialize" do
148
+
149
+ it "initializes an empty DataFrame with no arguments" do
150
+ df = Daru::DataFrame.new
151
+ expect(df.nrows).to eq(0)
152
+ expect(df.ncols).to eq(0)
153
+ end
154
+
148
155
  context Daru::Index do
149
- it "initializes an empty DataFrame" do
156
+ it "initializes an empty DataFrame with empty source arg" do
150
157
  df = Daru::DataFrame.new({}, order: [:a, :b])
151
158
 
152
159
  expect(df.vectors).to eq(Daru::Index.new [:a, :b])
@@ -164,6 +171,13 @@ describe Daru::DataFrame do
164
171
  expect(df.a) .to eq([1,2,3,4,5].dv(:a, df.index))
165
172
  end
166
173
 
174
+ it "initializes from a Hash and preserves default order" do
175
+ df = Daru::DataFrame.new({b: [11,12,13,14,15], a: [1,2,3,4,5]},
176
+ index: [:one, :two, :three, :four, :five])
177
+
178
+ expect(df.vectors).to eq(Daru::Index.new [:b, :a])
179
+ end
180
+
167
181
  it "initializes from a Hash of Vectors" do
168
182
  va = Daru::Vector.new([1,2,3,4,5], index: [:one, :two, :three, :four, :five])
169
183
  vb = Daru::Vector.new([11,12,13,14,15], index: [:one, :two, :three, :four, :five])
@@ -221,7 +235,7 @@ describe Daru::DataFrame do
221
235
  df = Daru::DataFrame.new({b: [11,12,13,14,15], a: [1,2,3,4,5]})
222
236
 
223
237
  expect(df.index) .to eq(Daru::Index.new [0,1,2,3,4])
224
- expect(df.vectors).to eq(Daru::Index.new [:a, :b])
238
+ expect(df.vectors).to eq(Daru::Index.new [:b, :a])
225
239
  end
226
240
 
227
241
  it "aligns indexes properly" do
@@ -329,6 +343,14 @@ describe Daru::DataFrame do
329
343
  expect(df[:a]) .to eq(Daru::Vector.new([1,2,3,4,5]))
330
344
  end
331
345
 
346
+ it "allows creation of dataframe with a default order" do
347
+ arr_of_arrs_df = Daru::DataFrame.new([[1,2,3], [4,5,6], [7,8,9]])
348
+ arr_of_vectors_df = Daru::DataFrame.new([Daru::Vector.new([1,2,3]), Daru::Vector.new([4,5,6]), Daru::Vector.new([7,8,9])])
349
+
350
+ expect(arr_of_arrs_df.vectors.to_a).to eq([0,1,2])
351
+ expect(arr_of_vectors_df.vectors.to_a).to eq([0,1,2])
352
+ end
353
+
332
354
  it "raises error for incomplete DataFrame index" do
333
355
  expect {
334
356
  df = Daru::DataFrame.new({b: [11,12,13,14,15], a: [1,2,3,4,5],
@@ -516,12 +538,31 @@ describe Daru::DataFrame do
516
538
  index: [:one, :two, :three, :four, :five]))
517
539
  end
518
540
 
541
+ it "assigns new vector with default length if given just a value" do
542
+ @df[:d] = 1.0
543
+ expect(@df[:d]).to eq(Daru::Vector.new([1.0, 1.0, 1.0, 1.0, 1.0],
544
+ index: [:one, :two, :three, :four, :five], name: :d))
545
+ end
546
+
547
+ it "updates vector with default length if given just a value" do
548
+ @df[:c] = 1.0
549
+ expect(@df[:c]).to eq(Daru::Vector.new([1.0, 1.0, 1.0, 1.0, 1.0],
550
+ index: [:one, :two, :three, :four, :five], name: :c))
551
+ end
552
+
519
553
  it "appends an Array as a Daru::Vector" do
520
554
  @df[:d] = [69,99,108,85,49]
521
555
 
522
556
  expect(@df.d.class).to eq(Daru::Vector)
523
557
  end
524
558
 
559
+ it "appends an arbitrary enumerable as a Daru::Vector" do
560
+ @df[:d] = Set.new([69,99,108,85,49])
561
+
562
+ expect(@df[:d]).to eq(Daru::Vector.new([69, 99, 108, 85, 49],
563
+ index: [:one, :two, :three, :four, :five], name: :c))
564
+ end
565
+
525
566
  it "replaces an already present vector" do
526
567
  @df[:a] = [69,99,108,85,49].dv(nil, [:one, :two, :three, :four, :five])
527
568
 
@@ -697,6 +738,47 @@ describe Daru::DataFrame do
697
738
  }
698
739
  end
699
740
 
741
+ context "#insert_vector" do
742
+ subject(:data_frame) {
743
+ Daru::DataFrame.new({b: [11,12,13,14,15], a: [1,2,3,4,5],
744
+ c: [11,22,33,44,55]}, order: [:a, :b, :c],
745
+ index: [:one, :two, :three, :four, :five])
746
+ }
747
+
748
+ it "insert a new vector at the desired slot" do
749
+ df = Daru::DataFrame.new({
750
+ a: [1,2,3,4,5],
751
+ d: [710, 720, 730, 740, 750],
752
+ b: [11, 12, 13, 14, 15],
753
+ c: [11,22,33,44,55]}, order: [:a, :d, :b, :c],
754
+ index: [:one, :two, :three, :four, :five]
755
+ )
756
+ data_frame.insert_vector 1, :d, [710, 720, 730, 740, 750]
757
+ expect(subject).to eq df
758
+ end
759
+
760
+ it "raises error for data array being too big" do
761
+ expect {
762
+ source = (1..8).to_a
763
+ data_frame.insert_vector 1, :d, source
764
+ }.to raise_error(IndexError)
765
+ end
766
+
767
+ it "raises error for invalid index value" do
768
+ expect {
769
+ source = (1..5).to_a
770
+ data_frame.insert_vector 4, :d, source
771
+ }.to raise_error(ArgumentError)
772
+ end
773
+
774
+ it "raises error for invalid source type" do
775
+ expect {
776
+ source = 14
777
+ data_frame.insert_vector 3, :d, source
778
+ }.to raise_error(ArgumentError)
779
+ end
780
+ end
781
+
700
782
  context "#row[]=" do
701
783
  context Daru::Index do
702
784
  before :each do
@@ -1546,6 +1628,22 @@ describe Daru::DataFrame do
1546
1628
  }
1547
1629
  end
1548
1630
 
1631
+ context 'with mulitiindex DF' do
1632
+ subject(:data_frame) {
1633
+ Daru::DataFrame.new({b: [11,12,13], a: [1,2,3],
1634
+ c: [11,22,33]}, order: [:a, :b, :c],
1635
+ index: Daru::MultiIndex.from_tuples([[:one, :two], [:one, :three], [:two, :four]]))
1636
+ }
1637
+
1638
+ before { data_frame.add_row [100,200,300], [:two, :five] }
1639
+
1640
+ it { is_expected.to eq(Daru::DataFrame.new({
1641
+ b: [11,12,13,200], a: [1,2,3,100],
1642
+ c: [11,22,33,300]}, order: [:a, :b, :c],
1643
+ index: Daru::MultiIndex.from_tuples([[:one, :two], [:one, :three], [:two, :four], [:two, :five]])))
1644
+ }
1645
+ end
1646
+
1549
1647
  it "allows adding rows after making empty DF by specfying only order" do
1550
1648
  df = Daru::DataFrame.new({}, order: [:a, :b, :c])
1551
1649
  df.add_row [1,2,3]
@@ -1573,6 +1671,13 @@ describe Daru::DataFrame do
1573
1671
  it 'has synonym' do
1574
1672
  expect(@data_frame.first(2)).to eq(@data_frame.head(2))
1575
1673
  end
1674
+
1675
+ it 'works on DateTime indexes' do
1676
+ idx = Daru::DateTimeIndex.new(['2017-01-01', '2017-02-01', '2017-03-01'])
1677
+ df = Daru::DataFrame.new({col1: ['a', 'b', 'c']}, index: idx)
1678
+ first = Daru::DataFrame.new({col1: ['a']}, index: Daru::DateTimeIndex.new(['2017-01-01']))
1679
+ expect(df.head(1)).to eq(first)
1680
+ end
1576
1681
  end
1577
1682
 
1578
1683
  context "#last" do
@@ -1756,6 +1861,59 @@ describe Daru::DataFrame do
1756
1861
  end
1757
1862
  end
1758
1863
 
1864
+ describe 'uniq' do
1865
+ let(:df) do
1866
+ Daru::DataFrame.from_csv 'spec/fixtures/duplicates.csv'
1867
+ end
1868
+
1869
+ context 'with no args' do
1870
+ it do
1871
+ result = df.uniq
1872
+ expect(result.shape.first).to eq 30
1873
+ end
1874
+ end
1875
+
1876
+ context 'given a vector' do
1877
+ it do
1878
+ result = df.uniq("color")
1879
+ expect(result.shape.first).to eq 2
1880
+ end
1881
+ end
1882
+
1883
+ context 'given an array of vectors' do
1884
+ it do
1885
+ result = df.uniq("color", "director_name")
1886
+ expect(result.shape.first).to eq 29
1887
+ end
1888
+ end
1889
+ end
1890
+
1891
+ context '#rolling_fillna!' do
1892
+ subject do
1893
+ Daru::DataFrame.new({
1894
+ a: [1, 2, 3, nil, Float::NAN, nil, 1, 7],
1895
+ b: [:a, :b, nil, Float::NAN, nil, 3, 5, nil],
1896
+ c: ['a', Float::NAN, 3, 4, 3, 5, nil, 7]
1897
+ })
1898
+ end
1899
+
1900
+ context 'rolling_fillna! forwards' do
1901
+ before { subject.rolling_fillna!(:forward) }
1902
+ it { expect(subject.rolling_fillna!(:forward)).to eq(subject) }
1903
+ its(:'a.to_a') { is_expected.to eq [1, 2, 3, 3, 3, 3, 1, 7] }
1904
+ its(:'b.to_a') { is_expected.to eq [:a, :b, :b, :b, :b, 3, 5, 5] }
1905
+ its(:'c.to_a') { is_expected.to eq ['a', 'a', 3, 4, 3, 5, 5, 7] }
1906
+ end
1907
+
1908
+ context 'rolling_fillna! backwards' do
1909
+ before { subject.rolling_fillna!(:backward) }
1910
+ it { expect(subject.rolling_fillna!(:backward)).to eq(subject) }
1911
+ its(:'a.to_a') { is_expected.to eq [1, 2, 3, 1, 1, 1, 1, 7] }
1912
+ its(:'b.to_a') { is_expected.to eq [:a, :b, 3, 3, 3, 3, 5, 0] }
1913
+ its(:'c.to_a') { is_expected.to eq ['a', 3, 3, 4, 3, 5, 7, 7] }
1914
+ end
1915
+ end
1916
+
1759
1917
  context "#clone" do
1760
1918
  it "returns a view of the whole dataframe" do
1761
1919
  cloned = @data_frame.clone
@@ -2603,6 +2761,26 @@ describe Daru::DataFrame do
2603
2761
  end
2604
2762
  end
2605
2763
 
2764
+ context "#rename_vectors!" do
2765
+ before do
2766
+ @df = Daru::DataFrame.new({
2767
+ a: [1,2,3,4,5],
2768
+ b: [11,22,33,44,55],
2769
+ c: %w(a b c d e)
2770
+ })
2771
+ end
2772
+
2773
+ it "returns self as modified dataframe" do
2774
+ expect(@df.rename_vectors!(:a => :alpha)).to eq(@df)
2775
+ end
2776
+
2777
+ it "re-uses rename_vectors method" do
2778
+ name_map = { :a => :alpha, :c => :gamma }
2779
+ expect(@df).to receive(:rename_vectors).with(name_map)
2780
+ @df.rename_vectors! name_map
2781
+ end
2782
+ end
2783
+
2606
2784
  context "#rename_vectors" do
2607
2785
  before do
2608
2786
  @df = Daru::DataFrame.new({
@@ -2612,6 +2790,10 @@ describe Daru::DataFrame do
2612
2790
  })
2613
2791
  end
2614
2792
 
2793
+ it "returns Daru::Index" do
2794
+ expect(@df.rename_vectors(:a => :alpha)).to be_kind_of(Daru::Index)
2795
+ end
2796
+
2615
2797
  it "renames vectors using a hash map" do
2616
2798
  @df.rename_vectors :a => :alpha, :c => :gamma
2617
2799
  expect(@df.vectors.to_a).to eq([:alpha, :b, :gamma])
@@ -2667,6 +2849,11 @@ describe Daru::DataFrame do
2667
2849
  :a => [1,2,3,4,5]
2668
2850
  }, order: [:b, 'a', :a]))
2669
2851
  end
2852
+
2853
+ it 'raises ArgumentError if argument was not an index' do
2854
+ df = Daru::DataFrame.new([])
2855
+ expect { df.reindex_vectors([]) }.to raise_error(ArgumentError)
2856
+ end
2670
2857
  end
2671
2858
 
2672
2859
  context "#to_matrix" do
@@ -3050,9 +3237,34 @@ describe Daru::DataFrame do
3050
3237
  end
3051
3238
 
3052
3239
  context "#summary" do
3053
- it "produces a summary of data frame" do
3054
- expect(@data_frame.summary.match("#{@data_frame.name}")).to_not eq(nil)
3055
- expect(@df_mi.summary.match("#{@df_mi.name}")).to_not eq(nil)
3240
+ subject { df.summary }
3241
+
3242
+ context "DataFrame" do
3243
+ let(:df) { Daru::DataFrame.new({a: [1,2,5], b: [1,2,"string"]}, order: [:a, :b], index: [:one, :two, :three], name: 'frame') }
3244
+ it { is_expected.to eq %Q{
3245
+ |= frame
3246
+ | Number of rows: 3
3247
+ | Element:[a]
3248
+ | == a
3249
+ | n :3
3250
+ | non-missing:3
3251
+ | median: 2
3252
+ | mean: 2.6667
3253
+ | std.dev.: 2.0817
3254
+ | std.err.: 1.2019
3255
+ | skew: 0.2874
3256
+ | kurtosis: -2.3333
3257
+ | Element:[b]
3258
+ | == b
3259
+ | n :3
3260
+ | non-missing:3
3261
+ | factors: 1,2,string
3262
+ | mode: 1,2,string
3263
+ | Distribution
3264
+ | 1 1 100.00%
3265
+ | 2 1 100.00%
3266
+ | string 1 100.00%
3267
+ }.unindent }
3056
3268
  end
3057
3269
  end
3058
3270
 
@@ -3101,6 +3313,15 @@ describe Daru::DataFrame do
3101
3313
  it { expect(subject['a'].to_a).to eq [1, 2, 3] }
3102
3314
  it { expect(subject[:b].to_a).to eq [4, 5, 6] }
3103
3315
  end
3316
+
3317
+ context "preserves indices for dataframes with same index" do
3318
+ let(:index) { ['one','two','three'] }
3319
+ let(:df1) { Daru::DataFrame.new({ 'a' => [1, 2, 3], 'b' => [3, 4, 5] }, index: index) }
3320
+ let(:df2) { Daru::DataFrame.new({ 'c' => [4, 5, 6], 'd' => [7, 8, 9] }, index: index) }
3321
+ subject { df1.merge df2 }
3322
+
3323
+ its(:index) { is_expected.to eq Daru::Index.new(index) }
3324
+ end
3104
3325
  end
3105
3326
 
3106
3327
  context "#vector_by_calculation" do
@@ -3115,25 +3336,41 @@ describe Daru::DataFrame do
3115
3336
  end
3116
3337
  end
3117
3338
 
3339
+ context "group_by" do
3340
+ context "on a single row DataFrame" do
3341
+ let(:df){ Daru::DataFrame.new(city: %w[Kyiv], year: [2015], value: [1]) }
3342
+ it "returns a groupby object" do
3343
+ expect(df.group_by([:city])).to be_a(Daru::Core::GroupBy)
3344
+ end
3345
+ it "has the correct index" do
3346
+ expect(df.group_by([:city]).groups).to eq({["Kyiv"]=>[0]})
3347
+ end
3348
+ end
3349
+ end
3350
+
3118
3351
  context "#vector_sum" do
3119
3352
  before do
3120
- a1 = Daru::Vector.new [1, 2, 3, 4, 5, nil]
3121
- a2 = Daru::Vector.new [10, 10, 20, 20, 20, 30]
3122
- b1 = Daru::Vector.new [nil, 1, 1, 1, 1, 2]
3123
- b2 = Daru::Vector.new [2, 2, 2, nil, 2, 3]
3353
+ a1 = Daru::Vector.new [1, 2, 3, 4, 5, nil, nil]
3354
+ a2 = Daru::Vector.new [10, 10, 20, 20, 20, 30, nil]
3355
+ b1 = Daru::Vector.new [nil, 1, 1, 1, 1, 2, nil]
3356
+ b2 = Daru::Vector.new [2, 2, 2, nil, 2, 3, nil]
3124
3357
  @df = Daru::DataFrame.new({ :a1 => a1, :a2 => a2, :b1 => b1, :b2 => b2 })
3125
3358
  end
3126
3359
 
3127
3360
  it "calculates complete vector sum" do
3128
- expect(@df.vector_sum).to eq(Daru::Vector.new [nil, 15, 26, nil, 28, nil])
3361
+ expect(@df.vector_sum).to eq(Daru::Vector.new [nil, 15, 26, nil, 28, nil, nil])
3362
+ end
3363
+
3364
+ it "ignores nils if skipnil is true" do
3365
+ expect(@df.vector_sum skipnil: true).to eq(Daru::Vector.new [13, 15, 26, 25, 28, 35, 0])
3129
3366
  end
3130
3367
 
3131
3368
  it "calculates partial vector sum" do
3132
3369
  a = @df.vector_sum([:a1, :a2])
3133
3370
  b = @df.vector_sum([:b1, :b2])
3134
3371
 
3135
- expect(a).to eq(Daru::Vector.new [11, 12, 23, 24, 25, nil])
3136
- expect(b).to eq(Daru::Vector.new [nil, 3, 3, nil, 3, 5])
3372
+ expect(a).to eq(Daru::Vector.new [11, 12, 23, 24, 25, nil, nil])
3373
+ expect(b).to eq(Daru::Vector.new [nil, 3, 3, nil, 3, 5, nil])
3137
3374
  end
3138
3375
  end
3139
3376
 
@@ -3321,7 +3558,8 @@ describe Daru::DataFrame do
3321
3558
  ev_b = Daru::Vector.new [1, 1, 0]
3322
3559
  ev_c = Daru::Vector.new [0, 1, 1]
3323
3560
  df2 = Daru::DataFrame.new({
3324
- :_id => ev_id, 'a' => ev_a, 'b' => ev_b, 'c' => ev_c })
3561
+ :_id => ev_id, 'a' => ev_a, 'b' => ev_b, 'c' => ev_c },
3562
+ order: ['a', 'b', 'c', :_id])
3325
3563
 
3326
3564
  expect(df2).to eq(df)
3327
3565
  end
@@ -3505,6 +3743,41 @@ describe Daru::DataFrame do
3505
3743
  end
3506
3744
  end
3507
3745
 
3746
+ context '#reset_index' do
3747
+ context 'when Index' do
3748
+ subject do
3749
+ Daru::DataFrame.new(
3750
+ {'vals' => [1,2,3,4,5]},
3751
+ index: Daru::Index.new(%w[a b c d e], name: 'indices')
3752
+ ).reset_index
3753
+ end
3754
+
3755
+ it { is_expected.to eq Daru::DataFrame.new(
3756
+ 'indices' => %w[a b c d e],
3757
+ 'vals' => [1,2,3,4,5]
3758
+ )}
3759
+ end
3760
+
3761
+ context 'when MultiIndex' do
3762
+ subject do
3763
+ mi = Daru::MultiIndex.from_tuples([
3764
+ [0, 'a'], [0, 'b'], [1, 'a'], [1, 'b']
3765
+ ])
3766
+ mi.name = %w[nums alphas]
3767
+ Daru::DataFrame.new(
3768
+ {'vals' => [1,2,3,4]},
3769
+ index: mi
3770
+ ).reset_index
3771
+ end
3772
+
3773
+ it { is_expected.to eq Daru::DataFrame.new(
3774
+ 'nums' => [0,0,1,1],
3775
+ 'alphas' => %w[a b a b],
3776
+ 'vals' => [1,2,3,4]
3777
+ )}
3778
+ end
3779
+ end
3780
+
3508
3781
  context "#set_index" do
3509
3782
  before(:each) do
3510
3783
  @df = Daru::DataFrame.new({
@@ -3544,6 +3817,26 @@ describe Daru::DataFrame do
3544
3817
  jholu.set_index(:a)
3545
3818
  }.to raise_error(ArgumentError)
3546
3819
  end
3820
+
3821
+ it "sets multiindex if array is given" do
3822
+ df = Daru::DataFrame.new({
3823
+ a: %w[a a b b],
3824
+ b: [1, 2, 1, 2],
3825
+ c: %w[a b c d]
3826
+ })
3827
+ df.set_index(%i[a b])
3828
+ expected =
3829
+ Daru::DataFrame.new(
3830
+ { c: %w[a b c d] },
3831
+ index: Daru::MultiIndex.from_tuples(
3832
+ [['a', 1], ['a', 2], ['b', 1], ['b', 2]]
3833
+ )
3834
+ ).tap do |df|
3835
+ df.index.name = %i[a b]
3836
+ df
3837
+ end
3838
+ expect(df).to eq(expected)
3839
+ end
3547
3840
  end
3548
3841
 
3549
3842
  context "#concat" do
@@ -3644,7 +3937,7 @@ describe Daru::DataFrame do
3644
3937
  expect(df_union.index.to_a).to eq v1 + v2
3645
3938
  end
3646
3939
  end
3647
-
3940
+
3648
3941
  context '#inspect' do
3649
3942
  subject { df.inspect }
3650
3943
 
@@ -3667,6 +3960,41 @@ describe Daru::DataFrame do
3667
3960
  }.unindent}
3668
3961
  end
3669
3962
 
3963
+ context 'if index name is set' do
3964
+ context 'single index with name' do
3965
+ let(:df) { Daru::DataFrame.new({a: [1,2,3], b: [3,4,5], c: [6,7,8]},
3966
+ name: 'test')}
3967
+ before { df.index.name = 'index_name' }
3968
+ it { should == %Q{
3969
+ |#<Daru::DataFrame: test (3x3)>
3970
+ | index_name a b c
3971
+ | 0 1 3 6
3972
+ | 1 2 4 7
3973
+ | 2 3 5 8
3974
+ }.unindent}
3975
+ end
3976
+
3977
+ context 'MultiIndex with name' do
3978
+ let(:mi) { Daru::MultiIndex.new(
3979
+ levels: [[:a,:b,:c], [:one, :two]],
3980
+ labels: [[0,0,1,1,2,2], [0,1,0,1,0,1]], name: ['s1', 's2']) }
3981
+ let(:df) { Daru::DataFrame.new({
3982
+ a: [11, 12, 13, 14, 15, 16], b: [21, 22, 23, 24, 25, 26]},
3983
+ name: 'test', index: mi)}
3984
+ it { should == %Q{
3985
+ |#<Daru::DataFrame: test (6x2)>
3986
+ | s1 s2 a b
3987
+ | a one 11 21
3988
+ | two 12 22
3989
+ | b one 13 23
3990
+ | two 14 24
3991
+ | c one 15 25
3992
+ | two 16 26
3993
+ }.unindent}
3994
+ end
3995
+
3996
+ end
3997
+
3670
3998
  context 'no name' do
3671
3999
  let(:df) { Daru::DataFrame.new({a: [1,2,3], b: [3,4,5], c: [6,7,8]})}
3672
4000
  it { should == %Q{
@@ -3709,6 +4037,21 @@ describe Daru::DataFrame do
3709
4037
  | 12 1 1 1
3710
4038
  | 13 1 1 1
3711
4039
  | 14 1 1 1
4040
+ | 15 1 1 1
4041
+ | 16 1 1 1
4042
+ | 17 1 1 1
4043
+ | 18 1 1 1
4044
+ | 19 1 1 1
4045
+ | 20 1 1 1
4046
+ | 21 1 1 1
4047
+ | 22 1 1 1
4048
+ | 23 1 1 1
4049
+ | 24 1 1 1
4050
+ | 25 1 1 1
4051
+ | 26 1 1 1
4052
+ | 27 1 1 1
4053
+ | 28 1 1 1
4054
+ | 29 1 1 1
3712
4055
  | ... ... ... ...
3713
4056
  }.unindent}
3714
4057
  end
@@ -3764,8 +4107,18 @@ describe Daru::DataFrame do
3764
4107
  end
3765
4108
 
3766
4109
  context '#to_s' do
3767
- it 'produces something, despite of how reasonable you think it is' do
3768
- expect(@data_frame.to_s).to eq @data_frame.to_html
4110
+ it 'produces a class, size description' do
4111
+ expect(@data_frame.to_s).to eq "#<Daru::DataFrame(5x3)>"
4112
+ end
4113
+
4114
+ it 'produces a class, name, size description' do
4115
+ @data_frame.name = "Test"
4116
+ expect(@data_frame.to_s).to eq "#<Daru::DataFrame: Test(5x3)>"
4117
+ end
4118
+
4119
+ it 'produces a class, name, size description when the name is a symbol' do
4120
+ @data_frame.name = :Test
4121
+ expect(@data_frame.to_s).to eq "#<Daru::DataFrame: Test(5x3)>"
3769
4122
  end
3770
4123
  end
3771
4124
 
@@ -3824,6 +4177,102 @@ describe Daru::DataFrame do
3824
4177
  end
3825
4178
  end
3826
4179
 
4180
+ context '#access_row_tuples_by_indexs' do
4181
+ let(:df) {
4182
+ Daru::DataFrame.new({col: [:a, :b, :c, :d, :e], num: [52,12,07,17,01]}) }
4183
+ let(:df_idx) {
4184
+ Daru::DataFrame.new({a: [52, 12, 07], b: [1, 2, 3]}, index: [:one, :two, :three])
4185
+ }
4186
+ let (:mi_idx) do
4187
+ Daru::MultiIndex.from_tuples [
4188
+ [:a,:one,:bar],
4189
+ [:a,:one,:baz],
4190
+ [:b,:two,:bar],
4191
+ [:a,:two,:baz],
4192
+ ]
4193
+ end
4194
+ let (:df_mi) do
4195
+ Daru::DataFrame.new({
4196
+ a: 1..4,
4197
+ b: 'a'..'d'
4198
+ }, index: mi_idx )
4199
+ end
4200
+ context 'when no index is given' do
4201
+ it 'returns empty Array' do
4202
+ expect(df.access_row_tuples_by_indexs()).to eq([])
4203
+ end
4204
+ end
4205
+ context 'when index(s) are given' do
4206
+ it 'returns Array of row tuples' do
4207
+ expect(df.access_row_tuples_by_indexs(1)).to eq([[:b, 12]])
4208
+ expect(df.access_row_tuples_by_indexs(0,3)).to eq([[:a, 52], [:d, 17]])
4209
+ end
4210
+ end
4211
+ context 'when custom index(s) are given' do
4212
+ it 'returns Array of row tuples' do
4213
+ expect(df_idx.access_row_tuples_by_indexs(:one,:three)).to eq(
4214
+ [[52, 1], [7, 3]]
4215
+ )
4216
+ end
4217
+ end
4218
+ context 'when multi index is given' do
4219
+ it 'returns Array of row tuples' do
4220
+ expect(df_mi.access_row_tuples_by_indexs(:a)).to eq(
4221
+ [[1, "a"], [2, "b"], [4, "d"]]
4222
+ )
4223
+ expect(df_mi.access_row_tuples_by_indexs(:a, :one, :baz)).to eq(
4224
+ [[2, "b"]]
4225
+ )
4226
+ end
4227
+ end
4228
+ end
4229
+
4230
+ context '#aggregate' do
4231
+ let(:cat_idx) { Daru::CategoricalIndex.new [:a, :b, :a, :a, :c] }
4232
+ let(:df) { Daru::DataFrame.new(num: [52,12,07,17,01], cat_index: cat_idx) }
4233
+ let(:df_cat_idx) {
4234
+ Daru::DataFrame.new({num: [52,12,07,17,01]}, index: cat_idx) }
4235
+
4236
+ it 'lambda function on particular column' do
4237
+ expect(df.aggregate(num_100_times: ->(df) { (df.num*100).first })).to eq(
4238
+ Daru::DataFrame.new(num_100_times: [5200, 1200, 700, 1700, 100])
4239
+ )
4240
+ end
4241
+ it 'aggregate sum on particular column' do
4242
+ expect(df_cat_idx.aggregate(num: :sum)).to eq(
4243
+ Daru::DataFrame.new({num: [76, 12, 1]}, index: [:a, :b, :c])
4244
+ )
4245
+ end
4246
+ end
4247
+
4248
+ context '#group_by_and_aggregate' do
4249
+ let(:spending_df) {
4250
+ Daru::DataFrame.rows([
4251
+ [2010, 'dev', 50, 1],
4252
+ [2010, 'dev', 150, 1],
4253
+ [2010, 'dev', 200, 1],
4254
+ [2011, 'dev', 50, 1],
4255
+ [2012, 'dev', 150, 1],
4256
+
4257
+ [2011, 'office', 300, 1],
4258
+
4259
+ [2010, 'market', 50, 1],
4260
+ [2011, 'market', 500, 1],
4261
+ [2012, 'market', 500, 1],
4262
+ [2012, 'market', 300, 1],
4263
+
4264
+ [2012, 'R&D', 10, 1],],
4265
+ order: [:year, :category, :spending, :nb_spending])
4266
+ }
4267
+
4268
+ it 'works as group_by + aggregate' do
4269
+ expect(spending_df.group_by_and_aggregate(:year, {spending: :sum})).to eq(
4270
+ spending_df.group_by(:year).aggregate(spending: :sum))
4271
+ expect(spending_df.group_by_and_aggregate([:year, :category], spending: :sum, nb_spending: :size)).to eq(
4272
+ spending_df.group_by([:year, :category]).aggregate(spending: :sum, nb_spending: :size))
4273
+ end
4274
+ end
4275
+
3827
4276
  context '#create_sql' do
3828
4277
  let(:df) { Daru::DataFrame.new({
3829
4278
  a: [1,2,3],
@@ -3839,4 +4288,12 @@ describe Daru::DataFrame do
3839
4288
  | c DATE) CHARACTER SET=UTF8;
3840
4289
  }.unindent}
3841
4290
  end
4291
+
4292
+ context "#by_single_key" do
4293
+ let(:df) { Daru::DataFrame.new(a: [1, 2, 3], b: [4, 5, 6] ) }
4294
+
4295
+ it 'raise error when vector is missing from dataframe' do
4296
+ expect { df[:c] }.to raise_error(IndexError, /Specified vector c does not exist/)
4297
+ end
4298
+ end
3842
4299
  end if mri?