lets-plot 4.8.1rc1__cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
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.
- lets_plot/__init__.py +382 -0
- lets_plot/_global_settings.py +192 -0
- lets_plot/_kbridge.py +197 -0
- lets_plot/_type_utils.py +133 -0
- lets_plot/_version.py +6 -0
- lets_plot/bistro/__init__.py +16 -0
- lets_plot/bistro/_plot2d_common.py +106 -0
- lets_plot/bistro/corr.py +448 -0
- lets_plot/bistro/im.py +196 -0
- lets_plot/bistro/joint.py +192 -0
- lets_plot/bistro/qq.py +207 -0
- lets_plot/bistro/residual.py +341 -0
- lets_plot/bistro/waterfall.py +332 -0
- lets_plot/export/__init__.py +6 -0
- lets_plot/export/ggsave_.py +172 -0
- lets_plot/frontend_context/__init__.py +8 -0
- lets_plot/frontend_context/_configuration.py +140 -0
- lets_plot/frontend_context/_dynamic_configure_html.py +115 -0
- lets_plot/frontend_context/_frontend_ctx.py +16 -0
- lets_plot/frontend_context/_html_contexts.py +223 -0
- lets_plot/frontend_context/_intellij_python_json_ctx.py +38 -0
- lets_plot/frontend_context/_isolated_webview_panel_ctx.py +81 -0
- lets_plot/frontend_context/_json_contexts.py +39 -0
- lets_plot/frontend_context/_jupyter_notebook_ctx.py +82 -0
- lets_plot/frontend_context/_mime_types.py +7 -0
- lets_plot/frontend_context/_static_html_page_ctx.py +76 -0
- lets_plot/frontend_context/_static_svg_ctx.py +26 -0
- lets_plot/frontend_context/_webbr_html_page_ctx.py +29 -0
- lets_plot/frontend_context/sandbox.py +5 -0
- lets_plot/geo_data/__init__.py +19 -0
- lets_plot/geo_data/core.py +335 -0
- lets_plot/geo_data/geocoder.py +988 -0
- lets_plot/geo_data/geocodes.py +512 -0
- lets_plot/geo_data/gis/__init__.py +0 -0
- lets_plot/geo_data/gis/fluent_dict.py +201 -0
- lets_plot/geo_data/gis/geocoding_service.py +42 -0
- lets_plot/geo_data/gis/geometry.py +91 -0
- lets_plot/geo_data/gis/json_request.py +232 -0
- lets_plot/geo_data/gis/json_response.py +308 -0
- lets_plot/geo_data/gis/request.py +492 -0
- lets_plot/geo_data/gis/response.py +247 -0
- lets_plot/geo_data/livemap_helper.py +65 -0
- lets_plot/geo_data/to_geo_data_frame.py +141 -0
- lets_plot/geo_data/type_assertion.py +34 -0
- lets_plot/geo_data_internals/__init__.py +4 -0
- lets_plot/geo_data_internals/constants.py +13 -0
- lets_plot/geo_data_internals/utils.py +33 -0
- lets_plot/mapping.py +115 -0
- lets_plot/package_data/lets-plot.min.js +3 -0
- lets_plot/plot/__init__.py +64 -0
- lets_plot/plot/_global_theme.py +14 -0
- lets_plot/plot/annotation.py +290 -0
- lets_plot/plot/coord.py +242 -0
- lets_plot/plot/core.py +1071 -0
- lets_plot/plot/expand_limits_.py +78 -0
- lets_plot/plot/facet.py +210 -0
- lets_plot/plot/font_features.py +71 -0
- lets_plot/plot/geom.py +9146 -0
- lets_plot/plot/geom_extras.py +53 -0
- lets_plot/plot/geom_function_.py +219 -0
- lets_plot/plot/geom_imshow_.py +393 -0
- lets_plot/plot/geom_livemap_.py +343 -0
- lets_plot/plot/ggbunch_.py +96 -0
- lets_plot/plot/gggrid_.py +139 -0
- lets_plot/plot/ggtb_.py +81 -0
- lets_plot/plot/guide.py +231 -0
- lets_plot/plot/label.py +187 -0
- lets_plot/plot/marginal_layer.py +181 -0
- lets_plot/plot/plot.py +245 -0
- lets_plot/plot/pos.py +344 -0
- lets_plot/plot/sampling.py +338 -0
- lets_plot/plot/sandbox_.py +26 -0
- lets_plot/plot/scale.py +3580 -0
- lets_plot/plot/scale_colormap_mpl.py +300 -0
- lets_plot/plot/scale_convenience.py +155 -0
- lets_plot/plot/scale_identity_.py +653 -0
- lets_plot/plot/scale_position.py +1342 -0
- lets_plot/plot/series_meta.py +209 -0
- lets_plot/plot/stat.py +585 -0
- lets_plot/plot/subplots.py +331 -0
- lets_plot/plot/subplots_util.py +24 -0
- lets_plot/plot/theme_.py +790 -0
- lets_plot/plot/theme_set.py +418 -0
- lets_plot/plot/tooltip.py +486 -0
- lets_plot/plot/util.py +267 -0
- lets_plot/settings_utils.py +244 -0
- lets_plot/tilesets.py +429 -0
- lets_plot-4.8.1rc1.dist-info/METADATA +221 -0
- lets_plot-4.8.1rc1.dist-info/RECORD +97 -0
- lets_plot-4.8.1rc1.dist-info/WHEEL +6 -0
- lets_plot-4.8.1rc1.dist-info/licenses/LICENSE +21 -0
- lets_plot-4.8.1rc1.dist-info/licenses/licenses/LICENSE.FreeType +166 -0
- lets_plot-4.8.1rc1.dist-info/licenses/licenses/LICENSE.ImageMagick +106 -0
- lets_plot-4.8.1rc1.dist-info/licenses/licenses/LICENSE.expat +21 -0
- lets_plot-4.8.1rc1.dist-info/licenses/licenses/LICENSE.fontconfig +200 -0
- lets_plot-4.8.1rc1.dist-info/top_level.txt +2 -0
- lets_plot_kotlin_bridge.cpython-311-x86_64-linux-gnu.so +0 -0
lets_plot/plot/guide.py
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2019. JetBrains s.r.o.
|
|
3
|
+
# Use of this source code is governed by the MIT license that can be found in the LICENSE file.
|
|
4
|
+
#
|
|
5
|
+
from .core import FeatureSpec
|
|
6
|
+
|
|
7
|
+
__all__ = ['guide_legend', 'guide_colorbar', 'guides', 'layer_key']
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def guide_legend(title=None, *, nrow=None, ncol=None, byrow=None, override_aes=None):
|
|
11
|
+
"""
|
|
12
|
+
Legend guide.
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
title : str
|
|
17
|
+
Title of guide.
|
|
18
|
+
nrow : int
|
|
19
|
+
Number of rows in legend's guide.
|
|
20
|
+
ncol : int
|
|
21
|
+
Number of columns in legend's guide.
|
|
22
|
+
byrow : bool, default=True
|
|
23
|
+
Type of output: by row, or by column.
|
|
24
|
+
override_aes : dict
|
|
25
|
+
Dictionary that maps aesthetic parameters to new values, overriding the default legend appearance.
|
|
26
|
+
Each value can be a constant applied to all keys or a list that changes particular keys.
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
Returns
|
|
30
|
+
-------
|
|
31
|
+
``FeatureSpec``
|
|
32
|
+
Legend guide specification.
|
|
33
|
+
|
|
34
|
+
Notes
|
|
35
|
+
-----
|
|
36
|
+
Legend type guide shows key (i.e., geoms) mapped onto values.
|
|
37
|
+
|
|
38
|
+
Examples
|
|
39
|
+
--------
|
|
40
|
+
.. jupyter-execute::
|
|
41
|
+
:linenos:
|
|
42
|
+
:emphasize-lines: 11
|
|
43
|
+
|
|
44
|
+
import numpy as np
|
|
45
|
+
from lets_plot import *
|
|
46
|
+
LetsPlot.setup_html()
|
|
47
|
+
n = 100
|
|
48
|
+
np.random.seed(42)
|
|
49
|
+
x = np.random.uniform(size=n)
|
|
50
|
+
y = np.random.uniform(size=n)
|
|
51
|
+
c = np.random.choice(list('abcdefgh'), size=n)
|
|
52
|
+
ggplot({'x': x, 'y': y, 'c': c}, aes('x', 'y')) + \\
|
|
53
|
+
geom_point(aes(shape='c'), size=4, alpha=.7) + \\
|
|
54
|
+
scale_shape(guide=guide_legend(nrow=3, override_aes={'color': 'red'}))
|
|
55
|
+
|
|
56
|
+
"""
|
|
57
|
+
return _guide('legend', **locals())
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def guide_colorbar(title=None, *, barwidth=None, barheight=None, nbin=None):
|
|
61
|
+
"""
|
|
62
|
+
Continuous color bar guide.
|
|
63
|
+
|
|
64
|
+
Parameters
|
|
65
|
+
----------
|
|
66
|
+
title : str
|
|
67
|
+
Title of guide.
|
|
68
|
+
barwidth : float
|
|
69
|
+
Color bar width in px.
|
|
70
|
+
barheight : float
|
|
71
|
+
Color bar height in px.
|
|
72
|
+
nbin : int
|
|
73
|
+
Number of bins in color bar.
|
|
74
|
+
|
|
75
|
+
Returns
|
|
76
|
+
-------
|
|
77
|
+
``FeatureSpec``
|
|
78
|
+
Color guide specification.
|
|
79
|
+
|
|
80
|
+
Notes
|
|
81
|
+
-----
|
|
82
|
+
Color bar guide shows continuous color scales mapped onto values.
|
|
83
|
+
Color bar is available with scale_fill and scale_color.
|
|
84
|
+
|
|
85
|
+
Examples
|
|
86
|
+
--------
|
|
87
|
+
.. jupyter-execute::
|
|
88
|
+
:linenos:
|
|
89
|
+
:emphasize-lines: 12
|
|
90
|
+
|
|
91
|
+
import numpy as np
|
|
92
|
+
from lets_plot import *
|
|
93
|
+
LetsPlot.setup_html()
|
|
94
|
+
n = 50
|
|
95
|
+
np.random.seed(42)
|
|
96
|
+
x = np.random.uniform(size=n)
|
|
97
|
+
y = np.random.uniform(size=n)
|
|
98
|
+
v = np.random.normal(size=n)
|
|
99
|
+
ggplot({'x': x, 'y': y, 'v': v}, aes('x', 'y')) + \\
|
|
100
|
+
geom_point(aes(fill='v'), size=4, shape=21, color='black') + \\
|
|
101
|
+
scale_fill_gradient2(low='red', mid='yellow', high='blue', \\
|
|
102
|
+
guide=guide_colorbar(nbin=8, barwidth=10))
|
|
103
|
+
|
|
104
|
+
"""
|
|
105
|
+
return _guide('colorbar', **locals())
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _guide(name, **kwargs):
|
|
109
|
+
if 'title' in kwargs and isinstance(kwargs['title'], int):
|
|
110
|
+
raise ValueError("Use keyword arguments for all other than 'title' parameters.")
|
|
111
|
+
return FeatureSpec('guide', name=name, **kwargs)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def guides(**kwargs):
|
|
115
|
+
"""
|
|
116
|
+
Set guides for each scale.
|
|
117
|
+
|
|
118
|
+
Parameters
|
|
119
|
+
----------
|
|
120
|
+
kwargs
|
|
121
|
+
Key-value pairs where the key can be:
|
|
122
|
+
|
|
123
|
+
- An aesthetic name
|
|
124
|
+
- 'manual' - a key referring to the default custom legend
|
|
125
|
+
- A group name referring to a custom legend where the group is defined via the `layer_key() <https://lets-plot.org/python/pages/api/lets_plot.layer_key.html>`__ function
|
|
126
|
+
|
|
127
|
+
The value can be either:
|
|
128
|
+
|
|
129
|
+
- A string ('colorbar', 'legend')
|
|
130
|
+
- A call to a guide function (`guide_colorbar() <https://lets-plot.org/python/pages/api/lets_plot.guide_colorbar.html>`__, `guide_legend() <https://lets-plot.org/python/pages/api/lets_plot.guide_legend.html>`__) specifying additional arguments
|
|
131
|
+
- 'none' to hide the guide
|
|
132
|
+
|
|
133
|
+
Returns
|
|
134
|
+
-------
|
|
135
|
+
``FeatureSpec``
|
|
136
|
+
Guides specification.
|
|
137
|
+
|
|
138
|
+
Examples
|
|
139
|
+
--------
|
|
140
|
+
.. jupyter-execute::
|
|
141
|
+
:linenos:
|
|
142
|
+
:emphasize-lines: 13-14
|
|
143
|
+
|
|
144
|
+
import numpy as np
|
|
145
|
+
from lets_plot import *
|
|
146
|
+
LetsPlot.setup_html()
|
|
147
|
+
n = 25
|
|
148
|
+
np.random.seed(42)
|
|
149
|
+
x = np.random.uniform(size=n)
|
|
150
|
+
y = np.random.uniform(size=n)
|
|
151
|
+
c = np.random.choice(list('abcdefgh'), size=n)
|
|
152
|
+
v = np.random.normal(size=n)
|
|
153
|
+
ggplot({'x': x, 'y': y, 'c': c, 'v': v}, aes('x', 'y')) + \\
|
|
154
|
+
geom_point(aes(shape='c', color='v'), size=4) + \\
|
|
155
|
+
scale_color_gradient2(low='red', mid='yellow', high='blue') + \\
|
|
156
|
+
guides(shape=guide_legend(ncol=2), \\
|
|
157
|
+
color=guide_colorbar(nbin=8, barwidth=20))
|
|
158
|
+
|
|
159
|
+
|
|
|
160
|
+
|
|
161
|
+
.. jupyter-execute::
|
|
162
|
+
:linenos:
|
|
163
|
+
:emphasize-lines: 11
|
|
164
|
+
|
|
165
|
+
import numpy as np
|
|
166
|
+
from lets_plot import *
|
|
167
|
+
LetsPlot.setup_html()
|
|
168
|
+
n = 10
|
|
169
|
+
np.random.seed(42)
|
|
170
|
+
x = list(range(n))
|
|
171
|
+
y = np.random.uniform(size=n)
|
|
172
|
+
ggplot({'x': x, 'y': y}, aes('x', 'y')) + \\
|
|
173
|
+
geom_point(color='red', manual_key="point") + \\
|
|
174
|
+
geom_line(color='blue', manual_key="line") + \\
|
|
175
|
+
guides(manual=guide_legend('Zones', ncol=2))
|
|
176
|
+
|
|
177
|
+
"""
|
|
178
|
+
return FeatureSpec('guides', name=None, **kwargs)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def layer_key(label, group=None, *, index=None, **kwargs):
|
|
182
|
+
"""
|
|
183
|
+
Configure custom legend.
|
|
184
|
+
|
|
185
|
+
Parameters
|
|
186
|
+
----------
|
|
187
|
+
label : str
|
|
188
|
+
Text for the element in the custom legend.
|
|
189
|
+
group : str, default='manual'
|
|
190
|
+
Group name by which elements are combined into a legend group.
|
|
191
|
+
index : int
|
|
192
|
+
Position of the element in the custom legend.
|
|
193
|
+
kwargs :
|
|
194
|
+
A list of aesthetic parameters to use in the custom legend.
|
|
195
|
+
|
|
196
|
+
Returns
|
|
197
|
+
-------
|
|
198
|
+
``FeatureSpec``
|
|
199
|
+
Custom legend specification.
|
|
200
|
+
|
|
201
|
+
Notes
|
|
202
|
+
-----
|
|
203
|
+
The group name specified with the ``group`` parameter can be used in the
|
|
204
|
+
`labs() <https://lets-plot.org/python/pages/api/lets_plot.labs.html>`__ and
|
|
205
|
+
`guides() <https://lets-plot.org/python/pages/api/lets_plot.guides.html>`__ functions
|
|
206
|
+
to further customize the display of this group (e.g. change its name).
|
|
207
|
+
In particular, items in the 'manual' group will be displayed without a title unless you change it manually.
|
|
208
|
+
|
|
209
|
+
----
|
|
210
|
+
|
|
211
|
+
If you set the same group and label for a legend element in different layers, they will merge into one complex legend element.
|
|
212
|
+
|
|
213
|
+
Examples
|
|
214
|
+
--------
|
|
215
|
+
.. jupyter-execute::
|
|
216
|
+
:linenos:
|
|
217
|
+
:emphasize-lines: 9-10
|
|
218
|
+
|
|
219
|
+
import numpy as np
|
|
220
|
+
from lets_plot import *
|
|
221
|
+
LetsPlot.setup_html()
|
|
222
|
+
n = 10
|
|
223
|
+
np.random.seed(42)
|
|
224
|
+
x = list(range(n))
|
|
225
|
+
y = np.random.uniform(size=n)
|
|
226
|
+
ggplot({'x': x, 'y': y}, aes('x', 'y')) + \\
|
|
227
|
+
geom_point(color='red', manual_key=layer_key("point", shape=21)) + \\
|
|
228
|
+
geom_line(color='blue', linetype=2, manual_key=layer_key("line", linetype=1))
|
|
229
|
+
|
|
230
|
+
"""
|
|
231
|
+
return FeatureSpec('layer_key', name=None, label=label, group=group, index=index, **kwargs)
|
lets_plot/plot/label.py
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2019. JetBrains s.r.o.
|
|
3
|
+
# Use of this source code is governed by the MIT license that can be found in the LICENSE file.
|
|
4
|
+
#
|
|
5
|
+
from .core import FeatureSpec, FeatureSpecArray
|
|
6
|
+
from .guide import _guide, guides
|
|
7
|
+
|
|
8
|
+
#
|
|
9
|
+
# Plot title
|
|
10
|
+
# Scale names: axis labels / legend titles
|
|
11
|
+
#
|
|
12
|
+
__all__ = ['ggtitle',
|
|
13
|
+
'labs',
|
|
14
|
+
'xlab', 'ylab']
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def ggtitle(label, subtitle=None):
|
|
18
|
+
"""
|
|
19
|
+
Add title to the plot.
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
label : str
|
|
24
|
+
The text for the plot title.
|
|
25
|
+
subtitle : str
|
|
26
|
+
The text for the plot subtitle.
|
|
27
|
+
|
|
28
|
+
Returns
|
|
29
|
+
-------
|
|
30
|
+
``FeatureSpec``
|
|
31
|
+
Plot title specification.
|
|
32
|
+
|
|
33
|
+
Notes
|
|
34
|
+
-----
|
|
35
|
+
Split a long title/subtitle into two lines or more using ``\\n`` as a text separator.
|
|
36
|
+
|
|
37
|
+
Examples
|
|
38
|
+
--------
|
|
39
|
+
.. jupyter-execute::
|
|
40
|
+
:linenos:
|
|
41
|
+
:emphasize-lines: 5
|
|
42
|
+
|
|
43
|
+
from lets_plot import *
|
|
44
|
+
LetsPlot.setup_html()
|
|
45
|
+
data = {'x': list(range(10)), 'y': list(range(10))}
|
|
46
|
+
ggplot(data, aes('x', 'y')) + geom_point(aes(size='y')) + \\
|
|
47
|
+
ggtitle('New Plot Title')
|
|
48
|
+
|
|
49
|
+
"""
|
|
50
|
+
return labs(title=label, subtitle=subtitle)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def xlab(label):
|
|
54
|
+
"""
|
|
55
|
+
Add label to the x axis.
|
|
56
|
+
|
|
57
|
+
Parameters
|
|
58
|
+
----------
|
|
59
|
+
label : str
|
|
60
|
+
The text for the x axis label.
|
|
61
|
+
|
|
62
|
+
Returns
|
|
63
|
+
-------
|
|
64
|
+
``FeatureSpec``
|
|
65
|
+
Axis label specification.
|
|
66
|
+
|
|
67
|
+
Examples
|
|
68
|
+
--------
|
|
69
|
+
.. jupyter-execute::
|
|
70
|
+
:linenos:
|
|
71
|
+
:emphasize-lines: 5
|
|
72
|
+
|
|
73
|
+
from lets_plot import *
|
|
74
|
+
LetsPlot.setup_html()
|
|
75
|
+
data = {'x': list(range(10)), 'y': list(range(10))}
|
|
76
|
+
ggplot(data, aes('x', 'y')) + geom_point(aes(size='y')) + \\
|
|
77
|
+
xlab('x axis label')
|
|
78
|
+
|
|
79
|
+
"""
|
|
80
|
+
return labs(x=label)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def ylab(label):
|
|
84
|
+
"""
|
|
85
|
+
Add label to the y-axis.
|
|
86
|
+
|
|
87
|
+
Parameters
|
|
88
|
+
----------
|
|
89
|
+
label : str
|
|
90
|
+
The text for the y-axis label.
|
|
91
|
+
|
|
92
|
+
Returns
|
|
93
|
+
-------
|
|
94
|
+
``FeatureSpec``
|
|
95
|
+
Axis label specification.
|
|
96
|
+
|
|
97
|
+
Examples
|
|
98
|
+
--------
|
|
99
|
+
.. jupyter-execute::
|
|
100
|
+
:linenos:
|
|
101
|
+
:emphasize-lines: 5
|
|
102
|
+
|
|
103
|
+
from lets_plot import *
|
|
104
|
+
LetsPlot.setup_html()
|
|
105
|
+
data = {'x': list(range(10)), 'y': list(range(10))}
|
|
106
|
+
ggplot(data, aes('x', 'y')) + geom_point(aes(size='y')) + \\
|
|
107
|
+
ylab('y axis label')
|
|
108
|
+
|
|
109
|
+
"""
|
|
110
|
+
return labs(y=label)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def labs(title=None, subtitle=None, caption=None, **labels):
|
|
114
|
+
"""
|
|
115
|
+
Change plot title, axis labels and legend titles.
|
|
116
|
+
|
|
117
|
+
Parameters
|
|
118
|
+
----------
|
|
119
|
+
title : str
|
|
120
|
+
The text for the plot title.
|
|
121
|
+
subtitle : str
|
|
122
|
+
The text for the plot subtitle.
|
|
123
|
+
caption : str
|
|
124
|
+
The text for the plot caption.
|
|
125
|
+
labels
|
|
126
|
+
Name-value pairs where the name can be:
|
|
127
|
+
|
|
128
|
+
- An aesthetic name.
|
|
129
|
+
- 'manual' - a key referring to the default custom legend.
|
|
130
|
+
- A group name referring to a custom legend where the group is defined via the `layer_key() <https://lets-plot.org/python/pages/api/lets_plot.layer_key.html>`__ function.
|
|
131
|
+
|
|
132
|
+
The value should be a string, e.g. ``color="New Color label"``.
|
|
133
|
+
|
|
134
|
+
Returns
|
|
135
|
+
-------
|
|
136
|
+
``FeatureSpec`` or ``FeatureSpecArray``
|
|
137
|
+
Labels specification.
|
|
138
|
+
|
|
139
|
+
Examples
|
|
140
|
+
--------
|
|
141
|
+
.. jupyter-execute::
|
|
142
|
+
:linenos:
|
|
143
|
+
:emphasize-lines: 5-6
|
|
144
|
+
|
|
145
|
+
from lets_plot import *
|
|
146
|
+
LetsPlot.setup_html()
|
|
147
|
+
data = {'x': list(range(10)), 'y': list(range(10))}
|
|
148
|
+
ggplot(data, aes('x', 'y')) + geom_point(aes(size='y')) + \\
|
|
149
|
+
labs(title='New plot title', subtitle='The plot subtitle', caption='The plot caption', \\
|
|
150
|
+
x='New x axis label', y='New y axis label', size='New legend title')
|
|
151
|
+
|
|
152
|
+
|
|
|
153
|
+
|
|
154
|
+
.. jupyter-execute::
|
|
155
|
+
:linenos:
|
|
156
|
+
:emphasize-lines: 11
|
|
157
|
+
|
|
158
|
+
import numpy as np
|
|
159
|
+
from lets_plot import *
|
|
160
|
+
LetsPlot.setup_html()
|
|
161
|
+
n = 10
|
|
162
|
+
np.random.seed(42)
|
|
163
|
+
x = list(range(n))
|
|
164
|
+
y = np.random.uniform(size=n)
|
|
165
|
+
ggplot({'x': x, 'y': y}, aes('x', 'y')) + \\
|
|
166
|
+
geom_point(color='red', manual_key="point") + \\
|
|
167
|
+
geom_line(color='blue', manual_key="line") + \\
|
|
168
|
+
labs(manual='Zones')
|
|
169
|
+
|
|
170
|
+
"""
|
|
171
|
+
specs = []
|
|
172
|
+
|
|
173
|
+
# handle ggtitle
|
|
174
|
+
if title is not None or subtitle is not None:
|
|
175
|
+
specs.append(FeatureSpec('ggtitle', name=None, text=title, subtitle=subtitle))
|
|
176
|
+
|
|
177
|
+
# plot caption
|
|
178
|
+
if caption is not None:
|
|
179
|
+
specs.append(FeatureSpec('caption', name=None, text=caption))
|
|
180
|
+
|
|
181
|
+
# guides
|
|
182
|
+
for key, label in labels.items():
|
|
183
|
+
specs.append(guides(**{key: _guide(name=None, title=label)}))
|
|
184
|
+
|
|
185
|
+
if len(specs) == 1:
|
|
186
|
+
return specs[0]
|
|
187
|
+
return FeatureSpecArray(*specs)
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Copyright (c) 2022. JetBrains s.r.o.
|
|
2
|
+
# Use of this source code is governed by the MIT license that can be found in the LICENSE file.
|
|
3
|
+
|
|
4
|
+
from typing import Union
|
|
5
|
+
|
|
6
|
+
from .core import FeatureSpec, LayerSpec, DummySpec, FeatureSpecArray
|
|
7
|
+
|
|
8
|
+
__all__ = ["ggmarginal"]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def ggmarginal(sides: str, *, size=None, layer: Union[LayerSpec, FeatureSpecArray]) -> FeatureSpec:
|
|
12
|
+
"""
|
|
13
|
+
Convert a given geometry layer to a marginal layer.
|
|
14
|
+
You can add one or more marginal layers to a plot to create a marginal plot.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
sides : str
|
|
19
|
+
A string specifying which sides of the plot the marginal layer will appear on.
|
|
20
|
+
It should be set to a string containing any of "trbl", for top, right, bottom, and left.
|
|
21
|
+
size : number or list of numbers, default=0.1
|
|
22
|
+
Size of marginal geometry (width or height, depending on the margin side) as a fraction of the entire
|
|
23
|
+
plotting area of the plot.
|
|
24
|
+
The value should be in range [0.01..0.95].
|
|
25
|
+
layer : ``LayerSpec``
|
|
26
|
+
A marginal geometry layer.
|
|
27
|
+
The result of calling of the ``geom_xxx()``/``stat_xxx()`` function.
|
|
28
|
+
Marginal plot works best with ``density``, ``histogram``, ``boxplot``, ``violin`` and ``freqpoly`` geometry layers.
|
|
29
|
+
|
|
30
|
+
Returns
|
|
31
|
+
-------
|
|
32
|
+
``FeatureSpec``
|
|
33
|
+
An object specifying a marginal geometry layer or a list of marginal geometry layers.
|
|
34
|
+
|
|
35
|
+
Notes
|
|
36
|
+
-----
|
|
37
|
+
A marginal plot is a scatterplot (sometimes a 2D density plot or other bivariate plot) that has histograms,
|
|
38
|
+
boxplots, or other distribution visualization layers in the margins of the x- and y-axes.
|
|
39
|
+
|
|
40
|
+
Examples
|
|
41
|
+
--------
|
|
42
|
+
.. jupyter-execute::
|
|
43
|
+
:linenos:
|
|
44
|
+
:emphasize-lines: 23
|
|
45
|
+
|
|
46
|
+
import numpy as np
|
|
47
|
+
from lets_plot import *
|
|
48
|
+
LetsPlot.setup_html()
|
|
49
|
+
LetsPlot.set_theme(theme_light())
|
|
50
|
+
|
|
51
|
+
np.random.seed(0)
|
|
52
|
+
|
|
53
|
+
cov0=[[1, -.8],
|
|
54
|
+
[-.8, 1]]
|
|
55
|
+
cov1=[[ 10, .1],
|
|
56
|
+
[.1, .1]]
|
|
57
|
+
|
|
58
|
+
x0, y0 = np.random.multivariate_normal(mean=[-2,0], cov=cov0, size=200).T
|
|
59
|
+
x1, y1 = np.random.multivariate_normal(mean=[0,1], cov=cov1, size=200).T
|
|
60
|
+
|
|
61
|
+
data = dict(
|
|
62
|
+
x = np.concatenate((x0,x1)),
|
|
63
|
+
y = np.concatenate((y0,y1)),
|
|
64
|
+
c = ["A"]*200 + ["B"]*200
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
p = ggplot(data, aes("x", "y", color="c", fill="c")) + geom_point()
|
|
68
|
+
p + ggmarginal("tr", layer=geom_density(alpha=0.3, show_legend=False))
|
|
69
|
+
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
if not isinstance(sides, str):
|
|
73
|
+
raise TypeError("'sides' must be a string.")
|
|
74
|
+
if not 0 < len(sides) <= 4:
|
|
75
|
+
raise ValueError("'sides' must be a string containing 1 to 4 chars: 'l','r','t','b'.")
|
|
76
|
+
|
|
77
|
+
# In some cases (only boxplot so far) a layer may consist of multiple sub-layers of type LayerSpec.
|
|
78
|
+
if isinstance(layer, LayerSpec):
|
|
79
|
+
sublayers = [layer]
|
|
80
|
+
elif isinstance(layer, FeatureSpecArray):
|
|
81
|
+
for sublayer in layer.elements():
|
|
82
|
+
if not isinstance(sublayer, LayerSpec):
|
|
83
|
+
raise TypeError("Invalid 'layer' type: {}".format(type(sublayer)))
|
|
84
|
+
sublayers = layer.elements()
|
|
85
|
+
else:
|
|
86
|
+
raise TypeError("Invalid 'layer' type: {}".format(type(layer)))
|
|
87
|
+
|
|
88
|
+
result = DummySpec()
|
|
89
|
+
|
|
90
|
+
for sublayer in sublayers:
|
|
91
|
+
for i in range(len(sides)):
|
|
92
|
+
side = sides[i]
|
|
93
|
+
margin_size = _to_size(size, i)
|
|
94
|
+
marginal_layer = _to_marginal(side, margin_size, sublayer)
|
|
95
|
+
result = result + marginal_layer
|
|
96
|
+
|
|
97
|
+
return result
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _to_size(size, side_index: int) -> float:
|
|
101
|
+
if size is None:
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
if isinstance(size, float):
|
|
105
|
+
return size
|
|
106
|
+
|
|
107
|
+
if not (isinstance(size, list) or isinstance(size, tuple)):
|
|
108
|
+
raise TypeError("Invalid 'size' type: {}. Expected: float, list or tuple.".format(type(size)))
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
return size[side_index]
|
|
112
|
+
except IndexError:
|
|
113
|
+
return None
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _to_marginal(side: str, size, layer: LayerSpec) -> LayerSpec:
|
|
117
|
+
if side not in ['l', 'r', 't', 'b']:
|
|
118
|
+
raise ValueError("Invalid 'side' value: {}. Valid values: 'l','r','t','b'.".format(side))
|
|
119
|
+
|
|
120
|
+
if size is not None:
|
|
121
|
+
if not 0.01 <= size <= 0.95:
|
|
122
|
+
raise ValueError("Invalid 'size' value: {}. Should be in range [0.01..0.95].".format(size))
|
|
123
|
+
|
|
124
|
+
layer_copy = LayerSpec.duplicate(layer)
|
|
125
|
+
marginal_options = dict(
|
|
126
|
+
marginal=True,
|
|
127
|
+
margin_side=side,
|
|
128
|
+
margin_size=size
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
layer_props = layer_copy.props()
|
|
132
|
+
layer_props.update(marginal_options)
|
|
133
|
+
|
|
134
|
+
layer_kind = None
|
|
135
|
+
stat = layer_props.get('stat')
|
|
136
|
+
if stat is not None:
|
|
137
|
+
if stat == 'bin':
|
|
138
|
+
layer_kind = 'histogram'
|
|
139
|
+
elif stat == 'ydensity':
|
|
140
|
+
layer_kind = 'violin'
|
|
141
|
+
elif stat in ('density', 'boxplot', 'boxplot_outlier'):
|
|
142
|
+
layer_kind = stat
|
|
143
|
+
else:
|
|
144
|
+
geom = layer_props.get('geom')
|
|
145
|
+
if geom in ('histogram', 'boxplot', 'violin', 'density', 'freqpoly'):
|
|
146
|
+
layer_kind = geom
|
|
147
|
+
|
|
148
|
+
auto_settings = {}
|
|
149
|
+
|
|
150
|
+
# choose a proper orientation
|
|
151
|
+
if (side in ('l', 'r') and layer_kind in ('histogram', 'density', 'freqpoly')):
|
|
152
|
+
auto_settings['orientation'] = 'y'
|
|
153
|
+
|
|
154
|
+
if layer_kind in ('boxplot', 'boxplot_outlier', 'violin'):
|
|
155
|
+
if side in ('l', 'r'):
|
|
156
|
+
auto_settings['x'] = 0
|
|
157
|
+
elif side in ('t', 'b'):
|
|
158
|
+
auto_settings['y'] = 0
|
|
159
|
+
auto_settings['orientation'] = 'y'
|
|
160
|
+
|
|
161
|
+
# Update layer's options with auto-generated and try not to override user-defined options.
|
|
162
|
+
filtered = {k: v for k, v in layer_props.items() if v is not None}
|
|
163
|
+
layer_props.update(
|
|
164
|
+
{**auto_settings, **filtered}
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# For 'histogram' set mapping of x or y to '..density..' for compatibility with 'density' geom.
|
|
168
|
+
if layer_kind == 'histogram':
|
|
169
|
+
if side in ('l', 'r'):
|
|
170
|
+
added_mapping = {'x': '..density..'}
|
|
171
|
+
elif side in ('t', 'b'):
|
|
172
|
+
added_mapping = {'y': '..density..'}
|
|
173
|
+
|
|
174
|
+
aes_feature_spec = layer_props.get('mapping')
|
|
175
|
+
mappings = aes_feature_spec.props() if isinstance(aes_feature_spec, FeatureSpec) else {}
|
|
176
|
+
filtered_mappings = {k: v for k, v in mappings.items() if v is not None}
|
|
177
|
+
updated_mappings = {**added_mapping, **filtered_mappings}
|
|
178
|
+
updated_aes_feature_spec = FeatureSpec('mapping', name=None, **updated_mappings)
|
|
179
|
+
layer_props['mapping'] = updated_aes_feature_spec
|
|
180
|
+
|
|
181
|
+
return layer_copy
|