daru 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rubocop.yml +99 -0
  4. data/.rubocop_todo.yml +44 -0
  5. data/.travis.yml +3 -1
  6. data/CONTRIBUTING.md +5 -1
  7. data/History.md +43 -0
  8. data/README.md +3 -4
  9. data/benchmarks/duplicating.rb +45 -0
  10. data/benchmarks/group_by.rb +7 -7
  11. data/benchmarks/joining.rb +52 -0
  12. data/benchmarks/sorting.rb +9 -2
  13. data/benchmarks/statistics.rb +39 -0
  14. data/daru.gemspec +4 -4
  15. data/lib/daru.rb +9 -9
  16. data/lib/daru/accessors/array_wrapper.rb +15 -11
  17. data/lib/daru/accessors/dataframe_by_row.rb +1 -1
  18. data/lib/daru/accessors/gsl_wrapper.rb +30 -19
  19. data/lib/daru/accessors/mdarray_wrapper.rb +1 -3
  20. data/lib/daru/accessors/nmatrix_wrapper.rb +15 -15
  21. data/lib/daru/core/group_by.rb +69 -16
  22. data/lib/daru/core/merge.rb +135 -151
  23. data/lib/daru/core/query.rb +9 -30
  24. data/lib/daru/dataframe.rb +476 -439
  25. data/lib/daru/date_time/index.rb +150 -137
  26. data/lib/daru/date_time/offsets.rb +45 -41
  27. data/lib/daru/extensions/rserve.rb +4 -4
  28. data/lib/daru/index.rb +88 -64
  29. data/lib/daru/io/io.rb +33 -34
  30. data/lib/daru/io/sql_data_source.rb +11 -11
  31. data/lib/daru/maths/arithmetic/dataframe.rb +19 -19
  32. data/lib/daru/maths/arithmetic/vector.rb +9 -14
  33. data/lib/daru/maths/statistics/dataframe.rb +89 -61
  34. data/lib/daru/maths/statistics/vector.rb +226 -97
  35. data/lib/daru/monkeys.rb +23 -30
  36. data/lib/daru/plotting/dataframe.rb +27 -28
  37. data/lib/daru/plotting/vector.rb +12 -13
  38. data/lib/daru/vector.rb +221 -330
  39. data/lib/daru/version.rb +2 -2
  40. data/spec/core/group_by_spec.rb +16 -0
  41. data/spec/core/merge_spec.rb +30 -14
  42. data/spec/dataframe_spec.rb +268 -14
  43. data/spec/index_spec.rb +23 -5
  44. data/spec/io/io_spec.rb +37 -16
  45. data/spec/math/statistics/dataframe_spec.rb +40 -8
  46. data/spec/math/statistics/vector_spec.rb +135 -10
  47. data/spec/monkeys_spec.rb +3 -3
  48. data/spec/vector_spec.rb +157 -25
  49. metadata +41 -21
@@ -37,6 +37,16 @@ describe Daru::Index do
37
37
  expect(idx.to_a).to eq(['speaker', 'mic', 'guitar', 'amp'])
38
38
  end
39
39
 
40
+ it "creates an Index from Range" do
41
+ idx = Daru::Index.new 1..5
42
+
43
+ expect(idx).to eq(Daru::Index.new [1, 2, 3, 4, 5])
44
+ end
45
+
46
+ it "raises ArgumentError on invalid input type" do
47
+ expect { Daru::Index.new 'foo' }.to raise_error ArgumentError
48
+ end
49
+
40
50
  it "accepts all sorts of objects for Indexing" do
41
51
  idx = Daru::Index.new [:a, 'a', :hello, '23', 23]
42
52
 
@@ -76,7 +86,7 @@ describe Daru::Index do
76
86
  context "#[]" do
77
87
  before do
78
88
  @id = Daru::Index.new [:one, :two, :three, :four, :five, :six, :seven]
79
- @mixed_id = Daru::Index.new ['a','b','c',:d,:a,0,3,5]
89
+ @mixed_id = Daru::Index.new ['a','b','c',:d,:a,8,3,5]
80
90
  end
81
91
 
82
92
  it "works with ranges" do
@@ -88,12 +98,12 @@ describe Daru::Index do
88
98
  expect(@mixed_id[0..2]).to eq(Daru::Index.new(['a','b','c']))
89
99
 
90
100
  # If atleast one is a number then refer to actual indexing
91
- expect(@mixed_id.slice('b',0)).to eq(Daru::Index.new(['b','c',:d,:a,0]))
101
+ expect(@mixed_id.slice('b',8)).to eq(Daru::Index.new(['b','c',:d,:a,8]))
92
102
  end
93
103
 
94
104
  it "returns multiple keys if specified multiple indices" do
95
- expect(@id[0,1,3,4]).to eq(Daru::Index.new([0,1,3,4]))
96
- expect(@mixed_id[0,5,3,2]).to eq(Daru::Index.new([5, 7, 6, 2]))
105
+ expect(@id[0,1,3,4]).to eq(Daru::Index.new([:one, :two, :four, :five]))
106
+ expect(@mixed_id[0,5,3,2]).to eq(Daru::Index.new(['a', 8, :d, 'c']))
97
107
  end
98
108
 
99
109
  it "returns correct index position for non-numeric index" do
@@ -102,7 +112,7 @@ describe Daru::Index do
102
112
  end
103
113
 
104
114
  it "returns correct index position for mixed index" do
105
- expect(@mixed_id[0]).to eq(5)
115
+ expect(@mixed_id[8]).to eq(5)
106
116
  expect(@mixed_id['c']).to eq(2)
107
117
  end
108
118
  end
@@ -204,6 +214,14 @@ describe Daru::MultiIndex do
204
214
  ]))
205
215
  end
206
216
 
217
+ it "raises error when specifying invalid index" do
218
+ expect { @multi_mi[:a, :three] }.to raise_error IndexError
219
+ expect { @multi_mi[:a, :one, :xyz] }.to raise_error IndexError
220
+ expect { @multi_mi[:x] }.to raise_error IndexError
221
+ expect { @multi_mi[:x, :one] }.to raise_error IndexError
222
+ expect { @multi_mi[:x, :one, :bar] }.to raise_error IndexError
223
+ end
224
+
207
225
  it "works with numerical first levels" do
208
226
  mi = Daru::MultiIndex.from_tuples([
209
227
  [2000, 'M'],
@@ -4,7 +4,7 @@ describe Daru::IO do
4
4
  describe Daru::DataFrame do
5
5
  context ".from_csv" do
6
6
  it "loads from a CSV file" do
7
- df = Daru::DataFrame.from_csv('spec/fixtures/matrix_test.csv',
7
+ df = Daru::DataFrame.from_csv('spec/fixtures/matrix_test.csv',
8
8
  col_sep: ' ', headers: true)
9
9
 
10
10
  df.vectors = [:image_resolution, :mls, :true_transform].to_index
@@ -15,7 +15,7 @@ describe Daru::IO do
15
15
 
16
16
  it "works properly for repeated headers" do
17
17
  df = Daru::DataFrame.from_csv('spec/fixtures/repeated_fields.csv',header_converters: :symbol)
18
- expect(df.vectors.to_a).to eq(['a1', 'age_1', 'age_2', 'city', 'id', 'name_1', 'name_2'])
18
+ expect(df.vectors.to_a).to eq(["id", "name_1", "age_1", "city", "a1", "name_2", "age_2"])
19
19
 
20
20
  age = Daru::Vector.new([3, 4, 5, 6, nil, 8])
21
21
  expect(df['age_2']).to eq(age)
@@ -29,20 +29,41 @@ describe Daru::IO do
29
29
  expect(y_ds).to be_within(0.001).of(y_expected)
30
30
  end
31
31
  end
32
+
33
+ it "follows the order of columns given in CSV" do
34
+ df = Daru::DataFrame.from_csv 'spec/fixtures/sales-funnel.csv'
35
+ expect(df.vectors.to_a).to eq(%W[Account Name Rep Manager Product Quantity Price Status])
36
+ end
32
37
  end
33
38
 
34
39
  context "#write_csv" do
35
- it "writes DataFrame to a CSV file" do
36
- df = Daru::DataFrame.new({
37
- 'a' => [1,2,3,4,5],
40
+ before do
41
+ @df = Daru::DataFrame.new({
42
+ 'a' => [1,2,3,4,5],
38
43
  'b' => [11,22,33,44,55],
39
44
  'c' => ['a', 'g', 4, 5,'addadf'],
40
45
  'd' => [nil, 23, 4,'a','ff']})
41
- t = Tempfile.new('data.csv')
42
- df.write_csv t.path
46
+ @tempfile = Tempfile.new('data.csv')
43
47
 
44
- expect(Daru::DataFrame.from_csv(t.path)).to eq(df)
45
48
  end
49
+
50
+ it "writes DataFrame to a CSV file" do
51
+ @df.write_csv @tempfile.path
52
+ expect(Daru::DataFrame.from_csv(@tempfile.path)).to eq(@df)
53
+ end
54
+
55
+ it "will write headers unless headers=false" do
56
+ @df.write_csv @tempfile.path
57
+ first_line = File.open(@tempfile.path, &:readline).chomp.split(',', -1)
58
+ expect(first_line).to eq @df.vectors.to_a
59
+ end
60
+
61
+ it "will not write headers when headers=false" do
62
+ @df.write_csv @tempfile.path, { headers: false }
63
+ first_line = File.open(@tempfile.path, &:readline).chomp.split(',', -1)
64
+ expect(first_line).to eq @df.head(1).map { |v| (v.first || '').to_s }
65
+ end
66
+
46
67
  end
47
68
 
48
69
  context ".from_excel" do
@@ -52,8 +73,8 @@ describe Daru::IO do
52
73
  age = Daru::Vector.new( [20, 23, 25, nil, 5.5, nil])
53
74
  city = Daru::Vector.new(['New York', 'London', 'London', 'Paris', 'Tome', nil])
54
75
  a1 = Daru::Vector.new(['a,b', 'b,c', 'a', nil, 'a,b,c', nil])
55
- @expected = Daru::DataFrame.new({
56
- :id => id, :name => name, :age => age, :city => city, :a1 => a1
76
+ @expected = Daru::DataFrame.new({
77
+ :id => id, :name => name, :age => age, :city => city, :a1 => a1
57
78
  }, order: [:id, :name, :age, :city, :a1])
58
79
  end
59
80
 
@@ -190,8 +211,8 @@ describe Daru::IO do
190
211
  df = Daru::DataFrame.new JSON.parse(json)
191
212
 
192
213
  expect(df.vectors).to eq([
193
- 'name', 'nativeName', 'tld', 'cca2', 'ccn3', 'cca3', 'currency', 'callingCode',
194
- 'capital', 'altSpellings', 'relevance', 'region', 'subregion', 'language',
214
+ 'name', 'nativeName', 'tld', 'cca2', 'ccn3', 'cca3', 'currency', 'callingCode',
215
+ 'capital', 'altSpellings', 'relevance', 'region', 'subregion', 'language',
195
216
  'languageCodes', 'translations', 'latlng', 'demonym', 'borders', 'area'].to_index)
196
217
 
197
218
  expect(df.row[0]['name']).to eq("Afghanistan")
@@ -208,9 +229,9 @@ describe Daru::IO do
208
229
 
209
230
  context "#save" do
210
231
  before do
211
- @data_frame = Daru::DataFrame.new({b: [11,12,13,14,15], a: [1,2,3,4,5],
212
- c: [11,22,33,44,55]},
213
- order: [:a, :b, :c],
232
+ @data_frame = Daru::DataFrame.new({b: [11,12,13,14,15], a: [1,2,3,4,5],
233
+ c: [11,22,33,44,55]},
234
+ order: [:a, :b, :c],
214
235
  index: [:one, :two, :three, :four, :five])
215
236
  end
216
237
 
@@ -235,7 +256,7 @@ describe Daru::IO do
235
256
  ALL_DTYPES.each do |dtype|
236
257
  it "saves to a file and returns the same Vector of type #{dtype}" do
237
258
  vector = Daru::Vector.new(
238
- [5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, 11, -99, -99],
259
+ [5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, 11, -99, -99],
239
260
  dtype: dtype)
240
261
  outfile = Tempfile.new('vector.vec')
241
262
  vector.save(outfile.path)
@@ -24,28 +24,40 @@ describe Daru::DataFrame do
24
24
  end
25
25
  end
26
26
 
27
+ context "#variance_sample" do
28
+ it "calculates variance of single level numeric only vectors and returns values in a Vector" do
29
+ expect(@df.variance_sample).to eq(Daru::Vector.new([4.0, 16.0, 400.0], index: [:d, :e, :f]))
30
+ end
31
+ end
32
+
27
33
  context "#std" do
28
- it "calculates standard deviation of single leavel numeric only vectors and returns values in a Vector" do
34
+ it "calculates standard deviation of single level numeric only vectors and returns values in a Vector" do
29
35
  expect(@df.std).to eq(Daru::Vector.new([2, 4, 20], index: [:d, :e, :f]))
30
36
  end
31
37
  end
32
38
 
33
39
  context "#sum" do
34
40
  it "calculates sum of single level numeric only vectors and returns values in a Vector" do
35
- # TODO - write tests
41
+ expect(@df.sum).to eq(Daru::Vector.new([33, 66, 330], index: [:d, :e, :f]))
36
42
  end
37
43
  end
38
44
 
39
45
  context "#count" do
40
- # TODO
46
+ it "counts number of non-nil single level numeric only vectors and returns values in a Vector" do
47
+ expect(@df.count).to eq(Daru::Vector.new([9, 9, 9], index: [:d, :e, :f]))
48
+ end
41
49
  end
42
50
 
43
51
  context "#mode" do
44
- # TODO
52
+ it "calculates mode of single level numeric only vectors and returns values in a Vector" do
53
+ expect(@df.mode).to eq(Daru::Vector.new([2, 4, 20], index: [:d, :e, :f]))
54
+ end
45
55
  end
46
56
 
47
57
  context "#median" do
48
- # TODO
58
+ it "calculates median of single level numeric only vectors and returns values in a Vector" do
59
+ expect(@df.median).to eq(Daru::Vector.new([3, 6, 30], index: [:d, :e, :f]))
60
+ end
49
61
  end
50
62
 
51
63
  context "#max" do
@@ -61,11 +73,21 @@ describe Daru::DataFrame do
61
73
  end
62
74
 
63
75
  context "#min" do
64
- # TODO
76
+ it "calculates mininum of single level numeric only vectors and returns values in a Vector" do
77
+ expect(@df.min).to eq(Daru::Vector.new([1, 2, 10], index: [:d, :e, :f]))
78
+ end
79
+ end
80
+
81
+ context "#range" do
82
+ it "calculates range of single level numeric only vectors and returns values in a Vector" do
83
+ expect(@df.range).to eq(Daru::Vector.new([6, 12, 60], index: [:d, :e, :f]))
84
+ end
65
85
  end
66
86
 
67
87
  context "#product" do
68
- # TODO
88
+ it "calculates product of single level numeric only vectors and returns values in a Vector" do
89
+ expect(@df.product).to eq(Daru::Vector.new([30240, 15482880, 30240000000000], index: [:d, :e, :f]))
90
+ end
69
91
  end
70
92
 
71
93
  context "#describe" do
@@ -79,6 +101,16 @@ describe Daru::DataFrame do
79
101
  end
80
102
  end
81
103
 
104
+ context "percent_change" do
105
+ it "calculates percent change of numeric vectors" do
106
+ expect(@df.percent_change.round(2)).to eq(Daru::DataFrame.new({
107
+ d: [nil, 1.0, 0.0, 0.5, 0.0, 0.33, 0.25, 0.2, 0.17],
108
+ e: [nil, 1.0, 0.0, 0.5, 0.0, 0.33, 0.25, 0.2, 0.17],
109
+ f: [nil, 1.0, 0.0, 0.5, 0.0, 0.33, 0.25, 0.2, 0.17] }
110
+ ))
111
+ end
112
+ end
113
+
82
114
  context "#cov" do
83
115
  it "calculates the variance covariance of the numeric vectors of DataFrame" do
84
116
  expect(@df.cov).to eq(Daru::DataFrame.new({
@@ -145,4 +177,4 @@ describe Daru::DataFrame do
145
177
  @df.standardize
146
178
  end
147
179
  end
148
- end
180
+ end
@@ -6,12 +6,15 @@ describe Daru::Vector do
6
6
  before do
7
7
  @dv = Daru::Vector.new [323, 11, 555, 666, 234, 21, 666, 343, 1, 2], dtype: dtype
8
8
  @dv_with_nils = Daru::Vector.new [323, 11, 555, nil, 666, 234, 21, 666, 343, nil, 1, 2]
9
+ @dv_with_missing = Daru::Vector.new [1, 2, 3, 3], missing_values: [3], dtype: dtype
10
+ @dv_with_all_missing = Daru::Vector.new [3, 3], missing_values: [3], dtype: dtype
9
11
  end
10
12
 
11
13
  context "#mean" do
12
14
  it "calculates mean" do
13
15
  expect(@dv.mean).to eq(282.2)
14
- expect(@dv_with_nils.mean).to eq(282.2)
16
+ expect(@dv_with_missing.mean).to eq(1.5)
17
+ expect(@dv_with_all_missing.mean).to eq(nil)
15
18
  end
16
19
  end
17
20
 
@@ -46,6 +49,22 @@ describe Daru::Vector do
46
49
  end
47
50
  end
48
51
 
52
+ context "#covariance_sample" do
53
+ it "calculates sample covariance" do
54
+ @dv_1 = Daru::Vector.new [323, 11, 555, 666, 234, 21, 666, 343, 1, 2]
55
+ @dv_2 = Daru::Vector.new [123, 22, 444, 555, 324, 21, 666, 434, 5, 8]
56
+ expect(@dv_1.covariance @dv_2).to be_within(0.00001).of(65603.62222)
57
+ end
58
+ end
59
+
60
+ context "#covariance_population" do
61
+ it "calculates population covariance" do
62
+ @dv_1 = Daru::Vector.new [323, 11, 555, 666, 234, 21, 666, 343, 1, 2]
63
+ @dv_2 = Daru::Vector.new [123, 22, 444, 555, 324, 21, 666, 434, 5, 8]
64
+ expect(@dv_1.covariance_population @dv_2).to be_within(0.01).of(59043.26)
65
+ end
66
+ end
67
+
49
68
  context "#sum_of_squared_deviation" do
50
69
  it "calculates sum of squared deviation" do
51
70
  expect(@dv.sum_of_squared_deviation).to eq(676069.6)
@@ -60,20 +79,44 @@ describe Daru::Vector do
60
79
 
61
80
  context "#max" do
62
81
  it "returns the max value" do
63
- @dv.max
82
+ expect(@dv.max).to eq(666)
64
83
  end
84
+
85
+ it "returns the max value without considering values set as missing" do
86
+ expect(@dv_with_missing.max).to eq(2)
87
+ end
88
+
89
+ it "returns nil when all values are set missing" do
90
+ expect(@dv_with_all_missing.max).to eq(nil)
91
+ end
65
92
  end
66
93
 
67
94
  context "#min" do
68
95
  it "returns the min value" do
69
- @dv.min
96
+ expect(@dv.min).to eq(1)
97
+ end
98
+
99
+ it "returns the min value without considering values set as missing" do
100
+ expect(@dv_with_missing.min).to eq(1)
70
101
  end
102
+
103
+ it "returns nil when all values are set missing" do
104
+ expect(@dv_with_all_missing.min).to eq(nil)
105
+ end
71
106
  end
72
107
 
73
108
  context "#sum" do
74
109
  it "returns the sum" do
75
- @dv.sum
110
+ expect(@dv.sum).to eq(2822)
111
+ end
112
+
113
+ it "returns the sum without considering values set as missing" do
114
+ expect(@dv_with_missing.sum).to eq(3)
76
115
  end
116
+
117
+ it "returns nil when all values are set missing" do
118
+ expect(@dv_with_all_missing.sum).to eq(nil)
119
+ end
77
120
  end
78
121
 
79
122
  context "#product" do
@@ -81,6 +124,14 @@ describe Daru::Vector do
81
124
  v = Daru::Vector.new [1, 2, 3, 4, 5], dtype: dtype
82
125
  expect(v.product).to eq(120)
83
126
  end
127
+
128
+ it "returns the product without considering values set as missing" do
129
+ expect(@dv_with_missing.product).to eq(2)
130
+ end
131
+
132
+ it "returns nil when all values are set missing" do
133
+ expect(@dv_with_all_missing.product).to eq(nil)
134
+ end
84
135
  end
85
136
 
86
137
  context "#median" do
@@ -91,7 +142,17 @@ describe Daru::Vector do
91
142
 
92
143
  context "#mode" do
93
144
  it "returns the mode" do
94
- @dv.mode
145
+ mode_test_example = Daru::Vector.new [1,2,3,2,4,4,4,4], dtype: dtype
146
+ expect(mode_test_example.mode).to eq(4)
147
+ end
148
+ end
149
+
150
+ context "#describe" do
151
+ it "generates count, mean, std, min and max of vectors in one shot" do
152
+ expect(@dv.describe.round(2)).to eq(Daru::Vector.new([10.00, 282.20, 274.08, 1.00, 666.00],
153
+ index: [:count, :mean, :std, :min, :max],
154
+ name: :statistics
155
+ ))
95
156
  end
96
157
  end
97
158
 
@@ -373,6 +434,20 @@ describe Daru::Vector do
373
434
  expect(acf[3]).to be_within(0.001) .of(0.486)
374
435
  end
375
436
  end
437
+
438
+ context "#percent_change" do
439
+ it "calculates percent change" do
440
+ vector = Daru::Vector.new([4,6,6,8,10],index: ['a','f','t','i','k'])
441
+ expect(vector.percent_change).to eq(
442
+ Daru::Vector.new([nil, 0.5, 0.0, 0.3333333333333333, 0.25], index: ['a','f','t','i','k']))
443
+ end
444
+
445
+ it "tests for numerical vectors with nils" do
446
+ vector2 = Daru::Vector.new([nil,6,nil,8,10],index: ['a','f','t','i','k'])
447
+ expect(vector2.percent_change).to eq(
448
+ Daru::Vector.new([nil, nil, nil, 0.3333333333333333, 0.25], index: ['a','f','t','i','k']))
449
+ end
450
+ end
376
451
 
377
452
  context "#diff" do
378
453
  it "performs the difference of the series" do
@@ -463,12 +538,12 @@ describe Daru::Vector do
463
538
  expect(ema10[-5]) .to be_within(0.00001).of( 17.19187)
464
539
  expect(ema10[-10]).to be_within(0.00001).of( 17.54918)
465
540
 
466
- # test with a different lookback period
541
+ # test with a different loopback period
467
542
  ema5 = @shares.ema 5
468
543
 
469
- expect(ema5[-1]) .to be_within( 0.0001).of(16.71299)
470
- expect(ema5[-10]).to be_within( 0.0001).of(17.49079)
471
- expect(ema5[-15]).to be_within( 0.0001).of(17.70067)
544
+ expect(ema5[-1]) .to be_within( 0.00001).of(16.71299)
545
+ expect(ema5[-10]).to be_within( 0.00001).of(17.49079)
546
+ expect(ema5[-15]).to be_within( 0.00001).of(17.70067)
472
547
 
473
548
  # test with a different smoother
474
549
  ema_w = @shares.ema 10, true
@@ -479,6 +554,56 @@ describe Daru::Vector do
479
554
  end
480
555
  end
481
556
 
557
+ context "#emv" do
558
+ it "calculates exponential moving variance" do
559
+ # test default
560
+ emv10 = @shares.emv
561
+
562
+ expect(emv10[-1]) .to be_within(0.00001).of(0.14441)
563
+ expect(emv10[-5]) .to be_within(0.00001).of(0.10797)
564
+ expect(emv10[-10]).to be_within(0.00001).of(0.03979)
565
+
566
+ # test with a different loopback period
567
+ emv5 = @shares.emv 5
568
+
569
+ expect(emv5[-1]) .to be_within(0.00001).of(0.05172)
570
+ expect(emv5[-10]).to be_within(0.00001).of(0.01736)
571
+ expect(emv5[-15]).to be_within(0.00001).of(0.04410)
572
+
573
+ # test with a different smoother
574
+ emv_w = @shares.emv 10, true
575
+
576
+ expect(emv_w[-1]) .to be_within(0.00001).of(0.20318)
577
+ expect(emv_w[-5]) .to be_within(0.00001).of(0.11319)
578
+ expect(emv_w[-10]).to be_within(0.00001).of(0.04289)
579
+ end
580
+ end
581
+
582
+ context "#emsd" do
583
+ it "calculates exponential moving standard deviation" do
584
+ # test default
585
+ emsd10 = @shares.emsd
586
+
587
+ expect(emsd10[-1]) .to be_within(0.00001).of(0.38002)
588
+ expect(emsd10[-5]) .to be_within(0.00001).of(0.32859)
589
+ expect(emsd10[-10]).to be_within(0.00001).of(0.19947)
590
+
591
+ # test with a different loopback period
592
+ emsd5 = @shares.emsd 5
593
+
594
+ expect(emsd5[-1]) .to be_within(0.00001).of(0.22742)
595
+ expect(emsd5[-10]).to be_within(0.00001).of(0.13174)
596
+ expect(emsd5[-15]).to be_within(0.00001).of(0.21000)
597
+
598
+ # test with a different smoother
599
+ emsd_w = @shares.emsd 10, true
600
+
601
+ expect(emsd_w[-1]) .to be_within(0.00001).of(0.45076)
602
+ expect(emsd_w[-5]) .to be_within(0.00001).of(0.33644)
603
+ expect(emsd_w[-10]).to be_within(0.00001).of(0.20710)
604
+ end
605
+ end
606
+
482
607
  context "#macd" do
483
608
  it "calculates moving average convergence divergence" do
484
609
  # MACD uses a lot more data than the other ones, so we need a bigger vector
@@ -506,4 +631,4 @@ describe Daru::Vector do
506
631
  Daru::Vector.new([1,3,6,10,15,21,28,36,45,55]))
507
632
  end
508
633
  end
509
- end
634
+ end