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
@@ -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