charty 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +38 -0
  3. data/charty.gemspec +1 -0
  4. data/examples/Gemfile +1 -0
  5. data/examples/active_record.ipynb +1 -1
  6. data/examples/daru.ipynb +1 -1
  7. data/examples/iris_dataset.ipynb +1 -1
  8. data/examples/nmatrix.ipynb +1 -1
  9. data/examples/{numo-narray.ipynb → numo_narray.ipynb} +1 -1
  10. data/examples/palette.rb +71 -0
  11. data/examples/sample.png +0 -0
  12. data/examples/sample_pyplot.ipynb +40 -38
  13. data/lib/charty.rb +7 -0
  14. data/lib/charty/backend_methods.rb +8 -0
  15. data/lib/charty/backends.rb +25 -1
  16. data/lib/charty/backends/bokeh.rb +29 -29
  17. data/lib/charty/backends/{google_chart.rb → google_charts.rb} +74 -32
  18. data/lib/charty/backends/plotly.rb +145 -7
  19. data/lib/charty/backends/pyplot.rb +163 -33
  20. data/lib/charty/backends/rubyplot.rb +1 -1
  21. data/lib/charty/palette.rb +235 -0
  22. data/lib/charty/plot_methods.rb +19 -0
  23. data/lib/charty/plotter.rb +9 -9
  24. data/lib/charty/plotters.rb +4 -0
  25. data/lib/charty/plotters/abstract_plotter.rb +81 -0
  26. data/lib/charty/plotters/bar_plotter.rb +19 -0
  27. data/lib/charty/plotters/box_plotter.rb +26 -0
  28. data/lib/charty/plotters/categorical_plotter.rb +148 -0
  29. data/lib/charty/statistics.rb +29 -0
  30. data/lib/charty/table.rb +1 -0
  31. data/lib/charty/table_adapters/active_record_adapter.rb +1 -1
  32. data/lib/charty/table_adapters/daru_adapter.rb +2 -0
  33. data/lib/charty/table_adapters/datasets_adapter.rb +4 -0
  34. data/lib/charty/table_adapters/hash_adapter.rb +2 -0
  35. data/lib/charty/table_adapters/narray_adapter.rb +1 -1
  36. data/lib/charty/table_adapters/nmatrix_adapter.rb +1 -1
  37. data/lib/charty/version.rb +1 -1
  38. metadata +30 -5
  39. data/.travis.yml +0 -10
@@ -0,0 +1,19 @@
1
+ module Charty
2
+ module PlotMethods
3
+ # Show the given data as rectangular bars.
4
+ #
5
+ # @param x
6
+ # @param y
7
+ # @param color
8
+ # @param data
9
+ def bar_plot(x=nil, y=nil, color=nil, **options, &block)
10
+ Plotters::BarPlotter.new(x, y, color, **options, &block)
11
+ end
12
+
13
+ def box_plot(x=nil, y=nil, color=nil, **options, &block)
14
+ Plotters::BoxPlotter.new(x, y, color, **options, &block)
15
+ end
16
+ end
17
+
18
+ extend PlotMethods
19
+ end
@@ -240,19 +240,19 @@ module Charty
240
240
  @backend.render(self, filename)
241
241
  end
242
242
 
243
- def save(filename=nil)
244
- @backend.save(self, filename)
243
+ def save(filename=nil, **kw)
244
+ @backend.save(self, filename, **kw)
245
245
  end
246
246
 
247
247
  def apply(backend)
248
248
  case
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" )
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" )
256
256
  end
257
257
 
258
258
  @backend = backend
@@ -0,0 +1,4 @@
1
+ require_relative "plotters/abstract_plotter"
2
+ require_relative "plotters/categorical_plotter"
3
+ require_relative "plotters/bar_plotter"
4
+ require_relative "plotters/box_plotter"
@@ -0,0 +1,81 @@
1
+ module Charty
2
+ module Plotters
3
+ class AbstractPlotter
4
+ def initialize(x, y, color, **options)
5
+ self.x = x
6
+ self.y = y
7
+ self.color = color
8
+ self.data = data
9
+ self.palette = palette
10
+ substitute_options(options)
11
+ yield self if block_given?
12
+ end
13
+
14
+ attr_reader :x, :y, :color, :data, :palette
15
+
16
+ def x=(x)
17
+ @x = check_dimension(x, :x)
18
+ end
19
+
20
+ def y=(y)
21
+ @y = check_dimension(y, :y)
22
+ end
23
+
24
+ def color=(color)
25
+ # @color = check_dimension(color, :color)
26
+ unless color.nil?
27
+ raise NotImplementedError,
28
+ "Specifying color variable is not supported yet"
29
+ end
30
+ end
31
+
32
+ def data=(data)
33
+ @data = case data
34
+ when nil, Charty::Table
35
+ data
36
+ else
37
+ Charty::Table.new(data)
38
+ end
39
+ end
40
+
41
+ def palette=(palette)
42
+ @palette = case palette
43
+ when nil, Charty::Palette, Symbol, String
44
+ palette
45
+ else
46
+ raise ArgumentError,
47
+ "invalid type for palette (given #{palette.class}, " +
48
+ "expected Charty::Palette, Symbol, or String)"
49
+ end
50
+ end
51
+
52
+ private def substitute_options(options)
53
+ options.each do |key, val|
54
+ send("#{key}=", val)
55
+ end
56
+ end
57
+
58
+ private def check_dimension(value, name)
59
+ case value
60
+ when nil, Symbol, String, method(:array?)
61
+ value
62
+ when ->(x) { x.respond_to?(:to_str) }
63
+ value.to_str
64
+ else
65
+ raise ArgumentError,
66
+ "invalid type of dimension for #{name} (given #{value.inspect})",
67
+ caller
68
+ end
69
+ end
70
+
71
+ private def array?(value)
72
+ TableAdapters::HashAdapter.array?(value)
73
+ end
74
+
75
+ def to_iruby
76
+ result = render
77
+ ["text/html", result] if result
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,19 @@
1
+ module Charty
2
+ module Plotters
3
+ class BarPlotter < CategoricalPlotter
4
+ def render
5
+ backend = Backends.current
6
+ backend.begin_figure
7
+ draw_bars(backend)
8
+ annotate_axes(backend)
9
+ backend.show
10
+ end
11
+
12
+ private def draw_bars(backend)
13
+ statistic = @plot_data.map {|xs| Statistics.mean(xs) }
14
+ bar_pos = (0 ... statistic.length).to_a
15
+ backend.bar(bar_pos, statistic, color: @colors)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,26 @@
1
+ module Charty
2
+ module Plotters
3
+ class BoxPlotter < CategoricalPlotter
4
+ def render
5
+ backend = Backends.current
6
+ backend.begin_figure
7
+ draw_box_plot(backend)
8
+ annotate_axes(backend)
9
+ backend.show
10
+ end
11
+
12
+ private def draw_box_plot(backend)
13
+ plot_data = @plot_data.each do |group_data|
14
+ next nil if group_data.empty?
15
+
16
+ group_data = Array(group_data)
17
+ remove_na!(group_data)
18
+
19
+ group_data
20
+ end
21
+ backend.box_plot(plot_data, (0 ... @plot_data.length).to_a,
22
+ color: @colors, gray: @gray)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,148 @@
1
+ module Charty
2
+ module Plotters
3
+ class CategoricalPlotter < AbstractPlotter
4
+ def initialize(x, y, color, **options, &block)
5
+ super
6
+
7
+ setup_variables
8
+ setup_colors
9
+ end
10
+
11
+ attr_reader :order
12
+
13
+ def order=(order)
14
+ @order = Array(order).map(&method(:normalize_name))
15
+ end
16
+
17
+ private def normalize_name(value)
18
+ case value
19
+ when String, Symbol
20
+ value
21
+ else
22
+ value.to_str
23
+ end
24
+ end
25
+
26
+ attr_reader :group_names, :plot_data, :group_label, :value_label
27
+
28
+ private def setup_variables
29
+ if x.nil? && y.nil?
30
+ setup_variables_with_wide_form_dataset
31
+ else
32
+ setup_variables_with_long_form_dataset
33
+ end
34
+ end
35
+
36
+ private def setup_variables_with_wide_form_dataset
37
+ if @color
38
+ raise ArgumentError,
39
+ "Cannot use `color` without `x` or `y`"
40
+ end
41
+
42
+ # No color grouping with wide inputs
43
+ @plot_colors = nil
44
+ @color_title = nil
45
+ @color_names = nil
46
+
47
+ # No statistical units with wide inputs
48
+ @plot_units = nil
49
+
50
+ @value_label = nil
51
+ @group_label = nil
52
+
53
+ order = @order # TODO: supply order via parameter
54
+ unless order
55
+ order = @data.column_names.select do |cn|
56
+ @data[cn].all? {|x| Float(x, exception: false) }
57
+ end
58
+ end
59
+ order ||= @data.column_names
60
+ @plot_data = order.map {|cn| @data[cn] }
61
+ @group_names = order
62
+ end
63
+
64
+ private def setup_variables_with_long_form_dataset
65
+ x, y = @x, @y
66
+ if @data
67
+ x = @data[x] || x
68
+ y = @data[y] || y
69
+ end
70
+
71
+ # Validate inputs
72
+ [x, y].each do |input|
73
+ next if array?(input)
74
+ raise RuntimeError,
75
+ "Could not interpret interpret input `#{input.inspect}`"
76
+ end
77
+
78
+ if x.nil? || y.nil?
79
+ setup_single_data
80
+ else
81
+ # FIXME: Assume vertical plot
82
+ groups, vals = x, y
83
+
84
+ if groups.respond_to?(:name)
85
+ @group_label = groups.name
86
+ end
87
+
88
+ if vals.respond_to?(:name)
89
+ @value_label = vals.name
90
+ end
91
+
92
+ # FIXME: Assume groups has only unique values
93
+ @group_names = groups
94
+ @plot_data = vals.map {|v| [v] }
95
+ end
96
+ end
97
+
98
+ private def setup_single_data
99
+ raise NotImplementedError,
100
+ "Single data plot is not supported yet"
101
+ end
102
+
103
+ private def setup_colors
104
+ n_colors = @plot_data.length
105
+ if @palette.nil?
106
+ current_palette = Palette.default
107
+ if n_colors <= current_palette.n_colors
108
+ palette = Palette.new(current_palette.colors, n_colors)
109
+ else
110
+ palette = Palette.husl(n_colors, l: 0.7r)
111
+ end
112
+ else
113
+ case @palette
114
+ when Hash
115
+ # Assume @palette has a hash table that maps
116
+ # group_names to colors
117
+ palette = @group_names.map {|gn| @palette[gn] }
118
+ else
119
+ palette = @palette
120
+ end
121
+ palette = Palette.new(palette, n_colors)
122
+ end
123
+
124
+ @colors = palette.colors.map {|c| c.to_rgb }
125
+ lightness_values = @colors.map {|c| c.to_hsl.l }
126
+ lum = lightness_values.min * 0.6r
127
+ @gray = Colors::RGB.new(lum, lum, lum) # TODO: Use Charty::Gray
128
+ end
129
+
130
+ private def annotate_axes(backend)
131
+ backend.set_xlabel(@group_label)
132
+ backend.set_ylabel(@value_label)
133
+ backend.set_xticks((0 ... @plot_data.length).to_a)
134
+ backend.set_xtick_labels(@group_names)
135
+ backend.disable_xaxis_grid
136
+ backend.set_xlim(-0.5, @plot_data.length - 0.5)
137
+ end
138
+
139
+ private def remove_na!(ary)
140
+ ary.reject! do |x|
141
+ next true if x.nil?
142
+ x.respond_to?(:nan?) && x.nan?
143
+ end
144
+ ary
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,29 @@
1
+ module Charty
2
+ module Statistics
3
+ begin
4
+ require "enumerable/statistics"
5
+
6
+ def self.mean(enum)
7
+ enum.mean
8
+ end
9
+
10
+ def self.stdev(enum)
11
+ enum.stdev
12
+ end
13
+ rescue LoadError
14
+ def self.mean(enum)
15
+ xs = enum.to_a
16
+ xs.sum / xs.length.to_f
17
+ end
18
+
19
+ def self.stdev(enum, population: false)
20
+ xs = enum.to_a
21
+ n = xs.length
22
+ mean = xs.sum.to_f / n
23
+ ddof = population ? 0 : 1
24
+ var = xs.map {|x| (x - mean)**2 }.sum / (n - ddof)
25
+ Math.sqrt(var)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -26,6 +26,7 @@ module Charty
26
26
  attr_reader :adapter
27
27
 
28
28
  def_delegator :@adapter, :column_names
29
+ def_delegator :@adapter, :data, :raw_data
29
30
 
30
31
  def columns
31
32
  @column_accessor ||= ColumnAccessor.new(@adapter)
@@ -15,7 +15,7 @@ module Charty
15
15
  @columns = nil
16
16
  end
17
17
 
18
- attr_reader :column_names
18
+ attr_reader :column_names, :data
19
19
 
20
20
  def [](row, column)
21
21
  fetch_records unless @columns
@@ -13,6 +13,8 @@ module Charty
13
13
  @data = check_type(Daru::DataFrame, data, :data)
14
14
  end
15
15
 
16
+ attr_reader :data
17
+
16
18
  def column_names
17
19
  @data.vectors.to_a
18
20
  end
@@ -15,6 +15,10 @@ module Charty
15
15
  @records = []
16
16
  end
17
17
 
18
+ def data
19
+ @table
20
+ end
21
+
18
22
  def column_names
19
23
  @table.column_names
20
24
  end
@@ -61,6 +61,8 @@ module Charty
61
61
  end
62
62
  end
63
63
 
64
+ attr_reader :data
65
+
64
66
  def_delegator :@data, :keys, :column_names
65
67
 
66
68
  def [](row, column)
@@ -20,7 +20,7 @@ module Charty
20
20
  @column_names = generate_column_names(data.shape[1], columns)
21
21
  end
22
22
 
23
- attr_reader :column_names
23
+ attr_reader :column_names, :data
24
24
 
25
25
  def [](row, column)
26
26
  if row
@@ -20,7 +20,7 @@ module Charty
20
20
  @column_names = generate_column_names(data.shape[1], columns)
21
21
  end
22
22
 
23
- attr_reader :column_names
23
+ attr_reader :column_names, :data
24
24
 
25
25
  def [](row, column)
26
26
  if row
@@ -1,5 +1,5 @@
1
1
  module Charty
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.1"
3
3
 
4
4
  module Version
5
5
  numbers, TAG = VERSION.split("-")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: charty
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - youchan
@@ -10,8 +10,22 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2019-09-27 00:00:00.000000000 Z
13
+ date: 2020-01-09 00:00:00.000000000 Z
14
14
  dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: red-colors
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '0'
15
29
  - !ruby/object:Gem::Dependency
16
30
  name: bundler
17
31
  requirement: !ruby/object:Gem::Requirement
@@ -161,8 +175,8 @@ executables: []
161
175
  extensions: []
162
176
  extra_rdoc_files: []
163
177
  files:
178
+ - ".github/workflows/ci.yml"
164
179
  - ".gitignore"
165
- - ".travis.yml"
166
180
  - Dockerfile.dev
167
181
  - Gemfile
168
182
  - LICENSE
@@ -177,7 +191,9 @@ files:
177
191
  - examples/daru.ipynb
178
192
  - examples/iris_dataset.ipynb
179
193
  - examples/nmatrix.ipynb
180
- - examples/numo-narray.ipynb
194
+ - examples/numo_narray.ipynb
195
+ - examples/palette.rb
196
+ - examples/sample.png
181
197
  - examples/sample_bokeh.ipynb
182
198
  - examples/sample_google_chart.ipynb
183
199
  - examples/sample_gruff.ipynb
@@ -211,16 +227,25 @@ files:
211
227
  - examples/sample_rubyplot.ipynb
212
228
  - images/design_concept.png
213
229
  - lib/charty.rb
230
+ - lib/charty/backend_methods.rb
214
231
  - lib/charty/backends.rb
215
232
  - lib/charty/backends/bokeh.rb
216
- - lib/charty/backends/google_chart.rb
233
+ - lib/charty/backends/google_charts.rb
217
234
  - lib/charty/backends/gruff.rb
218
235
  - lib/charty/backends/plotly.rb
219
236
  - lib/charty/backends/pyplot.rb
220
237
  - lib/charty/backends/rubyplot.rb
221
238
  - lib/charty/layout.rb
222
239
  - lib/charty/linspace.rb
240
+ - lib/charty/palette.rb
241
+ - lib/charty/plot_methods.rb
223
242
  - lib/charty/plotter.rb
243
+ - lib/charty/plotters.rb
244
+ - lib/charty/plotters/abstract_plotter.rb
245
+ - lib/charty/plotters/bar_plotter.rb
246
+ - lib/charty/plotters/box_plotter.rb
247
+ - lib/charty/plotters/categorical_plotter.rb
248
+ - lib/charty/statistics.rb
224
249
  - lib/charty/table.rb
225
250
  - lib/charty/table_adapters.rb
226
251
  - lib/charty/table_adapters/active_record_adapter.rb