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.
@@ -1,94 +1,102 @@
1
- require 'rubyplot'
1
+ require 'fileutils'
2
2
 
3
3
  module Charty
4
- class Rubyplot < PlotterAdapter
5
- Name = "rubyplot"
4
+ module Backends
5
+ class Rubyplot
6
+ Backends.register(:rubyplot, self)
6
7
 
7
- def initialize
8
- @plot = ::Rubyplot
9
- end
8
+ class << self
9
+ def prepare
10
+ require 'rubyplot'
11
+ end
12
+ end
10
13
 
11
- def label(x, y)
12
- end
14
+ def initialize
15
+ @plot = ::Rubyplot
16
+ end
13
17
 
14
- def series=(series)
15
- @series = series
16
- end
18
+ def label(x, y)
19
+ end
20
+
21
+ def series=(series)
22
+ @series = series
23
+ end
17
24
 
18
- def render_layout(layout)
19
- (fig, axes) = *@plot.subplots(nrows: layout.num_rows, ncols: layout.num_cols)
20
- layout.rows.each_with_index do |row, y|
21
- row.each_with_index do |cel, x|
22
- plot = layout.num_rows > 1 ? axes[y][x] : axes[x]
23
- plot(plot, cel)
25
+ def render_layout(layout)
26
+ (fig, axes) = *@plot.subplots(nrows: layout.num_rows, ncols: layout.num_cols)
27
+ layout.rows.each_with_index do |row, y|
28
+ row.each_with_index do |cel, x|
29
+ plot = layout.num_rows > 1 ? axes[y][x] : axes[x]
30
+ plot(plot, cel)
31
+ end
24
32
  end
33
+ @plot.show
25
34
  end
26
- @plot.show
27
- end
28
35
 
29
- def render(context, filename="")
30
- FileUtils.mkdir_p(File.dirname(filename))
31
- plot(@plot, context).write(filename)
32
- end
36
+ def render(context, filename="")
37
+ FileUtils.mkdir_p(File.dirname(filename))
38
+ plot(@plot, context).write(filename)
39
+ end
33
40
 
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
41
+ def plot(plot, context)
42
+ # case
43
+ # when plot.respond_to?(:xlim)
44
+ # plot.xlim(context.range_x.begin, context.range_x.end)
45
+ # plot.ylim(context.range_y.begin, context.range_y.end)
46
+ # when plot.respond_to?(:set_xlim)
47
+ # plot.set_xlim(context.range_x.begin, context.range_x.end)
48
+ # plot.set_ylim(context.range_y.begin, context.range_y.end)
49
+ # end
43
50
 
44
- figure = ::Rubyplot::Figure.new
45
- axes = figure.add_subplot 0,0
46
- axes.title = context.title if context.title
47
- axes.x_title = context.xlabel if context.xlabel
48
- axes.y_title = context.ylabel if context.ylabel
51
+ figure = ::Rubyplot::Figure.new
52
+ axes = figure.add_subplot 0,0
53
+ axes.title = context.title if context.title
54
+ axes.x_title = context.xlabel if context.xlabel
55
+ axes.y_title = context.ylabel if context.ylabel
49
56
 
50
- case context.method
51
- when :bar
52
- context.series.each do |data|
53
- axes.bar! do |p|
54
- p.data(data.xs.to_a)
55
- p.label = data.label
57
+ case context.method
58
+ when :bar
59
+ context.series.each do |data|
60
+ axes.bar! do |p|
61
+ p.data(data.xs.to_a)
62
+ p.label = data.label
63
+ end
56
64
  end
57
- end
58
- figure
59
- when :barh
60
- raise NotImplementedError
61
- when :box_plot
62
- raise NotImplementedError
63
- when :bubble
64
- context.series.each do |data|
65
- axes.bubble! do |p|
66
- p.data(data.xs.to_a, data.ys.to_a, data.zs.to_a)
67
- p.label = data.label if data.label
65
+ figure
66
+ when :barh
67
+ raise NotImplementedError
68
+ when :box_plot
69
+ raise NotImplementedError
70
+ when :bubble
71
+ context.series.each do |data|
72
+ axes.bubble! do |p|
73
+ p.data(data.xs.to_a, data.ys.to_a, data.zs.to_a)
74
+ p.label = data.label if data.label
75
+ end
68
76
  end
69
- end
70
- figure
71
- when :curve
72
- context.series.each do |data|
73
- axes.line! do |p|
74
- p.data(data.xs.to_a, data.ys.to_a)
75
- p.label = data.label if data.label
77
+ figure
78
+ when :curve
79
+ context.series.each do |data|
80
+ axes.line! do |p|
81
+ p.data(data.xs.to_a, data.ys.to_a)
82
+ p.label = data.label if data.label
83
+ end
76
84
  end
77
- end
78
- figure
79
- when :scatter
80
- context.series.each do |data|
81
- axes.scatter! do |p|
82
- p.data(data.xs.to_a, data.ys.to_a)
83
- p.label = data.label if data.label
85
+ figure
86
+ when :scatter
87
+ context.series.each do |data|
88
+ axes.scatter! do |p|
89
+ p.data(data.xs.to_a, data.ys.to_a)
90
+ p.label = data.label if data.label
91
+ end
84
92
  end
93
+ figure
94
+ when :error_bar
95
+ # refs. https://github.com/SciRuby/rubyplot/issues/26
96
+ raise NotImplementedError
97
+ when :hist
98
+ raise NotImplementedError
85
99
  end
86
- figure
87
- when :error_bar
88
- # refs. https://github.com/SciRuby/rubyplot/issues/26
89
- raise NotImplementedError
90
- when :hist
91
- raise NotImplementedError
92
100
  end
93
101
  end
94
102
  end
@@ -1,7 +1,8 @@
1
1
  module Charty
2
2
  class Plotter
3
- def initialize(adapter_name)
4
- @plotter_adapter = PlotterAdapter.create(adapter_name)
3
+ def initialize(backend_name)
4
+ backend_class = Backends.find_backend_class(backend_name)
5
+ @backend = backend_class.new
5
6
  end
6
7
 
7
8
  def table=(data, **kwargs)
@@ -11,10 +12,11 @@ module Charty
11
12
  attr_reader :table
12
13
 
13
14
  def to_bar(x, y, **args, &block)
14
- seriesx, seriesy = *table.to_a(x, y)
15
+ seriesx = table[x]
16
+ seriesy = table[y]
15
17
  xrange = (seriesx.min)..(seriesx.max)
16
18
  yrange = (seriesy.min)..(seriesy.max)
17
- bar = bar do
19
+ bar do
18
20
  series seriesx, seriesy
19
21
  range x: xrange, y: yrange
20
22
  xlabel x
@@ -23,10 +25,11 @@ module Charty
23
25
  end
24
26
 
25
27
  def to_barh(x, y, **args, &block)
26
- seriesx, seriesy = *table.to_a(x, y)
28
+ seriesx = table[x]
29
+ seriesy = table[y]
27
30
  xrange = (seriesx.min)..(seriesx.max)
28
31
  yrange = (seriesy.min)..(seriesy.max)
29
- bar = barh do
32
+ barh do
30
33
  series seriesx, seriesy
31
34
  range x: xrange, y: yrange
32
35
  xlabel x
@@ -35,10 +38,10 @@ module Charty
35
38
  end
36
39
 
37
40
  def to_box_plot(x, y, **args, &block)
38
- serieses = table.to_a(x, y)
41
+ serieses = [table[x], table[y]]
39
42
  xrange = 0..serieses.size
40
43
  yrange = (serieses.flatten.min - 1)..(serieses.flatten.max + 1)
41
- box_plot = box_plot do
44
+ box_plot do
42
45
  data serieses
43
46
  range x: xrange, y: yrange
44
47
  xlabel x
@@ -47,10 +50,12 @@ module Charty
47
50
  end
48
51
 
49
52
  def to_bubble(x, y, z, **args, &block)
50
- seriesx, seriesy, seriesz = *table.to_a(x, y, z)
53
+ seriesx = table[x]
54
+ seriesy = table[y]
55
+ seriesz = table[z]
51
56
  xrange = (seriesx.min)..(seriesx.max)
52
57
  yrange = (seriesy.min)..(seriesy.max)
53
- bubble = bubble do
58
+ bubble do
54
59
  series seriesx, seriesy, seriesz
55
60
  range x: xrange, y: yrange
56
61
  xlabel x
@@ -59,10 +64,11 @@ module Charty
59
64
  end
60
65
 
61
66
  def to_curve(x, y, **args, &block)
62
- seriesx, seriesy = *table.to_a(x, y)
67
+ seriesx = table[x]
68
+ seriesy = table[y]
63
69
  xrange = (seriesx.min)..(seriesx.max)
64
70
  yrange = (seriesy.min)..(seriesy.max)
65
- curve = curve do
71
+ curve do
66
72
  series seriesx, seriesy
67
73
  range x: xrange, y: yrange
68
74
  xlabel x
@@ -71,10 +77,11 @@ module Charty
71
77
  end
72
78
 
73
79
  def to_scatter(x, y, **args, &block)
74
- seriesx, seriesy = *table.to_a(x, y)
80
+ seriesx = table[x]
81
+ seriesy = table[y]
75
82
  xrange = (seriesx.min)..(seriesx.max)
76
83
  yrange = (seriesy.min)..(seriesy.max)
77
- scatter = scatter do
84
+ scatter do
78
85
  series seriesx, seriesy
79
86
  range x: xrange, y: yrange
80
87
  xlabel x
@@ -84,10 +91,11 @@ module Charty
84
91
 
85
92
  def to_error_bar(x, y, **args, &block)
86
93
  # TODO: It is not yet decided how to include data including xerror and yerror.
87
- seriesx, seriesy = *table.to_a(x, y)
94
+ seriesx = table[x]
95
+ seriesy = table[y]
88
96
  xrange = (seriesx.min)..(seriesx.max)
89
97
  yrange = (seriesy.min)..(seriesy.max)
90
- error_bar = error_bar do
98
+ error_bar do
91
99
  series seriesx, seriesy
92
100
  range x: xrange, y: yrange
93
101
  xlabel x
@@ -96,10 +104,10 @@ module Charty
96
104
  end
97
105
 
98
106
  def to_hst(x, y, **args, &block)
99
- serieses = table.to_a(x, y)
107
+ serieses = [table[x], table[y]]
100
108
  xrange = (serieses.flatten.min - 1)..(serieses.flatten.max + 1)
101
109
  yrange = 0..serieses[0].size
102
- hist = hist do
110
+ hist do
103
111
  data serieses
104
112
  range x: xrange, y: yrange
105
113
  xlabel x
@@ -109,46 +117,46 @@ module Charty
109
117
 
110
118
  def bar(**args, &block)
111
119
  context = RenderContext.new :bar, **args, &block
112
- context.apply(@plotter_adapter)
120
+ context.apply(@backend)
113
121
  end
114
122
 
115
123
  def barh(**args, &block)
116
124
  context = RenderContext.new :barh, **args, &block
117
- context.apply(@plotter_adapter)
125
+ context.apply(@backend)
118
126
  end
119
127
 
120
128
  def box_plot(**args, &block)
121
129
  context = RenderContext.new :box_plot, **args, &block
122
- context.apply(@plotter_adapter)
130
+ context.apply(@backend)
123
131
  end
124
132
 
125
133
  def bubble(**args, &block)
126
134
  context = RenderContext.new :bubble, **args, &block
127
- context.apply(@plotter_adapter)
135
+ context.apply(@backend)
128
136
  end
129
137
 
130
138
  def curve(**args, &block)
131
139
  context = RenderContext.new :curve, **args, &block
132
- context.apply(@plotter_adapter)
140
+ context.apply(@backend)
133
141
  end
134
142
 
135
143
  def scatter(**args, &block)
136
144
  context = RenderContext.new :scatter, **args, &block
137
- context.apply(@plotter_adapter)
145
+ context.apply(@backend)
138
146
  end
139
147
 
140
148
  def error_bar(**args, &block)
141
149
  context = RenderContext.new :error_bar, **args, &block
142
- context.apply(@plotter_adapter)
150
+ context.apply(@backend)
143
151
  end
144
152
 
145
153
  def hist(**args, &block)
146
154
  context = RenderContext.new :hist, **args, &block
147
- context.apply(@plotter_adapter)
155
+ context.apply(@backend)
148
156
  end
149
157
 
150
158
  def layout(definition=:horizontal)
151
- Layout.new(@plotter_adapter, definition)
159
+ Layout.new(@backend, definition)
152
160
  end
153
161
  end
154
162
 
@@ -160,7 +168,7 @@ module Charty
160
168
  def initialize(method, **args, &block)
161
169
  @method = method
162
170
  configurator = Configurator.new(**args)
163
- configurator.instance_eval &block
171
+ configurator.instance_eval(&block)
164
172
  # TODO: label も外から付けられた方がよさそう
165
173
  (@range, @series, @function, @data, @title, @xlabel, @ylabel, @labels) = configurator.to_a
166
174
  end
@@ -229,17 +237,17 @@ module Charty
229
237
  end
230
238
 
231
239
  def render(filename=nil)
232
- @plotter_adapter.render(self, filename)
240
+ @backend.render(self, filename)
233
241
  end
234
242
 
235
243
  def save(filename=nil)
236
- @plotter_adapter.save(self, filename)
244
+ @backend.save(self, filename)
237
245
  end
238
246
 
239
- def apply(plotter_adapter)
247
+ def apply(backend)
240
248
  case
241
249
  when !@series.empty?
242
- plotter_adapter.series = @series
250
+ backend.series = @series
243
251
  when @function
244
252
  linspace = Linspace.new(@range[:x], 100)
245
253
  # TODO: set label with function
@@ -247,7 +255,7 @@ module Charty
247
255
  @series << Series.new(linspace.to_a, linspace.map{|x| @function.call(x) }, label: "function" )
248
256
  end
249
257
 
250
- @plotter_adapter = plotter_adapter
258
+ @backend = backend
251
259
  self
252
260
  end
253
261
  end
@@ -1,11 +1,52 @@
1
+ require 'forwardable'
1
2
 
2
3
  module Charty
4
+ class ColumnAccessor
5
+ def initialize(adapter)
6
+ @adapter = adapter
7
+ end
8
+
9
+ def [](column_name)
10
+ @adapter[nil, column_name]
11
+ end
12
+ end
13
+
3
14
  class Table
4
- def initialize(table)
5
- @table = table
15
+ extend Forwardable
16
+
17
+ def initialize(data, **kwargs)
18
+ adapter_class = TableAdapters.find_adapter_class(data)
19
+ if kwargs.empty?
20
+ @adapter = adapter_class.new(data)
21
+ else
22
+ @adapter = adapter_class.new(data, **kwargs)
23
+ end
6
24
  end
7
25
 
8
- attr_reader :table
26
+ attr_reader :adapter
27
+
28
+ def_delegator :@adapter, :column_names
29
+
30
+ def columns
31
+ @column_accessor ||= ColumnAccessor.new(@adapter)
32
+ end
33
+
34
+ def [](*args)
35
+ n_args = args.length
36
+ case n_args
37
+ when 1
38
+ row = nil
39
+ column = args[0]
40
+ @adapter[row, column]
41
+ when 2
42
+ row = args[0]
43
+ column = args[1]
44
+ @adapter[row, column]
45
+ else
46
+ message = "wrong number of arguments (given #{n_args}, expected 1..2)"
47
+ raise ArgumentError, message
48
+ end
49
+ end
9
50
 
10
51
  def to_a(x=nil, y=nil, z=nil)
11
52
  case
@@ -0,0 +1,23 @@
1
+ module Charty
2
+ module TableAdapters
3
+ @adapters = {}
4
+
5
+ def self.register(name, adapter_class)
6
+ @adapters[name] = adapter_class
7
+ end
8
+
9
+ def self.find_adapter_class(data)
10
+ @adapters.each_value do |adapter_class|
11
+ return adapter_class if adapter_class.supported?(data)
12
+ end
13
+ raise ArgumentError, "Unsupported data class: #{data.class}"
14
+ end
15
+ end
16
+ end
17
+
18
+ require_relative 'table_adapters/hash_adapter'
19
+ require_relative 'table_adapters/narray_adapter'
20
+ require_relative 'table_adapters/datasets_adapter'
21
+ require_relative 'table_adapters/daru_adapter'
22
+ require_relative 'table_adapters/active_record_adapter'
23
+ require_relative 'table_adapters/nmatrix_adapter'