quandl_operation 0.4.1 → 0.4.2.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 72987e8e8aaab8342e2311e9205cb7a3a933510e
4
+ data.tar.gz: b212ef085932a197ba784ef3e06a011bf4c4b49e
5
+ SHA512:
6
+ metadata.gz: 415c5c15bd5232bb50866dcd9287fe2476f5d586e743e52d328c688cfd94b80c879019f840063f3c92a88dd89a8dfadc7885a44d7fabca1b5275b22b1f8694a6
7
+ data.tar.gz: f5a428b56387e30ba758eb0e28a8dc59dd967ef3141f7499d38a5deea63e1a2d76fb459d2d5d8c410b2b1658d45df5308f8e5b9ca8d5c7f5c5010dce2a9dcca3
@@ -0,0 +1,19 @@
1
+ AllCops:
2
+ DisplayCopNames: true
3
+ DisplayStyleGuide: true
4
+ LineLength:
5
+ Max: 120
6
+ Style/Documentation:
7
+ Enabled: false
8
+ Metrics/MethodLength:
9
+ Max: 40
10
+ Metrics/PerceivedComplexity:
11
+ Enabled: false
12
+ Metrics/CyclomaticComplexity:
13
+ Enabled: false
14
+ Metrics/AbcSize:
15
+ Enabled: false
16
+ Metrics/MethodLength:
17
+ Max: 100
18
+ Metrics/ClassLength:
19
+ Max: 180
data/Gemfile CHANGED
@@ -1,2 +1,2 @@
1
- source "https://rubygems.org"
1
+ source 'https://rubygems.org'
2
2
  gemspec
data/Guardfile CHANGED
@@ -1,8 +1,7 @@
1
1
  group :specs do
2
2
  guard :rspec, cmd: 'bundle exec rspec --fail-fast -f doc --color' do
3
- watch(%r{^spec/.+_spec\.rb$})
4
- watch(%r{^(lib/.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
5
- watch('spec/spec_helper.rb') { 'spec' }
3
+ watch(%r{^spec/.+_spec\.rb$})
4
+ watch(%r{^(lib/.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
5
+ watch('spec/spec_helper.rb') { 'spec' }
6
6
  end
7
7
  end
8
-
data/Rakefile CHANGED
@@ -1,13 +1,13 @@
1
- require "bundler"
2
- require "rake"
3
- require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
1
+ require 'bundler'
2
+ require 'rake'
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
5
 
6
- task :default => :spec
6
+ task default: :spec
7
7
 
8
- desc "Run all specs"
8
+ desc 'Run all specs'
9
9
  RSpec::Core::RakeTask.new(:spec) do |task|
10
- task.pattern = "spec/**/*_spec.rb"
10
+ task.pattern = 'spec/**/*_spec.rb'
11
11
  end
12
12
 
13
13
  require 'quandl/utility/rake_tasks'
@@ -16,5 +16,5 @@ Quandl::Utility::Tasks.configure do |c|
16
16
  c.version_path = 'VERSION'
17
17
  c.changelog_path = 'UPGRADE.md'
18
18
  c.tag_prefix = 'v'
19
- c.changelog_matching = ['^QUGC','^WIKI']
19
+ c.changelog_matching = ['^QUGC', '^WIKI']
20
20
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.1
1
+ 0.4.2.rc1
@@ -1,12 +1,12 @@
1
1
  require 'quandl/logger'
2
2
 
3
- require "quandl/operation/version"
3
+ require 'quandl/operation/version'
4
4
 
5
5
  require 'csv'
6
6
 
7
- require "active_support"
8
- require "active_support/inflector"
9
- require "active_support/core_ext/hash"
7
+ require 'active_support'
8
+ require 'active_support/inflector'
9
+ require 'active_support/core_ext/hash'
10
10
 
11
11
  require 'quandl/operation/core_ext'
12
12
  require 'quandl/operation/collapse'
@@ -16,6 +16,6 @@ require 'quandl/operation/qdate'
16
16
  require 'quandl/operation/value'
17
17
 
18
18
  module Quandl
19
- module Operation
19
+ module Operation
20
+ end
20
21
  end
21
- end
@@ -2,126 +2,130 @@
2
2
  require 'quandl/operation/collapse/guess'
3
3
  # collapse
4
4
  module Quandl
5
- module Operation
6
-
7
- class Collapse
8
-
9
- class << self
10
-
11
- def perform(data, type)
12
- assert_valid_arguments!(data, type)
13
- # nothing to do with an empty array
14
- return data unless data.compact.present?
15
- # source order
16
- order = Sort.order?(data)
17
- # operations expect data in ascending order
18
- data = Sort.asc(data)
19
- # collapse
20
- data = collapse(data, type)
21
- # return to original order
22
- data = Sort.desc(data) if order == :desc
23
- # onwards
24
- data
25
- end
26
-
27
- def assert_valid_arguments!(data, type)
28
- raise ArgumentError, "data must be an Array. Received: #{data.class}" unless data.is_a?(Array)
29
- raise ArgumentError, "frequency must be one of #{valid_collapses}. Received: #{type}" unless valid?(type)
30
- end
31
-
32
- def valid_collapse?(type)
33
- valid?(type)
34
- end
35
-
36
- def valid?(type)
37
- valid_collapses.include?( type.try(:to_sym) )
38
- end
39
-
40
- def valid_collapses
41
- [ :daily, :weekly, :monthly, :quarterly, :annual ]
42
- end
43
-
44
- def collapse(data, frequency)
45
- return data unless valid_collapse?( frequency )
46
- # store the new collapsed data
47
- collapsed_data = {}
48
- range = find_end_of_range( data[0][0], frequency )
49
- # iterate over the data
50
- data.each do |row|
51
- # grab date and values
52
- date, value = row[0], row[1..-1]
53
- value = value.first if value.count == 1
54
- # bump to the next range if it exceeds the current one
55
- range = find_end_of_range(date, frequency) unless inside_range?(date, range)
56
- # consider the value for the next range
57
- if inside_range?(date, range) && value.present?
58
- # merge this and previous row if nils are present
59
- value = merge_row_values( value, collapsed_data[range] ) unless collapsed_data[range].nil?
60
- # assign value
61
- collapsed_data[range] = value
5
+ module Operation
6
+ class Collapse
7
+ class << self
8
+ def perform(data, type)
9
+ assert_valid_arguments!(data, type)
10
+ # nothing to do with an empty array
11
+ return data unless data.compact.present?
12
+ # source order
13
+ order = Sort.order?(data)
14
+ # operations expect data in ascending order
15
+ data = Sort.asc(data)
16
+ # collapse
17
+ data = collapse(data, type)
18
+ # return to original order
19
+ data = Sort.desc(data) if order == :desc
20
+ # onwards
21
+ data
62
22
  end
63
- end
64
- to_table(collapsed_data)
65
- end
66
-
67
- def to_table(data)
68
- data.collect do |date, values|
69
- if values.is_a?(Array)
70
- values.unshift(date)
71
- else
72
- [date, values]
23
+
24
+ def assert_valid_arguments!(data, type)
25
+ fail ArgumentError, "data must be an Array. Received: #{data.class}" unless data.is_a?(Array)
26
+ fail ArgumentError, "frequency must be one of #{valid_collapses}. Received: #{type}" unless valid?(type)
73
27
  end
74
- end
75
- end
76
-
77
- def merge_row_values(top_row, bottom_row)
78
- # merge previous values when nils are present
79
- if top_row.is_a?(Array) && top_row.include?(nil)
80
- # find nil indexes
81
- indexes = find_each_index(top_row, nil)
82
- # merge nils with previous values
83
- indexes.each{|index| top_row[index] = bottom_row[index] }
84
- end
85
- top_row
86
- end
87
-
88
- def collapses_greater_than(freq)
89
- return [] unless freq.respond_to?(:to_sym)
90
- index = valid_collapses.index(freq.to_sym)
91
- index.present? ? valid_collapses.slice( index + 1, valid_collapses.count ) : []
92
- end
93
-
94
- def collapses_greater_than_or_equal_to(freq)
95
- return [] unless freq.respond_to?(:to_sym)
96
- valid_collapses.slice( valid_collapses.index(freq.to_sym), valid_collapses.count )
97
- end
98
-
99
- def frequency?(data)
100
- Guess.frequency(data)
101
- end
102
-
103
- def inside_range?(date, range)
104
- date <= range
105
- end
106
-
107
- def find_end_of_range(date, frequency)
108
- date.end_of_frequency(frequency)
109
- end
110
-
111
- def find_each_index(array, find)
112
- found, index, q = -1, -1, []
113
- while found
114
- found = array[index+1..-1].index(find)
115
- if found
116
- index = index + found + 1
117
- q << index
28
+
29
+ def valid_collapse?(type)
30
+ valid?(type)
31
+ end
32
+
33
+ def valid?(type)
34
+ valid_collapses.include?(type.try(:to_sym))
35
+ end
36
+
37
+ def valid_collapses
38
+ [:daily, :weekly, :monthly, :quarterly, :annual]
39
+ end
40
+
41
+ def collapse(data, frequency)
42
+ return data unless valid_collapse?(frequency)
43
+
44
+ # Special scenario where we are only fetching the `date` column
45
+ date_column_only = data.count > 0 && data[0].count == 1
46
+
47
+ # store the new collapsed data
48
+ collapsed_data = {}
49
+ range = find_end_of_range(data[0][0], frequency)
50
+
51
+ # iterate over the data
52
+ data.each do |row|
53
+ # grab date and values
54
+ date = row[0]
55
+ value = row[1..-1]
56
+ value = value.first if value.count == 1
57
+
58
+ # bump to the next range if it exceeds the current one
59
+ range = find_end_of_range(date, frequency) unless inside_range?(date, range)
60
+
61
+ # consider the value for the next range
62
+ next unless inside_range?(date, range) && (value.present? || date_column_only)
63
+ value = merge_row_values(value, collapsed_data[range]) unless collapsed_data[range].nil?
64
+ # assign value
65
+ collapsed_data[range] = value
66
+ end
67
+
68
+ to_table(collapsed_data)
69
+ end
70
+
71
+ def to_table(data)
72
+ data.collect do |date, values|
73
+ if values.is_a?(Array)
74
+ values.unshift(date)
75
+ else
76
+ [date, values]
77
+ end
78
+ end
79
+ end
80
+
81
+ def merge_row_values(top_row, bottom_row)
82
+ # merge previous values when nils are present
83
+ if top_row.is_a?(Array) && top_row.include?(nil)
84
+ # find nil indexes
85
+ indexes = find_each_index(top_row, nil)
86
+ # merge nils with previous values
87
+ indexes.each { |index| top_row[index] = bottom_row[index] }
88
+ end
89
+ top_row
90
+ end
91
+
92
+ def collapses_greater_than(freq)
93
+ return [] unless freq.respond_to?(:to_sym)
94
+ index = valid_collapses.index(freq.to_sym)
95
+ index.present? ? valid_collapses.slice(index + 1, valid_collapses.count) : []
96
+ end
97
+
98
+ def collapses_greater_than_or_equal_to(freq)
99
+ return [] unless freq.respond_to?(:to_sym)
100
+ valid_collapses.slice(valid_collapses.index(freq.to_sym), valid_collapses.count)
101
+ end
102
+
103
+ def frequency?(data)
104
+ Guess.frequency(data)
105
+ end
106
+
107
+ def inside_range?(date, range)
108
+ date <= range
109
+ end
110
+
111
+ def find_end_of_range(date, frequency)
112
+ date.end_of_frequency(frequency)
113
+ end
114
+
115
+ def find_each_index(array, find)
116
+ found = -1
117
+ index = -1
118
+ q = []
119
+ while found
120
+ found = array[index + 1..-1].index(find)
121
+ if found
122
+ index = index + found + 1
123
+ q << index
124
+ end
125
+ end
126
+ q
118
127
  end
119
128
  end
120
- q
121
129
  end
122
-
123
130
  end
124
-
125
- end
126
131
  end
127
- end
@@ -1,90 +1,84 @@
1
1
  module Quandl
2
- module Operation
3
- class Collapse
2
+ module Operation
3
+ class Collapse
4
+ class Guess
5
+ class << self
6
+ def frequency(data)
7
+ return :annual unless data && data[0] && data[0][0]
8
+ # find the smallest point of difference between dates
9
+ gap = find_average_gap(data)
10
+ # ensure gap is not negative
11
+ gap = ensure_positive_gap(gap)
12
+ # determine the freq from the size of the smallest gap
13
+ frequency_from_gap(gap)
14
+ end
4
15
 
5
- class Guess
6
- class << self
7
-
8
- def frequency(data)
9
- return :annual unless data && data[0] && data[0][0]
10
- # find the smallest point of difference between dates
11
- gap = find_average_gap(data)
12
- # ensure gap is not negative
13
- gap = ensure_positive_gap(gap)
14
- # determine the freq from the size of the smallest gap
15
- frequency_from_gap(gap)
16
- end
17
-
18
- def find_smallest_gap(data)
19
- # init
20
- gap = 100_000
21
- pdate = nil
22
- # find the smallest gap
23
- data.each do |row|
24
- # if the gap is 1, we're done
25
- break if gap <= 1
26
- # this row's date
27
- date = row[0]
28
- # only if pdate is present
29
- if pdate
30
- # calculate the gap
31
- diff = (pdate - date).to_i
32
- # replace the previous gap if it is smaller
33
- gap = diff if diff < gap
34
- end
35
- # previous row's date
36
- pdate = date
37
- end
38
- gap
39
- end
40
-
41
- def find_average_gap(data)
42
- # init
43
- gap = 100_000
44
- pdate = nil
45
- row_count = data.count
46
- majority_count = (row_count * 0.55).to_i
47
- gaps = {}
48
- # find the smallest gap
49
- data.each do |row|
50
- # this row's date
51
- date = row[0]
52
- # only if pdate is present
53
- if pdate
54
- # calculate the gap
55
- diff = (pdate - date).to_i
56
- # increment the gap counter
57
- gaps[diff] ||= 0
58
- gaps[diff] += 1
59
- # if the diff count is greater than majority_count, we have a consensus
60
- return diff if gaps[diff] > majority_count
61
- end
62
- # previous row's date
63
- pdate = date
64
- end
65
- gaps.to_a.sort_by{|r| r[1] }.try(:last).try(:first)
66
- end
16
+ def find_smallest_gap(data)
17
+ # init
18
+ gap = 100_000
19
+ pdate = nil
20
+ # find the smallest gap
21
+ data.each do |row|
22
+ # if the gap is 1, we're done
23
+ break if gap <= 1
24
+ # this row's date
25
+ date = row[0]
26
+ # only if pdate is present
27
+ if pdate
28
+ # calculate the gap
29
+ diff = (pdate - date).to_i
30
+ # replace the previous gap if it is smaller
31
+ gap = diff if diff < gap
32
+ end
33
+ # previous row's date
34
+ pdate = date
35
+ end
36
+ gap
37
+ end
67
38
 
68
- def frequency_from_gap(gap)
69
-
70
- case
71
- when gap <= 1 then :daily
72
- when gap <= 10 then :weekly
73
- when gap <= 31 then :monthly
74
- when gap <= 93 then :quarterly
75
- else :annual
39
+ def find_average_gap(data)
40
+ # init
41
+ pdate = nil
42
+ row_count = data.count
43
+ majority_count = (row_count * 0.55).to_i
44
+ gaps = {}
45
+ # find the smallest gap
46
+ data.each do |row|
47
+ # this row's date
48
+ date = row[0]
49
+ # only if pdate is present
50
+ if pdate
51
+ # calculate the gap
52
+ diff = (pdate - date).to_i
53
+ # increment the gap counter
54
+ gaps[diff] ||= 0
55
+ gaps[diff] += 1
56
+ # if the diff count is greater than majority_count, we have a consensus
57
+ return diff if gaps[diff] > majority_count
58
+ end
59
+ # previous row's date
60
+ pdate = date
61
+ end
62
+ gaps.to_a.sort_by { |r| r[1] }.try(:last).try(:first)
63
+ end
64
+
65
+ def frequency_from_gap(gap)
66
+ case
67
+ when gap <= 1 then :daily
68
+ when gap <= 10 then :weekly
69
+ when gap <= 31 then :monthly
70
+ when gap <= 93 then :quarterly
71
+ else :annual
72
+ end
73
+ end
74
+
75
+ def ensure_positive_gap(gap)
76
+ gap = gap.to_i
77
+ gap *= -1 if gap < 0
78
+ gap
79
+ end
80
+ end
76
81
  end
77
82
  end
78
-
79
- def ensure_positive_gap(gap)
80
- gap = gap.to_i
81
- gap = gap * -1 if gap < 0
82
- gap
83
- end
84
-
85
83
  end
86
-
87
- end
88
- end
89
84
  end
90
- end