daru 0.1.4.1 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/.travis.yml +3 -0
  4. data/CONTRIBUTING.md +27 -3
  5. data/Guardfile +7 -0
  6. data/History.md +39 -1
  7. data/README.md +1 -1
  8. data/daru.gemspec +9 -2
  9. data/lib/daru.rb +4 -1
  10. data/lib/daru/accessors/gsl_wrapper.rb +93 -91
  11. data/lib/daru/accessors/nmatrix_wrapper.rb +109 -107
  12. data/lib/daru/category.rb +22 -15
  13. data/lib/daru/core/group_by.rb +13 -2
  14. data/lib/daru/core/merge.rb +37 -31
  15. data/lib/daru/core/query.rb +10 -2
  16. data/lib/daru/dataframe.rb +95 -34
  17. data/lib/daru/date_time/index.rb +15 -16
  18. data/lib/daru/date_time/offsets.rb +14 -11
  19. data/lib/daru/formatters/table.rb +2 -2
  20. data/lib/daru/index/categorical_index.rb +201 -0
  21. data/lib/daru/index/index.rb +289 -0
  22. data/lib/daru/index/multi_index.rb +266 -0
  23. data/lib/daru/maths/statistics/vector.rb +13 -9
  24. data/lib/daru/monkeys.rb +0 -7
  25. data/lib/daru/plotting/gruff/category.rb +1 -0
  26. data/lib/daru/plotting/gruff/dataframe.rb +3 -3
  27. data/lib/daru/plotting/nyaplot/dataframe.rb +1 -1
  28. data/lib/daru/vector.rb +36 -21
  29. data/lib/daru/version.rb +1 -1
  30. data/spec/accessors/array_wrapper_spec.rb +3 -0
  31. data/spec/accessors/{wrappers_spec.rb → gsl_wrapper_spec.rb} +0 -35
  32. data/spec/accessors/nmatrix_wrapper_spec.rb +32 -0
  33. data/spec/{categorical_spec.rb → category_spec.rb} +3 -0
  34. data/spec/core/group_by_spec.rb +17 -1
  35. data/spec/core/merge_spec.rb +38 -1
  36. data/spec/core/query_spec.rb +5 -0
  37. data/spec/dataframe_spec.rb +230 -57
  38. data/spec/date_time/offsets_spec.rb +84 -3
  39. data/spec/formatters/table_formatter_spec.rb +9 -0
  40. data/spec/index/categorical_index_spec.rb +2 -0
  41. data/spec/index/index_spec.rb +17 -2
  42. data/spec/{math → maths}/arithmetic/dataframe_spec.rb +0 -0
  43. data/spec/{math → maths}/arithmetic/vector_spec.rb +0 -0
  44. data/spec/{math → maths}/statistics/dataframe_spec.rb +1 -1
  45. data/spec/{math → maths}/statistics/vector_spec.rb +7 -12
  46. data/spec/plotting/gruff/category_spec.rb +44 -0
  47. data/spec/plotting/gruff/dataframe_spec.rb +84 -0
  48. data/spec/plotting/gruff/vector_spec.rb +70 -0
  49. data/spec/plotting/nyaplot/category_spec.rb +51 -0
  50. data/spec/plotting/{dataframe_spec.rb → nyaplot/dataframe_spec.rb} +0 -83
  51. data/spec/plotting/nyaplot/vector_spec.rb +66 -0
  52. data/spec/spec_helper.rb +3 -2
  53. data/spec/vector_spec.rb +68 -1
  54. metadata +53 -24
  55. data/lib/daru/index.rb +0 -761
  56. data/spec/plotting/vector_spec.rb +0 -230
@@ -1,3 +1,3 @@
1
1
  module Daru
2
- VERSION = '0.1.4.1'.freeze
2
+ VERSION = '0.1.5'.freeze
3
3
  end
@@ -0,0 +1,3 @@
1
+ describe Daru::Accessors::ArrayWrapper do
2
+ # TODO:
3
+ end
@@ -1,38 +1,3 @@
1
- describe Daru::Accessors::NMatrixWrapper do
2
- before :each do
3
- stub_context = Object.new
4
- @nm_wrapper = Daru::Accessors::NMatrixWrapper.new([1,2,3,4,5], stub_context, :float32)
5
- end
6
-
7
- it "checks for actual NMatrix creation" do
8
- expect(@nm_wrapper.data.class).to eq(NMatrix)
9
- end
10
-
11
- it "checks the actual size of the NMatrix object" do
12
- expect(@nm_wrapper.data.size).to eq(10)
13
- end
14
-
15
- it "checks that @size is the number of elements in the vector" do
16
- expect(@nm_wrapper.size).to eq(5)
17
- end
18
-
19
- it "checks for underlying NMatrix data type" do
20
- expect(@nm_wrapper.data.dtype).to eq(:float32)
21
- end
22
-
23
- it "resizes" do
24
- @nm_wrapper.resize(100)
25
-
26
- expect(@nm_wrapper.size).to eq(5)
27
- expect(@nm_wrapper.data.size).to eq(100)
28
- expect(@nm_wrapper.data).to eq(NMatrix.new [100], [1,2,3,4,5])
29
- end
30
- end
31
-
32
- describe Daru::Accessors::ArrayWrapper do
33
-
34
- end
35
-
36
1
  describe Daru::Accessors::GSLWrapper do
37
2
  before :each do
38
3
  @stub_context = Object.new
@@ -0,0 +1,32 @@
1
+ require 'spec_helper.rb'
2
+
3
+ describe Daru::Accessors::NMatrixWrapper do
4
+ before :each do
5
+ stub_context = Object.new
6
+ @nm_wrapper = Daru::Accessors::NMatrixWrapper.new([1,2,3,4,5], stub_context, :float32)
7
+ end
8
+
9
+ it "checks for actual NMatrix creation" do
10
+ expect(@nm_wrapper.data.class).to eq(NMatrix)
11
+ end
12
+
13
+ it "checks the actual size of the NMatrix object" do
14
+ expect(@nm_wrapper.data.size).to eq(10)
15
+ end
16
+
17
+ it "checks that @size is the number of elements in the vector" do
18
+ expect(@nm_wrapper.size).to eq(5)
19
+ end
20
+
21
+ it "checks for underlying NMatrix data type" do
22
+ expect(@nm_wrapper.data.dtype).to eq(:float32)
23
+ end
24
+
25
+ it "resizes" do
26
+ @nm_wrapper.resize(100)
27
+
28
+ expect(@nm_wrapper.size).to eq(5)
29
+ expect(@nm_wrapper.data.size).to eq(100)
30
+ expect(@nm_wrapper.data).to eq(NMatrix.new [100], [1,2,3,4,5])
31
+ end
32
+ end
@@ -305,6 +305,9 @@ describe Daru::Vector, "categorical" do
305
305
  its(:'index.to_a') { is_expected.to eq [:a, :b, :c, :d, 1] }
306
306
  its(:to_a) { is_expected.to eq [0.4, 0, 0.2, 0, 0.4] }
307
307
  end
308
+ context "invalid argument" do
309
+ it { expect { dv.frequencies :hash }.to raise_error ArgumentError }
310
+ end
308
311
  end
309
312
 
310
313
  context "#to_category" do
@@ -5,7 +5,7 @@ describe Daru::Core::GroupBy do
5
5
  b: %w{one one two three two two one three},
6
6
  c: [1 ,2 ,3 ,1 ,3 ,6 ,3 ,8],
7
7
  d: [11 ,22 ,33 ,44 ,55 ,66 ,77 ,88]
8
- })
8
+ }, order: [:a, :b, :c, :d])
9
9
 
10
10
  @sl_group = @df.group_by(:a)
11
11
  @dl_group = @df.group_by([:a, :b])
@@ -402,4 +402,20 @@ describe Daru::Core::GroupBy do
402
402
  Daru::Vector.new(['one', 'three', 'two', 'oneone', 'three', 'twotwo'], index: @dl_multi_index)
403
403
  end
404
404
  end
405
+
406
+ context 'groups by first vector if no vector mentioned' do
407
+ subject { @df.group_by }
408
+
409
+ it { is_expected.to be_a Daru::Core::GroupBy }
410
+ its(:groups) { is_expected.to eq @sl_group.groups }
411
+ its(:size) { is_expected.to eq @sl_group.size }
412
+ end
413
+
414
+ context 'group and sum with numeric indices' do
415
+ let(:df) { Daru::DataFrame.new({ g: ['a','a','a'], num: [1,2,3]}, index: [2,12,23]) }
416
+
417
+ subject { df.group_by([:g]).sum }
418
+
419
+ it { is_expected.to eq Daru::DataFrame.new({num: [6]}, index: ['a']) }
420
+ end
405
421
  end
@@ -37,6 +37,18 @@ describe Daru::DataFrame do
37
37
  expect(@left.join(@right_many, how: :inner, on: [:id])).to eq(answer)
38
38
  end
39
39
 
40
+ it "performs an inner join of two dataframes that has many to one mapping" do
41
+ left_many = @right_many
42
+ right = @left
43
+
44
+ answer = Daru::DataFrame.new({
45
+ :name_2 => ['Pirate', 'Pirate', 'Pirate', 'Pirate'],
46
+ :id => [1,1,1,1],
47
+ :name_1 => ['Rutabaga', 'Pirate', 'Darth Vader', 'Ninja']
48
+ }, order: [:name_1, :id, :name_2])
49
+ expect(left_many.join(right, how: :inner, on: [:id])).to eq(answer)
50
+ end
51
+
40
52
  it "performs an inner join of two dataframes that has many to many mapping" do
41
53
  @left[:id].recode! { |v| v == 2 ? 1 : v }
42
54
  answer = Daru::DataFrame.new({
@@ -56,6 +68,18 @@ describe Daru::DataFrame do
56
68
  expect(@left.join(@right, how: :outer, on: [:name])).to eq(answer)
57
69
  end
58
70
 
71
+ it "adds a left/right indicator" do
72
+ answer = Daru::DataFrame.new({
73
+ :id_1 => [nil,2,3,1,nil,4],
74
+ :name => ["Darth Vader", "Monkey", "Ninja", "Pirate", "Rutabaga", "Spaghetti"],
75
+ :id_2 => [3,nil,4,2,1,nil]
76
+ }, order: [:id_1, :name, :id_2])
77
+
78
+ outer = @left.join(@right, how: :outer, on: [:name], indicator: :my_indicator)
79
+ expect(outer[:my_indicator].to_a).to eq [:right_only, :left_only, :both, :both, :right_only, :left_only]
80
+ end
81
+
82
+
59
83
  it "performs a full outer join when the right join keys have nils" do
60
84
  @right[:name].recode! { |v| v == 'Rutabaga' ? nil : v }
61
85
  answer = Daru::DataFrame.new({
@@ -117,8 +141,21 @@ describe Daru::DataFrame do
117
141
  expect(@left.join(@right, how: :right, on: [:name])).to eq(answer)
118
142
  end
119
143
 
144
+ it "doesn't convert false into nil when joining boolean values" do
145
+ left = Daru::DataFrame.new({ key: [1,2,3], left_value: [true, false, true] })
146
+ right = Daru::DataFrame.new({ key: [1,2,3], right_value: [true, false, true] })
147
+
148
+ answer = Daru::DataFrame.new({
149
+ left_value: [true, false, true],
150
+ key: [1,2,3],
151
+ right_value: [true, false, true]
152
+ }, order: [:left_value, :key, :right_value] )
153
+
154
+ expect(left.join(right, on: [:key], how: :inner)).to eq answer
155
+ end
156
+
120
157
  it "raises if :on field are absent in one of dataframes" do
121
- @right.vectors = [:id, :other_name]
158
+ @right.vectors = Daru::Index.new [:id, :other_name]
122
159
  expect { @left.join(@right, how: :right, on: [:name]) }.to \
123
160
  raise_error(ArgumentError, /Both dataframes expected .* :name/)
124
161
 
@@ -266,6 +266,11 @@ describe "Arel-like syntax" do
266
266
  @df.where (@df[:names].eq('james') | @df[:sym].eq(:four))
267
267
  ).to eq(answer)
268
268
  end
269
+
270
+ it "does not give SystemStackError" do
271
+ v = Daru::Vector.new [1]*300_000
272
+ expect { v.where v.eq(1) }.not_to raise_error
273
+ end
269
274
  end
270
275
 
271
276
  context Daru::Vector do
@@ -55,6 +55,13 @@ describe Daru::DataFrame do
55
55
  expect(df[:a]) .to eq(Daru::Vector.new [1,1,1,1])
56
56
  end
57
57
 
58
+ it "creates empty dataframe" do
59
+ df = Daru::DataFrame.rows [], order: [:a, :b, :c]
60
+
61
+ expect(df.vectors).to eq(Daru::Index.new [:a,:b,:c])
62
+ expect(df.index).to be_empty
63
+ end
64
+
58
65
  it "creates a DataFrame from Vector rows" do
59
66
  rows = @rows.map { |r| Daru::Vector.new r, index: [:a,:b,:c,:d,:e] }
60
67
 
@@ -170,14 +177,14 @@ describe Daru::DataFrame do
170
177
  end
171
178
 
172
179
  it "initializes from an Array of Hashes" do
173
- df = Daru::DataFrame.new([{a: 1, b: 11}, {a: 2, b: 12}, {a: 3, b: 13},
180
+ df = Daru::DataFrame.new([{a: 1, b: 11}, {a: false, b: 12}, {a: 3, b: 13},
174
181
  {a: 4, b: 14}, {a: 5, b: 15}], order: [:b, :a],
175
182
  index: [:one, :two, :three, :four, :five])
176
183
 
177
184
  expect(df.index) .to eq(Daru::Index.new [:one, :two, :three, :four, :five])
178
185
  expect(df.vectors).to eq(Daru::Index.new [:b, :a])
179
186
  expect(df.a.class).to eq(Daru::Vector)
180
- expect(df.a) .to eq([1,2,3,4,5].dv(:a,[:one, :two, :three, :four, :five]))
187
+ expect(df.a) .to eq([1,false,3,4,5].dv(:a,[:one, :two, :three, :four, :five]))
181
188
  end
182
189
 
183
190
  it "initializes from Array of Arrays" do
@@ -612,48 +619,63 @@ describe Daru::DataFrame do
612
619
  end
613
620
 
614
621
  context '#method_missing' do
615
- subject(:data_frame) {
616
- Daru::DataFrame.new({b: [11,12,13,14,15], a: [1,2,3,4,5],
617
- c: [11,22,33,44,55]}, order: [:a, :b, :c],
618
- index: [:one, :two, :three, :four, :five])
619
- }
622
+ let(:df) { Daru::DataFrame.new({
623
+ :a => [1, 2, 3, 4, 5],
624
+ 'b' => [5, 4, 3, 2, 1]
625
+ }, index: 11..15)}
620
626
 
621
- context 'getting the vector' do
622
- subject{
623
- data_frame.a
624
- }
625
- it { is_expected.to eq [1,2,3,4,5].dv(:a, [:one, :two, :three, :four, :five]) }
627
+ context 'get vector' do
628
+ context 'by string' do
629
+ subject { df.b }
630
+
631
+ it { is_expected.to be_a Daru::Vector }
632
+ its(:to_a) { is_expected.to eq [5, 4, 3, 2, 1] }
633
+ its(:'index.to_a') { is_expected.to eq [11, 12, 13, 14, 15] }
634
+ end
635
+
636
+ context 'by symbol' do
637
+ subject { df.a }
638
+
639
+ it { is_expected.to be_a Daru::Vector }
640
+ its(:to_a) { is_expected.to eq [1, 2, 3, 4, 5] }
641
+ its(:'index.to_a') { is_expected.to eq [11, 12, 13, 14, 15] }
642
+ end
626
643
  end
627
644
 
628
- context 'setting existing vector' do
629
- before{
630
- data_frame.a = [100,200,300,400,500]
631
- }
632
- it { is_expected.to eq(Daru::DataFrame.new({
633
- b: [11,12,13,14,15],
634
- a: [100,200,300,400,500],
635
- c: [11,22,33,44,55]}, order: [:a, :b, :c],
636
- index: [:one, :two, :three, :four, :five]))
637
- }
645
+ context 'set existing vector' do
646
+ context 'by string' do
647
+ before { df.b = [:a, :b, :c, :d, :e] }
648
+ subject { df }
649
+
650
+ it { is_expected.to be_a Daru::DataFrame }
651
+ its(:'vectors.to_a') { is_expected.to eq [:a, 'b'] }
652
+ its(:'b.to_a') { is_expected.to eq [:a, :b, :c, :d, :e] }
653
+ its(:'index.to_a') { is_expected.to eq [11, 12, 13, 14, 15] }
654
+ end
655
+
656
+ context 'by symbol' do
657
+ before { df.a = [:a, :b, :c, :d, :e] }
658
+ subject { df }
659
+
660
+ it { is_expected.to be_a Daru::DataFrame }
661
+ its(:'vectors.to_a') { is_expected.to eq [:a, 'b'] }
662
+ its(:'a.to_a') { is_expected.to eq [:a, :b, :c, :d, :e] }
663
+ its(:'index.to_a') { is_expected.to eq [11, 12, 13, 14, 15] }
664
+ end
638
665
  end
639
666
 
640
- context 'setting new vector' do
641
- before{
642
- data_frame.d = [100,200,300,400,500]
643
- }
644
- it { is_expected.to eq(Daru::DataFrame.new({
645
- b: [11,12,13,14,15],
646
- a: [1,2,3,4,5],
647
- d: [100,200,300,400,500],
648
- c: [11,22,33,44,55]}, order: [:a, :b, :c, :d],
649
- index: [:one, :two, :three, :four, :five]))
650
- }
667
+ context 'set new vector' do
668
+ before { df.c = [5, 5, 5, 5, 5] }
669
+ subject { df }
670
+
671
+ it { is_expected.to be_a Daru::DataFrame }
672
+ its(:'vectors.to_a') { is_expected.to eq [:a, 'b', :c] }
673
+ its(:'c.to_a') { is_expected.to eq [5, 5, 5, 5, 5] }
674
+ its(:'index.to_a') { is_expected.to eq [11, 12, 13, 14, 15] }
651
675
  end
652
676
 
653
- context 'no vector found' do
654
- it 'should raise' do
655
- expect { data_frame.e }.to raise_error(NoMethodError)
656
- end
677
+ context 'reference invalid vector' do
678
+ it { expect { df.d }.to raise_error NoMethodError }
657
679
  end
658
680
  end
659
681
 
@@ -1370,6 +1392,15 @@ describe Daru::DataFrame do
1370
1392
  c: [11,22,33,44,55]}, order: [:a, :b, :c])
1371
1393
 
1372
1394
  expect(df.row[0]).to eq([1,11,11].dv(nil, [:a, :b, :c]))
1395
+ expect(df.row[3]).to eq([4,14,44].dv(nil, [:a, :b, :c]))
1396
+ end
1397
+
1398
+ it "returns a row with given Integer index for numerical index DataFrame" do
1399
+ df = Daru::DataFrame.new({b: [11,12,13,14,15], a: [1,2,3,4,5],
1400
+ c: [11,22,33,44,55]}, order: [:a, :b, :c], index: [1,2,3,4,5])
1401
+
1402
+ expect(df.row[0]).to eq([1,11,11].dv(nil, [:a, :b, :c]))
1403
+ expect(df.row[3]).to eq([3,13,33].dv(nil, [:a, :b, :c]))
1373
1404
  end
1374
1405
  end
1375
1406
 
@@ -1616,7 +1647,7 @@ describe Daru::DataFrame do
1616
1647
  }, index: 11..18)
1617
1648
  end
1618
1649
  before { df.to_category :b }
1619
-
1650
+
1620
1651
  context 'remove nils only' do
1621
1652
  subject { df.reject_values nil }
1622
1653
  it { is_expected.to be_a Daru::DataFrame }
@@ -1626,7 +1657,7 @@ describe Daru::DataFrame do
1626
1657
  its(:'c.to_a') { is_expected.to eq ['a', Float::NAN, 7] }
1627
1658
  its(:'index.to_a') { is_expected.to eq [11, 12, 18] }
1628
1659
  end
1629
-
1660
+
1630
1661
  context 'remove Float::NAN only' do
1631
1662
  subject { df.reject_values Float::NAN }
1632
1663
  it { is_expected.to be_a Daru::DataFrame }
@@ -1636,7 +1667,7 @@ describe Daru::DataFrame do
1636
1667
  its(:'c.to_a') { is_expected.to eq ['a', 3, 5, nil, 7] }
1637
1668
  its(:'index.to_a') { is_expected.to eq [11, 13, 16, 17, 18] }
1638
1669
  end
1639
-
1670
+
1640
1671
  context 'remove both nil and Float::NAN' do
1641
1672
  subject { df.reject_values nil, Float::NAN }
1642
1673
  it { is_expected.to be_a Daru::DataFrame }
@@ -1646,7 +1677,7 @@ describe Daru::DataFrame do
1646
1677
  its(:'c.to_a') { is_expected.to eq ['a', 7] }
1647
1678
  its(:'index.to_a') { is_expected.to eq [11, 18] }
1648
1679
  end
1649
-
1680
+
1650
1681
  context 'any other values' do
1651
1682
  subject { df.reject_values 1, 5 }
1652
1683
  it { is_expected.to be_a Daru::DataFrame }
@@ -1664,9 +1695,9 @@ describe Daru::DataFrame do
1664
1695
  its(:'a.to_a') { is_expected.to eq [7] }
1665
1696
  its(:'b.to_a') { is_expected.to eq [8] }
1666
1697
  its(:'c.to_a') { is_expected.to eq [7] }
1667
- its(:'index.to_a') { is_expected.to eq [18] }
1698
+ its(:'index.to_a') { is_expected.to eq [18] }
1668
1699
  end
1669
-
1700
+
1670
1701
  context 'when resultant dataframe is empty' do
1671
1702
  subject { df.reject_values 1, 2, 3, 4, 5, 6, 7, nil, Float::NAN }
1672
1703
  it { is_expected.to be_a Daru::DataFrame }
@@ -1674,10 +1705,10 @@ describe Daru::DataFrame do
1674
1705
  its(:'a.to_a') { is_expected.to eq [] }
1675
1706
  its(:'b.to_a') { is_expected.to eq [] }
1676
1707
  its(:'c.to_a') { is_expected.to eq [] }
1677
- its(:'index.to_a') { is_expected.to eq [] }
1708
+ its(:'index.to_a') { is_expected.to eq [] }
1678
1709
  end
1679
1710
  end
1680
-
1711
+
1681
1712
  context '#replace_values' do
1682
1713
  subject do
1683
1714
  Daru::DataFrame.new({
@@ -1687,7 +1718,7 @@ describe Daru::DataFrame do
1687
1718
  })
1688
1719
  end
1689
1720
  before { subject.to_category :b }
1690
-
1721
+
1691
1722
  context 'replace nils only' do
1692
1723
  before { subject.replace_values nil, 10 }
1693
1724
  it { is_expected.to be_a Daru::DataFrame }
@@ -1696,7 +1727,7 @@ describe Daru::DataFrame do
1696
1727
  its(:'b.to_a') { is_expected.to eq [:a, :b, 10, Float::NAN, 10, 3, 5, 8] }
1697
1728
  its(:'c.to_a') { is_expected.to eq ['a', Float::NAN, 3, 4, 3, 5, 10, 7] }
1698
1729
  end
1699
-
1730
+
1700
1731
  context 'replace Float::NAN only' do
1701
1732
  before { subject.replace_values Float::NAN, 10 }
1702
1733
  it { is_expected.to be_a Daru::DataFrame }
@@ -1705,7 +1736,7 @@ describe Daru::DataFrame do
1705
1736
  its(:'b.to_a') { is_expected.to eq [:a, :b, nil, 10, nil, 3, 5, 8] }
1706
1737
  its(:'c.to_a') { is_expected.to eq ['a', 10, 3, 4, 3, 5, nil, 7] }
1707
1738
  end
1708
-
1739
+
1709
1740
  context 'replace both nil and Float::NAN' do
1710
1741
  before { subject.replace_values [nil, Float::NAN], 10 }
1711
1742
  it { is_expected.to be_a Daru::DataFrame }
@@ -1714,7 +1745,7 @@ describe Daru::DataFrame do
1714
1745
  its(:'b.to_a') { is_expected.to eq [:a, :b, 10, 10, 10, 3, 5, 8] }
1715
1746
  its(:'c.to_a') { is_expected.to eq ['a', 10, 3, 4, 3, 5, 10, 7] }
1716
1747
  end
1717
-
1748
+
1718
1749
  context 'replace other values' do
1719
1750
  before { subject.replace_values [1, 5], 10 }
1720
1751
  it { is_expected.to be_a Daru::DataFrame }
@@ -1722,7 +1753,7 @@ describe Daru::DataFrame do
1722
1753
  its(:'a.to_a') { is_expected.to eq [10, 2, 3, nil, Float::NAN, nil, 10, 7] }
1723
1754
  its(:'b.to_a') { is_expected.to eq [:a, :b, nil, Float::NAN, nil, 3, 10, 8] }
1724
1755
  its(:'c.to_a') { is_expected.to eq ['a', Float::NAN, 3, 4, 3, 10, nil, 7] }
1725
- end
1756
+ end
1726
1757
  end
1727
1758
 
1728
1759
  context "#clone" do
@@ -2124,14 +2155,28 @@ describe Daru::DataFrame do
2124
2155
 
2125
2156
  context "#filter_rows" do
2126
2157
  context Daru::Index do
2127
- it "filters rows" do
2128
- df = Daru::DataFrame.new({a: [1,2,3], b: [2,3,4]})
2158
+ context "when specified no index" do
2159
+ it "filters rows" do
2160
+ df = Daru::DataFrame.new({a: [1,2,3], b: [2,3,4]})
2129
2161
 
2130
- a = df.filter_rows do |row|
2131
- row[:a] % 2 == 0
2162
+ a = df.filter_rows do |row|
2163
+ row[:a] % 2 == 0
2164
+ end
2165
+
2166
+ expect(a).to eq(Daru::DataFrame.new({a: [2], b: [3]}, order: [:a, :b], index: [1]))
2132
2167
  end
2168
+ end
2133
2169
 
2134
- expect(a).to eq(Daru::DataFrame.new({a: [2], b: [3]}, order: [:a, :b], index: [1]))
2170
+ context "when specified numerical index" do
2171
+ it "filters rows" do
2172
+ df = Daru::DataFrame.new({a: [1,2,3], b: [2,3,4]}, index: [1,2,3])
2173
+
2174
+ a = df.filter_rows do |row|
2175
+ row[:a] % 2 == 0
2176
+ end
2177
+
2178
+ expect(a).to eq(Daru::DataFrame.new({a: [2], b: [3]}, order: [:a, :b], index: [2]))
2179
+ end
2135
2180
  end
2136
2181
 
2137
2182
  it "preserves names of vectors" do
@@ -2500,6 +2545,32 @@ describe Daru::DataFrame do
2500
2545
  end
2501
2546
  end
2502
2547
 
2548
+ context '#order=' do
2549
+ let(:df) do
2550
+ Daru::DataFrame.new({
2551
+ a: [1, 2, 3],
2552
+ b: [4, 5, 6]
2553
+ }, order: [:a, :b])
2554
+ end
2555
+
2556
+ context 'correct order' do
2557
+ before { df.order = [:b, :a] }
2558
+ subject { df }
2559
+
2560
+ its(:'vectors.to_a') { is_expected.to eq [:b, :a] }
2561
+ its(:'b.to_a') { is_expected.to eq [4, 5, 6] }
2562
+ its(:'a.to_a') { is_expected.to eq [1, 2, 3] }
2563
+ end
2564
+
2565
+ context 'insufficient vectors' do
2566
+ it { expect { df.order = [:a] }.to raise_error }
2567
+ end
2568
+
2569
+ context 'wrong vectors' do
2570
+ it { expect { df.order = [:a, :b, 'b'] }.to raise_error }
2571
+ end
2572
+ end
2573
+
2503
2574
  context "#vectors=" do
2504
2575
  before :each do
2505
2576
  @df = Daru::DataFrame.new({
@@ -2523,6 +2594,13 @@ describe Daru::DataFrame do
2523
2594
  @df.vectors = Daru::Index.new([1,2,'3',4,'5'])
2524
2595
  }.to raise_error(ArgumentError)
2525
2596
  end
2597
+
2598
+ it "change name of vectors in @data" do
2599
+ new_index_array = [:k, :l, :m]
2600
+ @df.vectors = Daru::Index.new(new_index_array)
2601
+
2602
+ expect(@df.data.map { |vector| vector.name }).to eq(new_index_array)
2603
+ end
2526
2604
  end
2527
2605
 
2528
2606
  context "#rename_vectors" do
@@ -2912,6 +2990,43 @@ describe Daru::DataFrame do
2912
2990
  )
2913
2991
  )
2914
2992
  end
2993
+
2994
+ it 'performs date pivoting' do
2995
+ categories = %i[jan feb mar apr may jun jul aug sep oct nov dec]
2996
+ df = Daru::DataFrame.rows([
2997
+ [2014, 2, 1600.0, 20.0],
2998
+ [2014, 3, 1680.0, 21.0],
2999
+ [2016, 2, 1600.0, 20.0],
3000
+ [2016, 4, 1520.0, 19.0],
3001
+ ], order: [:year, :month, :visitors, :days])
3002
+ df[:averages] = df[:visitors] / df[:days]
3003
+ df[:month] = df[:month].map{|i| categories[i - 1]}
3004
+ actual = df.pivot_table(index: :month, vectors: [:year], values: :averages)
3005
+
3006
+ # NB: As you can see, there are some "illogical" parts:
3007
+ # months are sorted lexicographically, then made into multi-index
3008
+ # with one-element-per-tuple, then order of columns is dependent
3009
+ # on which month is lexicographically first (its apr, so, apr-2016
3010
+ # is first row to gather, so 2016 is first column).
3011
+ #
3012
+ # All of it is descendance of our group_by implementation (which
3013
+ # always sorts results & always make array keys). I hope that fixing
3014
+ # group_by, even to the extend described at https://github.com/v0dro/daru/issues/152,
3015
+ # will be fix this case also.
3016
+ expected =
3017
+ Daru::DataFrame.new(
3018
+ [
3019
+ [80.0, 80.0, nil],
3020
+ [nil, 80.0, 80.0],
3021
+ ], index: Daru::MultiIndex.from_tuples([[:apr], [:feb], [:mar]]),
3022
+ order: Daru::MultiIndex.from_tuples([[:averages, 2016], [:averages, 2014]])
3023
+ )
3024
+ # Comparing their parts previous to full comparison allows to
3025
+ # find complicated differences.
3026
+ expect(actual.vectors).to eq expected.vectors
3027
+ expect(actual.index).to eq expected.index
3028
+ expect(actual).to eq expected
3029
+ end
2915
3030
  end
2916
3031
 
2917
3032
  context "#shape" do
@@ -3059,14 +3174,14 @@ describe Daru::DataFrame do
3059
3174
  })
3060
3175
  end
3061
3176
  before { df.to_category :b }
3062
-
3177
+
3063
3178
  context 'true' do
3064
3179
  it { expect(df.include_values? nil).to eq true }
3065
3180
  it { expect(df.include_values? Float::NAN).to eq true }
3066
3181
  it { expect(df.include_values? nil, Float::NAN).to eq true }
3067
3182
  it { expect(df.include_values? 1, 30).to eq true }
3068
3183
  end
3069
-
3184
+
3070
3185
  context 'false' do
3071
3186
  it { expect(df[:a, :c].include_values? nil).to eq false }
3072
3187
  it { expect(df[:c, :d].include_values? Float::NAN).to eq false }
@@ -3472,6 +3587,64 @@ describe Daru::DataFrame do
3472
3587
 
3473
3588
  end
3474
3589
 
3590
+ context "#union" do
3591
+ before do
3592
+ @df1 = Daru::DataFrame.new({
3593
+ a: [1, 2, 3],
3594
+ b: [1, 2, 3]},
3595
+ index: [1,3,5] )
3596
+
3597
+ @df2 = Daru::DataFrame.new({
3598
+ a: [4, 5, 6],
3599
+ c: [4, 5, 6]},
3600
+ index: [7,9,11])
3601
+
3602
+ @df3 = Daru::DataFrame.new({
3603
+ a: [4, 5, 6],
3604
+ c: [4, 5, 6]},
3605
+ index: [5,7,9])
3606
+ end
3607
+
3608
+ it 'does not modify the original dataframes' do
3609
+ df1_a = @df1[:a].to_a.dup
3610
+ df2_a = @df2[:a].to_a.dup
3611
+
3612
+ _ = @df1.union @df2
3613
+ expect(@df1[:a].to_a).to eq df1_a
3614
+ expect(@df2[:a].to_a).to eq df2_a
3615
+ end
3616
+
3617
+ it 'creates a new dataframe that is a concatenation of the two dataframe arguments' do
3618
+ df1_a = @df1[:a].to_a.dup
3619
+ df2_a = @df2[:a].to_a.dup
3620
+
3621
+ df_union = @df1.union @df2
3622
+ expect(df_union[:a].to_a).to eq df1_a + df2_a
3623
+ end
3624
+
3625
+ it 'fills in missing vectors with nils' do
3626
+ df1_b = @df1[:b].to_a.dup
3627
+ df2_c = @df2[:c].to_a.dup
3628
+
3629
+ df_union = @df1.union @df2
3630
+ expect(df_union[:b].to_a).to eq df1_b + [nil] * @df2.size
3631
+ expect(df_union[:c].to_a).to eq [nil] * @df1.size + df2_c
3632
+ end
3633
+
3634
+ it 'overwrites part of the first dataframe if there are double indices' do
3635
+ vec = Daru::Vector.new({a: 4, b: nil, c: 4})
3636
+ expect(@df1.union(@df3).row[5]).to eq vec
3637
+ end
3638
+
3639
+ it 'concats the indices' do
3640
+ v1 = @df1.index.to_a
3641
+ v2 = @df2.index.to_a
3642
+
3643
+ df_union = @df1.union @df2
3644
+ expect(df_union.index.to_a).to eq v1 + v2
3645
+ end
3646
+ end
3647
+
3475
3648
  context '#inspect' do
3476
3649
  subject { df.inspect }
3477
3650