charty 0.2.7 → 0.2.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/charty.gemspec +1 -0
- data/examples/bar_plot.rb +19 -0
- data/examples/box_plot.rb +17 -0
- data/examples/scatter_plot.rb +17 -0
- data/images/penguins_body_mass_g_flipper_length_mm_species_scatter_plot.png +0 -0
- data/images/penguins_body_mass_g_flipper_length_mm_species_sex_scatter_plot.png +0 -0
- data/images/penguins_species_body_mass_g_bar_plot_h.png +0 -0
- data/images/penguins_species_body_mass_g_bar_plot_v.png +0 -0
- data/images/penguins_species_body_mass_g_box_plot_h.png +0 -0
- data/images/penguins_species_body_mass_g_box_plot_v.png +0 -0
- data/images/penguins_species_body_mass_g_sex_bar_plot_v.png +0 -0
- data/images/penguins_species_body_mass_g_sex_box_plot_v.png +0 -0
- data/lib/charty/backends/plotly.rb +53 -22
- data/lib/charty/backends/plotly_helpers/notebook_renderer.rb +4 -1
- data/lib/charty/backends/pyplot.rb +73 -0
- data/lib/charty/backends/unicode_plot.rb +16 -11
- data/lib/charty/index.rb +9 -0
- data/lib/charty/plot_methods.rb +46 -10
- data/lib/charty/plotters/abstract_plotter.rb +41 -9
- data/lib/charty/plotters/bar_plotter.rb +39 -0
- data/lib/charty/plotters/categorical_plotter.rb +9 -1
- data/lib/charty/plotters/distribution_plotter.rb +44 -7
- data/lib/charty/plotters/histogram_plotter.rb +97 -35
- data/lib/charty/plotters/line_plotter.rb +38 -5
- data/lib/charty/plotters/scatter_plotter.rb +4 -2
- data/lib/charty/statistics.rb +2 -2
- data/lib/charty/table.rb +30 -23
- data/lib/charty/table_adapters/arrow_adapter.rb +53 -0
- data/lib/charty/table_adapters/base_adapter.rb +88 -0
- data/lib/charty/table_adapters/daru_adapter.rb +41 -1
- data/lib/charty/table_adapters/hash_adapter.rb +58 -10
- data/lib/charty/table_adapters/pandas_adapter.rb +49 -1
- data/lib/charty/table_adapters.rb +1 -0
- data/lib/charty/vector.rb +30 -2
- data/lib/charty/vector_adapters/array_adapter.rb +1 -1
- data/lib/charty/vector_adapters/arrow_adapter.rb +156 -0
- data/lib/charty/vector_adapters/daru_adapter.rb +3 -6
- data/lib/charty/vector_adapters/narray_adapter.rb +10 -1
- data/lib/charty/vector_adapters/nmatrix_adapter.rb +1 -5
- data/lib/charty/vector_adapters/numpy_adapter.rb +4 -0
- data/lib/charty/vector_adapters/pandas_adapter.rb +10 -1
- data/lib/charty/vector_adapters/vector_adapter.rb +62 -0
- data/lib/charty/vector_adapters.rb +22 -0
- data/lib/charty/version.rb +1 -1
- metadata +23 -3
@@ -35,6 +35,8 @@ module Charty
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def data=(data)
|
38
|
+
# TODO: Convert a Charty::Vector to a Charty::Table so that
|
39
|
+
# the Charty::Vector is handled as a wide form data
|
38
40
|
@data = case data
|
39
41
|
when nil, Charty::Table
|
40
42
|
data
|
@@ -81,6 +83,24 @@ module Charty
|
|
81
83
|
end
|
82
84
|
end
|
83
85
|
|
86
|
+
attr_reader :x_label
|
87
|
+
|
88
|
+
def x_label=(val)
|
89
|
+
@x_label = check_string(val, :x_label, allow_nil: true)
|
90
|
+
end
|
91
|
+
|
92
|
+
attr_reader :y_label
|
93
|
+
|
94
|
+
def y_label=(val)
|
95
|
+
@y_label = check_string(val, :y_label, allow_nil: true)
|
96
|
+
end
|
97
|
+
|
98
|
+
attr_reader :title
|
99
|
+
|
100
|
+
def title=(val)
|
101
|
+
@title = check_string(val, :title, allow_nil: true)
|
102
|
+
end
|
103
|
+
|
84
104
|
private def substitute_options(options)
|
85
105
|
options.each do |key, val|
|
86
106
|
send("#{key}=", val)
|
@@ -138,6 +158,27 @@ module Charty
|
|
138
158
|
end
|
139
159
|
end
|
140
160
|
|
161
|
+
private def check_string(value, name, allow_nil: false)
|
162
|
+
case value
|
163
|
+
when Symbol
|
164
|
+
value.to_s
|
165
|
+
else
|
166
|
+
if allow_nil && value.nil?
|
167
|
+
nil
|
168
|
+
else
|
169
|
+
orig_value = value
|
170
|
+
value = String.try_convert(value)
|
171
|
+
if value.nil?
|
172
|
+
raise ArgumentError,
|
173
|
+
"`#{name}` must be convertible to String: %p" % orig_value,
|
174
|
+
caller
|
175
|
+
else
|
176
|
+
value
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
141
182
|
private def variable_type(vector, boolean_type=:numeric)
|
142
183
|
if vector.numeric?
|
143
184
|
:numeric
|
@@ -181,15 +222,6 @@ module Charty
|
|
181
222
|
data = processed ? processed_data : plot_data
|
182
223
|
data = data.drop_na if drop_na
|
183
224
|
|
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
225
|
if not grouping_vars.empty?
|
194
226
|
grouped = data.group_by(grouping_vars, sort: false)
|
195
227
|
grouped.each_group do |group_key, group_data|
|
@@ -42,6 +42,12 @@ module Charty
|
|
42
42
|
@cap_size = check_number(cap_size, :cap_size, allow_nil: true)
|
43
43
|
end
|
44
44
|
|
45
|
+
attr_reader :log
|
46
|
+
|
47
|
+
def log=(val)
|
48
|
+
@log = check_boolean(val, :log)
|
49
|
+
end
|
50
|
+
|
45
51
|
private def render_plot(backend, **)
|
46
52
|
draw_bars(backend)
|
47
53
|
annotate_axes(backend)
|
@@ -81,6 +87,39 @@ module Charty
|
|
81
87
|
end
|
82
88
|
end
|
83
89
|
|
90
|
+
private def annotate_axes(backend)
|
91
|
+
super
|
92
|
+
|
93
|
+
if self.log
|
94
|
+
min_value, max_value = @estimations.minmax
|
95
|
+
if @plot_colors
|
96
|
+
unless @conf_int.empty?
|
97
|
+
min_value = [min_value, @conf_int[0]].min
|
98
|
+
max_value = [max_value, @conf_int[1]].max
|
99
|
+
end
|
100
|
+
else
|
101
|
+
ci_min = Util.filter_map(@conf_int) { |ci| ci[0] unless ci.empty? }
|
102
|
+
ci_max = Util.filter_map(@conf_int) { |ci| ci[1] unless ci.empty? }
|
103
|
+
min_value = [min_value, ci_min.min].min unless ci_min.empty?
|
104
|
+
max_value = [max_value, ci_max.max].max unless ci_max.empty?
|
105
|
+
end
|
106
|
+
if min_value > 1
|
107
|
+
min_value = 0
|
108
|
+
else
|
109
|
+
min_value = Math.log10(min_value).floor
|
110
|
+
end
|
111
|
+
max_value = Math.log10(max_value).ceil
|
112
|
+
case self.orient
|
113
|
+
when :v
|
114
|
+
backend.set_yscale(:log)
|
115
|
+
backend.set_ylim(min_value, max_value)
|
116
|
+
else
|
117
|
+
backend.set_xscale(:log)
|
118
|
+
backend.set_xlim(min_value, max_value)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
84
123
|
private def setup_estimations
|
85
124
|
if @color_names.nil?
|
86
125
|
setup_estimations_with_single_color_group
|
@@ -134,6 +134,7 @@ module Charty
|
|
134
134
|
order = @order # TODO: supply order via parameter
|
135
135
|
unless order
|
136
136
|
order = @data.column_names.select do |cn|
|
137
|
+
# TODO: Use Charty::Vector#numeric?
|
137
138
|
@data[cn].all? {|x| Float(x, exception: false) }
|
138
139
|
end
|
139
140
|
end
|
@@ -230,6 +231,7 @@ module Charty
|
|
230
231
|
end
|
231
232
|
return :h
|
232
233
|
end
|
234
|
+
|
233
235
|
case orient
|
234
236
|
when :v
|
235
237
|
if require_numeric && y_type != :numeric
|
@@ -263,7 +265,9 @@ module Charty
|
|
263
265
|
private def group_long_form(vals, groups, group_order)
|
264
266
|
grouped_vals = vals.group_by(groups)
|
265
267
|
|
266
|
-
plot_data = group_order.map
|
268
|
+
plot_data = group_order.map do |g|
|
269
|
+
grouped_vals[g] || Charty::Vector.new([])
|
270
|
+
end
|
267
271
|
|
268
272
|
if vals.respond_to?(:name)
|
269
273
|
value_label = vals.name
|
@@ -347,11 +351,15 @@ module Charty
|
|
347
351
|
end
|
348
352
|
|
349
353
|
private def annotate_axes(backend)
|
354
|
+
backend.set_title(self.title) if self.title
|
355
|
+
|
350
356
|
if orient == :v
|
351
357
|
xlabel, ylabel = @group_label, @value_label
|
352
358
|
else
|
353
359
|
xlabel, ylabel = @value_label, @group_label
|
354
360
|
end
|
361
|
+
xlabel = self.x_label if self.x_label
|
362
|
+
ylabel = self.y_label if self.y_label
|
355
363
|
backend.set_xlabel(xlabel) unless xlabel.nil?
|
356
364
|
backend.set_ylabel(ylabel) unless ylabel.nil?
|
357
365
|
|
@@ -3,7 +3,14 @@ module Charty
|
|
3
3
|
class DistributionPlotter < AbstractPlotter
|
4
4
|
def flat_structure
|
5
5
|
{
|
6
|
-
x:
|
6
|
+
x: :@values
|
7
|
+
}
|
8
|
+
end
|
9
|
+
|
10
|
+
def wide_structure
|
11
|
+
{
|
12
|
+
x: :@values,
|
13
|
+
color: :@columns
|
7
14
|
}
|
8
15
|
end
|
9
16
|
|
@@ -14,6 +21,12 @@ module Charty
|
|
14
21
|
setup_variables
|
15
22
|
end
|
16
23
|
|
24
|
+
attr_reader :weights
|
25
|
+
|
26
|
+
def weights=(val)
|
27
|
+
@weights = check_dimension(val, :weights)
|
28
|
+
end
|
29
|
+
|
17
30
|
attr_reader :variables
|
18
31
|
|
19
32
|
attr_reader :color_norm
|
@@ -65,7 +78,6 @@ module Charty
|
|
65
78
|
return
|
66
79
|
end
|
67
80
|
|
68
|
-
# TODO: detect flat data
|
69
81
|
flat = data.is_a?(Charty::Vector)
|
70
82
|
if flat
|
71
83
|
@plot_data = {}
|
@@ -73,10 +85,10 @@ module Charty
|
|
73
85
|
|
74
86
|
[:x, :y].each do |var|
|
75
87
|
case self.flat_structure[var]
|
76
|
-
when
|
88
|
+
when :@index
|
77
89
|
@plot_data[var] = data.index.to_a
|
78
90
|
@variables[var] = data.index.name
|
79
|
-
when
|
91
|
+
when :@values
|
80
92
|
@plot_data[var] = data.to_a
|
81
93
|
@variables[var] = data.name
|
82
94
|
end
|
@@ -84,8 +96,32 @@ module Charty
|
|
84
96
|
|
85
97
|
@plot_data = Charty::Table.new(@plot_data)
|
86
98
|
else
|
87
|
-
|
88
|
-
|
99
|
+
numeric_columns = @data.column_names.select do |cn|
|
100
|
+
@data[cn].numeric?
|
101
|
+
end
|
102
|
+
wide_data = @data[numeric_columns]
|
103
|
+
|
104
|
+
melt_params = {var_name: :@columns, value_name: :@values }
|
105
|
+
if self.wide_structure.include?(:index)
|
106
|
+
melt_params[:id_vars] = :@index
|
107
|
+
end
|
108
|
+
|
109
|
+
@plot_data = wide_data.melt(**melt_params)
|
110
|
+
@variables = {}
|
111
|
+
self.wide_structure.each do |var, attr|
|
112
|
+
@plot_data[var] = @plot_data[attr]
|
113
|
+
|
114
|
+
@variables[var] = case attr
|
115
|
+
when :@columns
|
116
|
+
wide_data.columns.name
|
117
|
+
when :@index
|
118
|
+
wide_data.index.name
|
119
|
+
else
|
120
|
+
nil
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
@plot_data = @plot_data[self.wide_structure.keys]
|
89
125
|
end
|
90
126
|
end
|
91
127
|
|
@@ -103,10 +139,11 @@ module Charty
|
|
103
139
|
x: self.x,
|
104
140
|
y: self.y,
|
105
141
|
color: self.color,
|
142
|
+
weights: self.weights
|
106
143
|
}.each do |key, val|
|
107
144
|
next if val.nil?
|
108
145
|
|
109
|
-
if data.
|
146
|
+
if data.column?(val)
|
110
147
|
plot_data[key] = data[val]
|
111
148
|
variables[key] = val
|
112
149
|
else
|
@@ -12,16 +12,6 @@ module Charty
|
|
12
12
|
([:x, :y] & self.variables.keys)[0]
|
13
13
|
end
|
14
14
|
|
15
|
-
attr_reader :weights
|
16
|
-
|
17
|
-
def weights=(val)
|
18
|
-
@weights = check_weights(val)
|
19
|
-
end
|
20
|
-
|
21
|
-
private def check_weights(val)
|
22
|
-
raise NotImplementedError, "weights is not supported yet"
|
23
|
-
end
|
24
|
-
|
25
15
|
attr_reader :stat
|
26
16
|
|
27
17
|
def stat=(val)
|
@@ -65,10 +55,44 @@ module Charty
|
|
65
55
|
end
|
66
56
|
|
67
57
|
# TODO: bin_width
|
68
|
-
|
58
|
+
|
59
|
+
attr_reader :bin_range
|
60
|
+
|
61
|
+
def bin_range=(val)
|
62
|
+
@bin_range = check_bin_range(val)
|
63
|
+
end
|
64
|
+
|
65
|
+
private def check_bin_range(val)
|
66
|
+
case val
|
67
|
+
when nil, Range
|
68
|
+
return val
|
69
|
+
when Array
|
70
|
+
if val.length == 2
|
71
|
+
val.each_with_index do |v, i|
|
72
|
+
check_number(v, "bin_range[#{i}]")
|
73
|
+
end
|
74
|
+
return val
|
75
|
+
else
|
76
|
+
amount = val.length < 2 ? "few" : "many"
|
77
|
+
raise ArgumentError,
|
78
|
+
"Too #{amount} items in `bin_range` array (%p for 2)" % val.length
|
79
|
+
end
|
80
|
+
else
|
81
|
+
raise ArgumentError,
|
82
|
+
"Invalid value for `bin_range` " +
|
83
|
+
"(%p for a range or a pair of numbers)" % val
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
69
87
|
# TODO: discrete
|
70
88
|
# TODO: cumulative
|
71
|
-
|
89
|
+
|
90
|
+
attr_reader :common_bins
|
91
|
+
|
92
|
+
def common_bins=(val)
|
93
|
+
@common_bins = check_boolean(val, :common_bins)
|
94
|
+
end
|
95
|
+
|
72
96
|
# TODO: common_norm
|
73
97
|
|
74
98
|
attr_reader :multiple
|
@@ -127,24 +151,52 @@ module Charty
|
|
127
151
|
private def draw_univariate_histogram(backend)
|
128
152
|
map_color(palette: palette, order: color_order, norm: color_norm)
|
129
153
|
|
154
|
+
key_color = self.key_color
|
155
|
+
if key_color.nil? && !self.variables.key?(:color)
|
156
|
+
palette = case self.palette
|
157
|
+
when Palette
|
158
|
+
self.palette
|
159
|
+
when nil
|
160
|
+
Palette.default
|
161
|
+
else
|
162
|
+
Palette[self.palette]
|
163
|
+
end
|
164
|
+
key_color = palette[0]
|
165
|
+
end
|
166
|
+
|
130
167
|
# TODO: calculate histogram here and use bar plot to visualize
|
131
168
|
data_variable = self.univariate_variable
|
132
169
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
170
|
+
if common_bins
|
171
|
+
all_data = processed_data.drop_na
|
172
|
+
all_observations = all_data[data_variable].to_a
|
173
|
+
|
174
|
+
bins = self.bins
|
175
|
+
bins = 10 if self.variables.key?(:color) && bins == :auto
|
176
|
+
|
177
|
+
case bins
|
178
|
+
when Integer
|
179
|
+
case bin_range
|
180
|
+
when Range
|
181
|
+
start = bin_range.begin
|
182
|
+
stop = bin_range.end
|
183
|
+
when Array
|
184
|
+
start, stop = bin_range.minmax
|
185
|
+
end
|
186
|
+
data_range = all_observations.minmax
|
187
|
+
start ||= data_range[0]
|
188
|
+
stop ||= data_range[1]
|
189
|
+
if start == stop
|
190
|
+
start -= 0.5
|
191
|
+
stop += 0.5
|
192
|
+
end
|
193
|
+
common_bin_edges = Linspace.new(start .. stop, bins + 1).map(&:to_f)
|
194
|
+
else
|
195
|
+
params = {}
|
196
|
+
params[:weights] = all_data[:weights].to_a if all_data.column?(:weights)
|
197
|
+
h = Statistics.histogram(all_observations, bins, **params)
|
198
|
+
common_bin_edges = h.edges
|
199
|
+
end
|
148
200
|
end
|
149
201
|
|
150
202
|
if self.variables.key?(:color)
|
@@ -154,27 +206,37 @@ module Charty
|
|
154
206
|
end
|
155
207
|
|
156
208
|
each_subset([:color], processed: true) do |sub_vars, sub_data|
|
157
|
-
name = sub_vars[:color]
|
158
209
|
observations = sub_data[data_variable].drop_na.to_a
|
210
|
+
params = {}
|
211
|
+
params[:weights] = sub_data[:weights].to_a if sub_data.column?(:weights)
|
212
|
+
params[:edges] = common_bin_edges if common_bin_edges
|
213
|
+
hist = Statistics.histogram(observations, bins, **params)
|
159
214
|
|
160
|
-
|
161
|
-
|
162
|
-
name, @color_mapper
|
215
|
+
name = sub_vars[:color]
|
216
|
+
backend.univariate_histogram(hist, name, data_variable, stat,
|
217
|
+
alpha, name, key_color, @color_mapper,
|
218
|
+
multiple, :bars, true, 1r)
|
163
219
|
end
|
164
220
|
end
|
165
221
|
|
166
222
|
private def annotate_axes(backend)
|
223
|
+
backend.set_title(self.title) if self.title
|
224
|
+
|
167
225
|
if univariate?
|
168
|
-
xlabel = self.variables[:x]
|
169
|
-
ylabel = self.variables[:y]
|
226
|
+
xlabel = self.x_label || self.variables[:x]
|
227
|
+
ylabel = self.y_label || self.variables[:y]
|
170
228
|
case self.univariate_variable
|
171
229
|
when :x
|
172
|
-
ylabel
|
230
|
+
ylabel ||= self.stat.to_s.capitalize
|
173
231
|
else
|
174
|
-
xlabel
|
232
|
+
xlabel ||= self.stat.to_s.capitalize
|
175
233
|
end
|
176
234
|
backend.set_ylabel(ylabel) if ylabel
|
177
235
|
backend.set_xlabel(xlabel) if xlabel
|
236
|
+
|
237
|
+
if self.variables.key?(:color)
|
238
|
+
backend.legend(loc: :best, title: self.variables[:color])
|
239
|
+
end
|
178
240
|
end
|
179
241
|
end
|
180
242
|
end
|
@@ -122,7 +122,7 @@ module Charty
|
|
122
122
|
|
123
123
|
include RandomSupport
|
124
124
|
|
125
|
-
attr_reader :sort, :err_style, :err_kws, :error_bar
|
125
|
+
attr_reader :sort, :err_style, :err_kws, :error_bar
|
126
126
|
|
127
127
|
def sort=(val)
|
128
128
|
@sort = check_boolean(val, :sort)
|
@@ -211,17 +211,21 @@ module Charty
|
|
211
211
|
[method, level]
|
212
212
|
end
|
213
213
|
|
214
|
+
attr_reader :x_scale
|
215
|
+
|
214
216
|
def x_scale=(val)
|
215
217
|
@x_scale = check_axis_scale(val, :x)
|
216
218
|
end
|
217
219
|
|
220
|
+
attr_reader :y_scale
|
221
|
+
|
218
222
|
def y_scale=(val)
|
219
223
|
@y_scale = check_axis_scale(val, :y)
|
220
224
|
end
|
221
225
|
|
222
226
|
private def check_axis_scale(val, axis)
|
223
227
|
case val
|
224
|
-
when :linear, "linear", :
|
228
|
+
when :linear, "linear", :log, "log"
|
225
229
|
val.to_sym
|
226
230
|
else
|
227
231
|
raise ArgumentError,
|
@@ -252,6 +256,15 @@ module Charty
|
|
252
256
|
sub_data = sub_data.sort_values(sort_cols)
|
253
257
|
end
|
254
258
|
|
259
|
+
# Perform axis scaling
|
260
|
+
if x_scale != :linear
|
261
|
+
sub_data[:x] = sub_data[:x].scale(x_scale)
|
262
|
+
end
|
263
|
+
if y_scale != :linear
|
264
|
+
sub_data[:y] = sub_data[:y].scale(x_scale)
|
265
|
+
end
|
266
|
+
|
267
|
+
# Perform estimation and error calculation
|
255
268
|
unless estimator.nil?
|
256
269
|
if self.variables.include?(:units)
|
257
270
|
raise "`estimator` is must be nil when specifying `units`"
|
@@ -261,7 +274,22 @@ module Charty
|
|
261
274
|
sub_data = grouped.apply(agg_var, &aggregator.method(:aggregate)).reset_index
|
262
275
|
end
|
263
276
|
|
264
|
-
#
|
277
|
+
# Perform axis inverse scaling
|
278
|
+
if x_scale != :linear
|
279
|
+
sub_data.column_names.each do |cn|
|
280
|
+
if cn.start_with?("x")
|
281
|
+
sub_data[cn] = sub_data[cn].scale_inverse(x_scale)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
if y_scale != :linear
|
287
|
+
sub_data.column_names.each do |cn|
|
288
|
+
if cn.start_with?("y")
|
289
|
+
sub_data[cn] = sub_data[cn].scale_inverse(x_scale)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
265
293
|
|
266
294
|
unit_grouping = if self.variables.include?(:units)
|
267
295
|
sub_data.group_by(:units).each_group
|
@@ -290,10 +318,15 @@ module Charty
|
|
290
318
|
end
|
291
319
|
|
292
320
|
private def annotate_axes(backend)
|
293
|
-
|
294
|
-
|
321
|
+
backend.set_title(self.title) if self.title
|
322
|
+
|
323
|
+
xlabel = self.x_label || self.variables[:x]
|
324
|
+
ylabel = self.y_label || self.variables[:y]
|
295
325
|
backend.set_xlabel(xlabel) unless xlabel.nil?
|
296
326
|
backend.set_ylabel(ylabel) unless ylabel.nil?
|
327
|
+
|
328
|
+
backend.set_xscale(x_scale)
|
329
|
+
backend.set_yscale(y_scale)
|
297
330
|
end
|
298
331
|
end
|
299
332
|
end
|