charty 0.2.3 → 0.2.4
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/.github/workflows/ci.yml +56 -23
- data/.github/workflows/nmatrix.yml +67 -0
- data/.github/workflows/pycall.yml +86 -0
- data/Gemfile +18 -0
- data/README.md +123 -4
- data/Rakefile +4 -5
- data/charty.gemspec +1 -3
- data/examples/sample_images/hist_gruff.png +0 -0
- data/images/penguins_body_mass_g_flipper_length_mm_scatter_plot.png +0 -0
- data/images/penguins_body_mass_g_flipper_length_mm_species_scatter_plot.png +0 -0
- data/images/penguins_body_mass_g_flipper_length_mm_species_sex_scatter_plot.png +0 -0
- data/images/penguins_species_body_mass_g_bar_plot_h.png +0 -0
- data/images/penguins_species_body_mass_g_bar_plot_v.png +0 -0
- data/images/penguins_species_body_mass_g_box_plot_h.png +0 -0
- data/images/penguins_species_body_mass_g_box_plot_v.png +0 -0
- data/images/penguins_species_body_mass_g_sex_bar_plot_v.png +0 -0
- data/images/penguins_species_body_mass_g_sex_box_plot_v.png +0 -0
- data/lib/charty.rb +4 -0
- data/lib/charty/backends/gruff.rb +13 -2
- data/lib/charty/backends/plotly.rb +322 -20
- data/lib/charty/backends/pyplot.rb +416 -64
- data/lib/charty/index.rb +213 -0
- data/lib/charty/linspace.rb +1 -1
- data/lib/charty/missing_value_support.rb +14 -0
- data/lib/charty/plot_methods.rb +173 -8
- data/lib/charty/plotters.rb +7 -0
- data/lib/charty/plotters/abstract_plotter.rb +87 -12
- data/lib/charty/plotters/bar_plotter.rb +200 -3
- data/lib/charty/plotters/box_plotter.rb +75 -7
- data/lib/charty/plotters/categorical_plotter.rb +272 -40
- data/lib/charty/plotters/count_plotter.rb +7 -0
- data/lib/charty/plotters/estimation_support.rb +84 -0
- data/lib/charty/plotters/random_support.rb +25 -0
- data/lib/charty/plotters/relational_plotter.rb +518 -0
- data/lib/charty/plotters/scatter_plotter.rb +115 -0
- data/lib/charty/plotters/vector_plotter.rb +6 -0
- data/lib/charty/statistics.rb +87 -2
- data/lib/charty/table.rb +50 -15
- data/lib/charty/table_adapters.rb +2 -0
- data/lib/charty/table_adapters/active_record_adapter.rb +17 -9
- data/lib/charty/table_adapters/base_adapter.rb +69 -0
- data/lib/charty/table_adapters/daru_adapter.rb +37 -3
- data/lib/charty/table_adapters/datasets_adapter.rb +6 -2
- data/lib/charty/table_adapters/hash_adapter.rb +130 -16
- data/lib/charty/table_adapters/narray_adapter.rb +25 -6
- data/lib/charty/table_adapters/nmatrix_adapter.rb +15 -5
- data/lib/charty/table_adapters/pandas_adapter.rb +81 -0
- data/lib/charty/vector.rb +69 -0
- data/lib/charty/vector_adapters.rb +183 -0
- data/lib/charty/vector_adapters/array_adapter.rb +109 -0
- data/lib/charty/vector_adapters/daru_adapter.rb +171 -0
- data/lib/charty/vector_adapters/narray_adapter.rb +187 -0
- data/lib/charty/vector_adapters/nmatrix_adapter.rb +37 -0
- data/lib/charty/vector_adapters/numpy_adapter.rb +168 -0
- data/lib/charty/vector_adapters/pandas_adapter.rb +200 -0
- data/lib/charty/version.rb +1 -1
- metadata +33 -45
@@ -1,29 +1,63 @@
|
|
1
|
+
require "delegate"
|
2
|
+
|
1
3
|
module Charty
|
2
4
|
module TableAdapters
|
3
|
-
class DaruAdapter
|
5
|
+
class DaruAdapter < BaseAdapter
|
4
6
|
TableAdapters.register(:daru, self)
|
5
7
|
|
8
|
+
extend Forwardable
|
6
9
|
include Enumerable
|
7
10
|
|
8
11
|
def self.supported?(data)
|
9
12
|
defined?(Daru::DataFrame) && data.is_a?(Daru::DataFrame)
|
10
13
|
end
|
11
14
|
|
12
|
-
def initialize(data)
|
15
|
+
def initialize(data, columns: nil, index: nil)
|
13
16
|
@data = check_type(Daru::DataFrame, data, :data)
|
17
|
+
|
18
|
+
self.columns = columns unless columns.nil?
|
19
|
+
self.index = index unless index.nil?
|
14
20
|
end
|
15
21
|
|
16
22
|
attr_reader :data
|
17
23
|
|
24
|
+
def index
|
25
|
+
DaruIndex.new(data.index)
|
26
|
+
end
|
27
|
+
|
28
|
+
def_delegator :data, :index=
|
29
|
+
|
30
|
+
def columns
|
31
|
+
DaruIndex.new(data.vectors)
|
32
|
+
end
|
33
|
+
|
34
|
+
def columns=(values)
|
35
|
+
data.vectors = Daru::Index.coerce(values)
|
36
|
+
end
|
37
|
+
|
18
38
|
def column_names
|
19
39
|
@data.vectors.to_a
|
20
40
|
end
|
21
41
|
|
42
|
+
def compare_data_equality(other)
|
43
|
+
case other
|
44
|
+
when DaruAdapter
|
45
|
+
data == other.data
|
46
|
+
else
|
47
|
+
super
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
22
51
|
def [](row, column)
|
23
52
|
if row
|
24
53
|
@data[column][row]
|
25
54
|
else
|
26
|
-
@data
|
55
|
+
column_data = if @data.has_vector?(column)
|
56
|
+
@data[column]
|
57
|
+
else
|
58
|
+
@data[column.to_s]
|
59
|
+
end
|
60
|
+
Vector.new(column_data)
|
27
61
|
end
|
28
62
|
end
|
29
63
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Charty
|
2
2
|
module TableAdapters
|
3
|
-
class DatasetsAdapter
|
3
|
+
class DatasetsAdapter < BaseAdapter
|
4
4
|
TableAdapters.register(:datasets, self)
|
5
5
|
|
6
6
|
include Enumerable
|
@@ -23,6 +23,10 @@ module Charty
|
|
23
23
|
@table.column_names
|
24
24
|
end
|
25
25
|
|
26
|
+
def length
|
27
|
+
data.n_rows
|
28
|
+
end
|
29
|
+
|
26
30
|
def each(&block)
|
27
31
|
return to_enum(__method__) unless block_given?
|
28
32
|
|
@@ -37,7 +41,7 @@ module Charty
|
|
37
41
|
return nil if record.nil?
|
38
42
|
record[column]
|
39
43
|
else
|
40
|
-
@table[column]
|
44
|
+
Vector.new(@table[column], index: index, name: column)
|
41
45
|
end
|
42
46
|
end
|
43
47
|
end
|
@@ -1,14 +1,10 @@
|
|
1
1
|
require 'date'
|
2
|
-
require 'forwardable'
|
3
2
|
|
4
3
|
module Charty
|
5
4
|
module TableAdapters
|
6
|
-
class HashAdapter
|
5
|
+
class HashAdapter < BaseAdapter
|
7
6
|
TableAdapters.register(:hash, self)
|
8
7
|
|
9
|
-
extend Forwardable
|
10
|
-
include Enumerable
|
11
|
-
|
12
8
|
def self.supported?(data)
|
13
9
|
case data
|
14
10
|
when []
|
@@ -29,47 +25,165 @@ module Charty
|
|
29
25
|
|
30
26
|
def self.array?(data)
|
31
27
|
case data
|
32
|
-
when
|
33
|
-
|
34
|
-
|
35
|
-
|
28
|
+
when Charty::Vector
|
29
|
+
true
|
30
|
+
when Array, method(:daru_vector?), method(:narray_vector?), method(:nmatrix_vector?),
|
31
|
+
method(:numpy_vector?), method(:pandas_series?)
|
36
32
|
true
|
37
33
|
else
|
38
34
|
false
|
39
35
|
end
|
40
36
|
end
|
41
37
|
|
42
|
-
def
|
38
|
+
def self.daru_vector?(x)
|
39
|
+
defined?(Daru::Vector) && x.is_a?(Daru::Vector)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.narray_vector?(x)
|
43
|
+
defined?(Numo::NArray) && x.is_a?(Numo::NArray) && x.ndim == 1
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.nmatrix_vector?(x)
|
47
|
+
defined?(NMatrix) && x.is_a?(NMatrix) && x.dim == 1
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.numpy_vector?(x)
|
51
|
+
defined?(Numpy::NDArray) && x.is_a?(Numpy::NDArray) && x.ndim == 1
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.pandas_series?(x)
|
55
|
+
defined?(Pandas::Series) && x.is_a?(Pandas::Series)
|
56
|
+
end
|
57
|
+
|
58
|
+
def initialize(data, columns: nil, index: nil)
|
43
59
|
case data
|
44
60
|
when Hash
|
45
|
-
|
61
|
+
arrays = data.values
|
62
|
+
columns ||= data.keys
|
46
63
|
when Array
|
47
64
|
case data[0]
|
48
65
|
when Numeric, String, Time, Date
|
49
|
-
|
50
|
-
@data = make_data_from_records(data, columns)
|
66
|
+
arrays = [data]
|
51
67
|
when Hash
|
52
|
-
|
68
|
+
raise NotImplementedError,
|
69
|
+
"an array of records is not supported"
|
53
70
|
when self.class.method(:array?)
|
54
71
|
unsupported_data_format unless data.all?(&self.class.method(:array?))
|
55
|
-
|
72
|
+
arrays = data.map(&:to_a).transpose
|
56
73
|
else
|
57
74
|
unsupported_data_format
|
58
75
|
end
|
59
76
|
else
|
60
77
|
unsupported_data_format
|
61
78
|
end
|
79
|
+
|
80
|
+
unless arrays.empty?
|
81
|
+
arrays, columns, index = check_data(arrays, columns, index)
|
82
|
+
end
|
83
|
+
|
84
|
+
@data = arrays.map.with_index {|a, i| [columns[i], a] }.to_h
|
85
|
+
self.columns = columns unless columns.nil?
|
86
|
+
self.index = index unless index.nil?
|
87
|
+
end
|
88
|
+
|
89
|
+
private def check_data(arrays, columns, index)
|
90
|
+
# NOTE: After Ruby 2.7, we can write the following code by filter_map:
|
91
|
+
# indexes = arrays.filter_map {|ary| ary.index if ary.is_a?(Charty::Vector) }
|
92
|
+
indexes = []
|
93
|
+
arrays.each do |array|
|
94
|
+
index = case array
|
95
|
+
when Charty::Vector
|
96
|
+
array.index
|
97
|
+
when ->(x) { defined?(Daru) && x.is_a?(Daru::Vector) }
|
98
|
+
Charty::DaruIndex.new(array.index)
|
99
|
+
when ->(x) { defined?(Pandas) && x.is_a?(Pandas::Series) }
|
100
|
+
Charty::PandasIndex.new(array.index)
|
101
|
+
else
|
102
|
+
if index.nil?
|
103
|
+
RangeIndex.new(0 ... array.size)
|
104
|
+
else
|
105
|
+
check_and_convert_index(index, :index, array.size)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
indexes << index
|
109
|
+
end
|
110
|
+
index = union_indexes(*indexes)
|
111
|
+
|
112
|
+
arrays = arrays.map do |array|
|
113
|
+
case array
|
114
|
+
when Charty::Vector
|
115
|
+
array.data
|
116
|
+
when Hash
|
117
|
+
raise NotImplementedError
|
118
|
+
when self.class.method(:array?)
|
119
|
+
array
|
120
|
+
else
|
121
|
+
Array.try_convert(array)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
columns = generate_column_names(arrays.length, columns)
|
126
|
+
|
127
|
+
return arrays, columns, index
|
128
|
+
end
|
129
|
+
|
130
|
+
private def union_indexes(*indexes)
|
131
|
+
result = nil
|
132
|
+
while result.nil? && indexes.length > 0
|
133
|
+
result = indexes.shift
|
134
|
+
end
|
135
|
+
indexes.each do |index|
|
136
|
+
next if index.nil?
|
137
|
+
result = result.union(index)
|
138
|
+
end
|
139
|
+
result
|
62
140
|
end
|
63
141
|
|
64
142
|
attr_reader :data
|
65
143
|
|
66
144
|
def_delegator :@data, :keys, :column_names
|
67
145
|
|
146
|
+
def length
|
147
|
+
case
|
148
|
+
when column_names.empty?
|
149
|
+
0
|
150
|
+
else
|
151
|
+
data[column_names[0]].size
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def column_length
|
156
|
+
data.length
|
157
|
+
end
|
158
|
+
|
159
|
+
def compare_data_equality(other)
|
160
|
+
case other
|
161
|
+
when DaruAdapter, PandasDataFrameAdapter
|
162
|
+
other.compare_data_equality(self)
|
163
|
+
else
|
164
|
+
super
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
68
168
|
def [](row, column)
|
69
169
|
if row
|
70
170
|
@data[column][row]
|
71
171
|
else
|
72
|
-
|
172
|
+
case column
|
173
|
+
when Symbol
|
174
|
+
sym_key = column
|
175
|
+
str_key = column.to_s
|
176
|
+
else
|
177
|
+
str_key = String.try_convert(column)
|
178
|
+
sym_key = str_key.to_sym
|
179
|
+
end
|
180
|
+
|
181
|
+
column_data = if @data.key?(sym_key)
|
182
|
+
@data[sym_key]
|
183
|
+
else
|
184
|
+
@data[str_key]
|
185
|
+
end
|
186
|
+
Vector.new(column_data, index: index, name: column)
|
73
187
|
end
|
74
188
|
end
|
75
189
|
|
@@ -1,13 +1,13 @@
|
|
1
1
|
module Charty
|
2
2
|
module TableAdapters
|
3
|
-
class NArrayAdapter
|
3
|
+
class NArrayAdapter < BaseAdapter
|
4
4
|
TableAdapters.register(:narray, self)
|
5
5
|
|
6
6
|
def self.supported?(data)
|
7
7
|
defined?(Numo::NArray) && data.is_a?(Numo::NArray) && data.ndim <= 2
|
8
8
|
end
|
9
9
|
|
10
|
-
def initialize(data, columns: nil)
|
10
|
+
def initialize(data, columns: nil, index: nil)
|
11
11
|
case data.ndim
|
12
12
|
when 1
|
13
13
|
data = data.reshape(data.length, 1)
|
@@ -17,23 +17,42 @@ module Charty
|
|
17
17
|
raise ArgumentError, "Unsupported data format"
|
18
18
|
end
|
19
19
|
@data = data
|
20
|
-
|
20
|
+
self.columns = Index.new(generate_column_names(data.shape[1], columns))
|
21
|
+
self.index = index || RangeIndex.new(0 ... length)
|
21
22
|
end
|
22
23
|
|
23
|
-
attr_reader :
|
24
|
+
attr_reader :data
|
25
|
+
|
26
|
+
def length
|
27
|
+
data.shape[0]
|
28
|
+
end
|
29
|
+
|
30
|
+
def column_length
|
31
|
+
data.shape[1]
|
32
|
+
end
|
33
|
+
|
34
|
+
def compare_data_equality(other)
|
35
|
+
case other
|
36
|
+
when NArrayAdapter
|
37
|
+
data == other.data
|
38
|
+
else
|
39
|
+
super
|
40
|
+
end
|
41
|
+
end
|
24
42
|
|
25
43
|
def [](row, column)
|
26
44
|
if row
|
27
45
|
@data[row, resolve_column_index(column)]
|
28
46
|
else
|
29
|
-
@data[true, resolve_column_index(column)]
|
47
|
+
column_data = @data[true, resolve_column_index(column)]
|
48
|
+
Charty::Vector.new(column_data, index: index, name: column)
|
30
49
|
end
|
31
50
|
end
|
32
51
|
|
33
52
|
private def resolve_column_index(column)
|
34
53
|
case column
|
35
54
|
when String, Symbol
|
36
|
-
index = column_names.index(column.to_s)
|
55
|
+
index = column_names.index(column.to_sym) || column_names.index(column.to_s)
|
37
56
|
return index if index
|
38
57
|
raise IndexError, "invalid column name: #{column}"
|
39
58
|
when Integer
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Charty
|
2
2
|
module TableAdapters
|
3
|
-
class NMatrixAdapter
|
3
|
+
class NMatrixAdapter < BaseAdapter
|
4
4
|
TableAdapters.register(:nmatrix, self)
|
5
5
|
|
6
6
|
def self.supported?(data)
|
@@ -17,23 +17,33 @@ module Charty
|
|
17
17
|
raise ArgumentError, "Unsupported data format"
|
18
18
|
end
|
19
19
|
@data = data
|
20
|
-
|
20
|
+
self.columns = Index.new(generate_column_names(data.shape[1], columns))
|
21
|
+
self.index = index || RangeIndex.new(0 ... length)
|
21
22
|
end
|
22
23
|
|
23
|
-
attr_reader :
|
24
|
+
attr_reader :data
|
25
|
+
|
26
|
+
def length
|
27
|
+
data.shape[0]
|
28
|
+
end
|
29
|
+
|
30
|
+
def column_length
|
31
|
+
data.shape[1]
|
32
|
+
end
|
24
33
|
|
25
34
|
def [](row, column)
|
26
35
|
if row
|
27
36
|
@data[row, resolve_column_index(column)]
|
28
37
|
else
|
29
|
-
@data[:*, resolve_column_index(column)].reshape([@data.shape[0]])
|
38
|
+
column_data = @data[:*, resolve_column_index(column)].reshape([@data.shape[0]])
|
39
|
+
Charty::Vector.new(column_data, index: index, name: column)
|
30
40
|
end
|
31
41
|
end
|
32
42
|
|
33
43
|
private def resolve_column_index(column)
|
34
44
|
case column
|
35
45
|
when String, Symbol
|
36
|
-
index = column_names.index(column.to_s)
|
46
|
+
index = column_names.index(column.to_sym) || column_names.index(column.to_s)
|
37
47
|
return index if index
|
38
48
|
raise IndexError, "invalid column name: #{column}"
|
39
49
|
when Integer
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
3
|
+
module Charty
|
4
|
+
module TableAdapters
|
5
|
+
class PandasDataFrameAdapter < BaseAdapter
|
6
|
+
TableAdapters.register(:pandas_data_frame, self)
|
7
|
+
|
8
|
+
extend Forwardable
|
9
|
+
include Enumerable
|
10
|
+
|
11
|
+
def self.supported?(data)
|
12
|
+
defined?(Pandas::DataFrame) && data.is_a?(Pandas::DataFrame)
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(data, columns: nil, index: nil)
|
16
|
+
@data = check_type(Pandas::DataFrame, data, :data)
|
17
|
+
|
18
|
+
self.columns = columns unless columns.nil?
|
19
|
+
self.index = index unless index.nil?
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_reader :data
|
23
|
+
|
24
|
+
def columns
|
25
|
+
PandasIndex.new(data.columns)
|
26
|
+
end
|
27
|
+
|
28
|
+
def columns=(new_columns)
|
29
|
+
case new_columns
|
30
|
+
when PandasIndex
|
31
|
+
data.columns = new_columns.values
|
32
|
+
when Index
|
33
|
+
data.columns = new_columns.to_a
|
34
|
+
else
|
35
|
+
data.columns = new_columns
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def index
|
40
|
+
PandasIndex.new(data.index)
|
41
|
+
end
|
42
|
+
|
43
|
+
def index=(new_index)
|
44
|
+
case new_index
|
45
|
+
when PandasIndex
|
46
|
+
data.index = new_index.values
|
47
|
+
when Index
|
48
|
+
data.index = new_index.to_a
|
49
|
+
else
|
50
|
+
data.index = new_index
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def column_names
|
55
|
+
@data.columns.to_a
|
56
|
+
end
|
57
|
+
|
58
|
+
def compare_data_equality(other)
|
59
|
+
case other
|
60
|
+
when PandasDataFrameAdapter
|
61
|
+
data.equals(other.data)
|
62
|
+
else
|
63
|
+
super
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def [](row, column)
|
68
|
+
if row
|
69
|
+
@data[column][row]
|
70
|
+
else
|
71
|
+
Vector.new(@data[column])
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
private def check_type(type, data, name)
|
76
|
+
return data if data.is_a?(type)
|
77
|
+
raise TypeError, "#{name} must be a #{type}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|