charty 0.2.0 → 0.2.1
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 +38 -0
- data/charty.gemspec +1 -0
- 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_pyplot.ipynb +40 -38
- data/lib/charty.rb +7 -0
- data/lib/charty/backend_methods.rb +8 -0
- data/lib/charty/backends.rb +25 -1
- data/lib/charty/backends/bokeh.rb +29 -29
- data/lib/charty/backends/{google_chart.rb → google_charts.rb} +74 -32
- data/lib/charty/backends/plotly.rb +145 -7
- data/lib/charty/backends/pyplot.rb +163 -33
- data/lib/charty/backends/rubyplot.rb +1 -1
- data/lib/charty/palette.rb +235 -0
- data/lib/charty/plot_methods.rb +19 -0
- data/lib/charty/plotter.rb +9 -9
- data/lib/charty/plotters.rb +4 -0
- data/lib/charty/plotters/abstract_plotter.rb +81 -0
- data/lib/charty/plotters/bar_plotter.rb +19 -0
- data/lib/charty/plotters/box_plotter.rb +26 -0
- data/lib/charty/plotters/categorical_plotter.rb +148 -0
- data/lib/charty/statistics.rb +29 -0
- data/lib/charty/table.rb +1 -0
- data/lib/charty/table_adapters/active_record_adapter.rb +1 -1
- data/lib/charty/table_adapters/daru_adapter.rb +2 -0
- data/lib/charty/table_adapters/datasets_adapter.rb +4 -0
- data/lib/charty/table_adapters/hash_adapter.rb +2 -0
- data/lib/charty/table_adapters/narray_adapter.rb +1 -1
- data/lib/charty/table_adapters/nmatrix_adapter.rb +1 -1
- data/lib/charty/version.rb +1 -1
- metadata +30 -5
- data/.travis.yml +0 -10
@@ -0,0 +1,19 @@
|
|
1
|
+
module Charty
|
2
|
+
module PlotMethods
|
3
|
+
# Show the given data as rectangular bars.
|
4
|
+
#
|
5
|
+
# @param x
|
6
|
+
# @param y
|
7
|
+
# @param color
|
8
|
+
# @param data
|
9
|
+
def bar_plot(x=nil, y=nil, color=nil, **options, &block)
|
10
|
+
Plotters::BarPlotter.new(x, y, color, **options, &block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def box_plot(x=nil, y=nil, color=nil, **options, &block)
|
14
|
+
Plotters::BoxPlotter.new(x, y, color, **options, &block)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
extend PlotMethods
|
19
|
+
end
|
data/lib/charty/plotter.rb
CHANGED
@@ -240,19 +240,19 @@ module Charty
|
|
240
240
|
@backend.render(self, filename)
|
241
241
|
end
|
242
242
|
|
243
|
-
def save(filename=nil)
|
244
|
-
@backend.save(self, filename)
|
243
|
+
def save(filename=nil, **kw)
|
244
|
+
@backend.save(self, filename, **kw)
|
245
245
|
end
|
246
246
|
|
247
247
|
def apply(backend)
|
248
248
|
case
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
249
|
+
when !@series.empty?
|
250
|
+
backend.series = @series
|
251
|
+
when @function
|
252
|
+
linspace = Linspace.new(@range[:x], 100)
|
253
|
+
# TODO: set label with function
|
254
|
+
# TODO: set ys to xs when gruff curve with function
|
255
|
+
@series << Series.new(linspace.to_a, linspace.map{|x| @function.call(x) }, label: "function" )
|
256
256
|
end
|
257
257
|
|
258
258
|
@backend = backend
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Charty
|
2
|
+
module Plotters
|
3
|
+
class AbstractPlotter
|
4
|
+
def initialize(x, y, color, **options)
|
5
|
+
self.x = x
|
6
|
+
self.y = y
|
7
|
+
self.color = color
|
8
|
+
self.data = data
|
9
|
+
self.palette = palette
|
10
|
+
substitute_options(options)
|
11
|
+
yield self if block_given?
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :x, :y, :color, :data, :palette
|
15
|
+
|
16
|
+
def x=(x)
|
17
|
+
@x = check_dimension(x, :x)
|
18
|
+
end
|
19
|
+
|
20
|
+
def y=(y)
|
21
|
+
@y = check_dimension(y, :y)
|
22
|
+
end
|
23
|
+
|
24
|
+
def color=(color)
|
25
|
+
# @color = check_dimension(color, :color)
|
26
|
+
unless color.nil?
|
27
|
+
raise NotImplementedError,
|
28
|
+
"Specifying color variable is not supported yet"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def data=(data)
|
33
|
+
@data = case data
|
34
|
+
when nil, Charty::Table
|
35
|
+
data
|
36
|
+
else
|
37
|
+
Charty::Table.new(data)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def palette=(palette)
|
42
|
+
@palette = case palette
|
43
|
+
when nil, Charty::Palette, Symbol, String
|
44
|
+
palette
|
45
|
+
else
|
46
|
+
raise ArgumentError,
|
47
|
+
"invalid type for palette (given #{palette.class}, " +
|
48
|
+
"expected Charty::Palette, Symbol, or String)"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private def substitute_options(options)
|
53
|
+
options.each do |key, val|
|
54
|
+
send("#{key}=", val)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private def check_dimension(value, name)
|
59
|
+
case value
|
60
|
+
when nil, Symbol, String, method(:array?)
|
61
|
+
value
|
62
|
+
when ->(x) { x.respond_to?(:to_str) }
|
63
|
+
value.to_str
|
64
|
+
else
|
65
|
+
raise ArgumentError,
|
66
|
+
"invalid type of dimension for #{name} (given #{value.inspect})",
|
67
|
+
caller
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private def array?(value)
|
72
|
+
TableAdapters::HashAdapter.array?(value)
|
73
|
+
end
|
74
|
+
|
75
|
+
def to_iruby
|
76
|
+
result = render
|
77
|
+
["text/html", result] if result
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Charty
|
2
|
+
module Plotters
|
3
|
+
class BarPlotter < CategoricalPlotter
|
4
|
+
def render
|
5
|
+
backend = Backends.current
|
6
|
+
backend.begin_figure
|
7
|
+
draw_bars(backend)
|
8
|
+
annotate_axes(backend)
|
9
|
+
backend.show
|
10
|
+
end
|
11
|
+
|
12
|
+
private def draw_bars(backend)
|
13
|
+
statistic = @plot_data.map {|xs| Statistics.mean(xs) }
|
14
|
+
bar_pos = (0 ... statistic.length).to_a
|
15
|
+
backend.bar(bar_pos, statistic, color: @colors)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Charty
|
2
|
+
module Plotters
|
3
|
+
class BoxPlotter < CategoricalPlotter
|
4
|
+
def render
|
5
|
+
backend = Backends.current
|
6
|
+
backend.begin_figure
|
7
|
+
draw_box_plot(backend)
|
8
|
+
annotate_axes(backend)
|
9
|
+
backend.show
|
10
|
+
end
|
11
|
+
|
12
|
+
private def draw_box_plot(backend)
|
13
|
+
plot_data = @plot_data.each do |group_data|
|
14
|
+
next nil if group_data.empty?
|
15
|
+
|
16
|
+
group_data = Array(group_data)
|
17
|
+
remove_na!(group_data)
|
18
|
+
|
19
|
+
group_data
|
20
|
+
end
|
21
|
+
backend.box_plot(plot_data, (0 ... @plot_data.length).to_a,
|
22
|
+
color: @colors, gray: @gray)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
module Charty
|
2
|
+
module Plotters
|
3
|
+
class CategoricalPlotter < AbstractPlotter
|
4
|
+
def initialize(x, y, color, **options, &block)
|
5
|
+
super
|
6
|
+
|
7
|
+
setup_variables
|
8
|
+
setup_colors
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :order
|
12
|
+
|
13
|
+
def order=(order)
|
14
|
+
@order = Array(order).map(&method(:normalize_name))
|
15
|
+
end
|
16
|
+
|
17
|
+
private def normalize_name(value)
|
18
|
+
case value
|
19
|
+
when String, Symbol
|
20
|
+
value
|
21
|
+
else
|
22
|
+
value.to_str
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_reader :group_names, :plot_data, :group_label, :value_label
|
27
|
+
|
28
|
+
private def setup_variables
|
29
|
+
if x.nil? && y.nil?
|
30
|
+
setup_variables_with_wide_form_dataset
|
31
|
+
else
|
32
|
+
setup_variables_with_long_form_dataset
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private def setup_variables_with_wide_form_dataset
|
37
|
+
if @color
|
38
|
+
raise ArgumentError,
|
39
|
+
"Cannot use `color` without `x` or `y`"
|
40
|
+
end
|
41
|
+
|
42
|
+
# No color grouping with wide inputs
|
43
|
+
@plot_colors = nil
|
44
|
+
@color_title = nil
|
45
|
+
@color_names = nil
|
46
|
+
|
47
|
+
# No statistical units with wide inputs
|
48
|
+
@plot_units = nil
|
49
|
+
|
50
|
+
@value_label = nil
|
51
|
+
@group_label = nil
|
52
|
+
|
53
|
+
order = @order # TODO: supply order via parameter
|
54
|
+
unless order
|
55
|
+
order = @data.column_names.select do |cn|
|
56
|
+
@data[cn].all? {|x| Float(x, exception: false) }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
order ||= @data.column_names
|
60
|
+
@plot_data = order.map {|cn| @data[cn] }
|
61
|
+
@group_names = order
|
62
|
+
end
|
63
|
+
|
64
|
+
private def setup_variables_with_long_form_dataset
|
65
|
+
x, y = @x, @y
|
66
|
+
if @data
|
67
|
+
x = @data[x] || x
|
68
|
+
y = @data[y] || y
|
69
|
+
end
|
70
|
+
|
71
|
+
# Validate inputs
|
72
|
+
[x, y].each do |input|
|
73
|
+
next if array?(input)
|
74
|
+
raise RuntimeError,
|
75
|
+
"Could not interpret interpret input `#{input.inspect}`"
|
76
|
+
end
|
77
|
+
|
78
|
+
if x.nil? || y.nil?
|
79
|
+
setup_single_data
|
80
|
+
else
|
81
|
+
# FIXME: Assume vertical plot
|
82
|
+
groups, vals = x, y
|
83
|
+
|
84
|
+
if groups.respond_to?(:name)
|
85
|
+
@group_label = groups.name
|
86
|
+
end
|
87
|
+
|
88
|
+
if vals.respond_to?(:name)
|
89
|
+
@value_label = vals.name
|
90
|
+
end
|
91
|
+
|
92
|
+
# FIXME: Assume groups has only unique values
|
93
|
+
@group_names = groups
|
94
|
+
@plot_data = vals.map {|v| [v] }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
private def setup_single_data
|
99
|
+
raise NotImplementedError,
|
100
|
+
"Single data plot is not supported yet"
|
101
|
+
end
|
102
|
+
|
103
|
+
private def setup_colors
|
104
|
+
n_colors = @plot_data.length
|
105
|
+
if @palette.nil?
|
106
|
+
current_palette = Palette.default
|
107
|
+
if n_colors <= current_palette.n_colors
|
108
|
+
palette = Palette.new(current_palette.colors, n_colors)
|
109
|
+
else
|
110
|
+
palette = Palette.husl(n_colors, l: 0.7r)
|
111
|
+
end
|
112
|
+
else
|
113
|
+
case @palette
|
114
|
+
when Hash
|
115
|
+
# Assume @palette has a hash table that maps
|
116
|
+
# group_names to colors
|
117
|
+
palette = @group_names.map {|gn| @palette[gn] }
|
118
|
+
else
|
119
|
+
palette = @palette
|
120
|
+
end
|
121
|
+
palette = Palette.new(palette, n_colors)
|
122
|
+
end
|
123
|
+
|
124
|
+
@colors = palette.colors.map {|c| c.to_rgb }
|
125
|
+
lightness_values = @colors.map {|c| c.to_hsl.l }
|
126
|
+
lum = lightness_values.min * 0.6r
|
127
|
+
@gray = Colors::RGB.new(lum, lum, lum) # TODO: Use Charty::Gray
|
128
|
+
end
|
129
|
+
|
130
|
+
private def annotate_axes(backend)
|
131
|
+
backend.set_xlabel(@group_label)
|
132
|
+
backend.set_ylabel(@value_label)
|
133
|
+
backend.set_xticks((0 ... @plot_data.length).to_a)
|
134
|
+
backend.set_xtick_labels(@group_names)
|
135
|
+
backend.disable_xaxis_grid
|
136
|
+
backend.set_xlim(-0.5, @plot_data.length - 0.5)
|
137
|
+
end
|
138
|
+
|
139
|
+
private def remove_na!(ary)
|
140
|
+
ary.reject! do |x|
|
141
|
+
next true if x.nil?
|
142
|
+
x.respond_to?(:nan?) && x.nan?
|
143
|
+
end
|
144
|
+
ary
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Charty
|
2
|
+
module Statistics
|
3
|
+
begin
|
4
|
+
require "enumerable/statistics"
|
5
|
+
|
6
|
+
def self.mean(enum)
|
7
|
+
enum.mean
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.stdev(enum)
|
11
|
+
enum.stdev
|
12
|
+
end
|
13
|
+
rescue LoadError
|
14
|
+
def self.mean(enum)
|
15
|
+
xs = enum.to_a
|
16
|
+
xs.sum / xs.length.to_f
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.stdev(enum, population: false)
|
20
|
+
xs = enum.to_a
|
21
|
+
n = xs.length
|
22
|
+
mean = xs.sum.to_f / n
|
23
|
+
ddof = population ? 0 : 1
|
24
|
+
var = xs.map {|x| (x - mean)**2 }.sum / (n - ddof)
|
25
|
+
Math.sqrt(var)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/charty/table.rb
CHANGED
data/lib/charty/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: charty
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- youchan
|
@@ -10,8 +10,22 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2020-01-09 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: red-colors
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - ">="
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '0'
|
15
29
|
- !ruby/object:Gem::Dependency
|
16
30
|
name: bundler
|
17
31
|
requirement: !ruby/object:Gem::Requirement
|
@@ -161,8 +175,8 @@ executables: []
|
|
161
175
|
extensions: []
|
162
176
|
extra_rdoc_files: []
|
163
177
|
files:
|
178
|
+
- ".github/workflows/ci.yml"
|
164
179
|
- ".gitignore"
|
165
|
-
- ".travis.yml"
|
166
180
|
- Dockerfile.dev
|
167
181
|
- Gemfile
|
168
182
|
- LICENSE
|
@@ -177,7 +191,9 @@ files:
|
|
177
191
|
- examples/daru.ipynb
|
178
192
|
- examples/iris_dataset.ipynb
|
179
193
|
- examples/nmatrix.ipynb
|
180
|
-
- examples/
|
194
|
+
- examples/numo_narray.ipynb
|
195
|
+
- examples/palette.rb
|
196
|
+
- examples/sample.png
|
181
197
|
- examples/sample_bokeh.ipynb
|
182
198
|
- examples/sample_google_chart.ipynb
|
183
199
|
- examples/sample_gruff.ipynb
|
@@ -211,16 +227,25 @@ files:
|
|
211
227
|
- examples/sample_rubyplot.ipynb
|
212
228
|
- images/design_concept.png
|
213
229
|
- lib/charty.rb
|
230
|
+
- lib/charty/backend_methods.rb
|
214
231
|
- lib/charty/backends.rb
|
215
232
|
- lib/charty/backends/bokeh.rb
|
216
|
-
- lib/charty/backends/
|
233
|
+
- lib/charty/backends/google_charts.rb
|
217
234
|
- lib/charty/backends/gruff.rb
|
218
235
|
- lib/charty/backends/plotly.rb
|
219
236
|
- lib/charty/backends/pyplot.rb
|
220
237
|
- lib/charty/backends/rubyplot.rb
|
221
238
|
- lib/charty/layout.rb
|
222
239
|
- lib/charty/linspace.rb
|
240
|
+
- lib/charty/palette.rb
|
241
|
+
- lib/charty/plot_methods.rb
|
223
242
|
- lib/charty/plotter.rb
|
243
|
+
- lib/charty/plotters.rb
|
244
|
+
- lib/charty/plotters/abstract_plotter.rb
|
245
|
+
- lib/charty/plotters/bar_plotter.rb
|
246
|
+
- lib/charty/plotters/box_plotter.rb
|
247
|
+
- lib/charty/plotters/categorical_plotter.rb
|
248
|
+
- lib/charty/statistics.rb
|
224
249
|
- lib/charty/table.rb
|
225
250
|
- lib/charty/table_adapters.rb
|
226
251
|
- lib/charty/table_adapters/active_record_adapter.rb
|