active_data_frame 0.1.1 → 0.1.2
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.
- checksums.yaml +4 -4
- data/Rakefile +9 -1
- data/active_data_frame-0.1.1.gem +0 -0
- data/active_data_frame.gemspec +5 -1
- data/active_data_frame.todo +13 -36
- data/examples.rb +46 -0
- data/lib/active_data_frame.rb +2 -0
- data/lib/active_data_frame/bounds.rb +4 -0
- data/lib/active_data_frame/data_frame_proxy.rb +48 -18
- data/lib/active_data_frame/database.rb +115 -0
- data/lib/active_data_frame/group_proxy.rb +40 -0
- data/lib/active_data_frame/has_data_frame.rb +298 -107
- data/lib/active_data_frame/point.rb +4 -0
- data/lib/active_data_frame/row.rb +22 -68
- data/lib/active_data_frame/table.rb +13 -14
- data/lib/active_data_frame/version.rb +1 -1
- data/lib/generators/active_data_frame/install_generator.rb +13 -5
- data/lib/generators/active_data_frame/templates/has_concern.rb +1 -4
- data/lib/generators/active_data_frame/templates/migration.rb +1 -3
- metadata +72 -4
@@ -3,8 +3,8 @@ module ActiveDataFrame
|
|
3
3
|
|
4
4
|
attr_accessor :instance
|
5
5
|
|
6
|
-
def initialize(block_type, data_frame_type, instance)
|
7
|
-
super(block_type, data_frame_type)
|
6
|
+
def initialize(block_type, data_frame_type, instance, value_map: nil, singular_df_name: '', plural_df_name: '')
|
7
|
+
super(block_type, data_frame_type, value_map: value_map, singular_df_name: singular_df_name, plural_df_name: plural_df_name)
|
8
8
|
self.instance = instance
|
9
9
|
end
|
10
10
|
|
@@ -16,25 +16,30 @@ module ActiveDataFrame
|
|
16
16
|
to = (from + values.length) - 1
|
17
17
|
bounds = get_bounds(from, to)
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
end
|
19
|
+
new_blocks = Hash.new do |h, k|
|
20
|
+
h[k] = [[0] * block_type::BLOCK_SIZE]
|
21
|
+
end
|
23
22
|
|
24
|
-
|
25
|
-
[period_index, [block_values, id]]
|
26
|
-
end.to_h
|
23
|
+
deleted_indices = []
|
27
24
|
|
28
|
-
|
29
|
-
|
25
|
+
existing = blocks_between([bounds]).pluck(:data_frame_id, :period_index, *block_type::COLUMNS).map do |id, period_index, *block_values|
|
26
|
+
[period_index, [block_values, id]]
|
27
|
+
end.to_h
|
28
|
+
|
29
|
+
iterate_bounds([bounds]) do |index, left, right, cursor, size|
|
30
|
+
chunk = values[cursor...cursor + size]
|
31
|
+
if size == block_type::BLOCK_SIZE && chunk.all?(&:zero?)
|
32
|
+
deleted_indices << index
|
33
|
+
else
|
30
34
|
block = existing[index] || new_blocks[index]
|
31
35
|
block.first[left..right] = chunk.to_a
|
32
36
|
end
|
33
|
-
|
34
|
-
bulk_update(existing) unless existing.size.zero?
|
35
|
-
bulk_insert(new_blocks) unless new_blocks.size.zero?
|
36
|
-
values
|
37
37
|
end
|
38
|
+
|
39
|
+
database.bulk_delete(self.id, deleted_indices) unless deleted_indices.size.zero?
|
40
|
+
database.bulk_update(existing) unless existing.size.zero?
|
41
|
+
database.bulk_insert(new_blocks, instance) unless new_blocks.size.zero?
|
42
|
+
values
|
38
43
|
end
|
39
44
|
|
40
45
|
def get(ranges)
|
@@ -60,9 +65,9 @@ module ActiveDataFrame
|
|
60
65
|
[range.first, range.size, last_total]
|
61
66
|
end
|
62
67
|
index_of = ->(column){
|
63
|
-
selected = range_sizes.find{|start, size
|
68
|
+
selected = range_sizes.find{|start, size| start <= column && start + size >= column}
|
64
69
|
if selected
|
65
|
-
start,
|
70
|
+
start, _, total = selected
|
66
71
|
(column - start) + total
|
67
72
|
else
|
68
73
|
nil
|
@@ -76,57 +81,6 @@ module ActiveDataFrame
|
|
76
81
|
end
|
77
82
|
|
78
83
|
private
|
79
|
-
##
|
80
|
-
# Update block data for all blocks in a single call
|
81
|
-
##
|
82
|
-
def bulk_update(existing)
|
83
|
-
case ActiveRecord::Base.connection_config[:adapter]
|
84
|
-
when 'postgresql'
|
85
|
-
# Fast bulk update
|
86
|
-
updates = ''
|
87
|
-
existing.each do |period_index, (values, id)|
|
88
|
-
updates << "(#{id}, #{values.map{|v| v.inspect.gsub('"',"'") }.join(',')}),"
|
89
|
-
end
|
90
|
-
perform_update(updates)
|
91
|
-
else
|
92
|
-
ids = existing.map {|_, (_, id)| id}
|
93
|
-
updates = block_type::COLUMNS.map.with_index do |column, column_idx|
|
94
|
-
[column, "CASE period_index\n#{existing.map{|period_index, (values, id)| "WHEN #{period_index} then #{values[column_idx]}"}.join("\n")} \nEND\n"]
|
95
|
-
end.to_h
|
96
|
-
update_statement = updates.map{|cl, up| "#{cl} = #{up}" }.join(', ')
|
97
|
-
block_type.connection.execute("UPDATE #{block_type.table_name} SET #{update_statement} WHERE #{block_type.table_name}.id IN (#{ids.join(',')});")
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
##
|
102
|
-
# Insert block data for all blocks in a single call
|
103
|
-
##
|
104
|
-
def bulk_insert(new_blocks)
|
105
|
-
inserts = ''
|
106
|
-
new_blocks.each do |period_index, (values)|
|
107
|
-
inserts << \
|
108
|
-
case ActiveRecord::Base.connection_config[:adapter]
|
109
|
-
when 'postgresql', 'mysql2' then "(#{values.map{|v| v.inspect.gsub('"',"'") }.join(',')}, #{instance.id}, #{period_index}, '#{data_frame_type.name}', now(), now()),"
|
110
|
-
else "(#{values.map{|v| v.inspect.gsub('"',"'") }.join(',')}, #{instance.id}, #{period_index}, '#{data_frame_type.name}', datetime(), datetime()),"
|
111
|
-
end
|
112
|
-
end
|
113
|
-
perform_insert(inserts)
|
114
|
-
end
|
115
|
-
|
116
|
-
def perform_update(updates)
|
117
|
-
block_type.transaction do
|
118
|
-
block_type.connection.execute(
|
119
|
-
"UPDATE #{block_type.table_name} SET #{block_type::COLUMNS.map{|col| "#{col} = t.#{col}" }.join(", ")} FROM(VALUES #{updates[0..-2]}) as t(id, #{block_type::COLUMNS.join(',')}) WHERE #{block_type.table_name}.id = t.id"
|
120
|
-
)
|
121
|
-
end
|
122
|
-
true
|
123
|
-
end
|
124
|
-
|
125
|
-
def perform_insert(inserts)
|
126
|
-
sql = "INSERT INTO #{block_type.table_name} (#{block_type::COLUMNS.join(',')}, data_frame_id, period_index, data_frame_type, created_at, updated_at) VALUES #{inserts[0..-2]}"
|
127
|
-
block_type.connection.execute sql
|
128
|
-
end
|
129
|
-
|
130
84
|
def scope
|
131
85
|
@scope ||= block_type.where(data_frame_type: data_frame_type.name, data_frame_id: instance.id)
|
132
86
|
end
|
@@ -2,8 +2,10 @@ module ActiveDataFrame
|
|
2
2
|
class Table < DataFrameProxy
|
3
3
|
|
4
4
|
def set(from, values)
|
5
|
-
|
6
|
-
|
5
|
+
ActiveDataFrame::Database.batch do
|
6
|
+
data_frame_type.each do |instance|
|
7
|
+
Row.new(self.block_type, self.data_frame_type, instance).set(from, values)
|
8
|
+
end
|
7
9
|
end
|
8
10
|
end
|
9
11
|
|
@@ -35,7 +37,7 @@ module ActiveDataFrame
|
|
35
37
|
map
|
36
38
|
end
|
37
39
|
|
38
|
-
def column_cases(cases,
|
40
|
+
def column_cases(cases, aggregation_function=nil)
|
39
41
|
block_type::COLUMNS.map do |col|
|
40
42
|
col_cases = cases[col].sort_by(&:begin).reduce([]) do |agg, col_case|
|
41
43
|
if agg.empty?
|
@@ -51,9 +53,9 @@ module ActiveDataFrame
|
|
51
53
|
end
|
52
54
|
end
|
53
55
|
|
54
|
-
if
|
56
|
+
if aggregation_function
|
55
57
|
case col_cases.length
|
56
|
-
when 0 then "NULL as #{col}"
|
58
|
+
when 0 then "NULL::float as #{col}"
|
57
59
|
else
|
58
60
|
case_str = col_cases.map do |match|
|
59
61
|
case
|
@@ -61,7 +63,7 @@ module ActiveDataFrame
|
|
61
63
|
else "period_index BETWEEN #{match.begin} AND #{match.end}"
|
62
64
|
end
|
63
65
|
end.join(" OR ")
|
64
|
-
"CASE WHEN #{case_str} THEN #{
|
66
|
+
"CASE WHEN #{case_str} THEN #{aggregation_function}(#{col}) ELSE NULL END"
|
65
67
|
end
|
66
68
|
else
|
67
69
|
case col_cases.length
|
@@ -92,7 +94,6 @@ module ActiveDataFrame
|
|
92
94
|
index_map = {}
|
93
95
|
res = ActiveRecord::Base.transaction do
|
94
96
|
ids = data_frame_type.pluck(:id)
|
95
|
-
|
96
97
|
as_sql = blocks_between(
|
97
98
|
all_bounds,
|
98
99
|
block_scope: data_frame_type.unscoped
|
@@ -130,9 +131,9 @@ module ActiveDataFrame
|
|
130
131
|
[range.first, range.size, last_total]
|
131
132
|
end
|
132
133
|
index_of = ->(column){
|
133
|
-
selected = range_sizes.find{|start, size
|
134
|
+
selected = range_sizes.find{|start, size| start <= column && start + size >= column}
|
134
135
|
if selected
|
135
|
-
start,
|
136
|
+
start, _, total = selected
|
136
137
|
(column - start) + total
|
137
138
|
else
|
138
139
|
nil
|
@@ -156,11 +157,11 @@ module ActiveDataFrame
|
|
156
157
|
end
|
157
158
|
|
158
159
|
def idx_where_sum_gte(*ranges, max)
|
159
|
-
select_agg_indices(extract_ranges(ranges), 'SUM', ->(x, y){ x <= y } , 'SUM(%)
|
160
|
+
select_agg_indices(extract_ranges(ranges), 'SUM', ->(x, y){ x <= y } , 'SUM(%) >= :max', max: max)
|
160
161
|
end
|
161
162
|
|
162
163
|
def idx_where_sum_lte(*ranges, min)
|
163
|
-
select_agg_indices(extract_ranges(ranges), 'SUM', ->(x, y){ x >= y } , 'SUM(%)
|
164
|
+
select_agg_indices(extract_ranges(ranges), 'SUM', ->(x, y){ x >= y } , 'SUM(%) <= :min', min: min)
|
164
165
|
end
|
165
166
|
|
166
167
|
def AggregateProxy(agg)
|
@@ -234,9 +235,7 @@ module ActiveDataFrame
|
|
234
235
|
end
|
235
236
|
|
236
237
|
case_map = build_case_map(all_bounds)
|
237
|
-
existing = blocks_between(all_bounds)
|
238
|
-
.group(:period_index)
|
239
|
-
.pluck(:period_index, *column_cases(case_map, agg))
|
238
|
+
existing = blocks_between(all_bounds).group(:period_index).pluck(:period_index, *column_cases(case_map, agg))
|
240
239
|
.map{|pi, *values| [pi, values]}.to_h
|
241
240
|
result = M.blank(columns: all_bounds.map(&:length).sum, typecode: block_type::TYPECODE)
|
242
241
|
|
@@ -4,7 +4,7 @@ module ActiveDataFrame
|
|
4
4
|
class InstallGenerator < ActiveRecord::Generators::Base
|
5
5
|
desc "Generates a new data_frame type"
|
6
6
|
|
7
|
-
STREAM_TYPES = %w(bit byte
|
7
|
+
STREAM_TYPES = %w(bit byte integer long float double)
|
8
8
|
# Commandline options can be defined here using Thor-like options:
|
9
9
|
argument :type, :type => :string, :default => 'float', :desc => "DataFrame type. One of(#{STREAM_TYPES*" ,"})"
|
10
10
|
argument :columns, :type => :numeric, :default => 512, :desc => "Number of columns"
|
@@ -46,12 +46,20 @@ module ActiveDataFrame
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
+
def get_typecode
|
50
|
+
case type
|
51
|
+
when "float", "double" then M::Typecode::FLOAT
|
52
|
+
when "integer", "long" then M::Typecode::INT
|
53
|
+
when "bit", "byte" then M::Typecode::BYTE
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
49
57
|
def inject_data_frame_helpers
|
50
58
|
content = \
|
51
59
|
<<RUBY
|
52
60
|
BLOCK_SIZE = #{columns}
|
53
61
|
COLUMNS = %w(#{columns.times.map{|i| "t#{i+1}" }.join(" ")})
|
54
|
-
TYPECODE =
|
62
|
+
TYPECODE = #{get_typecode}
|
55
63
|
self.table_name = '#{block_table_name}'
|
56
64
|
RUBY
|
57
65
|
class_name = "Blocks::#{singular_block_table_name.camelize}"
|
@@ -76,9 +84,9 @@ RUBY
|
|
76
84
|
|
77
85
|
def migration_data
|
78
86
|
<<RUBY
|
79
|
-
t.integer :data_frame_id
|
80
|
-
t.string :data_frame_type
|
81
|
-
t.integer :period_index
|
87
|
+
t.integer :data_frame_id
|
88
|
+
t.string :data_frame_type
|
89
|
+
t.integer :period_index
|
82
90
|
#{
|
83
91
|
columns.times.map do |i|
|
84
92
|
" t.#{type} :t#{i+1}"
|
@@ -1,6 +1,3 @@
|
|
1
|
-
require 'active_support/concern'
|
2
|
-
|
3
1
|
module <%= concern_name %>
|
4
|
-
|
5
|
-
include ActiveDataFrame::HasDataFrame('<%= singular_table_name %>', '<%= table_name %>',Blocks::<%= block_type %>)
|
2
|
+
include ActiveDataFrame::HasDataFrame('<%= singular_table_name %>', Blocks::<%= block_type %>)
|
6
3
|
end
|
@@ -2,10 +2,8 @@ class ActiveDataFrameCreate<%= table_name.camelize %> < ActiveRecord::Migration<
|
|
2
2
|
def change
|
3
3
|
create_table :<%= block_table_name %> do |t|
|
4
4
|
<%= migration_data -%>
|
5
|
-
t.timestamps null: false
|
6
5
|
end
|
7
6
|
|
8
|
-
|
9
|
-
add_index :<%= block_table_name %>, [:data_frame_type, :data_frame_id , :period_index], :unique => true, name: 'index_<%= block_table_name %>_on_type_id_and_index'
|
7
|
+
add_index :<%= block_table_name %>, [:data_frame_id , :period_index, :data_frame_type], :unique => true, name: 'index_<%= block_table_name %>_id_index_and_type'
|
10
8
|
end
|
11
9
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_data_frame
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wouter Coppieters
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-04-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -78,20 +78,82 @@ dependencies:
|
|
78
78
|
- - ">="
|
79
79
|
- !ruby/object:Gem::Version
|
80
80
|
version: 0.10.0
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: pg
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: minitest
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - "~>"
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '5.11'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - "~>"
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '5.11'
|
109
|
+
- !ruby/object:Gem::Dependency
|
110
|
+
name: minitest-reporters
|
111
|
+
requirement: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - "~>"
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '1.1'
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: 1.1.0
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - "~>"
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '1.1'
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: 1.1.0
|
129
|
+
- !ruby/object:Gem::Dependency
|
130
|
+
name: minitest-around
|
131
|
+
requirement: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - '='
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: 0.4.1
|
136
|
+
type: :development
|
137
|
+
prerelease: false
|
138
|
+
version_requirements: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - '='
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: 0.4.1
|
81
143
|
- !ruby/object:Gem::Dependency
|
82
144
|
name: activerecord
|
83
145
|
requirement: !ruby/object:Gem::Requirement
|
84
146
|
requirements:
|
85
147
|
- - "~>"
|
86
148
|
- !ruby/object:Gem::Version
|
87
|
-
version: 5.0
|
149
|
+
version: '5.0'
|
88
150
|
type: :runtime
|
89
151
|
prerelease: false
|
90
152
|
version_requirements: !ruby/object:Gem::Requirement
|
91
153
|
requirements:
|
92
154
|
- - "~>"
|
93
155
|
- !ruby/object:Gem::Version
|
94
|
-
version: 5.0
|
156
|
+
version: '5.0'
|
95
157
|
- !ruby/object:Gem::Dependency
|
96
158
|
name: rmatrix
|
97
159
|
requirement: !ruby/object:Gem::Requirement
|
@@ -124,13 +186,19 @@ files:
|
|
124
186
|
- Gemfile
|
125
187
|
- README.md
|
126
188
|
- Rakefile
|
189
|
+
- active_data_frame-0.1.1.gem
|
127
190
|
- active_data_frame.gemspec
|
128
191
|
- active_data_frame.todo
|
129
192
|
- bin/console
|
130
193
|
- bin/setup
|
194
|
+
- examples.rb
|
131
195
|
- lib/active_data_frame.rb
|
196
|
+
- lib/active_data_frame/bounds.rb
|
132
197
|
- lib/active_data_frame/data_frame_proxy.rb
|
198
|
+
- lib/active_data_frame/database.rb
|
199
|
+
- lib/active_data_frame/group_proxy.rb
|
133
200
|
- lib/active_data_frame/has_data_frame.rb
|
201
|
+
- lib/active_data_frame/point.rb
|
134
202
|
- lib/active_data_frame/row.rb
|
135
203
|
- lib/active_data_frame/table.rb
|
136
204
|
- lib/active_data_frame/version.rb
|