charty 0.1.5.dev → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +4 -5
- data/README.md +1 -1
- data/charty.gemspec +6 -0
- data/examples/sample_bokeh.ipynb +156 -0
- data/examples/sample_google_chart.ipynb +229 -68
- data/examples/sample_images/bar_bokeh.html +85 -0
- data/examples/sample_images/barh_bokeh.html +85 -0
- data/examples/sample_images/box_plot_bokeh.html +85 -0
- data/examples/sample_images/curve_bokeh.html +85 -0
- data/examples/sample_images/curve_with_function_bokeh.html +85 -0
- data/examples/sample_images/scatter_bokeh.html +85 -0
- data/lib/charty.rb +2 -1
- data/lib/charty/backends.rb +55 -0
- data/lib/charty/backends/bokeh.rb +61 -55
- data/lib/charty/backends/google_chart.rb +187 -129
- data/lib/charty/backends/gruff.rb +91 -83
- data/lib/charty/backends/plotly.rb +109 -0
- data/lib/charty/backends/pyplot.rb +96 -88
- data/lib/charty/backends/rubyplot.rb +82 -74
- data/lib/charty/plotter.rb +41 -33
- data/lib/charty/table.rb +44 -3
- data/lib/charty/table_adapters.rb +23 -0
- data/lib/charty/table_adapters/active_record_adapter.rb +55 -0
- data/lib/charty/table_adapters/daru_adapter.rb +34 -0
- data/lib/charty/table_adapters/datasets_adapter.rb +41 -0
- data/lib/charty/table_adapters/hash_adapter.rb +108 -0
- data/lib/charty/table_adapters/narray_adapter.rb +57 -0
- data/lib/charty/table_adapters/nmatrix_adapter.rb +57 -0
- data/lib/charty/version.rb +1 -1
- metadata +104 -5
- data/lib/charty/plotter_adapter.rb +0 -17
@@ -1,98 +1,106 @@
|
|
1
|
-
require '
|
1
|
+
require 'fileutils'
|
2
2
|
|
3
3
|
module Charty
|
4
|
-
|
5
|
-
|
4
|
+
module Backends
|
5
|
+
class Gruff
|
6
|
+
Backends.register(:gruff, self)
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
class << self
|
9
|
+
def prepare
|
10
|
+
require 'gruff'
|
11
|
+
end
|
12
|
+
end
|
10
13
|
|
11
|
-
|
12
|
-
|
14
|
+
def initialize
|
15
|
+
@plot = ::Gruff
|
16
|
+
end
|
13
17
|
|
14
|
-
|
15
|
-
|
16
|
-
end
|
18
|
+
def label(x, y)
|
19
|
+
end
|
17
20
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
+
def series=(series)
|
22
|
+
@series = series
|
23
|
+
end
|
21
24
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
25
|
+
def render_layout(layout)
|
26
|
+
raise NotImplementedError
|
27
|
+
end
|
28
|
+
|
29
|
+
def render(context, filename="")
|
30
|
+
FileUtils.mkdir_p(File.dirname(filename))
|
31
|
+
plot(@plot, context).write(filename)
|
32
|
+
end
|
26
33
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
34
|
+
def plot(plot, context)
|
35
|
+
# case
|
36
|
+
# when plot.respond_to?(:xlim)
|
37
|
+
# plot.xlim(context.range_x.begin, context.range_x.end)
|
38
|
+
# plot.ylim(context.range_y.begin, context.range_y.end)
|
39
|
+
# when plot.respond_to?(:set_xlim)
|
40
|
+
# plot.set_xlim(context.range_x.begin, context.range_x.end)
|
41
|
+
# plot.set_ylim(context.range_y.begin, context.range_y.end)
|
42
|
+
# end
|
36
43
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
44
|
+
case context.method
|
45
|
+
when :bar
|
46
|
+
p = plot::Bar.new
|
47
|
+
p.title = context.title if context.title
|
48
|
+
p.x_axis_label = context.xlabel if context.xlabel
|
49
|
+
p.y_axis_label = context.ylabel if context.ylabel
|
50
|
+
context.series.each do |data|
|
51
|
+
p.data(data.label, data.xs.to_a)
|
52
|
+
end
|
53
|
+
p
|
54
|
+
when :barh
|
55
|
+
p = plot::SideBar.new
|
56
|
+
p.title = context.title if context.title
|
57
|
+
p.x_axis_label = context.xlabel if context.xlabel
|
58
|
+
p.y_axis_label = context.ylabel if context.ylabel
|
59
|
+
labels = context.series.map {|data| data.xs.to_a}.flatten.uniq
|
60
|
+
labels.each do |label|
|
61
|
+
data_ys = context.series.map do |data|
|
62
|
+
if data.xs.to_a.index(label)
|
63
|
+
data.ys.to_a[data.xs.to_a.index(label)]
|
64
|
+
else
|
65
|
+
0
|
66
|
+
end
|
59
67
|
end
|
68
|
+
p.data(label, data_ys)
|
60
69
|
end
|
61
|
-
p.
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
p
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
p
|
70
|
+
p.labels = context.series.each_with_index.inject({}) do |attr, (data, i)|
|
71
|
+
attr[i] = data.label
|
72
|
+
attr
|
73
|
+
end
|
74
|
+
p
|
75
|
+
when :box_plot
|
76
|
+
# refs. https://github.com/topfunky/gruff/issues/155
|
77
|
+
raise NotImplementedError
|
78
|
+
when :bubble
|
79
|
+
raise NotImplementedError
|
80
|
+
when :curve
|
81
|
+
p = plot::Line.new
|
82
|
+
p.title = context.title if context.title
|
83
|
+
p.x_axis_label = context.xlabel if context.xlabel
|
84
|
+
p.y_axis_label = context.ylabel if context.ylabel
|
85
|
+
context.series.each do |data|
|
86
|
+
p.data(data.label, data.xs.to_a)
|
87
|
+
end
|
88
|
+
p
|
89
|
+
when :scatter
|
90
|
+
p = plot::Scatter.new
|
91
|
+
p.title = context.title if context.title
|
92
|
+
p.x_axis_label = context.xlabel if context.xlabel
|
93
|
+
p.y_axis_label = context.ylabel if context.ylabel
|
94
|
+
context.series.each do |data|
|
95
|
+
p.data(data.label, data.xs.to_a, data.ys.to_a)
|
96
|
+
end
|
97
|
+
p
|
98
|
+
when :error_bar
|
99
|
+
# refs. https://github.com/topfunky/gruff/issues/163
|
100
|
+
raise NotImplementedError
|
101
|
+
when :hist
|
102
|
+
raise NotImplementedError
|
89
103
|
end
|
90
|
-
p
|
91
|
-
when :error_bar
|
92
|
-
# refs. https://github.com/topfunky/gruff/issues/163
|
93
|
-
raise NotImplementedError
|
94
|
-
when :hist
|
95
|
-
raise NotImplementedError
|
96
104
|
end
|
97
105
|
end
|
98
106
|
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Charty
|
4
|
+
module Backends
|
5
|
+
class Plotly
|
6
|
+
Backends.register(:plotly, self)
|
7
|
+
|
8
|
+
attr_reader :context
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_writer :chart_id, :with_api_load_tag, :plotly_src
|
12
|
+
|
13
|
+
def chart_id
|
14
|
+
@chart_id ||= 0
|
15
|
+
end
|
16
|
+
|
17
|
+
def with_api_load_tag
|
18
|
+
return @with_api_load_tag unless @with_api_load_tag.nil?
|
19
|
+
|
20
|
+
@with_api_load_tag = true
|
21
|
+
end
|
22
|
+
|
23
|
+
def plotly_src
|
24
|
+
@plotly_src ||= 'https://cdn.plot.ly/plotly-latest.min.js'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def initilize
|
29
|
+
end
|
30
|
+
|
31
|
+
def label(x, y)
|
32
|
+
end
|
33
|
+
|
34
|
+
def series=(series)
|
35
|
+
@series = series
|
36
|
+
end
|
37
|
+
|
38
|
+
def render(context, filename)
|
39
|
+
plot(nil, context)
|
40
|
+
end
|
41
|
+
|
42
|
+
def plot(plot, context)
|
43
|
+
context = context
|
44
|
+
self.class.chart_id += 1
|
45
|
+
|
46
|
+
case context.method
|
47
|
+
when :bar
|
48
|
+
render_graph(context, :bar)
|
49
|
+
when :curve
|
50
|
+
render_graph(context, :scatter)
|
51
|
+
when :scatter
|
52
|
+
render_graph(context, nil, options: {data: {mode: "markers"}})
|
53
|
+
else
|
54
|
+
raise NotImplementedError
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def plotly_load_tag
|
61
|
+
if self.class.with_api_load_tag
|
62
|
+
"<script type='text/javascript' src='#{self.class.plotly_src}'></script>"
|
63
|
+
else
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def div_id
|
68
|
+
"charty-plotly-#{self.class.chart_id}"
|
69
|
+
end
|
70
|
+
|
71
|
+
def div_style
|
72
|
+
"width: 100%;height: 100%;"
|
73
|
+
end
|
74
|
+
|
75
|
+
def render_graph(context, type, options: {})
|
76
|
+
data = context.series.map do |series|
|
77
|
+
{
|
78
|
+
type: type,
|
79
|
+
x: series.xs.to_a,
|
80
|
+
y: series.ys.to_a,
|
81
|
+
name: series.label
|
82
|
+
}.merge(options[:data] || {})
|
83
|
+
end
|
84
|
+
layout = {
|
85
|
+
title: { text: context.title },
|
86
|
+
xaxis: {
|
87
|
+
title: context.xlabel,
|
88
|
+
range: [context.range[:x].first, context.range[:x].last]
|
89
|
+
},
|
90
|
+
yaxis: {
|
91
|
+
title: context.ylabel,
|
92
|
+
range: [context.range[:y].first, context.range[:y].last]
|
93
|
+
}
|
94
|
+
}
|
95
|
+
render_html(data, layout)
|
96
|
+
end
|
97
|
+
|
98
|
+
def render_html(data, layout)
|
99
|
+
<<~FRAGMENT
|
100
|
+
#{plotly_load_tag unless self.class.chart_id > 1}
|
101
|
+
<div id="#{div_id}" style="#{div_style}"></div>
|
102
|
+
<script>
|
103
|
+
Plotly.plot('#{div_id}', #{JSON.dump(data)}, #{JSON.dump(layout)} );
|
104
|
+
</script>
|
105
|
+
FRAGMENT
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -1,109 +1,117 @@
|
|
1
|
-
require '
|
1
|
+
require 'fileutils'
|
2
2
|
|
3
3
|
module Charty
|
4
|
-
|
5
|
-
|
4
|
+
module Backends
|
5
|
+
class Pyplot
|
6
|
+
Backends.register(:pyplot, self)
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
def self.activate_iruby_integration
|
12
|
-
require 'matplotlib/iruby'
|
13
|
-
Matplotlib::IRuby.activate
|
14
|
-
end
|
15
|
-
|
16
|
-
def label(x, y)
|
17
|
-
end
|
18
|
-
|
19
|
-
def series=(series)
|
20
|
-
@series = series
|
21
|
-
end
|
22
|
-
|
23
|
-
def render_layout(layout)
|
24
|
-
(fig, axes) = *@plot.subplots(nrows: layout.num_rows, ncols: layout.num_cols)
|
25
|
-
layout.rows.each_with_index do |row, y|
|
26
|
-
row.each_with_index do |cel, x|
|
27
|
-
plot = layout.num_rows > 1 ? axes[y][x] : axes[x]
|
28
|
-
plot(plot, cel, subplot: true)
|
8
|
+
class << self
|
9
|
+
def prepare
|
10
|
+
require 'matplotlib/pyplot'
|
29
11
|
end
|
30
12
|
end
|
31
|
-
@plot.show
|
32
|
-
end
|
33
13
|
|
34
|
-
|
35
|
-
|
36
|
-
if filename
|
37
|
-
FileUtils.mkdir_p(File.dirname(filename))
|
38
|
-
@plot.savefig(filename)
|
14
|
+
def initialize
|
15
|
+
@plot = ::Matplotlib::Pyplot
|
39
16
|
end
|
40
|
-
@plot.show
|
41
|
-
end
|
42
17
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
FileUtils.mkdir_p(File.dirname(filename))
|
47
|
-
@plot.savefig(filename)
|
18
|
+
def self.activate_iruby_integration
|
19
|
+
require 'matplotlib/iruby'
|
20
|
+
::Matplotlib::IRuby.activate
|
48
21
|
end
|
49
|
-
end
|
50
22
|
|
51
|
-
|
52
|
-
|
53
|
-
# case
|
54
|
-
# when plot.respond_to?(:xlim)
|
55
|
-
# plot.xlim(context.range_x.begin, context.range_x.end)
|
56
|
-
# plot.ylim(context.range_y.begin, context.range_y.end)
|
57
|
-
# when plot.respond_to?(:set_xlim)
|
58
|
-
# plot.set_xlim(context.range_x.begin, context.range_x.end)
|
59
|
-
# plot.set_ylim(context.range_y.begin, context.range_y.end)
|
60
|
-
# end
|
23
|
+
def label(x, y)
|
24
|
+
end
|
61
25
|
|
62
|
-
|
63
|
-
|
64
|
-
plot.xlabel(context.xlabel) if context.xlabel
|
65
|
-
plot.ylabel(context.ylabel) if context.ylabel
|
26
|
+
def series=(series)
|
27
|
+
@series = series
|
66
28
|
end
|
67
29
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
context.series.each do |data|
|
76
|
-
plot.barh(data.xs.to_a.map(&:to_s), data.ys.to_a)
|
30
|
+
def render_layout(layout)
|
31
|
+
_fig, axes = @plot.subplots(nrows: layout.num_rows, ncols: layout.num_cols)
|
32
|
+
layout.rows.each_with_index do |row, y|
|
33
|
+
row.each_with_index do |cel, x|
|
34
|
+
plot = layout.num_rows > 1 ? axes[y][x] : axes[x]
|
35
|
+
plot(plot, cel, subplot: true)
|
36
|
+
end
|
77
37
|
end
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
38
|
+
@plot.show
|
39
|
+
end
|
40
|
+
|
41
|
+
def render(context, filename)
|
42
|
+
plot(@plot, context)
|
43
|
+
if filename
|
44
|
+
FileUtils.mkdir_p(File.dirname(filename))
|
45
|
+
@plot.savefig(filename)
|
83
46
|
end
|
84
|
-
plot.
|
85
|
-
|
86
|
-
|
87
|
-
|
47
|
+
@plot.show
|
48
|
+
end
|
49
|
+
|
50
|
+
def save(context, filename)
|
51
|
+
plot(@plot, context)
|
52
|
+
if filename
|
53
|
+
FileUtils.mkdir_p(File.dirname(filename))
|
54
|
+
@plot.savefig(filename)
|
88
55
|
end
|
89
|
-
|
90
|
-
|
91
|
-
|
56
|
+
end
|
57
|
+
|
58
|
+
def plot(plot, context, subplot: false)
|
59
|
+
# TODO: Since it is not required, research and change conditions.
|
60
|
+
# case
|
61
|
+
# when plot.respond_to?(:xlim)
|
62
|
+
# plot.xlim(context.range_x.begin, context.range_x.end)
|
63
|
+
# plot.ylim(context.range_y.begin, context.range_y.end)
|
64
|
+
# when plot.respond_to?(:set_xlim)
|
65
|
+
# plot.set_xlim(context.range_x.begin, context.range_x.end)
|
66
|
+
# plot.set_ylim(context.range_y.begin, context.range_y.end)
|
67
|
+
# end
|
68
|
+
|
69
|
+
plot.title(context.title) if context.title
|
70
|
+
if !subplot
|
71
|
+
plot.xlabel(context.xlabel) if context.xlabel
|
72
|
+
plot.ylabel(context.ylabel) if context.ylabel
|
92
73
|
end
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
data.xs.to_a,
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
74
|
+
|
75
|
+
case context.method
|
76
|
+
when :bar
|
77
|
+
context.series.each do |data|
|
78
|
+
plot.bar(data.xs.to_a.map(&:to_s), data.ys.to_a, label: data.label)
|
79
|
+
end
|
80
|
+
plot.legend()
|
81
|
+
when :barh
|
82
|
+
context.series.each do |data|
|
83
|
+
plot.barh(data.xs.to_a.map(&:to_s), data.ys.to_a)
|
84
|
+
end
|
85
|
+
when :box_plot
|
86
|
+
plot.boxplot(context.data.to_a, labels: context.labels)
|
87
|
+
when :bubble
|
88
|
+
context.series.each do |data|
|
89
|
+
plot.scatter(data.xs.to_a, data.ys.to_a, s: data.zs.to_a, alpha: 0.5, label: data.label)
|
90
|
+
end
|
91
|
+
plot.legend()
|
92
|
+
when :curve
|
93
|
+
context.series.each do |data|
|
94
|
+
plot.plot(data.xs.to_a, data.ys.to_a)
|
95
|
+
end
|
96
|
+
when :scatter
|
97
|
+
context.series.each do |data|
|
98
|
+
plot.scatter(data.xs.to_a, data.ys.to_a, label: data.label)
|
99
|
+
end
|
100
|
+
plot.legend()
|
101
|
+
when :error_bar
|
102
|
+
context.series.each do |data|
|
103
|
+
plot.errorbar(
|
104
|
+
data.xs.to_a,
|
105
|
+
data.ys.to_a,
|
106
|
+
data.xerr,
|
107
|
+
data.yerr,
|
108
|
+
label: data.label,
|
109
|
+
)
|
110
|
+
end
|
111
|
+
plot.legend()
|
112
|
+
when :hist
|
113
|
+
plot.hist(context.data.to_a)
|
103
114
|
end
|
104
|
-
plot.legend()
|
105
|
-
when :hist
|
106
|
-
plot.hist(context.data.to_a)
|
107
115
|
end
|
108
116
|
end
|
109
117
|
end
|