charty 0.2.6 → 0.2.10
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.
- checksums.yaml +4 -4
- data/charty.gemspec +2 -1
- 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.rb +2 -0
- data/lib/charty/backends/plotly.rb +127 -24
- data/lib/charty/backends/plotly_helpers/html_renderer.rb +203 -0
- data/lib/charty/backends/plotly_helpers/notebook_renderer.rb +89 -0
- data/lib/charty/backends/plotly_helpers/plotly_renderer.rb +121 -0
- data/lib/charty/backends/pyplot.rb +74 -0
- data/lib/charty/backends/unicode_plot.rb +9 -9
- data/lib/charty/cache_dir.rb +27 -0
- data/lib/charty/iruby_helper.rb +18 -0
- data/lib/charty/plot_methods.rb +82 -6
- data/lib/charty/plotters.rb +3 -0
- data/lib/charty/plotters/abstract_plotter.rb +56 -16
- 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 +180 -0
- data/lib/charty/plotters/histogram_plotter.rb +244 -0
- data/lib/charty/plotters/line_plotter.rb +38 -5
- data/lib/charty/plotters/scatter_plotter.rb +4 -2
- data/lib/charty/statistics.rb +9 -0
- data/lib/charty/table.rb +30 -23
- 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 +59 -1
- data/lib/charty/table_adapters/pandas_adapter.rb +49 -1
- data/lib/charty/vector.rb +29 -1
- data/lib/charty/vector_adapters.rb +16 -0
- data/lib/charty/vector_adapters/pandas_adapter.rb +10 -1
- data/lib/charty/version.rb +1 -1
- metadata +39 -15
@@ -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
|
@@ -70,8 +70,10 @@ module Charty
|
|
70
70
|
end
|
71
71
|
|
72
72
|
private def annotate_axes(backend)
|
73
|
-
|
74
|
-
|
73
|
+
backend.set_title(self.title) if self.title
|
74
|
+
|
75
|
+
xlabel = self.x_label || self.variables[:x]
|
76
|
+
ylabel = self.y_label || self.variables[:y]
|
75
77
|
backend.set_xlabel(xlabel) unless xlabel.nil?
|
76
78
|
backend.set_ylabel(ylabel) unless ylabel.nil?
|
77
79
|
end
|
data/lib/charty/statistics.rb
CHANGED
@@ -10,6 +10,10 @@ module Charty
|
|
10
10
|
def self.stdev(enum, population: false)
|
11
11
|
enum.stdev(population: population)
|
12
12
|
end
|
13
|
+
|
14
|
+
def self.histogram(ary, *args, **kwargs)
|
15
|
+
ary.histogram(*args, **kwargs)
|
16
|
+
end
|
13
17
|
rescue LoadError
|
14
18
|
def self.mean(enum)
|
15
19
|
xs = enum.to_a
|
@@ -24,6 +28,11 @@ module Charty
|
|
24
28
|
var = xs.map {|x| (x - mean)**2 }.sum / (n - ddof)
|
25
29
|
Math.sqrt(var)
|
26
30
|
end
|
31
|
+
|
32
|
+
def self.histogram(ary, *args, **kwargs)
|
33
|
+
raise NotImplementedError,
|
34
|
+
"histogram is currently supported only with enumerable-statistics"
|
35
|
+
end
|
27
36
|
end
|
28
37
|
|
29
38
|
def self.bootstrap(vector, n_boot: 2000, func: :mean, units: nil, random: nil)
|
data/lib/charty/table.rb
CHANGED
@@ -32,20 +32,7 @@ module Charty
|
|
32
32
|
def_delegators :adapter, :columns, :columns=
|
33
33
|
def_delegators :adapter, :index, :index=
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
def column?(name)
|
38
|
-
return true if column_names.include?(name)
|
39
|
-
|
40
|
-
case name
|
41
|
-
when String
|
42
|
-
column_names.include?(name.to_sym)
|
43
|
-
when Symbol
|
44
|
-
column_names.include?(name.to_s)
|
45
|
-
else
|
46
|
-
false
|
47
|
-
end
|
48
|
-
end
|
35
|
+
def_delegators :@adapter, :column_names, :column?
|
49
36
|
|
50
37
|
def_delegator :@adapter, :data, :raw_data
|
51
38
|
|
@@ -65,17 +52,35 @@ module Charty
|
|
65
52
|
end
|
66
53
|
|
67
54
|
def [](key)
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
55
|
+
case key
|
56
|
+
when Array
|
57
|
+
@adapter[nil, key]
|
58
|
+
else
|
59
|
+
key = case key
|
60
|
+
when Symbol
|
61
|
+
key
|
62
|
+
else
|
63
|
+
String.try_convert(key).to_sym
|
64
|
+
end
|
65
|
+
if @column_cache.key?(key)
|
66
|
+
@column_cache[key]
|
67
|
+
else
|
68
|
+
@column_cache[key] = @adapter[nil, key]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def []=(key, values)
|
74
|
+
case key
|
75
|
+
when Array
|
76
|
+
raise ArgumentError,
|
77
|
+
"Substituting multiple keys is not supported"
|
78
|
+
when Symbol
|
79
|
+
# do nothing
|
76
80
|
else
|
77
|
-
|
81
|
+
key = key.to_str.to_sym
|
78
82
|
end
|
83
|
+
@adapter[key] = values
|
79
84
|
end
|
80
85
|
|
81
86
|
def group_by(grouper, sort: true, drop_na: true)
|
@@ -123,6 +128,8 @@ module Charty
|
|
123
128
|
|
124
129
|
def_delegator :adapter, :reset_index
|
125
130
|
|
131
|
+
def_delegator :adapter, :melt
|
132
|
+
|
126
133
|
class GroupByBase
|
127
134
|
end
|
128
135
|
|
@@ -16,6 +16,19 @@ module Charty
|
|
16
16
|
columns.to_a
|
17
17
|
end
|
18
18
|
|
19
|
+
def column?(name)
|
20
|
+
return true if column_names.include?(name)
|
21
|
+
|
22
|
+
case name
|
23
|
+
when String
|
24
|
+
column_names.include?(name.to_sym)
|
25
|
+
when Symbol
|
26
|
+
column_names.include?(name.to_s)
|
27
|
+
else
|
28
|
+
false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
19
32
|
attr_reader :index
|
20
33
|
|
21
34
|
def index=(values)
|
@@ -126,6 +139,81 @@ module Charty
|
|
126
139
|
)
|
127
140
|
end
|
128
141
|
|
142
|
+
def melt(id_vars: nil, value_vars: nil, var_name: nil, value_name: :value)
|
143
|
+
if column?(value_name)
|
144
|
+
raise ArgumentError,
|
145
|
+
"The value of `value_name` must not be matched to the existing column names."
|
146
|
+
end
|
147
|
+
|
148
|
+
case value_name
|
149
|
+
when Symbol
|
150
|
+
# do nothing
|
151
|
+
else
|
152
|
+
value_name = value.to_str.to_sym
|
153
|
+
end
|
154
|
+
|
155
|
+
id_vars = check_melt_vars(id_vars, :id_vars)
|
156
|
+
value_vars = check_melt_vars(value_vars, :value_vars) { self.column_names }
|
157
|
+
value_vars -= id_vars
|
158
|
+
|
159
|
+
case var_name
|
160
|
+
when nil
|
161
|
+
var_name = self.columns.name
|
162
|
+
var_name = :variable if var_name.nil?
|
163
|
+
when Symbol
|
164
|
+
# do nothing
|
165
|
+
else
|
166
|
+
var_name = var_name.to_str
|
167
|
+
end
|
168
|
+
var_name = var_name.to_sym
|
169
|
+
|
170
|
+
n_batch_rows = self.length
|
171
|
+
n_target_columns = value_vars.length
|
172
|
+
melted_data = id_vars.map { |cn|
|
173
|
+
id_values = self[nil, cn].to_a
|
174
|
+
[cn.to_sym, id_values * n_target_columns]
|
175
|
+
}.to_h
|
176
|
+
|
177
|
+
melted_data[var_name] = value_vars.map { |cn| Array.new(n_batch_rows, cn) }.flatten
|
178
|
+
|
179
|
+
melted_data[value_name] = value_vars.map { |cn| self[nil, cn].to_a }.flatten
|
180
|
+
|
181
|
+
Charty::Table.new(melted_data)
|
182
|
+
end
|
183
|
+
|
184
|
+
private def check_melt_vars(val, name)
|
185
|
+
if val.nil?
|
186
|
+
val = if block_given?
|
187
|
+
yield
|
188
|
+
else
|
189
|
+
[]
|
190
|
+
end
|
191
|
+
end
|
192
|
+
case val
|
193
|
+
when nil
|
194
|
+
nil
|
195
|
+
when Array
|
196
|
+
missing = val.reject {|cn| self.column?(cn) }
|
197
|
+
if missing.empty?
|
198
|
+
val.map do |v|
|
199
|
+
case v
|
200
|
+
when Symbol
|
201
|
+
v.to_s
|
202
|
+
else
|
203
|
+
v.to_str
|
204
|
+
end
|
205
|
+
end
|
206
|
+
else
|
207
|
+
raise ArgumentError,
|
208
|
+
"Missing column names in `#{name}` (%s)" % missing.join(", ")
|
209
|
+
end
|
210
|
+
when Symbol
|
211
|
+
[val.to_s]
|
212
|
+
else
|
213
|
+
[val.to_str]
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
129
217
|
private def check_na_position(val)
|
130
218
|
case val
|
131
219
|
when :first, "first"
|
@@ -57,12 +57,52 @@ module Charty
|
|
57
57
|
column_data = if @data.has_vector?(column)
|
58
58
|
@data[column]
|
59
59
|
else
|
60
|
-
|
60
|
+
case column
|
61
|
+
when String
|
62
|
+
@data[column.to_sym]
|
63
|
+
else
|
64
|
+
@data[column.to_s]
|
65
|
+
end
|
61
66
|
end
|
62
67
|
Vector.new(column_data)
|
63
68
|
end
|
64
69
|
end
|
65
70
|
|
71
|
+
def []=(key, values)
|
72
|
+
case key
|
73
|
+
when Symbol
|
74
|
+
sym_key = key
|
75
|
+
str_key = key.to_s
|
76
|
+
else
|
77
|
+
str_key = key.to_str
|
78
|
+
sym_key = str_key.to_sym
|
79
|
+
end
|
80
|
+
case
|
81
|
+
when @data.has_vector?(sym_key)
|
82
|
+
key = sym_key
|
83
|
+
when @data.has_vector?(str_key)
|
84
|
+
key = str_key
|
85
|
+
end
|
86
|
+
|
87
|
+
case values
|
88
|
+
when Charty::Vector
|
89
|
+
case values.adapter
|
90
|
+
when Charty::VectorAdapters::DaruVectorAdapter
|
91
|
+
@data[key] = values.adapter.data
|
92
|
+
else
|
93
|
+
@data[key] = values.to_a
|
94
|
+
end
|
95
|
+
else
|
96
|
+
orig_values = values
|
97
|
+
values = Array.try_convert(values)
|
98
|
+
if values.nil?
|
99
|
+
raise ArgumentError, "`values` must be convertible to Array"
|
100
|
+
end
|
101
|
+
@data[key] = values
|
102
|
+
end
|
103
|
+
return values
|
104
|
+
end
|
105
|
+
|
66
106
|
private def check_type(type, data, name)
|
67
107
|
return data if data.is_a?(type)
|
68
108
|
raise TypeError, "#{name} must be a #{type}"
|
@@ -20,6 +20,8 @@ module Charty
|
|
20
20
|
end
|
21
21
|
when Hash
|
22
22
|
true
|
23
|
+
when ->(x) { defined?(CSV::Table) && x.is_a?(CSV::Table) }
|
24
|
+
true
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
@@ -79,6 +81,9 @@ module Charty
|
|
79
81
|
else
|
80
82
|
unsupported_data_format
|
81
83
|
end
|
84
|
+
when ->(x) { defined?(CSV::Table) && x.is_a?(CSV::Table) }
|
85
|
+
columns ||= data.headers
|
86
|
+
arrays = data.headers.map {|x| data[x] }
|
82
87
|
else
|
83
88
|
unsupported_data_format
|
84
89
|
end
|
@@ -176,6 +181,11 @@ module Charty
|
|
176
181
|
@data[column][row]
|
177
182
|
else
|
178
183
|
case column
|
184
|
+
when Array
|
185
|
+
slice_data = column.map { |cn|
|
186
|
+
[cn, self[nil, cn]]
|
187
|
+
}.to_h
|
188
|
+
return Charty::Table.new(slice_data, index: self.index)
|
179
189
|
when Symbol
|
180
190
|
sym_key = column
|
181
191
|
str_key = column.to_s
|
@@ -189,8 +199,56 @@ module Charty
|
|
189
199
|
else
|
190
200
|
@data[str_key]
|
191
201
|
end
|
192
|
-
|
202
|
+
# FIXME: Here column_data need to be dupped to
|
203
|
+
# prevent to overwrite the name of Pandas::Series
|
204
|
+
Vector.new(column_data.dup, index: index, name: column)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def []=(key, values)
|
209
|
+
case key
|
210
|
+
when Symbol
|
211
|
+
str_key = key.to_s
|
212
|
+
sym_key = key
|
213
|
+
else
|
214
|
+
str_key = key.to_str
|
215
|
+
sym_key = str_key.to_sym
|
216
|
+
end
|
217
|
+
|
218
|
+
orig_values = values
|
219
|
+
case values
|
220
|
+
when Charty::Vector
|
221
|
+
values = values.data
|
222
|
+
else
|
223
|
+
values = Array.try_convert(values)
|
193
224
|
end
|
225
|
+
if values.nil?
|
226
|
+
raise ArgumentError,
|
227
|
+
"`values` must be convertible to Array"
|
228
|
+
end
|
229
|
+
|
230
|
+
if values.length != self.length
|
231
|
+
raise ArgumentError,
|
232
|
+
"`values` length does not match the length of the table"
|
233
|
+
end
|
234
|
+
|
235
|
+
if @data.key?(sym_key)
|
236
|
+
@data[sym_key] = values
|
237
|
+
elsif @data.key?(str_key)
|
238
|
+
@data[str_key] = values
|
239
|
+
elsif key == sym_key
|
240
|
+
@data[sym_key] = values
|
241
|
+
new_column = sym_key
|
242
|
+
else
|
243
|
+
@data[str_key] = values
|
244
|
+
new_column = sym_key
|
245
|
+
end
|
246
|
+
|
247
|
+
if new_column
|
248
|
+
self.columns = Index.new([*self.columns, new_column])
|
249
|
+
end
|
250
|
+
|
251
|
+
values
|
194
252
|
end
|
195
253
|
|
196
254
|
def each
|
@@ -42,6 +42,10 @@ module Charty
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
+
def column?(name)
|
46
|
+
data.__contains__(name)
|
47
|
+
end
|
48
|
+
|
45
49
|
def index
|
46
50
|
PandasIndex.new(data.index)
|
47
51
|
end
|
@@ -76,8 +80,33 @@ module Charty
|
|
76
80
|
if row
|
77
81
|
@data[column][row]
|
78
82
|
else
|
79
|
-
|
83
|
+
case column
|
84
|
+
when Array
|
85
|
+
Table.new(@data[column])
|
86
|
+
else
|
87
|
+
Vector.new(@data[column])
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def []=(key, values)
|
93
|
+
case values
|
94
|
+
when Charty::Vector
|
95
|
+
case values.adapter
|
96
|
+
when Charty::VectorAdapters::PandasSeriesAdapter
|
97
|
+
@data[key] = values.adapter.data
|
98
|
+
else
|
99
|
+
@data[key] = values.to_a
|
100
|
+
end
|
101
|
+
else
|
102
|
+
orig_values = values
|
103
|
+
values = Array.try_convert(values)
|
104
|
+
if values.nil?
|
105
|
+
raise ArgumentError, "`values` must be convertible to Array"
|
106
|
+
end
|
107
|
+
@data[key] = values
|
80
108
|
end
|
109
|
+
return values
|
81
110
|
end
|
82
111
|
|
83
112
|
def drop_na
|
@@ -101,6 +130,15 @@ module Charty
|
|
101
130
|
Charty::Table.new(data.reset_index)
|
102
131
|
end
|
103
132
|
|
133
|
+
def melt(id_vars: nil, value_vars: nil, var_name: nil, value_name: :value)
|
134
|
+
id_vars = check_melt_vars(id_vars, :id_vars) { nil }
|
135
|
+
value_vars = check_melt_vars(value_vars, :value_vars) { nil }
|
136
|
+
|
137
|
+
Charty::Table.new(data.melt(id_vars: id_vars, value_vars: value_vars,
|
138
|
+
var_name: var_name, value_name: value_name,
|
139
|
+
ignore_index: true))
|
140
|
+
end
|
141
|
+
|
104
142
|
class GroupBy < Charty::Table::GroupByBase
|
105
143
|
def initialize(groupby)
|
106
144
|
@groupby = groupby
|
@@ -116,6 +154,7 @@ module Charty
|
|
116
154
|
each_group_key.to_a
|
117
155
|
end
|
118
156
|
|
157
|
+
# TODO: test
|
119
158
|
def each_group_key
|
120
159
|
return enum_for(__method__) unless block_given?
|
121
160
|
|
@@ -146,6 +185,15 @@ module Charty
|
|
146
185
|
end
|
147
186
|
end
|
148
187
|
|
188
|
+
# TODO: test
|
189
|
+
def each_group
|
190
|
+
return enum_for(__method__) unless block_given?
|
191
|
+
|
192
|
+
each_group_key do |key|
|
193
|
+
yield(Array(key), self[key])
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
149
197
|
def apply(*args, &block)
|
150
198
|
res = @groupby.apply(->(data) {
|
151
199
|
res = block.call(Charty::Table.new(data), *args)
|