charty 0.1.4.dev → 0.2.4

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.
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