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