daru 0.1.5 → 0.3

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 (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
@@ -5,62 +5,52 @@ require 'active_record'
5
5
 
6
6
  RSpec.describe Daru::IO::SqlDataSource do
7
7
  include_context 'with accounts table in sqlite3 database'
8
- let(:dbi_handle) do
9
- DBI.connect("DBI:SQLite3:#{db_name}")
8
+
9
+ let(:query) do
10
+ 'select * from accounts'
10
11
  end
11
12
 
12
- let(:active_record_connection) do
13
+ let(:source) do
13
14
  ActiveRecord::Base.establish_connection("sqlite3:#{db_name}")
14
15
  ActiveRecord::Base.connection
15
16
  end
16
17
 
17
- let(:query) do
18
- 'select * from accounts'
19
- end
20
-
21
18
  describe '.make_dataframe' do
22
- context 'with DBI::DatabaseHandle' do
23
- it 'returns a dataframe' do
24
- result = Daru::IO::SqlDataSource.make_dataframe(dbi_handle, query)
25
- expect(result).to be_a(Daru::DataFrame)
26
- expect(result.nrows).to eq(2)
27
- expect(result.row[0][:id]).to eq(1)
28
- expect(result.row[0][:name]).to eq('Homer')
29
- end
19
+ subject(:df) { Daru::IO::SqlDataSource.make_dataframe(source, query) }
30
20
 
31
- context 'with an object not a string as a query' do
32
- it 'raises ArgumentError' do
33
- expect {
34
- Daru::IO::SqlDataSource.make_dataframe(dbi_handle, Object.new)
35
- }.to raise_error(ArgumentError)
36
- end
37
- end
21
+ context 'with DBI::DatabaseHandle' do
22
+ let(:source) { DBI.connect("DBI:SQLite3:#{db_name}") }
23
+ it { is_expected.to be_a(Daru::DataFrame) }
24
+ it { expect(df.row[0]).to have_attributes(id: 1, age: 20) }
25
+ its(:nrows) { is_expected.to eq 2 }
38
26
  end
39
27
 
40
28
  context 'with ActiveRecord::Connection' do
41
- it 'returns a dataframe' do
42
- result = Daru::IO::SqlDataSource.make_dataframe(active_record_connection, query)
43
- expect(result).to be_a(Daru::DataFrame)
44
- expect(result.nrows).to eq(2)
45
- expect(result.row[0][:id]).to eq(1)
46
- expect(result.row[0][:name]).to eq('Homer')
47
- end
29
+ it { is_expected.to be_a(Daru::DataFrame) }
30
+ it { expect(df.row[0]).to have_attributes(id: 1, age: 20) }
31
+ its(:nrows) { is_expected.to eq 2 }
32
+ end
48
33
 
49
- context 'with an object not a string as a query' do
50
- it 'raises ArgumentError' do
51
- expect {
52
- Daru::IO::SqlDataSource.make_dataframe(active_record_connection, Object.new)
53
- }.to raise_error(ArgumentError)
54
- end
55
- end
34
+ context 'with path to sqlite3 file' do
35
+ let(:source) { db_name }
36
+ it { is_expected.to be_a(Daru::DataFrame) }
37
+ it { expect(df.row[0]).to have_attributes(id: 1, age: 20) }
38
+ its(:nrows) { is_expected.to eq 2 }
39
+ end
40
+
41
+ context 'with an object not a string as a query' do
42
+ let(:query) { Object.new }
43
+ it { expect { df }.to raise_error(ArgumentError) }
56
44
  end
57
45
 
58
46
  context 'with an object not a database connection' do
59
- it 'raises ArgumentError' do
60
- expect {
61
- Daru::IO::SqlDataSource.make_dataframe(Object.new, query)
62
- }.to raise_error(ArgumentError)
63
- end
47
+ let(:source) { Object.new }
48
+ it { expect { df }.to raise_error(ArgumentError) }
49
+ end
50
+
51
+ context 'with path to unsupported db file' do
52
+ let(:source) { 'spec/fixtures/bank2.dat' }
53
+ it { expect { df }.to raise_error(ArgumentError) }
64
54
  end
65
55
  end
66
56
  end
@@ -1,7 +1,7 @@
1
1
  describe Daru::DataFrame, '#to_html' do
2
2
  let(:doc) { Nokogiri::HTML(df.to_html) }
3
3
  subject(:table) { doc.at('table') }
4
- let(:header) { table.at('tr:first-child > th:first-child') }
4
+ let(:header) { doc.at('b')}
5
5
  let(:name) { 'test' }
6
6
 
7
7
  let(:splitted_row) { row.inner_html.scan(/<t[dh].+?<\/t[dh]>/) }
@@ -13,18 +13,17 @@ describe Daru::DataFrame, '#to_html' do
13
13
  subject { header }
14
14
 
15
15
  it { is_expected.not_to be_nil }
16
- its(['colspan']) { is_expected.to eq (df.ncols + 1).to_s }
17
- its(:text) { is_expected.to eq "Daru::DataFrame: test (3x3)" }
16
+ its(:text) { is_expected.to eq " Daru::DataFrame: test (3x3) " }
18
17
 
19
18
  context 'without name' do
20
19
  let(:name) { nil }
21
20
 
22
- its(:text) { is_expected.to eq "Daru::DataFrame(3x3)" }
21
+ its(:text) { is_expected.to eq " Daru::DataFrame(3x3) " }
23
22
  end
24
23
  end
25
24
 
26
25
  describe 'column headers' do
27
- subject(:columns) { table.search('tr:nth-child(2) th').map(&:text) }
26
+ subject(:columns) { table.search('tr:nth-child(1) th').map(&:text) }
28
27
  its(:size) { is_expected.to eq df.ncols + 1 }
29
28
  it { is_expected.to eq ['', 'a', 'b', 'c'] }
30
29
  end
@@ -34,7 +33,7 @@ describe Daru::DataFrame, '#to_html' do
34
33
 
35
34
  subject { splitted_row }
36
35
  describe 'first row' do
37
- let(:row) { table.search('tr:nth-child(2)') }
36
+ let(:row) { table.search('thead > tr:nth-child(1)') }
38
37
 
39
38
  it { is_expected.to eq [
40
39
  '<th rowspan="2"></th>',
@@ -44,7 +43,7 @@ describe Daru::DataFrame, '#to_html' do
44
43
  end
45
44
 
46
45
  describe 'next row' do
47
- let(:row) { table.search('tr:nth-child(3)') }
46
+ let(:row) { table.search('thead > tr:nth-child(2)') }
48
47
 
49
48
  it { is_expected.to eq [
50
49
  '<th colspan="1">foo</th>',
@@ -62,7 +61,7 @@ describe Daru::DataFrame, '#to_html' do
62
61
 
63
62
  describe 'values' do
64
63
  subject(:values) {
65
- table.search('tr')[2..-1]
64
+ table.search('tr')[1..-1]
66
65
  .map { |tr| tr.search('td')[1..-1].map(&:text) }
67
66
  }
68
67
  its(:count) { is_expected.to eq df.nrows }
@@ -76,21 +75,21 @@ describe Daru::DataFrame, '#to_html' do
76
75
  describe 'header' do
77
76
  subject { header }
78
77
 
79
- its(:text) { is_expected.to eq "Daru::DataFrame: test (300x3)" }
78
+ its(:text) { is_expected.to eq " Daru::DataFrame: test (300x3) " }
80
79
  end
81
80
 
82
- it 'has only 30 rows (+ 2 header rows, + 2 finishing rows)' do
83
- expect(table.search('tr').size).to eq 34
81
+ it 'has only 30 rows (+ 1 header rows, + 2 finishing rows)' do
82
+ expect(table.search('tr').size).to eq 33
84
83
  end
85
84
 
86
85
  describe '"skipped" row' do
87
- subject(:row) { table.search('tr:nth-child(33) td').map(&:text) }
86
+ subject(:row) { table.search('tr:nth-child(31) td').map(&:text) }
88
87
  its(:count) { is_expected.to eq df.ncols + 1 }
89
88
  it { is_expected.to all eq '...' }
90
89
  end
91
90
 
92
91
  describe 'last row' do
93
- subject(:row) { table.search('tr:nth-child(34) td').map(&:text) }
92
+ subject(:row) { table.search('tr:nth-child(32) td').map(&:text) }
94
93
  its(:count) { is_expected.to eq df.ncols + 1 }
95
94
  it { is_expected.to eq ['299', *df.row[-1].map(&:to_s)] }
96
95
  end
@@ -119,12 +118,11 @@ describe Daru::DataFrame, '#to_html' do
119
118
  subject { header }
120
119
 
121
120
  it { is_expected.not_to be_nil }
122
- its(['colspan']) { is_expected.to eq (df.ncols + df.index.width).to_s }
123
- its(:text) { is_expected.to eq "Daru::DataFrame: test (7x2)" }
121
+ its(:text) { is_expected.to eq " Daru::DataFrame: test (7x2) " }
124
122
  end
125
123
 
126
124
  describe 'column headers' do
127
- let(:row) { table.search('tr:nth-child(2)') }
125
+ let(:row) { table.search('thead > tr:nth-child(1)') }
128
126
  subject { splitted_row }
129
127
 
130
128
  it { is_expected.to eq [
@@ -139,7 +137,7 @@ describe Daru::DataFrame, '#to_html' do
139
137
 
140
138
  subject { splitted_row }
141
139
  describe 'first row' do
142
- let(:row) { table.search('tr:nth-child(2)') }
140
+ let(:row) { table.search('thead > tr:nth-child(1)') }
143
141
 
144
142
  it { is_expected.to eq [
145
143
  '<th colspan="2" rowspan="2"></th>',
@@ -148,7 +146,7 @@ describe Daru::DataFrame, '#to_html' do
148
146
  end
149
147
 
150
148
  describe 'next row' do
151
- let(:row) { table.search('tr:nth-child(3)') }
149
+ let(:row) { table.search('thead > tr:nth-child(2)') }
152
150
 
153
151
  it { is_expected.to eq [
154
152
  '<th colspan="1">foo</th>',
@@ -158,7 +156,7 @@ describe Daru::DataFrame, '#to_html' do
158
156
  end
159
157
 
160
158
  describe 'first row' do
161
- let(:row) { table.search('tr:nth-child(3)') }
159
+ let(:row) { table.search('tbody > tr:nth-child(1)') }
162
160
  subject { splitted_row }
163
161
 
164
162
  it { is_expected.to eq [
@@ -2,65 +2,64 @@ describe Daru::Vector, '#to_html' do
2
2
  [nil, :category].each do |type|
3
3
  let(:doc) { Nokogiri::HTML(vector.to_html) }
4
4
  subject(:table) { doc.at('table') }
5
- let(:header) { table.at('tr:first-child > th:first-child') }
6
-
5
+ let(:header) { doc.at('b') }
6
+
7
7
  context 'simple' do
8
8
  let(:vector) { Daru::Vector.new [1,nil,3],
9
9
  index: [:a, :b, :c], name: 'test', type: type }
10
10
  it { is_expected.not_to be_nil }
11
-
11
+
12
12
  describe 'header' do
13
13
  subject { header }
14
14
  it { is_expected.not_to be_nil }
15
- its(['colspan']) { is_expected.to eq '2' }
16
- its(:text) { is_expected.to eq "Daru::Vector(3)"\
17
- "#{":category" if type == :category}" }
15
+ its(:text) { is_expected.to eq " Daru::Vector(3)"\
16
+ "#{":category" if type == :category} " }
18
17
  end
19
-
18
+
20
19
  describe 'name' do
21
- subject(:name) { table.at('tr:nth-child(2) > th:nth-child(2)') }
20
+ subject(:name) { table.at('tr:nth-child(1) > th:nth-child(2)') }
22
21
  it { is_expected.not_to be_nil }
23
22
  its(:text) { is_expected.to eq 'test' }
24
-
23
+
25
24
  context 'withought name' do
26
25
  let(:vector) { Daru::Vector.new [1,nil,3], index: [:a, :b, :c], type: type }
27
-
26
+
28
27
  it { is_expected.to be_nil }
29
28
  end
30
29
  end
31
-
30
+
32
31
  describe 'index' do
33
32
  subject(:indexes) { table.search('tr > td:first-child').map(&:text) }
34
33
  its(:count) { is_expected.to eq vector.size }
35
34
  it { is_expected.to eq vector.index.to_a.map(&:to_s) }
36
35
  end
37
-
36
+
38
37
  describe 'values' do
39
38
  subject(:indexes) { table.search('tr > td:last-child').map(&:text) }
40
39
  its(:count) { is_expected.to eq vector.size }
41
40
  it { is_expected.to eq vector.to_a.map(&:to_s) }
42
41
  end
43
42
  end
44
-
43
+
45
44
  context 'large vector' do
46
45
  subject(:vector) { Daru::Vector.new [1,2,3] * 100, name: 'test', type: type }
47
- it 'has only 30 rows (+ 2 header rows, + 2 finishing rows)' do
48
- expect(table.search('tr').size).to eq 34
46
+ it 'has only 30 rows (+ 1 header rows, + 2 finishing rows)' do
47
+ expect(table.search('tr').size).to eq 33
49
48
  end
50
-
49
+
51
50
  describe '"skipped" row' do
52
- subject(:row) { table.search('tr:nth-child(33) td').map(&:text) }
51
+ subject(:row) { table.search('tr:nth-child(31) td').map(&:text) }
53
52
  its(:count) { is_expected.to eq 2 }
54
53
  it { is_expected.to eq ['...', '...'] }
55
54
  end
56
-
55
+
57
56
  describe 'last row' do
58
- subject(:row) { table.search('tr:nth-child(34) td').map(&:text) }
57
+ subject(:row) { table.search('tr:nth-child(32) td').map(&:text) }
59
58
  its(:count) { is_expected.to eq 2 }
60
59
  it { is_expected.to eq ['299', '3'] }
61
60
  end
62
61
  end
63
-
62
+
64
63
  context 'multi-index' do
65
64
  subject(:vector) {
66
65
  Daru::Vector.new(
@@ -78,23 +77,22 @@ describe Daru::Vector, '#to_html' do
78
77
  ]),
79
78
  )
80
79
  }
81
-
80
+
82
81
  describe 'header' do
83
82
  subject { header }
84
83
  it { is_expected.not_to be_nil }
85
- its(['colspan']) { is_expected.to eq '3' }
86
- its(:text) { is_expected.to eq "Daru::Vector(7)"\
87
- "#{":category" if type == :category}" }
84
+ its(:text) { is_expected.to eq " Daru::Vector(7)"\
85
+ "#{":category" if type == :category} " }
88
86
  end
89
-
87
+
90
88
  describe 'name row' do
91
- subject(:row) { table.at('tr:nth-child(2)').search('th') }
89
+ subject(:row) { table.at('tr:nth-child(1)').search('th') }
92
90
  its(:count) { should == 2 }
93
91
  it { expect(row.first['colspan']).to eq '2' }
94
92
  end
95
-
93
+
96
94
  describe 'first data row' do
97
- let(:row) { table.at('tr:nth-child(3)') }
95
+ let(:row) { table.at('tbody > tr:first-child') }
98
96
  subject { row.inner_html.scan(/<t[dh].+?<\/t[dh]>/) }
99
97
  it { is_expected.to eq [
100
98
  '<th rowspan="3">foo</th>',
@@ -113,7 +113,7 @@ describe Daru::DataFrame do
113
113
  end
114
114
  end
115
115
 
116
- context "#round", focus: true do
116
+ context "#round" do
117
117
  it "rounds to precision" do
118
118
  df = Daru::DataFrame.new({
119
119
  a: [1.3434,2.4332,5.6655,12.3344,32.233],
@@ -78,6 +78,24 @@ describe Daru::Vector do
78
78
  end
79
79
  end
80
80
 
81
+ context "#add" do
82
+
83
+ it "adds two vectors with nils as 0 if skipnil is true" do
84
+ expect(@with_md1.add(@with_md2, skipnil: true)).to eq(Daru::Vector.new(
85
+ [1, 7, 3, 3, 1, 7],
86
+ name: :missing,
87
+ index: [:a, :b, :c, :corona, :obi, :wan]))
88
+ end
89
+
90
+ it "adds two vectors same as :+ if skipnil is false" do
91
+ expect(@with_md1.add(@with_md2, skipnil: false)).to eq(Daru::Vector.new(
92
+ [nil, 7, nil, nil, nil, 7],
93
+ name: :missing,
94
+ index: [:a, :b, :c, :corona, :obi, :wan]))
95
+ end
96
+
97
+ end
98
+
81
99
  context "#abs" do
82
100
  it "calculates abs value" do
83
101
  @with_md1.abs
@@ -12,6 +12,128 @@ describe Daru::Vector do
12
12
  end
13
13
  end
14
14
 
15
+ let(:dv) { dv = Daru::Vector.new (["Tyrion", "Daenerys", nil, "Jon Starkgaryen"]), index: Daru::Index.new([:t, :d, :n, :j]) }
16
+
17
+ context "#max" do
18
+ it "returns max value" do
19
+ expect(dv.max).to eq("Tyrion")
20
+ end
21
+ it "returns N max values" do
22
+ expect(dv.max(2)).to eq(["Tyrion","Jon Starkgaryen"])
23
+ end
24
+ it "returns max value, sorted by comparitive block input" do
25
+ expect(dv.max { |a,b| a.size <=> b.size }).to eq("Jon Starkgaryen")
26
+ end
27
+ it "returns N max values, sorted by comparitive block input" do
28
+ expect(dv.max(2) {|a,b| a.size <=> b.size}).to eq(["Jon Starkgaryen","Daenerys"])
29
+ end
30
+ end
31
+
32
+ context "#max_by" do
33
+ it "raises error without object block" do
34
+ expect { dv.max_by }.to raise_error(ArgumentError)
35
+ end
36
+ it "raises error without object block when N is given" do
37
+ expect { dv.max_by(2) }.to raise_error(ArgumentError)
38
+ end
39
+ it "returns max value, sorted by object block input" do
40
+ expect(dv.max_by { |x| x.size }).to eq("Jon Starkgaryen")
41
+ end
42
+ it "returns N max values, sorted by object block input" do
43
+ expect(dv.max_by(2) {|x| x.size }).to eq(["Jon Starkgaryen","Daenerys"])
44
+ end
45
+ end
46
+
47
+ context "#index_of_max" do
48
+ it "returns index_of_max value" do
49
+ expect(dv.index_of_max).to eq(:t)
50
+ end
51
+ it "returns N index_of_max values" do
52
+ expect(dv.index_of_max(2)).to eq([:t, :j])
53
+ end
54
+ it "returns index_of_max value, sorted by comparitive block input" do
55
+ expect(dv.index_of_max { |a,b| a.size <=> b.size }).to eq(:j)
56
+ end
57
+ it "returns N index_of_max values, sorted by comparitive block input" do
58
+ expect(dv.index_of_max(2) {|a,b| a.size <=> b.size}).to eq([:j, :d])
59
+ end
60
+ end
61
+
62
+ context "#index_of_max_by" do
63
+ it "raises error without object block" do
64
+ expect { dv.index_of_max_by }.to raise_error(ArgumentError)
65
+ end
66
+ it "raises error without object block when N is given" do
67
+ expect { dv.index_of_max_by(2) }.to raise_error(ArgumentError)
68
+ end
69
+ it "returns index_of_max value, sorted by object block input" do
70
+ expect(dv.index_of_max_by { |x| x.size }).to eq(:j)
71
+ end
72
+ it "returns N index_of_max values, sorted by object block input" do
73
+ expect(dv.index_of_max_by(2) {|x| x.size }).to eq([:j, :d])
74
+ end
75
+ end
76
+
77
+ context "#min" do
78
+ it "returns min value" do
79
+ expect(dv.min).to eq("Daenerys")
80
+ end
81
+ it "returns N min values" do
82
+ expect(dv.min(2)).to eq(["Daenerys","Jon Starkgaryen"])
83
+ end
84
+ it "returns min value, sorted by comparitive block input" do
85
+ expect(dv.min { |a,b| a.size <=> b.size }).to eq("Tyrion")
86
+ end
87
+ it "returns N min values, sorted by comparitive block input" do
88
+ expect(dv.min(2) {|a,b| a.size <=> b.size}).to eq(["Tyrion","Daenerys"])
89
+ end
90
+ end
91
+
92
+ context "#min_by" do
93
+ it "raises error without object block" do
94
+ expect { dv.min_by }.to raise_error(ArgumentError)
95
+ end
96
+ it "raises error without object block when N is given" do
97
+ expect { dv.min_by(2) }.to raise_error(ArgumentError)
98
+ end
99
+ it "returns min value, sorted by object block input" do
100
+ expect(dv.min_by { |x| x.size }).to eq("Tyrion")
101
+ end
102
+ it "returns N min values, sorted by object block input" do
103
+ expect(dv.min_by(2) {|x| x.size }).to eq(["Tyrion","Daenerys"])
104
+ end
105
+ end
106
+
107
+ context "#index_of_min" do
108
+ it "returns index of min value" do
109
+ expect(dv.index_of_min).to eq(:d)
110
+ end
111
+ it "returns N index of min values" do
112
+ expect(dv.index_of_min(2)).to eq([:d, :j])
113
+ end
114
+ it "returns index of min value, sorted by comparitive block input" do
115
+ expect(dv.index_of_min { |a,b| a.size <=> b.size }).to eq(:t)
116
+ end
117
+ it "returns N index of min values, sorted by comparitive block input" do
118
+ expect(dv.index_of_min(2) {|a,b| a.size <=> b.size}).to eq([:t, :d])
119
+ end
120
+ end
121
+
122
+ context "#index_of_min_by" do
123
+ it "raises error without object block" do
124
+ expect { dv.index_of_min_by }.to raise_error(ArgumentError)
125
+ end
126
+ it "raises error without object block when N is given" do
127
+ expect { dv.index_of_min_by(2) }.to raise_error(ArgumentError)
128
+ end
129
+ it "returns index of min value, sorted by object block input" do
130
+ expect(dv.index_of_min_by { |x| x.size }).to eq(:t)
131
+ end
132
+ it "returns N index of min values, sorted by object block input" do
133
+ expect(dv.index_of_min_by(2) {|x| x.size }).to eq([:t, :d])
134
+ end
135
+ end
136
+
15
137
  context "#sum_of_squares" do
16
138
  it "calcs sum of squares, omits nil values" do
17
139
  v = Daru::Vector.new [1,2,3,4,5,6], dtype: dtype
@@ -189,7 +311,11 @@ describe Daru::Vector do
189
311
 
190
312
  context "#proportions" do
191
313
  it "calculates proportions" do
192
- @dv.proportions
314
+ actual_proportions = {
315
+ array: {323=>0.1,11=>0.1,555=>0.1,666=>0.2,234=>0.1,21=>0.1,343=>0.1,1=>0.1,2=>0.1},
316
+ gsl: {323.0=>0.1, 11.0=>0.1, 555.0=>0.1, 666.0=>0.2, 234.0=>0.1, 21.0=>0.1, 343.0=>0.1, 1.0=>0.1, 2.0=>0.1}
317
+ }
318
+ expect(@dv.proportions).to eq(actual_proportions[dtype])
193
319
  end
194
320
  end
195
321
 
@@ -583,24 +709,36 @@ describe Daru::Vector do
583
709
  end
584
710
  end
585
711
 
586
- context "#macd" do
587
- it "calculates moving average convergence divergence" do
588
- # MACD uses a lot more data than the other ones, so we need a bigger vector
589
- data = Daru::Vector.new(
590
- File.readlines("spec/fixtures/stock_data.csv").map(&:to_f))
712
+ RSpec.shared_examples 'correct macd' do |*args|
713
+ let(:source) { Daru::DataFrame.from_csv('spec/fixtures/macd_data.csv') }
591
714
 
592
- macd, signal = data.macd
715
+ # skip initial records during compare as ema is sensitive to
716
+ # period used.
717
+ # http://ta-lib.org/d_api/ta_setunstableperiod.html
718
+ let(:stability_offset) { 90 }
719
+ let(:delta) { 0.001 }
720
+ let(:desc) { args.empty? ? '12_26_9' : args.join('_') }
593
721
 
594
- # check the MACD
595
- expect(macd[-1]).to be_within(1e-6).of(3.12e-4)
596
- expect(macd[-10]).to be_within(1e-4).of(-1.07e-2)
597
- expect(macd[-20]).to be_within(1e-5).of(-5.65e-3)
722
+ subject { source['price'].macd(*args) }
598
723
 
599
- # check the signal
600
- expect(signal[-1]).to be_within(1e-5).of(-0.00628)
601
- expect(signal[-10]).to be_within(1e-5).of(-0.00971)
602
- expect(signal[-20]).to be_within(1e-5).of(-0.00338)
724
+ %w[ macd macdsig macdhist ].each_with_index do |field, i|
725
+ it do
726
+ act = subject[i][stability_offset..-1]
727
+ exp = source["#{field}_#{desc}"][stability_offset..-1]
728
+ expect(act).to be_all_within(delta).of(exp)
729
+ end
730
+ end
731
+ end
732
+
733
+ describe '#macd' do
734
+ context 'by default' do
735
+ it_should_behave_like 'correct macd'
603
736
  end
737
+
738
+ context 'custom values for fast, slow, signal' do
739
+ it_should_behave_like 'correct macd', 6, 13, 4
740
+ end
741
+
604
742
  end
605
743
 
606
744
  context "#cumsum" do