charty 0.1.4.dev → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +71 -0
  3. data/.github/workflows/nmatrix.yml +67 -0
  4. data/.github/workflows/pycall.yml +86 -0
  5. data/Dockerfile.dev +9 -1
  6. data/Gemfile +18 -0
  7. data/README.md +128 -9
  8. data/Rakefile +4 -5
  9. data/charty.gemspec +7 -2
  10. data/examples/Gemfile +1 -0
  11. data/examples/active_record.ipynb +34 -34
  12. data/examples/daru.ipynb +71 -29
  13. data/examples/iris_dataset.ipynb +12 -5
  14. data/examples/nmatrix.ipynb +30 -30
  15. data/examples/numo_narray.ipynb +245 -0
  16. data/examples/palette.rb +71 -0
  17. data/examples/sample.png +0 -0
  18. data/examples/sample_bokeh.ipynb +156 -0
  19. data/examples/sample_google_chart.ipynb +229 -68
  20. data/examples/sample_gruff.ipynb +148 -133
  21. data/examples/sample_images/bar_bokeh.html +85 -0
  22. data/examples/sample_images/barh_bokeh.html +85 -0
  23. data/examples/sample_images/barh_gruff.png +0 -0
  24. data/examples/sample_images/box_plot_bokeh.html +85 -0
  25. data/examples/sample_images/{boxplot_pyplot.png → box_plot_pyplot.png} +0 -0
  26. data/examples/sample_images/curve_bokeh.html +85 -0
  27. data/examples/sample_images/curve_with_function_bokeh.html +85 -0
  28. data/examples/sample_images/{errorbar_pyplot.png → error_bar_pyplot.png} +0 -0
  29. data/examples/sample_images/hist_gruff.png +0 -0
  30. data/examples/sample_images/scatter_bokeh.html +85 -0
  31. data/examples/sample_pyplot.ipynb +37 -35
  32. data/images/penguins_body_mass_g_flipper_length_mm_scatter_plot.png +0 -0
  33. data/images/penguins_body_mass_g_flipper_length_mm_species_scatter_plot.png +0 -0
  34. data/images/penguins_body_mass_g_flipper_length_mm_species_sex_scatter_plot.png +0 -0
  35. data/images/penguins_species_body_mass_g_bar_plot_h.png +0 -0
  36. data/images/penguins_species_body_mass_g_bar_plot_v.png +0 -0
  37. data/images/penguins_species_body_mass_g_box_plot_h.png +0 -0
  38. data/images/penguins_species_body_mass_g_box_plot_v.png +0 -0
  39. data/images/penguins_species_body_mass_g_sex_bar_plot_v.png +0 -0
  40. data/images/penguins_species_body_mass_g_sex_box_plot_v.png +0 -0
  41. data/lib/charty.rb +13 -7
  42. data/lib/charty/backend_methods.rb +8 -0
  43. data/lib/charty/backends.rb +80 -0
  44. data/lib/charty/backends/bokeh.rb +80 -0
  45. data/lib/charty/backends/google_charts.rb +267 -0
  46. data/lib/charty/backends/gruff.rb +104 -67
  47. data/lib/charty/backends/plotly.rb +549 -0
  48. data/lib/charty/backends/pyplot.rb +584 -86
  49. data/lib/charty/backends/rubyplot.rb +82 -74
  50. data/lib/charty/backends/unicode_plot.rb +79 -0
  51. data/lib/charty/index.rb +213 -0
  52. data/lib/charty/linspace.rb +1 -1
  53. data/lib/charty/missing_value_support.rb +14 -0
  54. data/lib/charty/plot_methods.rb +184 -0
  55. data/lib/charty/plotter.rb +57 -41
  56. data/lib/charty/plotters.rb +11 -0
  57. data/lib/charty/plotters/abstract_plotter.rb +156 -0
  58. data/lib/charty/plotters/bar_plotter.rb +216 -0
  59. data/lib/charty/plotters/box_plotter.rb +94 -0
  60. data/lib/charty/plotters/categorical_plotter.rb +380 -0
  61. data/lib/charty/plotters/count_plotter.rb +7 -0
  62. data/lib/charty/plotters/estimation_support.rb +84 -0
  63. data/lib/charty/plotters/random_support.rb +25 -0
  64. data/lib/charty/plotters/relational_plotter.rb +518 -0
  65. data/lib/charty/plotters/scatter_plotter.rb +115 -0
  66. data/lib/charty/plotters/vector_plotter.rb +6 -0
  67. data/lib/charty/statistics.rb +114 -0
  68. data/lib/charty/table.rb +82 -3
  69. data/lib/charty/table_adapters.rb +25 -0
  70. data/lib/charty/table_adapters/active_record_adapter.rb +63 -0
  71. data/lib/charty/table_adapters/base_adapter.rb +69 -0
  72. data/lib/charty/table_adapters/daru_adapter.rb +70 -0
  73. data/lib/charty/table_adapters/datasets_adapter.rb +49 -0
  74. data/lib/charty/table_adapters/hash_adapter.rb +224 -0
  75. data/lib/charty/table_adapters/narray_adapter.rb +76 -0
  76. data/lib/charty/table_adapters/nmatrix_adapter.rb +67 -0
  77. data/lib/charty/table_adapters/pandas_adapter.rb +81 -0
  78. data/lib/charty/vector.rb +69 -0
  79. data/lib/charty/vector_adapters.rb +183 -0
  80. data/lib/charty/vector_adapters/array_adapter.rb +109 -0
  81. data/lib/charty/vector_adapters/daru_adapter.rb +171 -0
  82. data/lib/charty/vector_adapters/narray_adapter.rb +187 -0
  83. data/lib/charty/vector_adapters/nmatrix_adapter.rb +37 -0
  84. data/lib/charty/vector_adapters/numpy_adapter.rb +168 -0
  85. data/lib/charty/vector_adapters/pandas_adapter.rb +200 -0
  86. data/lib/charty/version.rb +1 -1
  87. metadata +127 -13
  88. data/.travis.yml +0 -11
  89. data/examples/numo-narray.ipynb +0 -234
  90. data/lib/charty/backends/google_chart.rb +0 -167
  91. data/lib/charty/plotter_adapter.rb +0 -17
@@ -8,7 +8,7 @@ module Charty
8
8
  end
9
9
 
10
10
  def each(&block)
11
- step = (@range.end - @range.begin).to_f / @num_step
11
+ step = (@range.end - @range.begin).to_r / (@num_step - 1)
12
12
  (@num_step - 1).times do |i|
13
13
  block.call(@range.begin + i * step)
14
14
  end
@@ -0,0 +1,14 @@
1
+ module Charty
2
+ module MissingValueSupport
3
+ def missing_value?(val)
4
+ case
5
+ when val.nil?
6
+ true
7
+ when val.respond_to?(:nan?) && val.nan?
8
+ true
9
+ else
10
+ false
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,184 @@
1
+ module Charty
2
+ module PlotMethods
3
+ # Show the given data as rectangular bars.
4
+ #
5
+ # @param x x-dimension input for plotting long-form data.
6
+ # @param y y-dimension input for plotting long-form data.
7
+ # @param color color-dimension input for plotting long-form data.
8
+ # @param data Dataset for plotting.
9
+ # @param order Order of the categorical dimension to plot the categorical levels in.
10
+ # @param color_order Order of the color dimension to plot the categorical levels in.
11
+ # @param estimator Statistical function to estimate withint each categorical bin.
12
+ # @param ci Size of confidence intervals to draw around estimated values.
13
+ # @param n_boot The size of bootstrap sample to use when computing confidence intervals.
14
+ # @param units Identifier of sampling unit.
15
+ # @param random Random seed or random number generator for reproducible bootstrapping.
16
+ # @param orient Orientation of the plot (:v for vertical, or :h for
17
+ # horizontal).
18
+ # @param key_color Color for all of the elements, or seed for a gradient palette.
19
+ # @param palette Colors to use for the different levels of the color-dimension variable.
20
+ # @param saturation Propotion of the original saturation to draw colors.
21
+ # @param error_color Color for the lines that represent the confidence intervals.
22
+ # @param error_width Thickness of error bar lines (and caps).
23
+ # @param cap_size Width of the caps on error bars.
24
+ # @param dodge [true,false] If true, bar position is shifted along the
25
+ # categorical axis for avoid overlapping when the color-dimension is used.
26
+ def bar_plot(x: nil, y: nil, color: nil, data: nil,
27
+ order: nil, color_order: nil,
28
+ estimator: :mean, ci: 95, n_boot: 1000, units: nil, random: nil,
29
+ orient: nil, key_color: nil, palette: nil, saturation: 1r,
30
+ error_color: [0.26, 0.26, 0.26], error_width: nil, cap_size: nil,
31
+ dodge: true, **options, &block)
32
+ Plotters::BarPlotter.new(
33
+ data: data, variables: { x: x, y: y, color: color },
34
+ order: order, orient: orient,
35
+ estimator: estimator, ci: ci, n_boot: n_boot, units: units, random: random,
36
+ color_order: color_order, key_color: key_color, palette: palette, saturation: saturation,
37
+ error_color: error_color, error_width: error_width, cap_size: cap_size,
38
+ dodge: dodge,
39
+ **options, &block
40
+ )
41
+ end
42
+
43
+ def count_plot(x: nil, y: nil, color: nil, data: nil,
44
+ order: nil, color_order: nil,
45
+ orient: nil, key_color: nil, palette: nil, saturation: 1r,
46
+ dodge: true, **options, &block)
47
+ case
48
+ when x.nil? && !y.nil?
49
+ x = y
50
+ orient = :h
51
+ when y.nil? && !x.nil?
52
+ y = x
53
+ orient = :v
54
+ when !x.nil? && !y.nil?
55
+ raise ArgumentError,
56
+ "Unable to pass both x and y to count_plot"
57
+ end
58
+
59
+ Plotters::CountPlotter.new(
60
+ data: data,
61
+ variables: { x: x, y: y, color: color },
62
+ order: order,
63
+ orient: orient,
64
+ estimator: :count,
65
+ ci: nil,
66
+ units: nil,
67
+ random: nil,
68
+ color_order: color_order,
69
+ key_color: key_color,
70
+ palette: palette,
71
+ saturation: saturation,
72
+ dodge: dodge,
73
+ **options
74
+ ) do |plotter|
75
+ plotter.value_label = "count"
76
+ block.(plotter) unless block.nil?
77
+ end
78
+ end
79
+
80
+ # Show the distributions of the given data by boxes and whiskers.
81
+ #
82
+ # @param x X-dimension input for plotting long-Form data.
83
+ # @param y Y-dimension input for plotting long-form data.
84
+ # @param color Color-dimension input for plotting long-form data.
85
+ # @param data Dataset for plotting.
86
+ # @param order Order of the categorical dimension to plot the categorical
87
+ # levels in.
88
+ # @param color_order Order of the color dimension to plot the categorical
89
+ # levels in.
90
+ # @param orient Orientation of the plot (:v for vertical, or :h for
91
+ # horizontal).
92
+ # @param key_color Color for all of the elements, or seed for a gradient
93
+ # palette.
94
+ # @param palette Colors to use for the different levels of the
95
+ # color-dimension variable.
96
+ # @param saturation Propotion of the original saturation to draw colors.
97
+ # @param width Width of a full element when not using the color-dimension,
98
+ # or width of all the elements for one level of the major grouping
99
+ # variable.
100
+ # @param dodge [true,false] If true, bar position is shifted along the
101
+ # categorical axis for avoid overlapping when the color-dimension
102
+ # is used.
103
+ # @param flier_size Size of the markers used to indicate outlier
104
+ # observations.
105
+ # @param line_width Width of the gray lines that frame the plot elements.
106
+ # @param whisker Propotion of the IQR past the low and high quartiles to
107
+ # extend the plot whiskers. Points outside of this range will be
108
+ # treated as outliers.
109
+ def box_plot(x: nil, y: nil, color: nil, data: nil,
110
+ order: nil, color_order: nil,
111
+ orient: nil, key_color: nil, palette: nil, saturation: 1r,
112
+ width: 0.8r, dodge: true, flier_size: 5, line_width: nil,
113
+ whisker: 1.5, **options, &block)
114
+ Plotters::BoxPlotter.new(
115
+ data: data,
116
+ variables: { x: x, y: y, color: color },
117
+ order: order,
118
+ color_order: color_order,
119
+ orient: orient,
120
+ key_color: key_color,
121
+ palette: palette,
122
+ saturation: saturation,
123
+ width: width,
124
+ dodge: dodge,
125
+ flier_size: flier_size,
126
+ line_width: line_width,
127
+ whisker: whisker,
128
+ **options,
129
+ &block
130
+ )
131
+ end
132
+
133
+ # Scatter plot
134
+ #
135
+ # @param x [vector-like object, key in data]
136
+ # @param y [vector-like object, key in data]
137
+ # @param color [vector-like object, key in data]
138
+ # @param style [vector-like object, key in data]
139
+ # @param size [vector-like object, key in data]
140
+ # @param data [table-like object]
141
+ # @param key_color [color object]
142
+ # @param palette [String,Array<Numeric>,Palette]
143
+ # @param color_order [Array<String>,Array<Symbol>]
144
+ # @param color_norm
145
+ # @param sizes [Array, Hash]
146
+ # @param size_order [Array]
147
+ # @param size_norm
148
+ # @param markers [true, false, Array, Hash]
149
+ # @param marker_order [Array]
150
+ # @param alpha [scalar number]
151
+ # Propotional opacity of the points.
152
+ # @param legend [:auto, :brief, :full, false]
153
+ # How to draw legend. If :brief, numeric color and size variables
154
+ # will be represented with a sample of evenly spaced values. If
155
+ # :full, every group will get an entry in the legend. If :auto,
156
+ # choose between brief or full representation based on number of
157
+ # levels. If false, no legend data is added and no legend is drawn.
158
+ def scatter_plot(x: nil, y: nil, color: nil, style: nil, size: nil,
159
+ data: nil, key_color: nil, palette: nil, color_order: nil,
160
+ color_norm: nil, sizes: nil, size_order: nil, size_norm: nil,
161
+ markers: true, marker_order: nil, alpha: nil, legend: :auto,
162
+ **options, &block)
163
+ Plotters::ScatterPlotter.new(
164
+ data: data,
165
+ variables: { x: x, y: y, color: color, style: style, size: size },
166
+ key_color: key_color,
167
+ palette: palette,
168
+ color_order: color_order,
169
+ color_norm: color_norm,
170
+ sizes: sizes,
171
+ size_order: size_order,
172
+ size_norm: size_norm,
173
+ markers: markers,
174
+ marker_order: marker_order,
175
+ alpha: alpha,
176
+ legend: legend,
177
+ **options,
178
+ &block
179
+ )
180
+ end
181
+ end
182
+
183
+ extend PlotMethods
184
+ 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,60 +117,60 @@ 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
 
155
163
  Series = Struct.new(:xs, :ys, :zs, :xerr, :yerr, :label)
156
164
 
157
165
  class RenderContext
158
- attr_reader :function, :range, :series, :method, :data, :title, :xlabel, :ylabel
166
+ attr_reader :function, :range, :series, :method, :data, :title, :xlabel, :ylabel, :labels
159
167
 
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
- (@range, @series, @function, @data, @title, @xlabel, @ylabel) = configurator.to_a
173
+ (@range, @series, @function, @data, @title, @xlabel, @ylabel, @labels) = configurator.to_a
166
174
  end
167
175
 
168
176
  class Configurator
@@ -191,6 +199,10 @@ module Charty
191
199
  @ylabel = ylabel
192
200
  end
193
201
 
202
+ def labels(labels)
203
+ @labels = labels
204
+ end
205
+
194
206
  def label(x, y)
195
207
 
196
208
  end
@@ -204,7 +216,7 @@ module Charty
204
216
  end
205
217
 
206
218
  def to_a
207
- [@range, @series, @function, @data, @title, @xlabel, @ylabel]
219
+ [@range, @series, @function, @data, @title, @xlabel, @ylabel, @labels]
208
220
  end
209
221
 
210
222
  def method_missing(method, *args)
@@ -225,21 +237,25 @@ module Charty
225
237
  end
226
238
 
227
239
  def render(filename=nil)
228
- @plotter_adapter.render(self, filename)
240
+ @backend.render(self, filename)
241
+ end
242
+
243
+ def save(filename=nil, **kw)
244
+ @backend.save(self, filename, **kw)
229
245
  end
230
246
 
231
- def apply(plotter_adapter)
247
+ def apply(backend)
232
248
  case
233
- when !@series.empty?
234
- plotter_adapter.series = @series
235
- when @function
236
- linspace = Linspace.new(@range[:x], 100)
237
- # TODO: set label with function
238
- # TODO: set ys to xs when gruff curve with function
239
- @series << Series.new(linspace.to_a, linspace.map{|x| @function.call(x) }, label: "function" )
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" )
240
256
  end
241
257
 
242
- @plotter_adapter = plotter_adapter
258
+ @backend = backend
243
259
  self
244
260
  end
245
261
  end