charty 0.2.6 → 0.2.7
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 -1
- data/lib/charty.rb +2 -0
- data/lib/charty/backends/plotly.rb +92 -20
- data/lib/charty/backends/plotly_helpers/html_renderer.rb +203 -0
- data/lib/charty/backends/plotly_helpers/notebook_renderer.rb +86 -0
- data/lib/charty/backends/plotly_helpers/plotly_renderer.rb +121 -0
- data/lib/charty/backends/pyplot.rb +1 -0
- data/lib/charty/cache_dir.rb +27 -0
- data/lib/charty/iruby_helper.rb +18 -0
- data/lib/charty/plot_methods.rb +40 -0
- data/lib/charty/plotters.rb +3 -0
- data/lib/charty/plotters/abstract_plotter.rb +16 -8
- data/lib/charty/plotters/distribution_plotter.rb +143 -0
- data/lib/charty/plotters/histogram_plotter.rb +182 -0
- data/lib/charty/statistics.rb +9 -0
- data/lib/charty/version.rb +1 -1
- metadata +23 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6e39148cc6e6fd7916cdfbe5dacda3bab8f95068cbe3ceb4dff673d9688487d0
|
4
|
+
data.tar.gz: 5743d17f30055707fe39004f99bb30fb2cb10b164dbf9b222f000068cc800708
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 562b312fb4ca59995d19930f404d6162f432f7731752e5a9016132b6b2696a507f6d7523d5eb6e5bc0141273726d2608b1351b497f2c200cec0fda5204e4fc19
|
7
|
+
data.tar.gz: 213f83930bc1bd809ae98af41d0608793c356b234064e9f2ec79b96e76b1c301a331fc44c9c04fc0f3f188f65b780b59af449dca206f68ec310cb121b6bd0e41
|
data/charty.gemspec
CHANGED
@@ -27,6 +27,7 @@ Gem::Specification.new do |spec|
|
|
27
27
|
spec.require_paths = ["lib"]
|
28
28
|
|
29
29
|
spec.add_dependency "red-colors", ">= 0.3.0"
|
30
|
+
spec.add_dependency "red-datasets", ">= 0.1.2"
|
30
31
|
spec.add_dependency "red-palette", ">= 0.5.0"
|
31
32
|
|
32
33
|
spec.add_dependency "matplotlib", ">= 1.2.0"
|
@@ -36,7 +37,6 @@ Gem::Specification.new do |spec|
|
|
36
37
|
spec.add_development_dependency "bundler", ">= 1.16"
|
37
38
|
spec.add_development_dependency "rake"
|
38
39
|
spec.add_development_dependency "test-unit"
|
39
|
-
spec.add_development_dependency "red-datasets", ">= 0.1.2"
|
40
40
|
spec.add_development_dependency "daru"
|
41
41
|
spec.add_development_dependency "matrix" # need for daru on Ruby > 3.0
|
42
42
|
spec.add_development_dependency "activerecord"
|
data/lib/charty.rb
CHANGED
@@ -3,7 +3,9 @@ require_relative "charty/version"
|
|
3
3
|
require "colors"
|
4
4
|
require "palette"
|
5
5
|
|
6
|
+
require_relative "charty/cache_dir"
|
6
7
|
require_relative "charty/util"
|
8
|
+
require_relative "charty/iruby_helper"
|
7
9
|
require_relative "charty/dash_pattern_generator"
|
8
10
|
require_relative "charty/backends"
|
9
11
|
require_relative "charty/backend_methods"
|
@@ -2,6 +2,10 @@ require "json"
|
|
2
2
|
require "securerandom"
|
3
3
|
require "tmpdir"
|
4
4
|
|
5
|
+
require_relative "plotly_helpers/html_renderer"
|
6
|
+
require_relative "plotly_helpers/notebook_renderer"
|
7
|
+
require_relative "plotly_helpers/plotly_renderer"
|
8
|
+
|
5
9
|
module Charty
|
6
10
|
module Backends
|
7
11
|
class Plotly
|
@@ -558,6 +562,52 @@ module Charty
|
|
558
562
|
end
|
559
563
|
end
|
560
564
|
|
565
|
+
PLOTLY_HISTNORM = {
|
566
|
+
count: "".freeze,
|
567
|
+
frequency: "density".freeze,
|
568
|
+
density: "probability density".freeze,
|
569
|
+
probability: "probability".freeze
|
570
|
+
}.freeze
|
571
|
+
|
572
|
+
def univariate_histogram(data, name, variable_name, stat,
|
573
|
+
bin_start, bin_end, bin_size, alpha,
|
574
|
+
color, color_mapper)
|
575
|
+
orientation = case variable_name
|
576
|
+
when :x
|
577
|
+
:v
|
578
|
+
else
|
579
|
+
:h
|
580
|
+
end
|
581
|
+
trace = {
|
582
|
+
type: "histogram",
|
583
|
+
name: name.to_s,
|
584
|
+
variable_name => data.to_a,
|
585
|
+
orientation: orientation,
|
586
|
+
histnorm: PLOTLY_HISTNORM[stat],
|
587
|
+
"#{variable_name}bins": {
|
588
|
+
start: bin_start,
|
589
|
+
end: bin_end,
|
590
|
+
size: bin_size
|
591
|
+
},
|
592
|
+
opacity: alpha
|
593
|
+
}
|
594
|
+
|
595
|
+
if color
|
596
|
+
trace[:marker] = {
|
597
|
+
color: color_mapper[color].to_rgb.to_hex_string
|
598
|
+
}
|
599
|
+
end
|
600
|
+
|
601
|
+
@traces << trace
|
602
|
+
|
603
|
+
@layout[:bargap] = 0.05
|
604
|
+
|
605
|
+
if @traces.length > 1
|
606
|
+
@layout[:barmode] = "overlay"
|
607
|
+
@layout[:showlegend] = true
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
561
611
|
def set_xlabel(label)
|
562
612
|
@layout[:xaxis] ||= {}
|
563
613
|
@layout[:xaxis][:title] = label
|
@@ -699,7 +749,7 @@ module Charty
|
|
699
749
|
|
700
750
|
def render(element_id: nil, format: nil, notebook: false)
|
701
751
|
case format
|
702
|
-
when :html, "html"
|
752
|
+
when :html, "html", nil
|
703
753
|
format = "text/html"
|
704
754
|
when :png, "png"
|
705
755
|
format = "image/png"
|
@@ -708,7 +758,7 @@ module Charty
|
|
708
758
|
end
|
709
759
|
|
710
760
|
case format
|
711
|
-
when "text/html"
|
761
|
+
when "text/html"
|
712
762
|
# render html after this case cause
|
713
763
|
when "image/png", "image/jpeg"
|
714
764
|
image_data = render_image(format, element_id: element_id, notebook: false)
|
@@ -722,32 +772,54 @@ module Charty
|
|
722
772
|
"Unsupported mime type to render: %p" % format
|
723
773
|
end
|
724
774
|
|
725
|
-
# TODO: size should be customizable
|
726
|
-
html = <<~HTML
|
727
|
-
<div id="%{id}" style="width: 100%%; height:525px;"></div>
|
728
|
-
<script type="text/javascript">
|
729
|
-
requirejs(["plotly"], function (Plotly) {
|
730
|
-
Plotly.newPlot("%{id}", %{data}, %{layout});
|
731
|
-
});
|
732
|
-
</script>
|
733
|
-
HTML
|
734
|
-
|
735
775
|
element_id = SecureRandom.uuid if element_id.nil?
|
736
776
|
|
737
|
-
|
738
|
-
|
739
|
-
data: JSON.dump(@traces),
|
740
|
-
layout: JSON.dump(@layout)
|
741
|
-
}
|
742
|
-
|
777
|
+
renderer = PlotlyHelpers::HtmlRenderer.new(full_html: !notebook)
|
778
|
+
html = renderer.render({data: @traces, layout: @layout}, element_id: element_id)
|
743
779
|
if notebook
|
744
|
-
|
745
|
-
["text/html", html]
|
780
|
+
[format, html]
|
746
781
|
else
|
747
782
|
html
|
748
783
|
end
|
749
784
|
end
|
750
785
|
|
786
|
+
def render_mimebundle(include: [], exclude: [])
|
787
|
+
types = case
|
788
|
+
when IRubyHelper.vscode?,
|
789
|
+
IRubyHelper.nteract?
|
790
|
+
[:plotly_mimetype]
|
791
|
+
else
|
792
|
+
[:plotly_mimetype, :notebook]
|
793
|
+
end
|
794
|
+
bundle = Util.filter_map(types) { |type|
|
795
|
+
case type
|
796
|
+
when :plotly_mimetype
|
797
|
+
render_plotly_mimetype_bundle
|
798
|
+
when :notebook
|
799
|
+
render_notebook_bundle
|
800
|
+
end
|
801
|
+
}.to_h
|
802
|
+
bundle
|
803
|
+
end
|
804
|
+
|
805
|
+
private def render_plotly_mimetype_bundle
|
806
|
+
renderer = PlotlyHelpers::PlotlyRenderer.new
|
807
|
+
obj = renderer.render({data: @traces, layout: @layout})
|
808
|
+
[ "application/vnd.plotly.v1+json", obj ]
|
809
|
+
end
|
810
|
+
|
811
|
+
private def render_notebook_bundle
|
812
|
+
renderer = self.class.notebook_renderer
|
813
|
+
renderer.activate
|
814
|
+
html = renderer.render({data: @traces, layout: @layout})
|
815
|
+
[ "text/html", html ]
|
816
|
+
end
|
817
|
+
|
818
|
+
# for new APIs
|
819
|
+
def self.notebook_renderer
|
820
|
+
@notebook_renderer ||= PlotlyHelpers::NotebookRenderer.new
|
821
|
+
end
|
822
|
+
|
751
823
|
private def render_image(format=nil, filename: nil, element_id: nil, notebook: false,
|
752
824
|
title: nil, width: nil, height: nil)
|
753
825
|
format = "image/png" if format.nil?
|
@@ -0,0 +1,203 @@
|
|
1
|
+
require "datasets/downloader"
|
2
|
+
require "json"
|
3
|
+
require "securerandom"
|
4
|
+
|
5
|
+
module Charty
|
6
|
+
module Backends
|
7
|
+
module PlotlyHelpers
|
8
|
+
class HtmlRenderer
|
9
|
+
def initialize(use_cdn: true,
|
10
|
+
full_html: false,
|
11
|
+
requirejs: true)
|
12
|
+
@use_cdn = use_cdn
|
13
|
+
@full_html = full_html
|
14
|
+
@requirejs = requirejs
|
15
|
+
end
|
16
|
+
|
17
|
+
PLOTLY_URL = "https://plot.ly".freeze
|
18
|
+
PLOTLY_LATEST_CDN_URL = "https://cdn.plot.ly/plotly-latest.min.js".freeze
|
19
|
+
MATHJAX_CDN_URL = ("https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js").freeze
|
20
|
+
|
21
|
+
DEFAULT_WIDTH = "100%".freeze
|
22
|
+
DEFAULT_HEIGHT = 525
|
23
|
+
|
24
|
+
def render(figure, element_id: nil, post_script: nil)
|
25
|
+
element_id = SecureRandom.uuid if element_id.nil?
|
26
|
+
plotly_html_div = build_plotly_html_div(figure, element_id, post_script)
|
27
|
+
|
28
|
+
if @full_html
|
29
|
+
<<~END_HTML % {div: plotly_html_div}
|
30
|
+
<!DOCTYPE html>
|
31
|
+
<html>
|
32
|
+
<head><meta charset="utf-8" /></head>
|
33
|
+
<body>
|
34
|
+
%{div}
|
35
|
+
</body>
|
36
|
+
</html>
|
37
|
+
END_HTML
|
38
|
+
else
|
39
|
+
plotly_html_div
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private def build_plotly_html_div(figure, element_id, post_script)
|
44
|
+
layout = figure.fetch(:layout, {})
|
45
|
+
|
46
|
+
json_data = JSON.dump(figure.fetch(:data, []))
|
47
|
+
json_layout = JSON.dump(layout)
|
48
|
+
json_frames = JSON.dump(figure[:frames]) if figure.key?(:frames)
|
49
|
+
|
50
|
+
# TODO: config and responsive support
|
51
|
+
|
52
|
+
template = layout.fetch(:template, {}).fetch(:layout, {})
|
53
|
+
div_width = layout.fetch(:width, template.fetch(:width, DEFAULT_WIDTH))
|
54
|
+
div_height = layout.fetch(:height, template.fetch(:height, DEFAULT_HEIGHT))
|
55
|
+
|
56
|
+
div_width = "#{div_width}px" if Float(div_width, exception: false)
|
57
|
+
div_height = "#{div_height}px" if Float(div_height, exception: false)
|
58
|
+
|
59
|
+
# TODO: showLink and showSendToCloud support
|
60
|
+
base_url_line = "window.PLOTLYENV.BASE_URL = '%{url}';" % {url: PLOTLY_URL}
|
61
|
+
|
62
|
+
## build script body
|
63
|
+
|
64
|
+
# TODO: post_script support
|
65
|
+
then_post_script = ""
|
66
|
+
if post_script
|
67
|
+
ary = Array.try_convert(post_script)
|
68
|
+
post_script = ary || [post_script]
|
69
|
+
post_script.each do |ps|
|
70
|
+
next if ps.nil?
|
71
|
+
then_post_script << '.then(function(){ %{post_script} })' % {
|
72
|
+
post_script: ps % {plot_id: element_id}
|
73
|
+
}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
then_addframes = ""
|
78
|
+
then_animate = ""
|
79
|
+
if json_frames
|
80
|
+
then_addframes = <<~END_ADDFRAMES % {id: element_id, frames: json_frames}
|
81
|
+
.then(function(){
|
82
|
+
Plotly.addFrames('%{id}', {frames});
|
83
|
+
})
|
84
|
+
END_ADDFRAMES
|
85
|
+
|
86
|
+
# TODO: auto_play support
|
87
|
+
end
|
88
|
+
|
89
|
+
json_config = JSON.dump({}) # TODO: config support
|
90
|
+
|
91
|
+
script = <<~END_SCRIPT
|
92
|
+
if (document.getElementById("%{id}")) {
|
93
|
+
Plotly.newPlot("%{id}", %{data}, %{layout}, %{config})%{then_addframes}%{then_animate}%{then_post_script};
|
94
|
+
}
|
95
|
+
END_SCRIPT
|
96
|
+
script = script % {
|
97
|
+
id: element_id,
|
98
|
+
data: json_data,
|
99
|
+
layout: json_layout,
|
100
|
+
config: json_config,
|
101
|
+
then_addframes: then_addframes,
|
102
|
+
then_animate: then_animate,
|
103
|
+
then_post_script: then_post_script
|
104
|
+
}
|
105
|
+
|
106
|
+
## Handle loading/initializing plotlyjs
|
107
|
+
|
108
|
+
case
|
109
|
+
when @requirejs
|
110
|
+
include_plotlyjs = :require
|
111
|
+
include_mathjax = false
|
112
|
+
when @use_cdn
|
113
|
+
include_plotlyjs = :cdn
|
114
|
+
include_mathjax = :cdn
|
115
|
+
else
|
116
|
+
include_plotlyjs = true
|
117
|
+
include_mathjax = :cdn
|
118
|
+
end
|
119
|
+
|
120
|
+
case include_plotlyjs
|
121
|
+
when :require
|
122
|
+
require_start = 'require(["plotly"], function (Plotly) {'
|
123
|
+
require_end = '});'
|
124
|
+
when :cdn
|
125
|
+
load_plotlyjs = <<~END_LOAD_PLOTLYJS % {win_config: window_plotly_config, url: PLOTLY_LATEST_CDN_URL}
|
126
|
+
%{win_config}
|
127
|
+
<script src="%{url}"></script>
|
128
|
+
END_LOAD_PLOTLYJS
|
129
|
+
when true
|
130
|
+
load_plotlyjs = <<~END_LOAD_PLOTLYJS % {win_config: window_plotly_config, script: get_plotlyjs}
|
131
|
+
%{win_config}
|
132
|
+
<script type="text/javascript">%{script}</script>
|
133
|
+
END_LOAD_PLOTLYJS
|
134
|
+
end
|
135
|
+
|
136
|
+
## Handle loading/initializing MathJax
|
137
|
+
|
138
|
+
mathjax_tmplate = %Q[<script src="%{url}?config=TeX-AMS-MML_SVG"></script>]
|
139
|
+
case include_mathjax
|
140
|
+
when :cdn
|
141
|
+
mathjax_script = mathjax_tmplate % {url: MATHJAX_CDN_URL}
|
142
|
+
mathjax_script << <<~END_SCRIPT % {mathjax_config: mathjax_config}
|
143
|
+
<script type="text/javascript">%{mathjax_config}</script>
|
144
|
+
END_SCRIPT
|
145
|
+
else
|
146
|
+
mathjax_script = ""
|
147
|
+
end
|
148
|
+
|
149
|
+
div_template = <<~END_DIV
|
150
|
+
<div>
|
151
|
+
%{mathjax_script}
|
152
|
+
%{load_plotlyjs}
|
153
|
+
<div id="%{id}" class="plotly-graph-div" style="height: %{height}; width: %{width};"></div>
|
154
|
+
<script type="text/javascript">
|
155
|
+
%{require_start}
|
156
|
+
window.PLOTLYENV = window.PLOTLYENV || {};
|
157
|
+
%{base_url_line}
|
158
|
+
%{script}
|
159
|
+
%{require_end}
|
160
|
+
</script>
|
161
|
+
</div>
|
162
|
+
END_DIV
|
163
|
+
|
164
|
+
plotly_html_div = div_template % {
|
165
|
+
mathjax_script: mathjax_script,
|
166
|
+
load_plotlyjs: load_plotlyjs,
|
167
|
+
id: element_id,
|
168
|
+
height: div_height,
|
169
|
+
width: div_width,
|
170
|
+
require_start: require_start,
|
171
|
+
base_url_line: base_url_line,
|
172
|
+
script: script,
|
173
|
+
require_end: require_end
|
174
|
+
}
|
175
|
+
plotly_html_div.strip!
|
176
|
+
|
177
|
+
plotly_html_div
|
178
|
+
end
|
179
|
+
|
180
|
+
private def window_plotly_config
|
181
|
+
%Q(window.PlotlyConfig = {MathJaxConfig: 'local'};)
|
182
|
+
end
|
183
|
+
|
184
|
+
private def mathjax_config
|
185
|
+
%Q(if (window.MathJax) { MathJax.Hub.Config({SVG: {font: "STIX-Web"}}); })
|
186
|
+
end
|
187
|
+
|
188
|
+
private def get_plotlyjs
|
189
|
+
cache_path = CacheDir.path("plotly.min.js")
|
190
|
+
unless cache_path.exist?
|
191
|
+
download_plotlyjs(cache_path)
|
192
|
+
end
|
193
|
+
cache_path.read
|
194
|
+
end
|
195
|
+
|
196
|
+
private def download_plotlyjs(output_path)
|
197
|
+
downloader = Datasets::Downloader.new(PLOTLY_LATEST_CDN_URL)
|
198
|
+
downloader.download(output_path)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Charty
|
2
|
+
module Backends
|
3
|
+
module PlotlyHelpers
|
4
|
+
class NotebookRenderer < HtmlRenderer
|
5
|
+
def initialize(use_cdn: false)
|
6
|
+
super(use_cdn: use_cdn, full_html: false, requirejs: true)
|
7
|
+
@initialized = false
|
8
|
+
end
|
9
|
+
|
10
|
+
def activate()
|
11
|
+
return if @initialized
|
12
|
+
|
13
|
+
unless IRubyHelper.iruby_notebook?
|
14
|
+
raise "IRuby is unavailable"
|
15
|
+
end
|
16
|
+
|
17
|
+
if @use_cdn
|
18
|
+
script = <<~END_SCRIPT % {win_config: window_plotly_config, mathjax_config: mathjax_config}
|
19
|
+
<script type="text/javascript">
|
20
|
+
%{win_config}
|
21
|
+
%{mathjax_config}
|
22
|
+
if (typeof require !== 'undefined') {
|
23
|
+
require.undef("plotly");
|
24
|
+
requirejs.config({
|
25
|
+
paths: {
|
26
|
+
'plotly': ['https://cdn.plot.ly/plotly-latest.min']
|
27
|
+
}
|
28
|
+
});
|
29
|
+
require(['plotly'], function (Plotly) {
|
30
|
+
window._Plotly = Plotly;
|
31
|
+
});
|
32
|
+
}
|
33
|
+
</script>
|
34
|
+
END_SCRIPT
|
35
|
+
else
|
36
|
+
script = <<~END_SCRIPT % {script: get_plotlyjs, win_config: window_plotly_config, mathjax_config: mathjax_config}
|
37
|
+
<script type="text/javascript">
|
38
|
+
%{win_config}
|
39
|
+
%{mathjax_config}
|
40
|
+
if (typeof require !== 'undefined') {
|
41
|
+
require.undef("plotly");
|
42
|
+
define('plotly', function (require, exports, module) {
|
43
|
+
%{script}
|
44
|
+
});
|
45
|
+
require(['plotly'], function (Plotly) {
|
46
|
+
window._Plotly = Plotly;
|
47
|
+
});
|
48
|
+
}
|
49
|
+
</script>
|
50
|
+
END_SCRIPT
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def render(figure, element_id: nil, post_script: nil)
|
55
|
+
ary = Array.try_convert(post_script)
|
56
|
+
post_script = ary || [post_script]
|
57
|
+
post_script.unshift(<<~END_POST_SCRIPT)
|
58
|
+
var gd = document.getElementById('%{plot_id}');
|
59
|
+
var x = new MutationObserver(function (mutations, observer) {
|
60
|
+
var display = window.getComputedStyle(gd).display;
|
61
|
+
if (!display || display === 'none') {
|
62
|
+
console.log([gd, 'removed']);
|
63
|
+
Plotly.purge(gd);
|
64
|
+
observer.disconnect();
|
65
|
+
}
|
66
|
+
});
|
67
|
+
|
68
|
+
// Listen for the removal of the full notebook cell
|
69
|
+
var notebookContainer = gd.closest('#notebook-container');
|
70
|
+
if (notebookContainer) {
|
71
|
+
x.observe(notebookContainer, {childList: true});
|
72
|
+
}
|
73
|
+
|
74
|
+
// Listen for the clearing of the current output cell
|
75
|
+
var outputEl = gd.closest('.output');
|
76
|
+
if (outputEl) {
|
77
|
+
x.observe(outputEl, {childList: true});
|
78
|
+
}
|
79
|
+
END_POST_SCRIPT
|
80
|
+
|
81
|
+
super(figure, element_id: element_id, post_script: post_script)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require "date"
|
2
|
+
require "json"
|
3
|
+
require "time"
|
4
|
+
|
5
|
+
module Charty
|
6
|
+
module Backends
|
7
|
+
module PlotlyHelpers
|
8
|
+
class PlotlyRenderer
|
9
|
+
def render(figure)
|
10
|
+
json = JSON.generate(figure, allow_nan: true)
|
11
|
+
case json
|
12
|
+
when /\b(?:Infinity|NaN)\b/
|
13
|
+
visit(figure)
|
14
|
+
else
|
15
|
+
JSON.load(json)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private def visit(obj)
|
20
|
+
case obj
|
21
|
+
when Integer, String, Symbol, true, false, nil
|
22
|
+
obj
|
23
|
+
|
24
|
+
when Numeric
|
25
|
+
visit_float(obj)
|
26
|
+
|
27
|
+
when Time
|
28
|
+
visit_time(obj)
|
29
|
+
|
30
|
+
when Date
|
31
|
+
visit_date(obj)
|
32
|
+
|
33
|
+
when DateTime
|
34
|
+
visit_datetime(obj)
|
35
|
+
|
36
|
+
when Array
|
37
|
+
visit_array(obj)
|
38
|
+
|
39
|
+
when Hash
|
40
|
+
visit_hash(obj)
|
41
|
+
|
42
|
+
when ->(x) { defined?(Numo::NArray) && obj.is_a?(Numo::NArray) }
|
43
|
+
visit_array(obj.to_a)
|
44
|
+
|
45
|
+
when ->(x) { defined?(NMatrix) && obj.is_a?(NMatrix) }
|
46
|
+
visit_array(obj.to_a)
|
47
|
+
|
48
|
+
when ->(x) { defined?(Numpy::NDArray) && obj.is_a?(Numpy::NDArray) }
|
49
|
+
visit_array(obj.to_a)
|
50
|
+
|
51
|
+
when ->(x) { defined?(PyCall::List) && obj.is_a?(PyCall::List) }
|
52
|
+
visit_array(obj.to_a)
|
53
|
+
|
54
|
+
when ->(x) { defined?(PyCall::Tuple) && obj.is_a?(PyCall::Tuple) }
|
55
|
+
visit_array(obj.to_a)
|
56
|
+
|
57
|
+
when ->(x) { defined?(PyCall::Dict) && obj.is_a?(PyCall::Dict) }
|
58
|
+
visit_hash(obj.to_h)
|
59
|
+
|
60
|
+
when ->(x) { defined?(Pandas::Series) && obj.is_a?(Pandas::Series) }
|
61
|
+
visit_array(obj.to_a)
|
62
|
+
|
63
|
+
else
|
64
|
+
str = String.try_convert(obj)
|
65
|
+
return str unless str.nil?
|
66
|
+
|
67
|
+
ary = Array.try_convert(obj)
|
68
|
+
return visit_array(ary) unless ary.nil?
|
69
|
+
|
70
|
+
hsh = Hash.try_convert(obj)
|
71
|
+
return visit_hash(hsh) unless hsh.nil?
|
72
|
+
|
73
|
+
type_error(obj)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private def visit_float(obj)
|
78
|
+
obj = obj.to_f
|
79
|
+
rescue RangeError
|
80
|
+
type_error(obj)
|
81
|
+
else
|
82
|
+
case
|
83
|
+
when obj.finite?
|
84
|
+
obj
|
85
|
+
else
|
86
|
+
nil
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
private def visit_time(obj)
|
91
|
+
obj.iso8601(6)
|
92
|
+
end
|
93
|
+
|
94
|
+
private def visit_date(obj)
|
95
|
+
obj.iso8601(6)
|
96
|
+
end
|
97
|
+
|
98
|
+
private def visit_datetime(obj)
|
99
|
+
obj.iso8601(6)
|
100
|
+
end
|
101
|
+
|
102
|
+
private def visit_array(obj)
|
103
|
+
obj.map {|x| visit(x) }
|
104
|
+
end
|
105
|
+
|
106
|
+
private def visit_hash(obj)
|
107
|
+
obj.map { |key, value|
|
108
|
+
[
|
109
|
+
key,
|
110
|
+
visit(value)
|
111
|
+
]
|
112
|
+
}.to_h
|
113
|
+
end
|
114
|
+
|
115
|
+
private def type_error(obj)
|
116
|
+
raise TypeError, "Unable to convert to JSON: %p" % obj
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
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,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
|
data/lib/charty/plot_methods.rb
CHANGED
@@ -248,6 +248,46 @@ module Charty
|
|
248
248
|
&block
|
249
249
|
)
|
250
250
|
end
|
251
|
+
|
252
|
+
def hist_plot(data: nil, x: nil, y: nil, color: nil,
|
253
|
+
stat: :count, bins: :auto,
|
254
|
+
key_color: nil, palette: nil, color_order: nil, color_norm: nil,
|
255
|
+
legend: true, **options, &block)
|
256
|
+
# TODO: support following arguments
|
257
|
+
# - wiehgts
|
258
|
+
# - binwidth
|
259
|
+
# - binrange
|
260
|
+
# - discrete
|
261
|
+
# - cumulative
|
262
|
+
# - common_bins
|
263
|
+
# - common_norm
|
264
|
+
# - multiple
|
265
|
+
# - element
|
266
|
+
# - fill
|
267
|
+
# - shrink
|
268
|
+
# - kde
|
269
|
+
# - kde_params
|
270
|
+
# - line_params
|
271
|
+
# - thresh
|
272
|
+
# - pthresh
|
273
|
+
# - pmax
|
274
|
+
# - cbar
|
275
|
+
# - cbar_params
|
276
|
+
# - x_log_scale
|
277
|
+
# - y_log_scale
|
278
|
+
Plotters::HistogramPlotter.new(
|
279
|
+
data: data,
|
280
|
+
variables: { x: x, y: y, color: color },
|
281
|
+
stat: stat,
|
282
|
+
bins: bins,
|
283
|
+
key_color: key_color,
|
284
|
+
palette: palette,
|
285
|
+
color_order: color_order,
|
286
|
+
color_norm: color_norm,
|
287
|
+
legend: legend,
|
288
|
+
**options,
|
289
|
+
&block)
|
290
|
+
end
|
251
291
|
end
|
252
292
|
|
253
293
|
extend PlotMethods
|
data/lib/charty/plotters.rb
CHANGED
@@ -10,3 +10,6 @@ require_relative "plotters/vector_plotter"
|
|
10
10
|
require_relative "plotters/relational_plotter"
|
11
11
|
require_relative "plotters/scatter_plotter"
|
12
12
|
require_relative "plotters/line_plotter"
|
13
|
+
|
14
|
+
require_relative "plotters/distribution_plotter"
|
15
|
+
require_relative "plotters/histogram_plotter"
|
@@ -183,7 +183,7 @@ module Charty
|
|
183
183
|
|
184
184
|
levels = var_levels.dup
|
185
185
|
|
186
|
-
[:x, :y].each do |axis|
|
186
|
+
([:x, :y] & grouping_vars).each do |axis|
|
187
187
|
levels[axis] = plot_data[axis].categorical_order()
|
188
188
|
if processed
|
189
189
|
# TODO: perform inverse conversion of axis scaling here
|
@@ -213,16 +213,19 @@ module Charty
|
|
213
213
|
|
214
214
|
def save(filename, **kwargs)
|
215
215
|
backend = Backends.current
|
216
|
-
backend
|
217
|
-
render_plot(backend, **kwargs)
|
216
|
+
call_render_plot(backend, notebook: false, **kwargs)
|
218
217
|
backend.save(filename, **kwargs)
|
219
218
|
end
|
220
219
|
|
221
220
|
def render(notebook: false, **kwargs)
|
222
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)
|
223
227
|
backend.begin_figure
|
224
228
|
render_plot(backend, notebook: notebook, **kwargs)
|
225
|
-
backend.render(notebook: notebook, **kwargs)
|
226
229
|
end
|
227
230
|
|
228
231
|
private def render_plot(*, **)
|
@@ -231,12 +234,17 @@ module Charty
|
|
231
234
|
end
|
232
235
|
|
233
236
|
def to_iruby
|
234
|
-
render(notebook: iruby_notebook?)
|
237
|
+
render(notebook: IRubyHelper.iruby_notebook?)
|
235
238
|
end
|
236
239
|
|
237
|
-
|
238
|
-
|
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
|
240
248
|
end
|
241
249
|
end
|
242
250
|
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module Charty
|
2
|
+
module Plotters
|
3
|
+
class DistributionPlotter < AbstractPlotter
|
4
|
+
def flat_structure
|
5
|
+
{
|
6
|
+
x: :values
|
7
|
+
}
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(data:, variables:, **options, &block)
|
11
|
+
x, y, color = variables.values_at(:x, :y, :color)
|
12
|
+
super(x, y, color, data: data, **options, &block)
|
13
|
+
|
14
|
+
setup_variables
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :variables
|
18
|
+
|
19
|
+
attr_reader :color_norm
|
20
|
+
|
21
|
+
def color_norm=(val)
|
22
|
+
unless val.nil?
|
23
|
+
raise NotImplementedError,
|
24
|
+
"Specifying color_norm is not supported yet"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_reader :legend
|
29
|
+
|
30
|
+
def legend=(val)
|
31
|
+
@legend = check_legend(val)
|
32
|
+
end
|
33
|
+
|
34
|
+
private def check_legend(val)
|
35
|
+
check_boolean(val, :legend)
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_reader :input_format, :plot_data, :variables, :var_types
|
39
|
+
|
40
|
+
# This should be the same as one in RelationalPlotter
|
41
|
+
# TODO: move this to AbstractPlotter and refactor with CategoricalPlotter
|
42
|
+
private def setup_variables
|
43
|
+
if x.nil? && y.nil?
|
44
|
+
@input_format = :wide
|
45
|
+
setup_variables_with_wide_form_dataset
|
46
|
+
else
|
47
|
+
@input_format = :long
|
48
|
+
setup_variables_with_long_form_dataset
|
49
|
+
end
|
50
|
+
|
51
|
+
@var_types = @plot_data.columns.map { |k|
|
52
|
+
[k, variable_type(@plot_data[k], :categorical)]
|
53
|
+
}.to_h
|
54
|
+
end
|
55
|
+
|
56
|
+
private def setup_variables_with_wide_form_dataset
|
57
|
+
unless color.nil?
|
58
|
+
raise ArgumentError,
|
59
|
+
"Unable to assign the following variables in wide-form data: color"
|
60
|
+
end
|
61
|
+
|
62
|
+
if data.nil? || data.empty?
|
63
|
+
@plot_data = Charty::Table.new({})
|
64
|
+
@variables = {}
|
65
|
+
return
|
66
|
+
end
|
67
|
+
|
68
|
+
# TODO: detect flat data
|
69
|
+
flat = data.is_a?(Charty::Vector)
|
70
|
+
if flat
|
71
|
+
@plot_data = {}
|
72
|
+
@variables = {}
|
73
|
+
|
74
|
+
[:x, :y].each do |var|
|
75
|
+
case self.flat_structure[var]
|
76
|
+
when :index
|
77
|
+
@plot_data[var] = data.index.to_a
|
78
|
+
@variables[var] = data.index.name
|
79
|
+
when :values
|
80
|
+
@plot_data[var] = data.to_a
|
81
|
+
@variables[var] = data.name
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
@plot_data = Charty::Table.new(@plot_data)
|
86
|
+
else
|
87
|
+
raise NotImplementedError,
|
88
|
+
"wide-form input is not supported"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
private def setup_variables_with_long_form_dataset
|
93
|
+
if data.nil? || data.empty?
|
94
|
+
@plot_data = Charty::Table.new({})
|
95
|
+
@variables = {}
|
96
|
+
return
|
97
|
+
end
|
98
|
+
|
99
|
+
plot_data = {}
|
100
|
+
variables = {}
|
101
|
+
|
102
|
+
{
|
103
|
+
x: self.x,
|
104
|
+
y: self.y,
|
105
|
+
color: self.color,
|
106
|
+
}.each do |key, val|
|
107
|
+
next if val.nil?
|
108
|
+
|
109
|
+
if data.column_names.include?(val)
|
110
|
+
plot_data[key] = data[val]
|
111
|
+
variables[key] = val
|
112
|
+
else
|
113
|
+
case val
|
114
|
+
when Charty::Vector
|
115
|
+
plot_data[key] = val
|
116
|
+
variables[key] = val.name
|
117
|
+
else
|
118
|
+
raise ArgumentError,
|
119
|
+
"Could not interpret value %p for parameter %p" % [val, key]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
@plot_data = Charty::Table.new(plot_data)
|
125
|
+
@variables = variables.select do |var, name|
|
126
|
+
@plot_data[var].notnull.any?
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
private def map_color(palette: nil, order: nil, norm: nil)
|
131
|
+
@color_mapper = ColorMapper.new(self, palette, order, norm)
|
132
|
+
end
|
133
|
+
|
134
|
+
private def map_size(sizes: nil, order: nil, norm: nil)
|
135
|
+
@size_mapper = SizeMapper.new(self, sizes, order, norm)
|
136
|
+
end
|
137
|
+
|
138
|
+
private def map_style(markers: nil, dashes: nil, order: nil)
|
139
|
+
@style_mapper = StyleMapper.new(self, markers, dashes, order)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
module Charty
|
2
|
+
module Plotters
|
3
|
+
class HistogramPlotter < DistributionPlotter
|
4
|
+
def univariate?
|
5
|
+
self.variables.key?(:x) != self.variables.key?(:y)
|
6
|
+
end
|
7
|
+
|
8
|
+
def univariate_variable
|
9
|
+
unless univariate?
|
10
|
+
raise TypeError, "This is not a univariate plot"
|
11
|
+
end
|
12
|
+
([:x, :y] & self.variables.keys)[0]
|
13
|
+
end
|
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
|
+
attr_reader :stat
|
26
|
+
|
27
|
+
def stat=(val)
|
28
|
+
@stat = check_stat(val)
|
29
|
+
end
|
30
|
+
|
31
|
+
private def check_stat(val)
|
32
|
+
case val
|
33
|
+
when :count, "count"
|
34
|
+
val.to_sym
|
35
|
+
when :frequency, "frequency",
|
36
|
+
:density, "density",
|
37
|
+
:probability, "probability"
|
38
|
+
raise ArgumentError,
|
39
|
+
"%p for `stat` is not supported yet" % val,
|
40
|
+
caller
|
41
|
+
else
|
42
|
+
raise ArgumentError,
|
43
|
+
"Invalid value for `stat` (%p)" % val,
|
44
|
+
caller
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
attr_reader :bins
|
49
|
+
|
50
|
+
def bins=(val)
|
51
|
+
@bins = check_bins(val)
|
52
|
+
end
|
53
|
+
|
54
|
+
private def check_bins(val)
|
55
|
+
case val
|
56
|
+
when :auto, "auto"
|
57
|
+
val.to_sym
|
58
|
+
when Integer
|
59
|
+
val
|
60
|
+
else
|
61
|
+
raise ArgumentError,
|
62
|
+
"Invalid value for `bins` (%p)" % val,
|
63
|
+
caller
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# TODO: bin_width
|
68
|
+
# TODO: bin_range
|
69
|
+
# TODO: discrete
|
70
|
+
# TODO: cumulative
|
71
|
+
# TODO: common_bins
|
72
|
+
# TODO: common_norm
|
73
|
+
|
74
|
+
attr_reader :multiple
|
75
|
+
|
76
|
+
def multiple=(val)
|
77
|
+
@multiple = check_multiple(val)
|
78
|
+
end
|
79
|
+
|
80
|
+
private def check_multiple(val)
|
81
|
+
case val
|
82
|
+
when :layer, "layer"
|
83
|
+
val.to_sym
|
84
|
+
when :dodge, "dodge",
|
85
|
+
:stack, "stack",
|
86
|
+
:fill, "fill"
|
87
|
+
val = val.to_sym
|
88
|
+
raise NotImplementedError,
|
89
|
+
"%p for `multiple` is not supported yet" % val,
|
90
|
+
caller
|
91
|
+
else
|
92
|
+
raise ArgumentError,
|
93
|
+
"Invalid value for `multiple` (%p)" % val,
|
94
|
+
caller
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# TODO: element
|
99
|
+
# TODO: fill
|
100
|
+
# TODO: shrink
|
101
|
+
|
102
|
+
attr_reader :kde
|
103
|
+
|
104
|
+
def kde=(val)
|
105
|
+
raise NotImplementedError, "kde is not supported yet"
|
106
|
+
end
|
107
|
+
|
108
|
+
attr_reader :kde_params
|
109
|
+
|
110
|
+
def kde_params=(val)
|
111
|
+
raise NotImplementedError, "kde_params is not supported yet"
|
112
|
+
end
|
113
|
+
|
114
|
+
# TODO: thresh
|
115
|
+
# TODO: pthresh
|
116
|
+
# TODO: pmax
|
117
|
+
# TODO: cbar
|
118
|
+
# TODO: cbar_params
|
119
|
+
# TODO: x_log_scale
|
120
|
+
# TODO: y_log_scale
|
121
|
+
|
122
|
+
private def render_plot(backend, **)
|
123
|
+
draw_univariate_histogram(backend)
|
124
|
+
annotate_axes(backend)
|
125
|
+
end
|
126
|
+
|
127
|
+
private def draw_univariate_histogram(backend)
|
128
|
+
map_color(palette: palette, order: color_order, norm: color_norm)
|
129
|
+
|
130
|
+
# TODO: calculate histogram here and use bar plot to visualize
|
131
|
+
data_variable = self.univariate_variable
|
132
|
+
|
133
|
+
histograms = {}
|
134
|
+
each_subset([:color], processed: true) do |sub_vars, sub_data|
|
135
|
+
key = sub_vars.to_a
|
136
|
+
observations = sub_data[data_variable].drop_na.to_a
|
137
|
+
hist = Statistics.histogram(observations)
|
138
|
+
histograms[key] = hist
|
139
|
+
end
|
140
|
+
|
141
|
+
bin_start, bin_end, bin_size = nil
|
142
|
+
histograms.each do |_, hist|
|
143
|
+
s, e = hist.edge.minmax
|
144
|
+
z = (e - s).to_f / (hist.edge.length - 1)
|
145
|
+
bin_start = [bin_start, s].compact.min
|
146
|
+
bin_end = [bin_end, e].compact.max
|
147
|
+
bin_size = [bin_size, z].compact.min
|
148
|
+
end
|
149
|
+
|
150
|
+
if self.variables.key?(:color)
|
151
|
+
alpha = 0.5
|
152
|
+
else
|
153
|
+
alpha = 0.75
|
154
|
+
end
|
155
|
+
|
156
|
+
each_subset([:color], processed: true) do |sub_vars, sub_data|
|
157
|
+
name = sub_vars[:color]
|
158
|
+
observations = sub_data[data_variable].drop_na.to_a
|
159
|
+
|
160
|
+
backend.univariate_histogram(observations, name, data_variable, stat,
|
161
|
+
bin_start, bin_end, bin_size, alpha,
|
162
|
+
name, @color_mapper)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
private def annotate_axes(backend)
|
167
|
+
if univariate?
|
168
|
+
xlabel = self.variables[:x]
|
169
|
+
ylabel = self.variables[:y]
|
170
|
+
case self.univariate_variable
|
171
|
+
when :x
|
172
|
+
ylabel = self.stat.to_s.capitalize
|
173
|
+
else
|
174
|
+
xlabel = self.stat.to_s.capitalize
|
175
|
+
end
|
176
|
+
backend.set_ylabel(ylabel) if ylabel
|
177
|
+
backend.set_xlabel(xlabel) if xlabel
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
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/version.rb
CHANGED
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.
|
4
|
+
version: 0.2.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- youchan
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date: 2021-06-
|
13
|
+
date: 2021-06-21 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: red-colors
|
@@ -26,6 +26,20 @@ dependencies:
|
|
26
26
|
- - ">="
|
27
27
|
- !ruby/object:Gem::Version
|
28
28
|
version: 0.3.0
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: red-datasets
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: 0.1.2
|
36
|
+
type: :runtime
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 0.1.2
|
29
43
|
- !ruby/object:Gem::Dependency
|
30
44
|
name: red-palette
|
31
45
|
requirement: !ruby/object:Gem::Requirement
|
@@ -124,20 +138,6 @@ dependencies:
|
|
124
138
|
- - ">="
|
125
139
|
- !ruby/object:Gem::Version
|
126
140
|
version: '0'
|
127
|
-
- !ruby/object:Gem::Dependency
|
128
|
-
name: red-datasets
|
129
|
-
requirement: !ruby/object:Gem::Requirement
|
130
|
-
requirements:
|
131
|
-
- - ">="
|
132
|
-
- !ruby/object:Gem::Version
|
133
|
-
version: 0.1.2
|
134
|
-
type: :development
|
135
|
-
prerelease: false
|
136
|
-
version_requirements: !ruby/object:Gem::Requirement
|
137
|
-
requirements:
|
138
|
-
- - ">="
|
139
|
-
- !ruby/object:Gem::Version
|
140
|
-
version: 0.1.2
|
141
141
|
- !ruby/object:Gem::Dependency
|
142
142
|
name: daru
|
143
143
|
requirement: !ruby/object:Gem::Requirement
|
@@ -287,11 +287,16 @@ files:
|
|
287
287
|
- lib/charty/backends/google_charts.rb
|
288
288
|
- lib/charty/backends/gruff.rb
|
289
289
|
- lib/charty/backends/plotly.rb
|
290
|
+
- lib/charty/backends/plotly_helpers/html_renderer.rb
|
291
|
+
- lib/charty/backends/plotly_helpers/notebook_renderer.rb
|
292
|
+
- lib/charty/backends/plotly_helpers/plotly_renderer.rb
|
290
293
|
- lib/charty/backends/pyplot.rb
|
291
294
|
- lib/charty/backends/rubyplot.rb
|
292
295
|
- lib/charty/backends/unicode_plot.rb
|
296
|
+
- lib/charty/cache_dir.rb
|
293
297
|
- lib/charty/dash_pattern_generator.rb
|
294
298
|
- lib/charty/index.rb
|
299
|
+
- lib/charty/iruby_helper.rb
|
295
300
|
- lib/charty/layout.rb
|
296
301
|
- lib/charty/linspace.rb
|
297
302
|
- lib/charty/plot_methods.rb
|
@@ -302,7 +307,9 @@ files:
|
|
302
307
|
- lib/charty/plotters/box_plotter.rb
|
303
308
|
- lib/charty/plotters/categorical_plotter.rb
|
304
309
|
- lib/charty/plotters/count_plotter.rb
|
310
|
+
- lib/charty/plotters/distribution_plotter.rb
|
305
311
|
- lib/charty/plotters/estimation_support.rb
|
312
|
+
- lib/charty/plotters/histogram_plotter.rb
|
306
313
|
- lib/charty/plotters/line_plotter.rb
|
307
314
|
- lib/charty/plotters/random_support.rb
|
308
315
|
- lib/charty/plotters/relational_plotter.rb
|