charty 0.2.4 → 0.2.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +64 -15
- data/charty.gemspec +10 -3
- data/lib/charty.rb +5 -2
- data/lib/charty/backends/bokeh.rb +2 -2
- data/lib/charty/backends/google_charts.rb +1 -1
- data/lib/charty/backends/gruff.rb +1 -1
- data/lib/charty/backends/plotly.rb +434 -32
- data/lib/charty/backends/plotly_helpers/html_renderer.rb +203 -0
- data/lib/charty/backends/plotly_helpers/notebook_renderer.rb +87 -0
- data/lib/charty/backends/plotly_helpers/plotly_renderer.rb +121 -0
- data/lib/charty/backends/pyplot.rb +187 -48
- data/lib/charty/backends/rubyplot.rb +1 -1
- data/lib/charty/cache_dir.rb +27 -0
- data/lib/charty/dash_pattern_generator.rb +57 -0
- data/lib/charty/index.rb +1 -1
- data/lib/charty/iruby_helper.rb +18 -0
- data/lib/charty/plot_methods.rb +115 -3
- data/lib/charty/plotter.rb +2 -2
- data/lib/charty/plotters.rb +4 -0
- data/lib/charty/plotters/abstract_plotter.rb +106 -11
- data/lib/charty/plotters/bar_plotter.rb +1 -16
- data/lib/charty/plotters/box_plotter.rb +1 -16
- data/lib/charty/plotters/distribution_plotter.rb +150 -0
- data/lib/charty/plotters/histogram_plotter.rb +242 -0
- data/lib/charty/plotters/line_plotter.rb +300 -0
- data/lib/charty/plotters/relational_plotter.rb +213 -96
- data/lib/charty/plotters/scatter_plotter.rb +8 -43
- data/lib/charty/statistics.rb +11 -2
- data/lib/charty/table.rb +124 -14
- data/lib/charty/table_adapters/base_adapter.rb +97 -0
- data/lib/charty/table_adapters/daru_adapter.rb +2 -0
- data/lib/charty/table_adapters/datasets_adapter.rb +7 -0
- data/lib/charty/table_adapters/hash_adapter.rb +19 -3
- data/lib/charty/table_adapters/pandas_adapter.rb +82 -0
- data/lib/charty/util.rb +28 -0
- data/lib/charty/vector_adapters.rb +5 -1
- data/lib/charty/vector_adapters/array_adapter.rb +2 -10
- data/lib/charty/vector_adapters/daru_adapter.rb +3 -11
- data/lib/charty/vector_adapters/narray_adapter.rb +1 -6
- data/lib/charty/vector_adapters/numpy_adapter.rb +1 -1
- data/lib/charty/vector_adapters/pandas_adapter.rb +0 -1
- data/lib/charty/version.rb +1 -1
- metadata +104 -11
- data/lib/charty/missing_value_support.rb +0 -14
@@ -16,29 +16,84 @@ module Charty
|
|
16
16
|
lookup_single_value(key, *args)
|
17
17
|
end
|
18
18
|
end
|
19
|
+
|
20
|
+
private def categorical_lut_from_hash(levels, pairs, name)
|
21
|
+
missing_keys = levels - pairs.keys
|
22
|
+
unless missing_keys.empty?
|
23
|
+
raise ArgumentError,
|
24
|
+
"The `#{name}` hash is missing keys: %p" % missing_keys
|
25
|
+
end
|
26
|
+
pairs.dup
|
27
|
+
end
|
28
|
+
|
29
|
+
private def categorical_lut_from_array(levels, values, name)
|
30
|
+
if levels.length != values.length
|
31
|
+
raise ArgumentError,
|
32
|
+
"The `#{name}` array has the wrong number of values " +
|
33
|
+
"(%d for %d)." % [values.length, levels.length]
|
34
|
+
end
|
35
|
+
levels.zip(values).to_h
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# TODO: This should be replaced with red-colors's Normalize feature
|
40
|
+
class SimpleNormalizer
|
41
|
+
def initialize(vmin=nil, vmax=nil)
|
42
|
+
@vmin = vmin
|
43
|
+
@vmax = vmax
|
44
|
+
end
|
45
|
+
|
46
|
+
attr_accessor :vmin, :vmax
|
47
|
+
|
48
|
+
def call(value, clip=nil)
|
49
|
+
scalar_p = false
|
50
|
+
vector_p = false
|
51
|
+
case value
|
52
|
+
when Charty::Vector
|
53
|
+
vector_p = true
|
54
|
+
value = value.to_a
|
55
|
+
when Array
|
56
|
+
# do nothing
|
57
|
+
else
|
58
|
+
scalar_p = true
|
59
|
+
value = [value]
|
60
|
+
end
|
61
|
+
|
62
|
+
@vmin = value.min if vmin.nil?
|
63
|
+
@vmax = value.max if vmax.nil?
|
64
|
+
|
65
|
+
result = value.map {|x| (x - vmin) / (vmax - vmin).to_f }
|
66
|
+
|
67
|
+
case
|
68
|
+
when scalar_p
|
69
|
+
result[0]
|
70
|
+
when vector_p
|
71
|
+
Charty::Vector.new(result, index: value.index)
|
72
|
+
else
|
73
|
+
result
|
74
|
+
end
|
75
|
+
end
|
19
76
|
end
|
20
77
|
|
21
78
|
class ColorMapper < BaseMapper
|
22
79
|
private def initialize_mapping(palette, order, norm)
|
23
80
|
@palette = palette
|
24
81
|
@order = order
|
25
|
-
@norm = norm
|
26
82
|
|
27
83
|
if plotter.variables.key?(:color)
|
28
84
|
data = plotter.plot_data[:color]
|
29
85
|
end
|
30
86
|
|
31
87
|
if data && data.notnull.any?
|
32
|
-
@map_type = infer_map_type(
|
88
|
+
@map_type = infer_map_type(palette, norm, @plotter.input_format, @plotter.var_types[:color])
|
33
89
|
|
34
90
|
case @map_type
|
35
91
|
when :numeric
|
36
|
-
|
37
|
-
"numeric color mapping is not supported"
|
92
|
+
@levels, @lookup_table, @norm, @cmap = numeric_mapping(data, palette, norm)
|
38
93
|
when :categorical
|
39
94
|
@cmap = nil
|
40
95
|
@norm = nil
|
41
|
-
@levels, @lookup_table = categorical_mapping(data,
|
96
|
+
@levels, @lookup_table = categorical_mapping(data, palette, order)
|
42
97
|
else
|
43
98
|
raise NotImplementedError,
|
44
99
|
"datetime color mapping is not supported"
|
@@ -46,18 +101,59 @@ module Charty
|
|
46
101
|
end
|
47
102
|
end
|
48
103
|
|
104
|
+
private def numeric_mapping(data, palette, norm)
|
105
|
+
case palette
|
106
|
+
when Hash
|
107
|
+
levels = palette.keys.sort
|
108
|
+
colors = palette.values_at(*levels)
|
109
|
+
cmap = Colors::ListedColormap.new(colors)
|
110
|
+
lookup_table = palette.dup
|
111
|
+
else
|
112
|
+
levels = data.drop_na.unique_values
|
113
|
+
levels.sort!
|
114
|
+
|
115
|
+
palette ||= "ch:"
|
116
|
+
cmap = case palette
|
117
|
+
when Colors::Colormap
|
118
|
+
palette
|
119
|
+
else
|
120
|
+
Palette.new(palette, 256).to_colormap
|
121
|
+
end
|
122
|
+
|
123
|
+
case norm
|
124
|
+
when nil
|
125
|
+
norm = SimpleNormalizer.new
|
126
|
+
when Array
|
127
|
+
norm = SimpleNormalizer.new(*norm)
|
128
|
+
#when Colors::Normalizer
|
129
|
+
# TODO: Must support red-color's Normalize feature
|
130
|
+
else
|
131
|
+
raise ArgumentError,
|
132
|
+
"`color_norm` must be nil, Array, or Normalizer object"
|
133
|
+
end
|
134
|
+
|
135
|
+
# initialize norm
|
136
|
+
norm.(data.drop_na.to_a)
|
137
|
+
|
138
|
+
lookup_table = levels.map { |level|
|
139
|
+
[
|
140
|
+
level,
|
141
|
+
cmap[norm.(level)]
|
142
|
+
]
|
143
|
+
}.to_h
|
144
|
+
end
|
145
|
+
|
146
|
+
return levels, lookup_table, norm, cmap
|
147
|
+
end
|
148
|
+
|
49
149
|
private def categorical_mapping(data, palette, order)
|
50
150
|
levels = data.categorical_order(order)
|
51
151
|
n_colors = levels.length
|
52
152
|
|
53
153
|
case palette
|
54
154
|
when Hash
|
55
|
-
|
56
|
-
|
57
|
-
raise ArgumentError,
|
58
|
-
"The palette hash is missing keys: %p" % missing_keys
|
59
|
-
end
|
60
|
-
return levels, palette
|
155
|
+
lookup_table = categorical_lut_from_hash(levels, palette, :palette)
|
156
|
+
return levels, lookup_table
|
61
157
|
|
62
158
|
when nil
|
63
159
|
current_palette = Palette.default
|
@@ -66,17 +162,15 @@ module Charty
|
|
66
162
|
else
|
67
163
|
colors = Palette.husl_colors(n_colors)
|
68
164
|
end
|
165
|
+
|
69
166
|
when Array
|
70
|
-
if palette.length != n_colors
|
71
|
-
raise ArgumentError,
|
72
|
-
"The palette list has the wrong number of colors"
|
73
|
-
end
|
74
167
|
colors = palette
|
168
|
+
|
75
169
|
else
|
76
170
|
colors = Palette.new(palette, n_colors).colors
|
77
171
|
end
|
78
|
-
lookup_table = levels.zip(colors).to_h
|
79
172
|
|
173
|
+
lookup_table = categorical_lut_from_array(levels, colors, :palette)
|
80
174
|
return levels, lookup_table
|
81
175
|
end
|
82
176
|
|
@@ -96,7 +190,7 @@ module Charty
|
|
96
190
|
end
|
97
191
|
end
|
98
192
|
|
99
|
-
attr_reader :palette, :
|
193
|
+
attr_reader :palette, :norm, :levels, :lookup_table, :map_type
|
100
194
|
|
101
195
|
def inverse_lookup_table
|
102
196
|
lookup_table.invert
|
@@ -107,61 +201,21 @@ module Charty
|
|
107
201
|
@lookup_table[key]
|
108
202
|
elsif @norm
|
109
203
|
# Use the colormap to interpolate between existing datapoints
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
# end
|
204
|
+
begin
|
205
|
+
normed = @norm.(key)
|
206
|
+
@cmap[normed]
|
207
|
+
rescue ArgumentError, TypeError => err
|
208
|
+
if Util.nan?(key)
|
209
|
+
return "#000000"
|
210
|
+
else
|
211
|
+
raise err
|
212
|
+
end
|
213
|
+
end
|
121
214
|
end
|
122
215
|
end
|
123
216
|
end
|
124
217
|
|
125
218
|
class SizeMapper < BaseMapper
|
126
|
-
# TODO: This should be replaced with red-colors's Normalize feature
|
127
|
-
class SimpleNormalizer
|
128
|
-
def initialize(vmin=nil, vmax=nil)
|
129
|
-
@vmin = vmin
|
130
|
-
@vmax = vmax
|
131
|
-
end
|
132
|
-
|
133
|
-
attr_accessor :vmin, :vmax
|
134
|
-
|
135
|
-
def call(value, clip=nil)
|
136
|
-
scalar_p = false
|
137
|
-
vector_p = false
|
138
|
-
case value
|
139
|
-
when Charty::Vector
|
140
|
-
vector_p = true
|
141
|
-
value = value.to_a
|
142
|
-
when Array
|
143
|
-
# do nothing
|
144
|
-
else
|
145
|
-
scalar_p = true
|
146
|
-
value = [value]
|
147
|
-
end
|
148
|
-
|
149
|
-
@vmin = value.min if vmin.nil?
|
150
|
-
@vmax = value.max if vmax.nil?
|
151
|
-
|
152
|
-
result = value.map {|x| (x - vmin) / (vmax - vmin).to_f }
|
153
|
-
|
154
|
-
case
|
155
|
-
when scalar_p
|
156
|
-
result[0]
|
157
|
-
when vector_p
|
158
|
-
Charty::Vector.new(result, index: value.index)
|
159
|
-
else
|
160
|
-
result
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
219
|
private def initialize_mapping(sizes, order, norm)
|
166
220
|
@sizes = sizes
|
167
221
|
@order = order
|
@@ -245,11 +299,30 @@ module Charty
|
|
245
299
|
end
|
246
300
|
|
247
301
|
private def categorical_mapping(data, sizes, order)
|
248
|
-
|
249
|
-
|
302
|
+
levels = data.categorical_order(order)
|
303
|
+
|
304
|
+
case sizes
|
305
|
+
when Hash
|
306
|
+
lookup_table = categorical_lut_from_hash(levels, sizes, :sizes)
|
307
|
+
return levels, lookup_table
|
308
|
+
|
309
|
+
when Array
|
310
|
+
# nothing to do
|
311
|
+
|
312
|
+
when Range, nil
|
313
|
+
# Reverse values to use the largest size for the first category
|
314
|
+
size_range = sizes || (0r .. 1r)
|
315
|
+
sizes = Linspace.new(size_range, levels.length).reverse_each.to_a
|
316
|
+
else
|
317
|
+
raise ArgumentError,
|
318
|
+
"Unable to recognize the value for `sizes`: %p" % sizes
|
319
|
+
end
|
320
|
+
|
321
|
+
lookup_table = categorical_lut_from_array(levels, sizes, :sizes)
|
322
|
+
return levels, lookup_table
|
250
323
|
end
|
251
324
|
|
252
|
-
attr_reader :palette, :order, :norm, :levels
|
325
|
+
attr_reader :palette, :order, :norm, :levels, :lookup_table, :map_type
|
253
326
|
|
254
327
|
def lookup_single_value(key)
|
255
328
|
if @lookup_table.key?(key)
|
@@ -261,8 +334,6 @@ module Charty
|
|
261
334
|
min + normed * (max - min)
|
262
335
|
end
|
263
336
|
end
|
264
|
-
|
265
|
-
# TODO
|
266
337
|
end
|
267
338
|
|
268
339
|
class StyleMapper < BaseMapper
|
@@ -279,13 +350,14 @@ module Charty
|
|
279
350
|
@levels = data.categorical_order(order)
|
280
351
|
|
281
352
|
markers = map_attributes(markers, @levels, unique_markers(@levels.length), :markers)
|
282
|
-
|
283
|
-
# TODO: dashes support
|
353
|
+
dashes = map_attributes(dashes, @levels, unique_dashes(@levels.length), :dashes)
|
284
354
|
|
285
355
|
@lookup_table = @levels.map {|key|
|
286
356
|
record = {
|
287
|
-
marker: markers[key]
|
357
|
+
marker: markers[key],
|
358
|
+
dashes: dashes[key]
|
288
359
|
}
|
360
|
+
record.compact!
|
289
361
|
[key, record]
|
290
362
|
}.to_h
|
291
363
|
end
|
@@ -303,6 +375,10 @@ module Charty
|
|
303
375
|
MARKER_NAMES[0, n]
|
304
376
|
end
|
305
377
|
|
378
|
+
private def unique_dashes(n)
|
379
|
+
DashPatternGenerator.take(n)
|
380
|
+
end
|
381
|
+
|
306
382
|
private def map_attributes(vals, levels, defaults, attr)
|
307
383
|
case vals
|
308
384
|
when true
|
@@ -320,7 +396,7 @@ module Charty
|
|
320
396
|
"%he `%s` argument has the wrong number of values" % attr
|
321
397
|
end
|
322
398
|
return levels.zip(vals).to_h
|
323
|
-
when nil
|
399
|
+
when nil, false
|
324
400
|
return {}
|
325
401
|
else
|
326
402
|
raise ArgumentError,
|
@@ -345,6 +421,13 @@ module Charty
|
|
345
421
|
end
|
346
422
|
|
347
423
|
class RelationalPlotter < AbstractPlotter
|
424
|
+
def flat_structure
|
425
|
+
{
|
426
|
+
x: :index,
|
427
|
+
y: :values
|
428
|
+
}
|
429
|
+
end
|
430
|
+
|
348
431
|
def initialize(x, y, color, style, size, data: nil, **options, &block)
|
349
432
|
super(x, y, color, data: data, **options, &block)
|
350
433
|
|
@@ -354,13 +437,19 @@ module Charty
|
|
354
437
|
setup_variables
|
355
438
|
end
|
356
439
|
|
357
|
-
attr_reader :style, :size
|
440
|
+
attr_reader :style, :size, :units
|
358
441
|
|
359
442
|
attr_reader :color_norm
|
360
443
|
|
361
444
|
attr_reader :sizes, :size_order, :size_norm
|
362
445
|
|
363
|
-
attr_reader :markers, :
|
446
|
+
attr_reader :markers, :dashes, :style_order
|
447
|
+
|
448
|
+
attr_reader :legend
|
449
|
+
|
450
|
+
def units=(units)
|
451
|
+
@units = check_dimension(units, :units)
|
452
|
+
end
|
364
453
|
|
365
454
|
def style=(val)
|
366
455
|
@style = check_dimension(val, :style)
|
@@ -378,10 +467,8 @@ module Charty
|
|
378
467
|
end
|
379
468
|
|
380
469
|
def sizes=(val)
|
381
|
-
|
382
|
-
|
383
|
-
"Specifying sizes is not supported yet"
|
384
|
-
end
|
470
|
+
# NOTE: the value check will be perfomed in SizeMapper
|
471
|
+
@sizes = val
|
385
472
|
end
|
386
473
|
|
387
474
|
def size_order=(val)
|
@@ -407,17 +494,38 @@ module Charty
|
|
407
494
|
val
|
408
495
|
end
|
409
496
|
|
410
|
-
def
|
497
|
+
def dashes=(val)
|
498
|
+
@dashes = check_dashes(val)
|
499
|
+
end
|
500
|
+
|
501
|
+
private def check_dashes(val)
|
502
|
+
# TODO
|
503
|
+
val
|
504
|
+
end
|
505
|
+
|
506
|
+
def style_order=(val)
|
411
507
|
unless val.nil?
|
412
508
|
raise NotImplementedError,
|
413
|
-
"Specifying
|
509
|
+
"Specifying style_order is not supported yet"
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
def legend=(val)
|
514
|
+
case val
|
515
|
+
when :auto, :brief, :full, false
|
516
|
+
@legend = val
|
517
|
+
when "auto", "brief", "full"
|
518
|
+
@legend = val.to_sym
|
519
|
+
else
|
520
|
+
raise ArgumentError,
|
521
|
+
"invalid value of legend (%p for :auto, :brief, :full, or false)" % val
|
414
522
|
end
|
415
523
|
end
|
416
524
|
|
417
525
|
attr_reader :input_format, :plot_data, :variables, :var_types
|
418
526
|
|
419
527
|
private def setup_variables
|
420
|
-
if x.nil? && y.
|
528
|
+
if x.nil? && y.nil?
|
421
529
|
@input_format = :wide
|
422
530
|
setup_variables_with_wide_form_dataset
|
423
531
|
else
|
@@ -447,11 +555,23 @@ module Charty
|
|
447
555
|
return
|
448
556
|
end
|
449
557
|
|
450
|
-
|
451
|
-
flat = false
|
452
|
-
|
558
|
+
flat = data.is_a?(Charty::Vector)
|
453
559
|
if flat
|
454
|
-
|
560
|
+
@plot_data = {}
|
561
|
+
@variables = {}
|
562
|
+
|
563
|
+
[:x, :y].each do |var|
|
564
|
+
case self.flat_structure[var]
|
565
|
+
when :index
|
566
|
+
@plot_data[var] = data.index.to_a
|
567
|
+
@variables[var] = data.index.name
|
568
|
+
when :values
|
569
|
+
@plot_data[var] = data.to_a
|
570
|
+
@variables[var] = data.name
|
571
|
+
end
|
572
|
+
end
|
573
|
+
|
574
|
+
@plot_data = Charty::Table.new(@plot_data)
|
455
575
|
else
|
456
576
|
raise NotImplementedError,
|
457
577
|
"wide-form input is not supported"
|
@@ -459,11 +579,7 @@ module Charty
|
|
459
579
|
end
|
460
580
|
|
461
581
|
private def setup_variables_with_long_form_dataset
|
462
|
-
if data.nil?
|
463
|
-
@plot_data = Charty::Table.new({})
|
464
|
-
@variables = {}
|
465
|
-
return
|
466
|
-
end
|
582
|
+
self.data = {} if data.nil?
|
467
583
|
|
468
584
|
plot_data = {}
|
469
585
|
variables = {}
|
@@ -473,11 +589,12 @@ module Charty
|
|
473
589
|
y: self.y,
|
474
590
|
color: self.color,
|
475
591
|
style: self.style,
|
476
|
-
size: self.size
|
592
|
+
size: self.size,
|
593
|
+
units: self.units
|
477
594
|
}.each do |key, val|
|
478
595
|
next if val.nil?
|
479
596
|
|
480
|
-
if data.
|
597
|
+
if data.column?(val)
|
481
598
|
plot_data[key] = data[val]
|
482
599
|
variables[key] = val
|
483
600
|
else
|