object_table 0.3.0 → 0.3.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 +5 -13
- data/.rspec +2 -0
- data/README.md +15 -0
- data/lib/object_table/basic_grid.rb +2 -9
- data/lib/object_table/column.rb +16 -6
- data/lib/object_table/grouped.rb +24 -28
- data/lib/object_table/masked_column.rb +7 -17
- data/lib/object_table/printable.rb +42 -42
- data/lib/object_table/stacker.rb +63 -0
- data/lib/object_table/table_methods.rb +14 -13
- data/lib/object_table/version.rb +1 -1
- data/lib/object_table/view.rb +2 -3
- data/lib/object_table/view_methods.rb +2 -0
- data/lib/object_table.rb +2 -112
- data/object_table.gemspec +1 -0
- data/spec/object_table/basic_grid_spec.rb +25 -1
- data/spec/object_table/column_spec.rb +34 -0
- data/spec/object_table/grouped_spec.rb +56 -3
- data/spec/object_table/masked_column_spec.rb +11 -11
- data/spec/object_table/view_spec.rb +28 -0
- data/spec/object_table_spec.rb +79 -208
- data/spec/spec_helper.rb +95 -0
- data/spec/support/object_table_example.rb +14 -0
- data/spec/support/stacker_example.rb +158 -0
- data/spec/support/view_example.rb +85 -35
- metadata +26 -6
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
ODg5Zjc3Nzk5ZGIyMjk1OWNjYjFlYzhjMzhlMGNlNzdjMGI3ZWE0OQ==
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8b283bc238922ed7b80fc82225cbb758c6a0bf44
|
4
|
+
data.tar.gz: 144b36aec60ae08d7ef391351dc6ec5e14d4c33c
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
NmIxMDRkY2I0NjQ3NTAwZGYxYzU3MjRiZGFkMjU5ODg2ODdjNzBmMjEwNGNh
|
11
|
-
ZTRmODBiOWRiM2Y2Zjc5ZjY3OWYwZTk4YzgxY2UzZDdkY2YyMDI=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
NjkwZWRiM2FlYmVhNjgyNzRhYmZmN2I1YzVhYzJjYTVlYzVlMDFmZTFiNGM0
|
14
|
-
NjcyMzdlYTIwMDM2MjljN2VkNmM2OTM1MzBjOWZiOWJiMTlhYmE5MWQxMTI2
|
15
|
-
YmQxMmYxNzQ2MjhjMTc4ZTk3ODhhYTcyZTM2OGE4ZjMwOTNkNDY=
|
6
|
+
metadata.gz: 4ae62ff61696c06ab3c4edd2974dc68cd4508de97388a0194d347865c2f617a3cd1694f87f6afdaa6cafd4fadca011e131cebf2f09925e6ea46de959b5eab2ea
|
7
|
+
data.tar.gz: 38110c1fc518f9d8bc5d0ab4b964eab0a266fb94641ee71c636510bcd1ab710901f9af64c61a677800e4617ac993e74610e69e3eff704079e503d38e499e72ad
|
data/.rspec
ADDED
data/README.md
CHANGED
@@ -1,6 +1,21 @@
|
|
1
1
|
ruby-object-table
|
2
2
|
=================
|
3
3
|
|
4
|
+
[![Gem Version][GV img]][Gem Version]
|
5
|
+
[![Build Status][BS img]][Build Status]
|
6
|
+
[![Code Climate][CC img]][Code Climate]
|
7
|
+
[![Coverage Status][CS img]][Coverage Status]
|
8
|
+
|
9
|
+
[Gem Version]: https://rubygems.org/gems/object_table
|
10
|
+
[Build Status]: https://travis-ci.org/lincheney/ruby-object-table
|
11
|
+
[Code Climate]: https://codeclimate.com/github/lincheney/ruby-object-table
|
12
|
+
[Coverage Status]: https://coveralls.io/r/lincheney/ruby-object-table
|
13
|
+
|
14
|
+
[GV img]: https://badge.fury.io/rb/object_table.png
|
15
|
+
[BS img]: https://travis-ci.org/lincheney/ruby-object-table.png
|
16
|
+
[CC img]: https://codeclimate.com/github/lincheney/ruby-object-table.png
|
17
|
+
[CS img]: https://coveralls.io/repos/lincheney/ruby-object-table/badge.png?branch=master
|
18
|
+
|
4
19
|
Simple data table/frame implementation in ruby
|
5
20
|
Probably slow and extremely inefficient, but it works and that's all that matters.
|
6
21
|
Uses NArrays (https://github.com/masa16/narray) for storing data.
|
@@ -8,14 +8,7 @@ class ObjectTable::BasicGrid < Hash
|
|
8
8
|
|
9
9
|
def _get_number_rows!
|
10
10
|
each{|k, v| self[k] = v.to_a if v.is_a?(Range)}
|
11
|
-
rows = map
|
12
|
-
case v
|
13
|
-
when Array
|
14
|
-
v.length
|
15
|
-
when NArray
|
16
|
-
v.shape.last or 0
|
17
|
-
end
|
18
|
-
end.compact.uniq
|
11
|
+
rows = map{|k, v| ObjectTable::Column.length_of(v) rescue nil}.compact.uniq
|
19
12
|
end
|
20
13
|
|
21
14
|
def _ensure_uniform_columns!(rows = nil)
|
@@ -29,7 +22,7 @@ class ObjectTable::BasicGrid < Hash
|
|
29
22
|
self[k] = [v] * rows unless (v.is_a?(Array) || v.is_a?(NArray))
|
30
23
|
end
|
31
24
|
|
32
|
-
|
25
|
+
rows
|
33
26
|
end
|
34
27
|
|
35
28
|
end
|
data/lib/object_table/column.rb
CHANGED
@@ -2,7 +2,19 @@ require 'narray'
|
|
2
2
|
|
3
3
|
module ObjectTable::Column
|
4
4
|
|
5
|
+
def self.length_of(array)
|
6
|
+
case array
|
7
|
+
when Array
|
8
|
+
array.length
|
9
|
+
when NArray
|
10
|
+
array.shape.last or 0
|
11
|
+
else
|
12
|
+
raise "Expected Array or NArray, got #{array}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
5
16
|
def self.stack(*columns)
|
17
|
+
columns = columns.reject(&:empty?)
|
6
18
|
return NArray[] if columns.empty?
|
7
19
|
return columns[0].clone if columns.length == 1
|
8
20
|
|
@@ -10,12 +22,10 @@ module ObjectTable::Column
|
|
10
22
|
first_col = columns.first
|
11
23
|
new_col = NArray.new(first_col.typecode, *first_col.shape[0...-1], new_rows)
|
12
24
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
new_col[*padding, row ... (row + col.shape[-1])] = col
|
18
|
-
row += col.shape[-1]
|
25
|
+
columns.reduce(0) do |row, col|
|
26
|
+
end_row = row + col.shape[-1]
|
27
|
+
new_col[false, row ... end_row] = col
|
28
|
+
end_row
|
19
29
|
end
|
20
30
|
|
21
31
|
new_col
|
data/lib/object_table/grouped.rb
CHANGED
@@ -37,15 +37,7 @@ class ObjectTable::Grouped
|
|
37
37
|
|
38
38
|
def each(&block)
|
39
39
|
names, groups = _groups()
|
40
|
-
|
41
|
-
enumerator = Enumerator.new do |y|
|
42
|
-
groups.each do |k, v|
|
43
|
-
keys = names.zip(k)
|
44
|
-
y.yield __group_cls__.new(@parent, Hash[keys], v)
|
45
|
-
end
|
46
|
-
@parent
|
47
|
-
end
|
48
|
-
|
40
|
+
enumerator = _make_groups(names, groups)
|
49
41
|
return enumerator unless block
|
50
42
|
enumerator.each{|grp| grp._apply_block(&block)}
|
51
43
|
end
|
@@ -53,37 +45,41 @@ class ObjectTable::Grouped
|
|
53
45
|
def apply(&block)
|
54
46
|
names, groups = _groups()
|
55
47
|
value_key = self.class._generate_name(DEFAULT_VALUE_PREFIX, names).to_sym
|
48
|
+
nrows = []
|
56
49
|
|
57
|
-
data = groups.map do |
|
58
|
-
|
59
|
-
value = __group_cls__.new(@parent, Hash[keys], v)._apply_block &block
|
50
|
+
data = _make_groups(names, groups).map do |group|
|
51
|
+
value = group._apply_block(&block)
|
60
52
|
|
61
53
|
case value
|
62
54
|
when ObjectTable::TableMethods
|
63
|
-
nrows
|
55
|
+
nrows.push(value.nrows)
|
64
56
|
value = value.columns
|
65
57
|
when ObjectTable::BasicGrid
|
66
|
-
value._ensure_uniform_columns!
|
67
|
-
nrows = NArray.to_na(value.values.first).shape[-1]
|
68
|
-
when Array
|
69
|
-
nrows = value.length
|
70
|
-
when NArray
|
71
|
-
nrows = value.shape.last
|
58
|
+
nrows.push(value._ensure_uniform_columns!)
|
72
59
|
else
|
73
|
-
nrows
|
60
|
+
nrows.push( (ObjectTable::Column.length_of(value) rescue 1) )
|
74
61
|
end
|
75
62
|
|
76
|
-
|
63
|
+
value = ObjectTable::BasicGrid[value_key, value] unless value.is_a?(ObjectTable::BasicGrid)
|
64
|
+
value
|
65
|
+
end
|
77
66
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
67
|
+
keys = groups.keys.transpose.map{|col| col.zip(nrows).flat_map{|key, rows| [key] * rows}}
|
68
|
+
keys = ObjectTable::BasicGrid[names.zip(keys)]
|
69
|
+
|
70
|
+
result = __table_cls__.stack(*data)
|
71
|
+
__table_cls__.new(keys.merge!(result.columns))
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
def _make_groups(names, groups)
|
76
|
+
key_struct = Struct.new(*names.map(&:to_sym))
|
77
|
+
enumerator = Enumerator.new do |y|
|
78
|
+
groups.each do |k, v|
|
79
|
+
y.yield __group_cls__.new(@parent, key_struct.new(*k), v)
|
83
80
|
end
|
81
|
+
@parent
|
84
82
|
end
|
85
|
-
|
86
|
-
__table_cls__.stack(*data)
|
87
83
|
end
|
88
84
|
|
89
85
|
def self._generate_name(prefix, existing_names)
|
@@ -1,41 +1,31 @@
|
|
1
1
|
require_relative 'column'
|
2
2
|
|
3
3
|
class ObjectTable::MaskedColumn < NArray
|
4
|
-
attr_accessor :indices, :parent
|
4
|
+
attr_accessor :indices, :parent
|
5
5
|
|
6
6
|
EMPTY = NArray[]
|
7
7
|
|
8
8
|
def self.mask(parent, indices)
|
9
|
-
if parent.
|
10
|
-
|
9
|
+
if parent.empty?
|
10
|
+
masked = parent.slice(indices)
|
11
11
|
else
|
12
|
-
|
12
|
+
masked = parent.slice(false, indices)
|
13
13
|
end
|
14
|
-
masked = parent.slice(*padded_dims, indices)
|
15
14
|
|
16
15
|
if masked.rank <= 0
|
17
16
|
column = self.new(masked.typecode, 0)
|
18
17
|
else
|
19
|
-
column = self.
|
20
|
-
column.super_slice_assign(masked)
|
18
|
+
column = self.cast(masked)
|
21
19
|
end
|
22
20
|
|
23
21
|
column.parent = parent
|
24
22
|
column.indices = indices
|
25
|
-
column.padded_dims = padded_dims
|
26
23
|
column
|
27
24
|
end
|
28
25
|
|
29
|
-
# let ObjectTable::Column do this, since we've overriden []=
|
30
|
-
def self.make(*args)
|
31
|
-
ObjectTable::Column.make(*args)
|
32
|
-
end
|
33
|
-
|
34
|
-
alias_method :super_slice_assign, :[]=
|
35
|
-
|
36
26
|
def []=(*keys, value)
|
37
27
|
unless parent.nil? or ((value.is_a?(Array) or value.is_a?(NArray)) and value.empty?)
|
38
|
-
parent[
|
28
|
+
parent[false, indices[*keys]] = value
|
39
29
|
end
|
40
30
|
super
|
41
31
|
end
|
@@ -44,7 +34,7 @@ class ObjectTable::MaskedColumn < NArray
|
|
44
34
|
%w{ fill! indgen! indgen random! map! collect! conj! imag= mod! add! div! sbt! mul! }.each do |op|
|
45
35
|
define_method(op) do |*args, &block|
|
46
36
|
result = super(*args, &block)
|
47
|
-
parent[
|
37
|
+
parent[false, indices] = result if parent
|
48
38
|
result
|
49
39
|
end
|
50
40
|
end
|
@@ -1,23 +1,38 @@
|
|
1
1
|
module ObjectTable::Printable
|
2
2
|
|
3
|
-
def self.get_printable_column(
|
4
|
-
|
5
|
-
|
6
|
-
row = column[*padding, i]
|
3
|
+
def self.get_printable_column(column)
|
4
|
+
column.shape[-1].times.map do |i|
|
5
|
+
row = column[false, i]
|
7
6
|
str = row.is_a?(NArray) ? row.inspect.partition("\n")[-1].strip : row.inspect
|
8
7
|
str.split("\n")
|
9
8
|
end
|
9
|
+
end
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
def self.calc_column_widths(columns)
|
12
|
+
columns.map{|col| col.flatten.map(&:length).max}
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
15
|
+
def _format_section(row_slice)
|
16
|
+
numbers = row_slice.map{|i| ["#{i}: "]}
|
17
|
+
section = columns.map do |name, c|
|
18
|
+
c = c.slice(false, row_slice)
|
19
|
+
ObjectTable::Printable.get_printable_column(c)
|
18
20
|
end
|
19
21
|
|
20
|
-
[
|
22
|
+
[numbers] + section
|
23
|
+
end
|
24
|
+
|
25
|
+
def _format_rows(rows, widths)
|
26
|
+
rows.flat_map do |row|
|
27
|
+
height = row.map(&:length).max
|
28
|
+
|
29
|
+
row = row.zip(widths).map do |cell, width|
|
30
|
+
cell += [" "] * (height - cell.length)
|
31
|
+
cell.map{|i| i.rjust(width)}
|
32
|
+
end
|
33
|
+
|
34
|
+
row.transpose.map(&:join)
|
35
|
+
end
|
21
36
|
end
|
22
37
|
|
23
38
|
def inspect(max_section = 5, col_padding = 2)
|
@@ -26,47 +41,32 @@ module ObjectTable::Printable
|
|
26
41
|
return (header + "(empty table)") if ncols == 0
|
27
42
|
return (header + "(empty table with columns: #{colnames.join(", ")})") if nrows == 0
|
28
43
|
|
29
|
-
|
44
|
+
column_headers = [''] + colnames.map(&:to_s)
|
30
45
|
|
31
46
|
if nrows > max_section * 2
|
32
|
-
head = (0...max_section)
|
33
|
-
tail = ((nrows - max_section)...nrows)
|
47
|
+
head = _format_section(0 ... max_section)
|
48
|
+
tail = _format_section((nrows - max_section) ... nrows)
|
34
49
|
|
35
|
-
|
50
|
+
columns = [column_headers, head, tail].transpose
|
51
|
+
widths = NArray.to_na(ObjectTable::Printable.calc_column_widths(columns)) + col_padding
|
52
|
+
total_width = widths.sum
|
53
|
+
|
54
|
+
rows = _format_rows(head.transpose, widths)
|
55
|
+
rows.push('-' * total_width)
|
56
|
+
rows += _format_rows(tail.transpose, widths)
|
36
57
|
|
37
|
-
printed_columns += columns.map do |name, c|
|
38
|
-
padding = [nil] * (c.rank - 1)
|
39
|
-
c = c.slice(*padding, [head, tail])
|
40
|
-
ObjectTable::Printable.get_printable_column(name, c)
|
41
|
-
end
|
42
58
|
else
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
59
|
+
section = _format_section(0...nrows)
|
60
|
+
columns = [column_headers, section].transpose
|
61
|
+
widths = NArray.to_na(ObjectTable::Printable.calc_column_widths(columns)) + col_padding
|
62
|
+
rows = _format_rows(section.transpose, widths)
|
48
63
|
end
|
49
64
|
|
50
|
-
|
51
|
-
|
52
|
-
header + printed_columns.transpose.each_with_index.map do |row, index|
|
53
|
-
height = row.map(&:length).max
|
54
|
-
|
55
|
-
row = row.zip(widths).map do |cell, width|
|
56
|
-
cell += [" "] * (height - cell.length)
|
57
|
-
cell.map{|i| i.rjust(width)}
|
58
|
-
end
|
59
|
-
|
60
|
-
row = row.transpose.map{|i| i.join('')}.join("\n")
|
61
|
-
|
62
|
-
if index == max_section
|
63
|
-
row += "\n" + '-'*widths.reduce(:+)
|
64
|
-
end
|
65
|
-
row
|
66
|
-
end.join("\n")
|
65
|
+
column_headers = _format_rows([[column_headers].transpose], widths).join
|
66
|
+
header + ([column_headers] + rows + [column_headers]).join("\n")
|
67
67
|
|
68
68
|
rescue NoMethodError => e
|
69
69
|
raise Exception.new(e)
|
70
70
|
end
|
71
71
|
|
72
|
-
end
|
72
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module ObjectTable::Stacker
|
2
|
+
|
3
|
+
def stack!(*others)
|
4
|
+
@columns.replace( self.class.stack(self, *others).columns )
|
5
|
+
self
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def stack(*grids)
|
10
|
+
keys = nil
|
11
|
+
|
12
|
+
grids = grids.map do |grid|
|
13
|
+
grid = _process_stackable_grid(grid, keys)
|
14
|
+
keys ||= grid.keys if grid
|
15
|
+
grid
|
16
|
+
end.compact
|
17
|
+
return self.new if grids.empty?
|
18
|
+
|
19
|
+
result = keys.map do |k|
|
20
|
+
segments = grids.map{|grid| grid[k]}
|
21
|
+
[k, _stack_segments(segments)]
|
22
|
+
end
|
23
|
+
|
24
|
+
self.new(ObjectTable::BasicGrid[result])
|
25
|
+
end
|
26
|
+
|
27
|
+
def _stack_segments(segments)
|
28
|
+
if segments.all?{|seg| seg.is_a? Array}
|
29
|
+
column = NArray.to_na(segments.flatten(1))
|
30
|
+
|
31
|
+
elsif segments.all?{|seg| seg.is_a? NArray} and segments.map{|seg| seg.shape}.uniq.length == 1
|
32
|
+
column = NArray.to_na(segments)
|
33
|
+
column = column.reshape(*column.shape[0...-2], column.shape[-2] * column.shape[-1])
|
34
|
+
|
35
|
+
else
|
36
|
+
segments.map!{|seg| NArray.to_na seg}
|
37
|
+
column = ObjectTable::Column.stack(*segments)
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def _process_stackable_grid(grid, keys)
|
43
|
+
case grid
|
44
|
+
when ObjectTable::TableMethods
|
45
|
+
grid = grid.columns
|
46
|
+
when ObjectTable::BasicGrid
|
47
|
+
grid._ensure_uniform_columns!
|
48
|
+
end
|
49
|
+
|
50
|
+
raise "Don't know how to join a #{grid.class}" unless grid.is_a?(ObjectTable::BasicGrid)
|
51
|
+
return if grid.empty?
|
52
|
+
raise 'Mismatch in column names' unless !keys or ( (keys - grid.keys).empty? and (grid.keys - keys).empty? )
|
53
|
+
return grid
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
def self.included(base)
|
60
|
+
base.extend(ClassMethods)
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -35,29 +35,30 @@ module ObjectTable::TableMethods
|
|
35
35
|
|
36
36
|
def set_column(name, value, *args)
|
37
37
|
column = get_column(name)
|
38
|
+
new_column = column.nil?
|
39
|
+
|
38
40
|
value = value.to_a if value.is_a?(Range)
|
41
|
+
is_vector = (value.is_a?(Array) or value.is_a?(NArray))
|
39
42
|
|
40
|
-
if
|
41
|
-
|
42
|
-
|
43
|
-
|
43
|
+
if new_column
|
44
|
+
if is_vector and args.empty?
|
45
|
+
value = NArray.to_na(value)
|
46
|
+
unless (value.shape[-1] or 0) == nrows
|
47
|
+
raise ArgumentError.new("Expected size of last dimension to be #{nrows}, was #{value.shape[-1].inspect}")
|
48
|
+
end
|
44
49
|
|
45
|
-
|
46
|
-
value = NArray.to_na(value)
|
47
|
-
unless (value.shape[-1] or 0) == nrows
|
48
|
-
raise ArgumentError.new("Expected size of last dimension to be #{nrows}, was #{value.shape[-1].inspect}")
|
50
|
+
args = [value.typecode] + value.shape[0...-1]
|
49
51
|
end
|
50
52
|
|
51
|
-
|
53
|
+
column = add_column(name, *args)
|
52
54
|
end
|
53
55
|
|
54
|
-
column
|
55
|
-
return column if value.is_a?(NArray) and value.empty?
|
56
|
+
return column if column.empty? and (!is_vector or value.empty?)
|
56
57
|
|
57
58
|
begin
|
58
59
|
column[] = value
|
59
60
|
rescue Exception => e
|
60
|
-
pop_column(name)
|
61
|
+
pop_column(name) if new_column
|
61
62
|
raise e
|
62
63
|
end
|
63
64
|
end
|
@@ -100,7 +101,7 @@ module ObjectTable::TableMethods
|
|
100
101
|
get_column(meth) or super
|
101
102
|
end
|
102
103
|
|
103
|
-
def respond_to?(meth)
|
104
|
+
def respond_to?(meth, include_all = false)
|
104
105
|
super or has_column?(meth)
|
105
106
|
end
|
106
107
|
|
data/lib/object_table/version.rb
CHANGED
data/lib/object_table/view.rb
CHANGED
@@ -14,14 +14,13 @@ class ObjectTable::View
|
|
14
14
|
@filter = block
|
15
15
|
end
|
16
16
|
|
17
|
-
def_delegators :@parent, :has_column?
|
18
|
-
|
19
17
|
def make_view
|
20
18
|
__static_view_cls__.new @parent, indices
|
21
19
|
end
|
22
20
|
|
23
21
|
def clone
|
24
|
-
|
22
|
+
cols = ObjectTable::BasicGrid[@parent.columns.map{|k, v| [k, v[false, indices]]}]
|
23
|
+
__table_cls__.new(cols)
|
25
24
|
end
|
26
25
|
|
27
26
|
def inspect(*args)
|
data/lib/object_table.rb
CHANGED
@@ -5,9 +5,11 @@ require_relative "object_table/view"
|
|
5
5
|
require_relative "object_table/static_view"
|
6
6
|
require_relative "object_table/column"
|
7
7
|
require_relative "object_table/grouped"
|
8
|
+
require_relative "object_table/stacker"
|
8
9
|
|
9
10
|
class ObjectTable
|
10
11
|
include TableMethods
|
12
|
+
include Stacker
|
11
13
|
|
12
14
|
attr_reader :columns
|
13
15
|
|
@@ -30,51 +32,6 @@ class ObjectTable
|
|
30
32
|
columns[name] = col
|
31
33
|
end
|
32
34
|
|
33
|
-
def stack!(*others)
|
34
|
-
new_values = Hash[colnames.zip(ncols.times.map{[]})]
|
35
|
-
|
36
|
-
others.each do |x|
|
37
|
-
case x
|
38
|
-
when ObjectTable::TableMethods
|
39
|
-
x = x.columns
|
40
|
-
when ObjectTable::BasicGrid
|
41
|
-
x._ensure_uniform_columns!
|
42
|
-
end
|
43
|
-
|
44
|
-
raise "Don't know how to append a #{x.class}" unless x.is_a?(ObjectTable::BasicGrid)
|
45
|
-
next if x.empty?
|
46
|
-
raise 'Mismatch in column names' unless (colnames - x.keys).empty?
|
47
|
-
|
48
|
-
x.each do |k, v|
|
49
|
-
unless new_values.include?(k) and v.empty?
|
50
|
-
new_values[k].push(NArray.to_na(v))
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
return self if new_values.values.first.empty?
|
56
|
-
|
57
|
-
new_values.each do |k, v|
|
58
|
-
@columns[k] = ObjectTable::Column.stack(@columns[k], *v)
|
59
|
-
end
|
60
|
-
self
|
61
|
-
end
|
62
|
-
|
63
|
-
def self.stack(*values)
|
64
|
-
return self.new if values.empty?
|
65
|
-
base = values.shift
|
66
|
-
|
67
|
-
case base
|
68
|
-
when ObjectTable::BasicGrid
|
69
|
-
base = self.new(base.clone)
|
70
|
-
when ObjectTable, ObjectTable::View
|
71
|
-
base = base.clone
|
72
|
-
else
|
73
|
-
raise "Don't know how to join a #{base.class}"
|
74
|
-
end
|
75
|
-
base.stack!(*values)
|
76
|
-
end
|
77
|
-
|
78
35
|
def sort_by!(*keys)
|
79
36
|
sort_index = _get_sort_index(keys)
|
80
37
|
|
@@ -84,73 +41,6 @@ class ObjectTable
|
|
84
41
|
self
|
85
42
|
end
|
86
43
|
|
87
|
-
def join(other, key, options={})
|
88
|
-
type = (options[:type] || 'inner')
|
89
|
-
key = [key] unless key.is_a?(Array)
|
90
|
-
|
91
|
-
right_cols = other.colnames - key
|
92
|
-
left_cols = colnames
|
93
|
-
|
94
|
-
lkeys = key.map{|k| get_column(k).to_a}.transpose
|
95
|
-
rkeys = key.map{|k| other[k].to_a}.transpose
|
96
|
-
|
97
|
-
rgroups = rkeys.length.times.group_by{|i| rkeys[i]}
|
98
|
-
rindex = rgroups.values_at(*lkeys)
|
99
|
-
|
100
|
-
if type == 'left' or type == 'outer'
|
101
|
-
common, lmissing = rindex.each_with_index.partition(&:first)
|
102
|
-
lmissing = lmissing.transpose[-1]
|
103
|
-
lindex = common.flat_map{|r, i| [i] * r.length}
|
104
|
-
rindex.flatten!.compact!
|
105
|
-
|
106
|
-
lindex += lmissing
|
107
|
-
rindex += [-1] * lmissing.length
|
108
|
-
else
|
109
|
-
lindex = rindex.each_with_index.flat_map{|r, i| [i] * r.length if r}.compact
|
110
|
-
rindex.flatten!.compact!
|
111
|
-
end
|
112
|
-
|
113
|
-
|
114
|
-
if type == 'right' or type == 'outer'
|
115
|
-
left_cols -= key
|
116
|
-
rmissing = (0...other.nrows).to_a - rindex
|
117
|
-
lindex += [-1] * rmissing.length
|
118
|
-
rindex += rmissing
|
119
|
-
end
|
120
|
-
|
121
|
-
lindex = NArray.to_na(lindex)
|
122
|
-
rindex = NArray.to_na(rindex)
|
123
|
-
lblank = lindex.eq(-1)
|
124
|
-
rblank = rindex.eq(-1)
|
125
|
-
|
126
|
-
data = [
|
127
|
-
[left_cols, lindex, lblank, self],
|
128
|
-
[right_cols, rindex, rblank, other],
|
129
|
-
].flat_map do |cols, index, blanks, table|
|
130
|
-
cols.map do |k|
|
131
|
-
col = table.get_column(k)
|
132
|
-
padding = [nil] * (col.rank - 1)
|
133
|
-
col = col[*padding, index]
|
134
|
-
col[*padding, blanks] = [nil]
|
135
|
-
[k, col]
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
keys = []
|
140
|
-
if type == 'right' or type == 'outer'
|
141
|
-
keys = key.map do |k|
|
142
|
-
col = get_column(k)
|
143
|
-
padding = [nil] * (col.rank - 1)
|
144
|
-
col = col[*padding, lindex]
|
145
|
-
col[*padding, lblank] = other[k][*padding, rmissing]
|
146
|
-
[k, col]
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
self.class.new(keys + data)
|
151
|
-
end
|
152
|
-
|
153
|
-
|
154
44
|
def __static_view_cls__
|
155
45
|
self.class::StaticView
|
156
46
|
end
|
data/object_table.gemspec
CHANGED