charty 0.2.0 → 0.2.6
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 +71 -0
- data/.github/workflows/nmatrix.yml +67 -0
- data/.github/workflows/pycall.yml +86 -0
- data/Dockerfile.dev +9 -1
- data/Gemfile +18 -0
- data/README.md +177 -9
- data/Rakefile +4 -5
- data/charty.gemspec +10 -4
- data/examples/Gemfile +1 -0
- data/examples/active_record.ipynb +1 -1
- data/examples/daru.ipynb +1 -1
- data/examples/iris_dataset.ipynb +1 -1
- data/examples/nmatrix.ipynb +1 -1
- data/examples/{numo-narray.ipynb → numo_narray.ipynb} +1 -1
- data/examples/palette.rb +71 -0
- data/examples/sample.png +0 -0
- data/examples/sample_images/hist_gruff.png +0 -0
- data/examples/sample_pyplot.ipynb +40 -38
- 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 +13 -1
- data/lib/charty/backend_methods.rb +8 -0
- data/lib/charty/backends.rb +26 -1
- data/lib/charty/backends/bokeh.rb +31 -31
- data/lib/charty/backends/{google_chart.rb → google_charts.rb} +75 -33
- data/lib/charty/backends/gruff.rb +14 -3
- data/lib/charty/backends/plotly.rb +774 -9
- data/lib/charty/backends/pyplot.rb +611 -34
- data/lib/charty/backends/rubyplot.rb +2 -2
- data/lib/charty/backends/unicode_plot.rb +79 -0
- data/lib/charty/dash_pattern_generator.rb +57 -0
- data/lib/charty/index.rb +213 -0
- data/lib/charty/linspace.rb +1 -1
- data/lib/charty/plot_methods.rb +254 -0
- data/lib/charty/plotter.rb +10 -10
- data/lib/charty/plotters.rb +12 -0
- data/lib/charty/plotters/abstract_plotter.rb +243 -0
- data/lib/charty/plotters/bar_plotter.rb +201 -0
- data/lib/charty/plotters/box_plotter.rb +79 -0
- data/lib/charty/plotters/categorical_plotter.rb +380 -0
- data/lib/charty/plotters/count_plotter.rb +7 -0
- data/lib/charty/plotters/estimation_support.rb +84 -0
- data/lib/charty/plotters/line_plotter.rb +300 -0
- data/lib/charty/plotters/random_support.rb +25 -0
- data/lib/charty/plotters/relational_plotter.rb +635 -0
- data/lib/charty/plotters/scatter_plotter.rb +80 -0
- data/lib/charty/plotters/vector_plotter.rb +6 -0
- data/lib/charty/statistics.rb +114 -0
- data/lib/charty/table.rb +161 -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 +166 -0
- data/lib/charty/table_adapters/daru_adapter.rb +41 -3
- data/lib/charty/table_adapters/datasets_adapter.rb +17 -2
- data/lib/charty/table_adapters/hash_adapter.rb +143 -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 +163 -0
- data/lib/charty/util.rb +28 -0
- data/lib/charty/vector.rb +69 -0
- data/lib/charty/vector_adapters.rb +187 -0
- data/lib/charty/vector_adapters/array_adapter.rb +101 -0
- data/lib/charty/vector_adapters/daru_adapter.rb +163 -0
- data/lib/charty/vector_adapters/narray_adapter.rb +182 -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 +199 -0
- data/lib/charty/version.rb +1 -1
- metadata +121 -22
- data/.travis.yml +0 -10
@@ -0,0 +1,101 @@
|
|
1
|
+
require "date"
|
2
|
+
|
3
|
+
module Charty
|
4
|
+
module VectorAdapters
|
5
|
+
class ArrayAdapter < BaseAdapter
|
6
|
+
VectorAdapters.register(:array, self)
|
7
|
+
|
8
|
+
extend Forwardable
|
9
|
+
include Enumerable
|
10
|
+
|
11
|
+
def self.supported?(data)
|
12
|
+
case data
|
13
|
+
when Array
|
14
|
+
case data[0]
|
15
|
+
when Numeric, String, Time, Date, DateTime, true, false, nil
|
16
|
+
true
|
17
|
+
else
|
18
|
+
false
|
19
|
+
end
|
20
|
+
else
|
21
|
+
false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(data, index: nil)
|
26
|
+
@data = check_data(data)
|
27
|
+
self.index = index || RangeIndex.new(0 ... length)
|
28
|
+
end
|
29
|
+
|
30
|
+
include NameSupport
|
31
|
+
include IndexSupport
|
32
|
+
|
33
|
+
def_delegators :data, :values_at
|
34
|
+
|
35
|
+
def where(mask)
|
36
|
+
masked_data, masked_index = where_in_array(mask)
|
37
|
+
Charty::Vector.new(masked_data, index: masked_index, name: name)
|
38
|
+
end
|
39
|
+
|
40
|
+
def first_nonnil
|
41
|
+
data.drop_while(&:nil?).first
|
42
|
+
end
|
43
|
+
|
44
|
+
def boolean?
|
45
|
+
case first_nonnil
|
46
|
+
when true, false
|
47
|
+
true
|
48
|
+
else
|
49
|
+
false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def numeric?
|
54
|
+
case first_nonnil
|
55
|
+
when Numeric
|
56
|
+
true
|
57
|
+
else
|
58
|
+
false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def categorical?
|
63
|
+
false
|
64
|
+
end
|
65
|
+
|
66
|
+
def categories
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
|
70
|
+
def_delegator :data, :uniq, :unique_values
|
71
|
+
|
72
|
+
def group_by(grouper)
|
73
|
+
groups = data.each_index.group_by {|i| grouper[i] }
|
74
|
+
groups.map { |g, vals|
|
75
|
+
vals.collect! {|i| self[i] }
|
76
|
+
[g, Charty::Vector.new(vals)]
|
77
|
+
}.to_h
|
78
|
+
end
|
79
|
+
|
80
|
+
def drop_na
|
81
|
+
if numeric?
|
82
|
+
Charty::Vector.new(data.reject { |x| Util.missing?(x) })
|
83
|
+
else
|
84
|
+
Charty::Vector.new(data.compact)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def eq(val)
|
89
|
+
Charty::Vector.new(data.map {|x| x == val },
|
90
|
+
index: index,
|
91
|
+
name: name)
|
92
|
+
end
|
93
|
+
|
94
|
+
def notnull
|
95
|
+
Charty::Vector.new(data.map {|x| ! Util.missing?(x) },
|
96
|
+
index: index,
|
97
|
+
name: name)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
module Charty
|
2
|
+
module VectorAdapters
|
3
|
+
class DaruVectorAdapter < BaseAdapter
|
4
|
+
VectorAdapters.register(:daru_vector, self)
|
5
|
+
|
6
|
+
def self.supported?(data)
|
7
|
+
defined?(Daru::Vector) && data.is_a?(Daru::Vector)
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(data)
|
11
|
+
@data = check_data(data)
|
12
|
+
end
|
13
|
+
|
14
|
+
def_delegator :data, :size, :length
|
15
|
+
|
16
|
+
def index
|
17
|
+
DaruIndex.new(data.index)
|
18
|
+
end
|
19
|
+
|
20
|
+
def index=(new_index)
|
21
|
+
case new_index
|
22
|
+
when DaruIndex
|
23
|
+
data.index = new_index.values
|
24
|
+
when Index
|
25
|
+
data.index = new_index.to_a
|
26
|
+
else
|
27
|
+
data.index = new_index
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def_delegators :data, :name, :name=
|
32
|
+
|
33
|
+
def compare_data_equality(other)
|
34
|
+
case other
|
35
|
+
when DaruVectorAdapter
|
36
|
+
data == other.data
|
37
|
+
when ArrayAdapter
|
38
|
+
data.to_a == other.data
|
39
|
+
when NArrayAdapter, NMatrixAdapter, NumpyAdapter, PandasSeriesAdapter
|
40
|
+
other.compare_data_equality(self)
|
41
|
+
else
|
42
|
+
data == other.data.to_a
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def [](key)
|
47
|
+
case key
|
48
|
+
when Charty::Vector
|
49
|
+
where(key)
|
50
|
+
else
|
51
|
+
data[key]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def_delegators :data, :[]=, :to_a
|
56
|
+
|
57
|
+
def values_at(*indices)
|
58
|
+
indices.map {|i| data.at(i) }
|
59
|
+
end
|
60
|
+
|
61
|
+
def where(mask)
|
62
|
+
masked_data, masked_index = where_in_array(mask)
|
63
|
+
Charty::Vector.new(Daru::Vector.new(masked_data, index: masked_index), name: name)
|
64
|
+
end
|
65
|
+
|
66
|
+
def where_in_array(mask)
|
67
|
+
mask = check_mask_vector(mask)
|
68
|
+
masked_data = []
|
69
|
+
masked_index = []
|
70
|
+
mask.each_with_index do |f, i|
|
71
|
+
case f
|
72
|
+
when true, 1
|
73
|
+
masked_data << data[i]
|
74
|
+
masked_index << data.index.key(i)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
return masked_data, masked_index
|
78
|
+
end
|
79
|
+
|
80
|
+
def first_nonnil
|
81
|
+
data.drop_while(&:nil?).first
|
82
|
+
end
|
83
|
+
|
84
|
+
def boolean?
|
85
|
+
case
|
86
|
+
when numeric?, categorical?
|
87
|
+
false
|
88
|
+
else
|
89
|
+
case first_nonnil
|
90
|
+
when true, false
|
91
|
+
true
|
92
|
+
else
|
93
|
+
false
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def_delegators :data, :numeric?
|
99
|
+
def_delegator :data, :category?, :categorical?
|
100
|
+
|
101
|
+
def categories
|
102
|
+
data.categories.compact if categorical?
|
103
|
+
end
|
104
|
+
|
105
|
+
def unique_values
|
106
|
+
data.uniq.to_a
|
107
|
+
end
|
108
|
+
|
109
|
+
def group_by(grouper)
|
110
|
+
case grouper
|
111
|
+
when Daru::Vector
|
112
|
+
if grouper.category?
|
113
|
+
# TODO: A categorical Daru::Vector cannot perform group_by well
|
114
|
+
grouper = Daru::Vector.new(grouper.to_a)
|
115
|
+
end
|
116
|
+
groups = grouper.group_by.groups
|
117
|
+
groups.map { |g, indices|
|
118
|
+
[g.first, Charty::Vector.new(data[*indices])]
|
119
|
+
}.to_h
|
120
|
+
when Charty::Vector
|
121
|
+
case grouper.data
|
122
|
+
when Daru::Vector
|
123
|
+
return group_by(grouper.data)
|
124
|
+
else
|
125
|
+
return group_by(Daru::Vector.new(grouper.to_a))
|
126
|
+
end
|
127
|
+
else
|
128
|
+
return group_by(Charty::Vector.new(grouper))
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def drop_na
|
133
|
+
values = data.reject {|x| Util.missing?(x) }
|
134
|
+
Charty::Vector.new(Daru::Vector.new(values))
|
135
|
+
end
|
136
|
+
|
137
|
+
def eq(val)
|
138
|
+
Charty::Vector.new(data.eq(val).to_a,
|
139
|
+
index: data.index.to_a,
|
140
|
+
name: name)
|
141
|
+
end
|
142
|
+
|
143
|
+
def notnull
|
144
|
+
notnull_data = data.map {|x| ! Util.missing?(x) }
|
145
|
+
Charty::Vector.new(notnull_data, index: data.index.to_a, name: name)
|
146
|
+
end
|
147
|
+
|
148
|
+
def_delegator :data, :mean
|
149
|
+
|
150
|
+
def stdev(population: false)
|
151
|
+
if population
|
152
|
+
data.standard_deviation_sample
|
153
|
+
else
|
154
|
+
data.standard_deviation_population
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def percentile(q)
|
159
|
+
data.linear_percentile(q)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
module Charty
|
2
|
+
module VectorAdapters
|
3
|
+
class NArrayAdapter < BaseAdapter
|
4
|
+
VectorAdapters.register(:narray, self)
|
5
|
+
|
6
|
+
extend Forwardable
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
def self.supported?(data)
|
10
|
+
defined?(Numo::NArray) && data.is_a?(Numo::NArray)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(data)
|
14
|
+
@data = check_data(data)
|
15
|
+
self.index = index || RangeIndex.new(0 ... length)
|
16
|
+
end
|
17
|
+
|
18
|
+
def compare_data_equality(other)
|
19
|
+
case other
|
20
|
+
when ArrayAdapter, NArrayAdapter
|
21
|
+
data == other.data
|
22
|
+
when NumpyAdapter, PandasSeriesAdapter
|
23
|
+
other.compare_data_equality(self)
|
24
|
+
else
|
25
|
+
data == other.data.to_a
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
include NameSupport
|
30
|
+
include IndexSupport
|
31
|
+
|
32
|
+
def where(mask)
|
33
|
+
mask = check_mask_vector(mask)
|
34
|
+
case mask.data
|
35
|
+
when Numo::Bit
|
36
|
+
bits = mask.data
|
37
|
+
masked_data = data[bits]
|
38
|
+
masked_index = bits.where.map {|i| index[i] }.to_a
|
39
|
+
else
|
40
|
+
masked_data, masked_index = where_in_array(mask)
|
41
|
+
masked_data = data.class[*masked_data]
|
42
|
+
end
|
43
|
+
Charty::Vector.new(masked_data, index: masked_index, name: name)
|
44
|
+
end
|
45
|
+
|
46
|
+
def boolean?
|
47
|
+
case data
|
48
|
+
when Numo::Bit
|
49
|
+
true
|
50
|
+
when Numo::RObject
|
51
|
+
i, n = 0, data.size
|
52
|
+
while i < n
|
53
|
+
case data[i]
|
54
|
+
when nil, true, false
|
55
|
+
# do nothing
|
56
|
+
else
|
57
|
+
return false
|
58
|
+
end
|
59
|
+
i += 1
|
60
|
+
end
|
61
|
+
true
|
62
|
+
else
|
63
|
+
false
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def numeric?
|
68
|
+
case data
|
69
|
+
when Numo::Bit,
|
70
|
+
Numo::RObject
|
71
|
+
false
|
72
|
+
else
|
73
|
+
true
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def categorical?
|
78
|
+
false
|
79
|
+
end
|
80
|
+
|
81
|
+
def categories
|
82
|
+
nil
|
83
|
+
end
|
84
|
+
|
85
|
+
def unique_values
|
86
|
+
existence = {}
|
87
|
+
i, n = 0, data.size
|
88
|
+
unique = []
|
89
|
+
while i < n
|
90
|
+
x = data[i]
|
91
|
+
unless existence[x]
|
92
|
+
unique << x
|
93
|
+
existence[x] = true
|
94
|
+
end
|
95
|
+
i += 1
|
96
|
+
end
|
97
|
+
unique
|
98
|
+
end
|
99
|
+
|
100
|
+
def group_by(grouper)
|
101
|
+
case grouper
|
102
|
+
when Charty::Vector
|
103
|
+
# nothing to do
|
104
|
+
else
|
105
|
+
grouper = Charty::Vector.new(grouper)
|
106
|
+
end
|
107
|
+
|
108
|
+
group_keys = grouper.unique_values
|
109
|
+
|
110
|
+
case grouper.data
|
111
|
+
when Numo::NArray
|
112
|
+
grouper = grouper.data
|
113
|
+
else
|
114
|
+
grouper = Numo::NArray[*grouper.to_a]
|
115
|
+
end
|
116
|
+
|
117
|
+
group_keys.map { |g|
|
118
|
+
[g, Charty::Vector.new(data[grouper.eq(g)])]
|
119
|
+
}.to_h
|
120
|
+
end
|
121
|
+
|
122
|
+
def drop_na
|
123
|
+
case data
|
124
|
+
when Numo::DFloat, Numo::SFloat, Numo::DComplex, Numo::SComplex
|
125
|
+
Charty::Vector.new(data[~data.isnan])
|
126
|
+
when Numo::RObject
|
127
|
+
where_is_nan = data.isnan
|
128
|
+
values = []
|
129
|
+
i, n = 0, data.size
|
130
|
+
while i < n
|
131
|
+
x = data[i]
|
132
|
+
unless x.nil? || where_is_nan[i] == 1
|
133
|
+
values << x
|
134
|
+
end
|
135
|
+
i += 1
|
136
|
+
end
|
137
|
+
Charty::Vector.new(Numo::RObject[*values])
|
138
|
+
else
|
139
|
+
self
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def eq(val)
|
144
|
+
Charty::Vector.new(data.eq(val),
|
145
|
+
index: index,
|
146
|
+
name: name)
|
147
|
+
end
|
148
|
+
|
149
|
+
def notnull
|
150
|
+
case data
|
151
|
+
when Numo::RObject
|
152
|
+
i, n = 0, length
|
153
|
+
notnull_data = Numo::Bit.zeros(n)
|
154
|
+
while i < n
|
155
|
+
notnull_data[i] = ! Util.missing?(data[i])
|
156
|
+
i += 1
|
157
|
+
end
|
158
|
+
when ->(x) { x.respond_to?(:isnan) }
|
159
|
+
notnull_data = ~data.isnan
|
160
|
+
else
|
161
|
+
notnull_data = Numo::Bit.ones(length)
|
162
|
+
end
|
163
|
+
Charty::Vector.new(notnull_data, index: index, name: name)
|
164
|
+
end
|
165
|
+
|
166
|
+
def mean
|
167
|
+
data.mean(nan: true)
|
168
|
+
end
|
169
|
+
|
170
|
+
def stdev(population: false)
|
171
|
+
s = data.stddev(nan: true)
|
172
|
+
if population
|
173
|
+
# Numo::NArray does not support population standard deviation
|
174
|
+
n = data.isnan.sum
|
175
|
+
s * (n - 1) / n
|
176
|
+
else
|
177
|
+
s
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Charty
|
2
|
+
module VectorAdapters
|
3
|
+
class NMatrixAdapter < BaseAdapter
|
4
|
+
VectorAdapters.register(:nmatrix, self)
|
5
|
+
|
6
|
+
extend Forwardable
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
def self.supported?(data)
|
10
|
+
defined?(NMatrix) && data.is_a?(NMatrix)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(data)
|
14
|
+
@data = check_data(data)
|
15
|
+
self.index = index || RangeIndex.new(0 ... length)
|
16
|
+
end
|
17
|
+
|
18
|
+
def compare_data_equality(other)
|
19
|
+
case other
|
20
|
+
when NMatrixAdapter
|
21
|
+
data == other.data
|
22
|
+
when ArrayAdapter, DaruVectorAdapter
|
23
|
+
data.to_a == other.data.to_a
|
24
|
+
when NArrayAdapter, NumpyAdapter, PandasSeriesAdapter
|
25
|
+
other.compare_data_equality(self)
|
26
|
+
else
|
27
|
+
data == other.data.to_a
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
include NameSupport
|
32
|
+
include IndexSupport
|
33
|
+
|
34
|
+
alias length size
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|