active_data_frame 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|