charty 0.2.4 → 0.2.9

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +64 -15
  3. data/charty.gemspec +10 -3
  4. data/lib/charty.rb +5 -2
  5. data/lib/charty/backends/bokeh.rb +2 -2
  6. data/lib/charty/backends/google_charts.rb +1 -1
  7. data/lib/charty/backends/gruff.rb +1 -1
  8. data/lib/charty/backends/plotly.rb +434 -32
  9. data/lib/charty/backends/plotly_helpers/html_renderer.rb +203 -0
  10. data/lib/charty/backends/plotly_helpers/notebook_renderer.rb +87 -0
  11. data/lib/charty/backends/plotly_helpers/plotly_renderer.rb +121 -0
  12. data/lib/charty/backends/pyplot.rb +187 -48
  13. data/lib/charty/backends/rubyplot.rb +1 -1
  14. data/lib/charty/cache_dir.rb +27 -0
  15. data/lib/charty/dash_pattern_generator.rb +57 -0
  16. data/lib/charty/index.rb +1 -1
  17. data/lib/charty/iruby_helper.rb +18 -0
  18. data/lib/charty/plot_methods.rb +115 -3
  19. data/lib/charty/plotter.rb +2 -2
  20. data/lib/charty/plotters.rb +4 -0
  21. data/lib/charty/plotters/abstract_plotter.rb +106 -11
  22. data/lib/charty/plotters/bar_plotter.rb +1 -16
  23. data/lib/charty/plotters/box_plotter.rb +1 -16
  24. data/lib/charty/plotters/distribution_plotter.rb +150 -0
  25. data/lib/charty/plotters/histogram_plotter.rb +242 -0
  26. data/lib/charty/plotters/line_plotter.rb +300 -0
  27. data/lib/charty/plotters/relational_plotter.rb +213 -96
  28. data/lib/charty/plotters/scatter_plotter.rb +8 -43
  29. data/lib/charty/statistics.rb +11 -2
  30. data/lib/charty/table.rb +124 -14
  31. data/lib/charty/table_adapters/base_adapter.rb +97 -0
  32. data/lib/charty/table_adapters/daru_adapter.rb +2 -0
  33. data/lib/charty/table_adapters/datasets_adapter.rb +7 -0
  34. data/lib/charty/table_adapters/hash_adapter.rb +19 -3
  35. data/lib/charty/table_adapters/pandas_adapter.rb +82 -0
  36. data/lib/charty/util.rb +28 -0
  37. data/lib/charty/vector_adapters.rb +5 -1
  38. data/lib/charty/vector_adapters/array_adapter.rb +2 -10
  39. data/lib/charty/vector_adapters/daru_adapter.rb +3 -11
  40. data/lib/charty/vector_adapters/narray_adapter.rb +1 -6
  41. data/lib/charty/vector_adapters/numpy_adapter.rb +1 -1
  42. data/lib/charty/vector_adapters/pandas_adapter.rb +0 -1
  43. data/lib/charty/version.rb +1 -1
  44. metadata +104 -11
  45. data/lib/charty/missing_value_support.rb +0 -14
@@ -33,7 +33,7 @@ module Charty
33
33
  @plot.show
34
34
  end
35
35
 
36
- def render(context, filename="")
36
+ def old_style_render(context, filename="")
37
37
  FileUtils.mkdir_p(File.dirname(filename))
38
38
  plot(@plot, context).write(filename)
39
39
  end
@@ -0,0 +1,27 @@
1
+ require "pathname"
2
+
3
+ module Charty
4
+ module CacheDir
5
+ module_function
6
+
7
+ def cache_dir_path
8
+ platform_cache_dir_path + "charty"
9
+ end
10
+
11
+ def platform_cache_dir_path
12
+ base_dir = case RUBY_PLATFORM
13
+ when /mswin/, /mingw/
14
+ ENV.fetch("LOCALAPPDATA", "~/AppData/Local")
15
+ when /darwin/
16
+ "~/Library/Caches"
17
+ else
18
+ ENV.fetch("XDG_CACHE_HOME", "~/.cache")
19
+ end
20
+ Pathname(base_dir).expand_path
21
+ end
22
+
23
+ def path(*path_components)
24
+ cache_dir_path.join(*path_components)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,57 @@
1
+ module Charty
2
+ module DashPatternGenerator
3
+ NAMED_PATTERNS = {
4
+ solid: "",
5
+ dash: [4, 1.5],
6
+ dot: [1, 1],
7
+ dashdot: [3, 1.25, 1.5, 1.25],
8
+ longdashdot: [5, 1, 1, 1],
9
+ }.freeze
10
+
11
+ def self.valid_name?(name)
12
+ name = case name
13
+ when Symbol, String
14
+ name.to_sym
15
+ else
16
+ name.to_str.to_sym
17
+ end
18
+ NAMED_PATTERNS.key?(name)
19
+ end
20
+
21
+ def self.pattern_to_name(pattern)
22
+ NAMED_PATTERNS.each do |key, val|
23
+ return key if pattern == val
24
+ end
25
+ nil
26
+ end
27
+
28
+ def self.each
29
+ return enum_for(__method__) unless block_given?
30
+
31
+ NAMED_PATTERNS.each_value do |pattern|
32
+ yield pattern
33
+ end
34
+
35
+ m = 3
36
+ while true
37
+ # Long and short dash combinations
38
+ a = [3, 1.25].repeated_combination(m).to_a[1..-2].reverse
39
+ b = [4, 1].repeated_combination(m).to_a[1..-2]
40
+
41
+ # Interleave these combinations
42
+ segment_list = a.zip(b).flatten(1)
43
+
44
+ # Insert the gaps
45
+ segment_list.each do |segment|
46
+ gap = segment.min
47
+ pattern = segment.map {|seg| [seg, gap] }.flatten
48
+ yield pattern
49
+ end
50
+
51
+ m += 1
52
+ end
53
+ end
54
+
55
+ extend Enumerable
56
+ end
57
+ end
data/lib/charty/index.rb CHANGED
@@ -49,7 +49,7 @@ module Charty
49
49
  return index.union(other) if index
50
50
  end
51
51
 
52
- Index.new(to_a.union(other.to_a))
52
+ Index.new(to_a.union(other.to_a), name: name)
53
53
  end
54
54
  end
55
55
 
@@ -0,0 +1,18 @@
1
+ module Charty
2
+ module IRubyHelper
3
+ module_function
4
+
5
+ def iruby_notebook?
6
+ # TODO: This cannot distinguish notebook and console.
7
+ defined?(IRuby)
8
+ end
9
+
10
+ def vscode?
11
+ ENV.key?("VSCODE_PID")
12
+ end
13
+
14
+ def nteract?
15
+ ENV.key?("NTERACT_EXE")
16
+ end
17
+ end
18
+ end
@@ -130,6 +130,76 @@ module Charty
130
130
  )
131
131
  end
132
132
 
133
+ # Line 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 dashes [true, false, Array, Hash]
149
+ # @param markers [true, false, Array, Hash]
150
+ # @param style_order [Array]
151
+ # @param units [vector-like object, key in data]
152
+ # @param estimator [:mean]
153
+ # @param n_boot [Integer]
154
+ # @param random [Integer, Random, nil]
155
+ # @param sort [true, false]
156
+ # @param err_style [:band, :bars]
157
+ # @param err_params [Hash]
158
+ # @param error_bar
159
+ # @param x_scale [:linear, :log10]
160
+ # @param y_scale [:linear, :log10]
161
+ # @param legend [:auto, :brief, :full, false]
162
+ # How to draw legend. If :brief, numeric color and size variables
163
+ # will be represented with a sample of evenly spaced values. If
164
+ # :full, every group will get an entry in the legend. If :auto,
165
+ # choose between brief or full representation based on number of
166
+ # levels. If false, no legend data is added and no legend is drawn.
167
+ def line_plot(x: nil, y: nil, color: nil, style: nil, size: nil,
168
+ data: nil, key_color: nil, palette: nil, color_order: nil,
169
+ color_norm: nil, sizes: nil, size_order: nil, size_norm: nil,
170
+ markers: nil, dashes: true, style_order: nil,
171
+ units: nil, estimator: :mean, n_boot: 1000, random: nil,
172
+ sort: true, err_style: :band, err_params: nil, error_bar: [:ci, 95],
173
+ x_scale: :linear, y_scale: :linear, legend: :auto, **options, &block)
174
+ Plotters::LinePlotter.new(
175
+ data: data,
176
+ variables: { x: x, y: y, color: color, style: style, size: size },
177
+ key_color: key_color,
178
+ palette: palette,
179
+ color_order: color_order,
180
+ color_norm: color_norm,
181
+ sizes: sizes,
182
+ size_order: size_order,
183
+ size_norm: size_norm,
184
+ markers: markers,
185
+ dashes: dashes,
186
+ style_order: style_order,
187
+ units: units,
188
+ estimator: estimator,
189
+ n_boot: n_boot,
190
+ random: random,
191
+ sort: sort,
192
+ err_style: err_style,
193
+ err_params: err_params,
194
+ error_bar: error_bar,
195
+ x_scale: x_scale,
196
+ y_scale: y_scale,
197
+ legend: legend,
198
+ **options,
199
+ &block
200
+ )
201
+ end
202
+
133
203
  # Scatter plot
134
204
  #
135
205
  # @param x [vector-like object, key in data]
@@ -146,7 +216,7 @@ module Charty
146
216
  # @param size_order [Array]
147
217
  # @param size_norm
148
218
  # @param markers [true, false, Array, Hash]
149
- # @param marker_order [Array]
219
+ # @param style_order [Array]
150
220
  # @param alpha [scalar number]
151
221
  # Propotional opacity of the points.
152
222
  # @param legend [:auto, :brief, :full, false]
@@ -158,7 +228,7 @@ module Charty
158
228
  def scatter_plot(x: nil, y: nil, color: nil, style: nil, size: nil,
159
229
  data: nil, key_color: nil, palette: nil, color_order: nil,
160
230
  color_norm: nil, sizes: nil, size_order: nil, size_norm: nil,
161
- markers: true, marker_order: nil, alpha: nil, legend: :auto,
231
+ markers: true, style_order: nil, alpha: nil, legend: :auto,
162
232
  **options, &block)
163
233
  Plotters::ScatterPlotter.new(
164
234
  data: data,
@@ -171,13 +241,55 @@ module Charty
171
241
  size_order: size_order,
172
242
  size_norm: size_norm,
173
243
  markers: markers,
174
- marker_order: marker_order,
244
+ style_order: style_order,
175
245
  alpha: alpha,
176
246
  legend: legend,
177
247
  **options,
178
248
  &block
179
249
  )
180
250
  end
251
+
252
+ def hist_plot(data: nil, x: nil, y: nil, color: nil, weights: nil,
253
+ stat: :count, bins: :auto,
254
+ bin_range: nil, common_bins: true,
255
+ key_color: nil, palette: nil, color_order: nil, color_norm: nil,
256
+ legend: true, **options, &block)
257
+ # TODO: support following arguments
258
+ # - wiehgts
259
+ # - binwidth
260
+ # - discrete
261
+ # - cumulative
262
+ # - common_norm
263
+ # - multiple
264
+ # - element
265
+ # - fill
266
+ # - shrink
267
+ # - kde
268
+ # - kde_params
269
+ # - line_params
270
+ # - thresh
271
+ # - pthresh
272
+ # - pmax
273
+ # - cbar
274
+ # - cbar_params
275
+ # - x_log_scale
276
+ # - y_log_scale
277
+ Plotters::HistogramPlotter.new(
278
+ data: data,
279
+ variables: { x: x, y: y, color: color },
280
+ weights: weights,
281
+ stat: stat,
282
+ bins: bins,
283
+ bin_range: bin_range,
284
+ common_bins: common_bins,
285
+ key_color: key_color,
286
+ palette: palette,
287
+ color_order: color_order,
288
+ color_norm: color_norm,
289
+ legend: legend,
290
+ **options,
291
+ &block)
292
+ end
181
293
  end
182
294
 
183
295
  extend PlotMethods
@@ -237,11 +237,11 @@ module Charty
237
237
  end
238
238
 
239
239
  def render(filename=nil)
240
- @backend.render(self, filename)
240
+ @backend.old_style_render(self, filename)
241
241
  end
242
242
 
243
243
  def save(filename=nil, **kw)
244
- @backend.save(self, filename, **kw)
244
+ @backend.old_style_save(self, filename, **kw)
245
245
  end
246
246
 
247
247
  def apply(backend)
@@ -9,3 +9,7 @@ require_relative "plotters/count_plotter"
9
9
  require_relative "plotters/vector_plotter"
10
10
  require_relative "plotters/relational_plotter"
11
11
  require_relative "plotters/scatter_plotter"
12
+ require_relative "plotters/line_plotter"
13
+
14
+ require_relative "plotters/distribution_plotter"
15
+ require_relative "plotters/histogram_plotter"
@@ -8,16 +8,38 @@ module Charty
8
8
  self.data = data
9
9
  self.palette = palette
10
10
  substitute_options(options)
11
+
12
+ @var_levels = {}
13
+ @var_ordered = {x: false, y: false}
14
+
11
15
  yield self if block_given?
12
16
  end
13
17
 
14
18
  attr_reader :data, :x, :y, :color
15
19
  attr_reader :color_order, :key_color, :palette
16
20
 
21
+ def var_levels
22
+ variables.each_key do |var|
23
+ # TODO: Move mappers from RelationalPlotter to here,
24
+ # and remove the use of instance_variable_get
25
+ if instance_variable_defined?(:"@#{var}_mapper")
26
+ mapper = instance_variable_get(:"@#{var}_mapper")
27
+ @var_levels[var] = mapper.levels
28
+ end
29
+ end
30
+ @var_levels
31
+ end
32
+
33
+ def inspect
34
+ "#<#{self.class}:0x%016x>" % self.object_id
35
+ end
36
+
17
37
  def data=(data)
18
38
  @data = case data
19
39
  when nil, Charty::Table
20
40
  data
41
+ when method(:array?)
42
+ Charty::Vector.new(data)
21
43
  else
22
44
  Charty::Table.new(data)
23
45
  end
@@ -36,11 +58,7 @@ module Charty
36
58
  end
37
59
 
38
60
  def color_order=(color_order)
39
- #@color_order = XXX
40
- unless color_order.nil?
41
- raise NotImplementedError,
42
- "Specifying color_order is not supported yet"
43
- end
61
+ @color_order = color_order
44
62
  end
45
63
 
46
64
  # TODO: move to categorical_plotter
@@ -140,16 +158,93 @@ module Charty
140
158
  end
141
159
 
142
160
  private def remove_na!(ary)
143
- ary.reject! do |x|
144
- next true if x.nil?
145
- x.respond_to?(:nan?) && x.nan?
146
- end
161
+ ary.reject! {|x| Util.missing?(x) }
147
162
  ary
148
163
  end
149
164
 
165
+ private def each_subset(grouping_vars, reverse: false, processed: false, by_facet: true, allow_empty: false, drop_na: true)
166
+ case grouping_vars
167
+ when nil
168
+ grouping_vars = []
169
+ when String, Symbol
170
+ grouping_vars = [grouping_vars.to_sym]
171
+ end
172
+
173
+ if by_facet
174
+ [:col, :row].each do |facet_var|
175
+ grouping_vars << facet_var if variables.key?(facet_var)
176
+ end
177
+ end
178
+
179
+ grouping_vars = grouping_vars.select {|var| variables.key?(var) }
180
+
181
+ data = processed ? processed_data : plot_data
182
+ data = data.drop_na if drop_na
183
+
184
+ levels = var_levels.dup
185
+
186
+ ([:x, :y] & grouping_vars).each do |axis|
187
+ levels[axis] = plot_data[axis].categorical_order()
188
+ if processed
189
+ # TODO: perform inverse conversion of axis scaling here
190
+ end
191
+ end
192
+
193
+ if not grouping_vars.empty?
194
+ grouped = data.group_by(grouping_vars, sort: false)
195
+ grouped.each_group do |group_key, group_data|
196
+ next if group_data.empty? && !allow_empty
197
+
198
+ yield(grouping_vars.zip(group_key).to_h, group_data)
199
+ end
200
+ else
201
+ yield({}, data.dup)
202
+ end
203
+ end
204
+
205
+ def processed_data
206
+ @processed_data ||= calculate_processed_data
207
+ end
208
+
209
+ private def calculate_processed_data
210
+ # TODO: axis scaling support
211
+ plot_data
212
+ end
213
+
214
+ def save(filename, **kwargs)
215
+ backend = Backends.current
216
+ call_render_plot(backend, notebook: false, **kwargs)
217
+ backend.save(filename, **kwargs)
218
+ end
219
+
220
+ def render(notebook: false, **kwargs)
221
+ backend = Backends.current
222
+ call_render_plot(backend, notebook: notebook, **kwargs)
223
+ backend.render(notebook: notebook, **kwargs)
224
+ end
225
+
226
+ private def call_render_plot(backend, notebook: false, **kwargs)
227
+ backend.begin_figure
228
+ render_plot(backend, notebook: notebook, **kwargs)
229
+ end
230
+
231
+ private def render_plot(*, **)
232
+ raise NotImplementedError,
233
+ "subclass must implement #{__method__}"
234
+ end
235
+
150
236
  def to_iruby
151
- result = render
152
- ["text/html", result] if result
237
+ render(notebook: IRubyHelper.iruby_notebook?)
238
+ end
239
+
240
+ def to_iruby_mimebundle(include: [], exclude: [])
241
+ backend = Backends.current
242
+ if backend.respond_to?(:render_mimebundle)
243
+ call_render_plot(backend, notebook: true)
244
+ backend.render_mimebundle(include: include, exclude: exclude)
245
+ else
246
+ {}
247
+ end
153
248
  end
154
249
  end
155
250
  end