charty 0.2.4 → 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
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