lets-plot 4.3.3__cp310-cp310-win_amd64.whl → 4.4.0rc1__cp310-cp310-win_amd64.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.
Potentially problematic release.
This version of lets-plot might be problematic. Click here for more details.
- lets_plot/__init__.py +2 -0
- lets_plot/_type_utils.py +8 -0
- lets_plot/_version.py +1 -1
- lets_plot/bistro/__init__.py +3 -1
- lets_plot/bistro/corr.py +3 -3
- lets_plot/bistro/im.py +1 -1
- lets_plot/bistro/residual.py +8 -3
- lets_plot/bistro/waterfall.py +242 -0
- lets_plot/geo_data/to_geo_data_frame.py +5 -1
- lets_plot/mapping.py +6 -7
- lets_plot/package_data/lets-plot.min.js +1 -1
- lets_plot/plot/__init__.py +5 -1
- lets_plot/plot/annotation.py +2 -4
- lets_plot/plot/coord.py +1 -2
- lets_plot/plot/core.py +25 -30
- lets_plot/plot/geom.py +434 -84
- lets_plot/plot/geom_function_.py +6 -1
- lets_plot/plot/geom_imshow_.py +1 -1
- lets_plot/plot/geom_livemap_.py +4 -0
- lets_plot/plot/ggtb_.py +11 -0
- lets_plot/plot/guide.py +84 -8
- lets_plot/plot/label.py +30 -6
- lets_plot/plot/marginal_layer.py +1 -2
- lets_plot/plot/plot.py +4 -2
- lets_plot/plot/scale_colormap_mpl.py +295 -0
- lets_plot/plot/series_meta.py +123 -0
- lets_plot/plot/stat.py +2 -4
- lets_plot/plot/theme_.py +11 -0
- lets_plot/plot/tooltip.py +24 -24
- lets_plot/plot/util.py +106 -113
- {lets_plot-4.3.3.dist-info → lets_plot-4.4.0rc1.dist-info}/METADATA +1 -1
- {lets_plot-4.3.3.dist-info → lets_plot-4.4.0rc1.dist-info}/RECORD +36 -32
- {lets_plot-4.3.3.dist-info → lets_plot-4.4.0rc1.dist-info}/WHEEL +1 -1
- lets_plot_kotlin_bridge.cp310-win_amd64.pyd +0 -0
- {lets_plot-4.3.3.dist-info → lets_plot-4.4.0rc1.dist-info}/LICENSE +0 -0
- {lets_plot-4.3.3.dist-info → lets_plot-4.4.0rc1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Copyright (c) 2024. 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
|
+
from datetime import datetime
|
|
4
|
+
from typing import Union, Dict, Iterable
|
|
5
|
+
|
|
6
|
+
from lets_plot._type_utils import is_polars_dataframe
|
|
7
|
+
from lets_plot.plot.util import is_pandas_data_frame
|
|
8
|
+
|
|
9
|
+
TYPE_INTEGER = 'int'
|
|
10
|
+
TYPE_FLOATING = 'float'
|
|
11
|
+
TYPE_STRING = 'str'
|
|
12
|
+
TYPE_BOOLEAN = 'bool'
|
|
13
|
+
TYPE_DATE_TIME = 'datetime'
|
|
14
|
+
TYPE_UNKNOWN = 'unknown'
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def infer_type(data: Union[Dict, 'pandas.DataFrame', 'polars.DataFrame']) -> Dict[str, str]:
|
|
18
|
+
type_info = {}
|
|
19
|
+
|
|
20
|
+
if is_pandas_data_frame(data):
|
|
21
|
+
import pandas as pd
|
|
22
|
+
import numpy as np # np is a dependency of pandas, we can import it without checking
|
|
23
|
+
|
|
24
|
+
for var_name, var_content in data.items():
|
|
25
|
+
if data.empty:
|
|
26
|
+
type_info[var_name] = TYPE_UNKNOWN
|
|
27
|
+
continue
|
|
28
|
+
|
|
29
|
+
inferred_type = pd.api.types.infer_dtype(var_content.values, skipna=True)
|
|
30
|
+
if inferred_type == "categorical":
|
|
31
|
+
dtype = var_content.cat.categories.dtype
|
|
32
|
+
|
|
33
|
+
if np.issubdtype(dtype, np.integer):
|
|
34
|
+
type_info[var_name] = TYPE_INTEGER
|
|
35
|
+
elif np.issubdtype(dtype, np.floating):
|
|
36
|
+
type_info[var_name] = TYPE_FLOATING
|
|
37
|
+
elif np.issubdtype(dtype, np.object_):
|
|
38
|
+
# Check if all elements are strings
|
|
39
|
+
if all(isinstance(x, str) for x in var_content.cat.categories):
|
|
40
|
+
type_info[var_name] = TYPE_STRING
|
|
41
|
+
else:
|
|
42
|
+
type_info[var_name] = TYPE_UNKNOWN
|
|
43
|
+
else:
|
|
44
|
+
type_info[var_name] = TYPE_UNKNOWN
|
|
45
|
+
else:
|
|
46
|
+
# see https://pandas.pydata.org/docs/reference/api/pandas.api.types.infer_dtype.html
|
|
47
|
+
if inferred_type == 'string':
|
|
48
|
+
type_info[var_name] = TYPE_STRING
|
|
49
|
+
elif inferred_type == 'floating':
|
|
50
|
+
type_info[var_name] = TYPE_FLOATING
|
|
51
|
+
elif inferred_type == 'integer':
|
|
52
|
+
type_info[var_name] = TYPE_INTEGER
|
|
53
|
+
elif inferred_type == 'boolean':
|
|
54
|
+
type_info[var_name] = TYPE_BOOLEAN
|
|
55
|
+
elif inferred_type == 'datetime64' or inferred_type == 'datetime':
|
|
56
|
+
type_info[var_name] = TYPE_DATE_TIME
|
|
57
|
+
elif inferred_type == "date":
|
|
58
|
+
type_info[var_name] = TYPE_DATE_TIME
|
|
59
|
+
elif inferred_type == 'empty': # for columns with all None values
|
|
60
|
+
type_info[var_name] = TYPE_UNKNOWN
|
|
61
|
+
else:
|
|
62
|
+
type_info[var_name] = 'unknown(pandas:' + inferred_type + ')'
|
|
63
|
+
elif is_polars_dataframe(data):
|
|
64
|
+
import polars as pl
|
|
65
|
+
from polars.datatypes.group import INTEGER_DTYPES, FLOAT_DTYPES
|
|
66
|
+
for var_name, var_type in data.schema.items():
|
|
67
|
+
|
|
68
|
+
# https://docs.pola.rs/api/python/stable/reference/datatypes.html
|
|
69
|
+
if var_type in FLOAT_DTYPES:
|
|
70
|
+
type_info[var_name] = TYPE_FLOATING
|
|
71
|
+
elif var_type in INTEGER_DTYPES:
|
|
72
|
+
type_info[var_name] = TYPE_INTEGER
|
|
73
|
+
elif var_type == pl.datatypes.String:
|
|
74
|
+
type_info[var_name] = TYPE_STRING
|
|
75
|
+
elif var_type == pl.datatypes.Boolean:
|
|
76
|
+
type_info[var_name] = TYPE_BOOLEAN
|
|
77
|
+
elif var_type == pl.datatypes.Date or var_type == pl.datatypes.Datetime:
|
|
78
|
+
type_info[var_name] = TYPE_DATE_TIME
|
|
79
|
+
else:
|
|
80
|
+
type_info[var_name] = 'unknown(polars:' + str(var_type) + ')'
|
|
81
|
+
elif isinstance(data, dict):
|
|
82
|
+
for var_name, var_content in data.items():
|
|
83
|
+
if isinstance(var_content, Iterable):
|
|
84
|
+
if not any(True for _ in var_content): # empty
|
|
85
|
+
type_info[var_name] = TYPE_UNKNOWN
|
|
86
|
+
continue
|
|
87
|
+
|
|
88
|
+
type_set = set(type(val) for val in var_content)
|
|
89
|
+
if None in type_set:
|
|
90
|
+
type_set.remove(None)
|
|
91
|
+
|
|
92
|
+
if len(type_set) > 1:
|
|
93
|
+
type_info[var_name] = 'unknown(mixed types)'
|
|
94
|
+
continue
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
import numpy
|
|
98
|
+
except ImportError:
|
|
99
|
+
numpy = None
|
|
100
|
+
|
|
101
|
+
type_obj = list(type_set)[0]
|
|
102
|
+
if type_obj == bool:
|
|
103
|
+
type_info[var_name] = TYPE_BOOLEAN
|
|
104
|
+
elif issubclass(type_obj, int):
|
|
105
|
+
type_info[var_name] = TYPE_INTEGER
|
|
106
|
+
elif issubclass(type_obj, float):
|
|
107
|
+
type_info[var_name] = TYPE_FLOATING
|
|
108
|
+
elif issubclass(type_obj, str):
|
|
109
|
+
type_info[var_name] = TYPE_STRING
|
|
110
|
+
elif issubclass(type_obj, datetime):
|
|
111
|
+
type_info[var_name] = TYPE_DATE_TIME
|
|
112
|
+
elif numpy and issubclass(type_obj, numpy.datetime64):
|
|
113
|
+
type_info[var_name] = TYPE_DATE_TIME
|
|
114
|
+
elif numpy and issubclass(type_obj, numpy.timedelta64):
|
|
115
|
+
type_info[var_name] = TYPE_DATE_TIME
|
|
116
|
+
elif numpy and issubclass(type_obj, numpy.integer):
|
|
117
|
+
type_info[var_name] = TYPE_INTEGER
|
|
118
|
+
elif numpy and issubclass(type_obj, numpy.floating):
|
|
119
|
+
type_info[var_name] = TYPE_FLOATING
|
|
120
|
+
else:
|
|
121
|
+
type_info[var_name] = 'unknown(python:' + str(type_obj) + ')'
|
|
122
|
+
|
|
123
|
+
return type_info
|
lets_plot/plot/stat.py
CHANGED
|
@@ -498,11 +498,10 @@ def stat_sum(mapping=None, *, data=None, geom=None, position=None, show_legend=N
|
|
|
498
498
|
--------
|
|
499
499
|
.. jupyter-execute::
|
|
500
500
|
:linenos:
|
|
501
|
-
:emphasize-lines:
|
|
501
|
+
:emphasize-lines: 9
|
|
502
502
|
|
|
503
503
|
import numpy as np
|
|
504
504
|
from lets_plot import *
|
|
505
|
-
from lets_plot.mapping import as_discrete
|
|
506
505
|
LetsPlot.setup_html()
|
|
507
506
|
n = 50
|
|
508
507
|
np.random.seed(42)
|
|
@@ -515,11 +514,10 @@ def stat_sum(mapping=None, *, data=None, geom=None, position=None, show_legend=N
|
|
|
515
514
|
|
|
516
515
|
.. jupyter-execute::
|
|
517
516
|
:linenos:
|
|
518
|
-
:emphasize-lines:
|
|
517
|
+
:emphasize-lines: 9
|
|
519
518
|
|
|
520
519
|
import numpy as np
|
|
521
520
|
from lets_plot import *
|
|
522
|
-
from lets_plot.mapping import as_discrete
|
|
523
521
|
LetsPlot.setup_html()
|
|
524
522
|
n = 50
|
|
525
523
|
np.random.seed(42)
|
lets_plot/plot/theme_.py
CHANGED
|
@@ -69,6 +69,9 @@ def theme(*,
|
|
|
69
69
|
plot_margin=None,
|
|
70
70
|
plot_inset=None,
|
|
71
71
|
|
|
72
|
+
plot_title_position=None,
|
|
73
|
+
plot_caption_position=None,
|
|
74
|
+
|
|
72
75
|
strip_background=None, # ToDo: x/y
|
|
73
76
|
strip_text=None, # ToDo: x/y
|
|
74
77
|
# ToDo: strip.placement
|
|
@@ -232,6 +235,14 @@ def theme(*,
|
|
|
232
235
|
- a list of four numbers - the insets are applied to the top, right, bottom and left in that order.
|
|
233
236
|
|
|
234
237
|
It is acceptable to use None for any side; in this case, the default value for the plot inset side will be used.
|
|
238
|
+
plot_title_position : {'panel', 'plot'}, default='panel'
|
|
239
|
+
Alignment of the plot title/subtitle.
|
|
240
|
+
A value of 'panel' means that title and subtitle are aligned to the plot panels.
|
|
241
|
+
A value of 'plot' means that title and subtitle are aligned to the entire plot (excluding margins).
|
|
242
|
+
plot_caption_position : {'panel', 'plot'}, default='panel'
|
|
243
|
+
Alignment of the plot caption.
|
|
244
|
+
A value of 'panel' means that caption is aligned to the plot panels.
|
|
245
|
+
A value of 'plot' means that caption is aligned to the entire plot (excluding margins).
|
|
235
246
|
strip_background : str or dict
|
|
236
247
|
Background of facet labels.
|
|
237
248
|
Set 'blank' or result of `element_blank()` to draw nothing.
|
lets_plot/plot/tooltip.py
CHANGED
|
@@ -155,7 +155,7 @@ class layer_tooltips(FeatureSpec):
|
|
|
155
155
|
- field='^X' - for all positional x,
|
|
156
156
|
- field='^Y' - for all positional y.
|
|
157
157
|
|
|
158
|
-
|
|
158
|
+
----
|
|
159
159
|
|
|
160
160
|
The string template in `format` will allow to change lines
|
|
161
161
|
for the default tooltip without `line` specifying.
|
|
@@ -163,7 +163,7 @@ class layer_tooltips(FeatureSpec):
|
|
|
163
163
|
Aes and var formats are not interchangeable, i.e. var format
|
|
164
164
|
will not be applied to aes, mapped to this variable.
|
|
165
165
|
|
|
166
|
-
|
|
166
|
+
----
|
|
167
167
|
|
|
168
168
|
For more info see https://lets-plot.org/python/pages/formats.html.
|
|
169
169
|
|
|
@@ -420,29 +420,29 @@ class layer_tooltips(FeatureSpec):
|
|
|
420
420
|
The resulting string will be at the beginning of the general tooltip, centered and highlighted in bold.
|
|
421
421
|
A long title can be split into multiple lines using `\\\\n` as a text separator.
|
|
422
422
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
423
|
+
Examples
|
|
424
|
+
--------
|
|
425
|
+
.. jupyter-execute::
|
|
426
|
+
:linenos:
|
|
427
|
+
:emphasize-lines: 15
|
|
428
428
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
429
|
+
import numpy as np
|
|
430
|
+
from lets_plot import *
|
|
431
|
+
LetsPlot.setup_html()
|
|
432
|
+
n = 100
|
|
433
|
+
np.random.seed(42)
|
|
434
|
+
data = {
|
|
435
|
+
'id': np.arange(n),
|
|
436
|
+
'x': np.random.normal(size=n),
|
|
437
|
+
'y': np.random.normal(size=n),
|
|
438
|
+
'c': np.random.choice(['a', 'b'], size=n),
|
|
439
|
+
'w': np.random.randint(1, 11, size=n)
|
|
440
|
+
}
|
|
441
|
+
ggplot(data, aes('x', 'y')) + \\
|
|
442
|
+
geom_point(aes(color='c', size='w'), show_legend=False, \\
|
|
443
|
+
tooltips=layer_tooltips().title('@id')
|
|
444
|
+
.line('color|@c')
|
|
445
|
+
.line('size|@w'))
|
|
446
446
|
|
|
447
447
|
"""
|
|
448
448
|
self._tooltip_title = value
|
lets_plot/plot/util.py
CHANGED
|
@@ -2,14 +2,13 @@
|
|
|
2
2
|
# Copyright (c) 2019. JetBrains s.r.o.
|
|
3
3
|
# Use of this source code is governed by the MIT license that can be found in the LICENSE file.
|
|
4
4
|
#
|
|
5
|
-
from
|
|
6
|
-
from datetime import datetime
|
|
7
|
-
from typing import Any, Tuple, Sequence, Optional, Dict
|
|
5
|
+
from typing import Any, Tuple, Sequence, Optional, Dict, List
|
|
8
6
|
|
|
9
|
-
from lets_plot._type_utils import
|
|
7
|
+
from lets_plot._type_utils import is_pandas_data_frame
|
|
10
8
|
from lets_plot.geo_data_internals.utils import find_geo_names
|
|
11
9
|
from lets_plot.mapping import MappingMeta
|
|
12
|
-
from lets_plot.plot.core import aes
|
|
10
|
+
from lets_plot.plot.core import aes, FeatureSpec
|
|
11
|
+
from lets_plot.plot.series_meta import infer_type, TYPE_UNKNOWN
|
|
13
12
|
|
|
14
13
|
|
|
15
14
|
def as_boolean(val, *, default):
|
|
@@ -19,104 +18,94 @@ def as_boolean(val, *, default):
|
|
|
19
18
|
return bool(val) and val != 'False'
|
|
20
19
|
|
|
21
20
|
|
|
22
|
-
def as_annotated_data(
|
|
23
|
-
|
|
21
|
+
def as_annotated_data(data: Any, mapping_spec: FeatureSpec) -> Tuple:
|
|
22
|
+
data_type_by_var: Dict[str, str] = {} # VarName to Type
|
|
23
|
+
mapping_meta_by_var: Dict[str, Dict[str, MappingMeta]] = {} # VarName to Dict[Aes, MappingMeta]
|
|
24
|
+
mappings = {} # Aes to VarName
|
|
24
25
|
|
|
25
|
-
#
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
for col_name in raw_data.col_names:
|
|
31
|
-
data[col_name] = []
|
|
32
|
-
|
|
33
|
-
data_meta.update({'pubsub': {'channel_id': raw_data.channel_id, 'col_names': raw_data.col_names}})
|
|
34
|
-
|
|
35
|
-
# mapping
|
|
36
|
-
mapping = {}
|
|
37
|
-
mapping_meta = []
|
|
38
|
-
# series annotations
|
|
39
|
-
series_meta = []
|
|
40
|
-
|
|
41
|
-
class VariableMeta:
|
|
42
|
-
def __init__(self):
|
|
43
|
-
self.levels = None
|
|
44
|
-
self.aesthetics = []
|
|
45
|
-
self.order = None
|
|
46
|
-
|
|
47
|
-
variables_meta: Dict[str, VariableMeta] = {}
|
|
48
|
-
|
|
49
|
-
if is_data_frame(data):
|
|
50
|
-
dtypes = data.dtypes.to_dict().items()
|
|
51
|
-
for column_name, dtype in dtypes:
|
|
52
|
-
if dtype.name == 'category' and dtype.ordered:
|
|
53
|
-
var_meta = VariableMeta()
|
|
54
|
-
var_meta.levels = dtype.categories.to_list()
|
|
55
|
-
variables_meta[column_name] = var_meta
|
|
56
|
-
|
|
57
|
-
if raw_mapping is not None:
|
|
58
|
-
for aesthetic, variable in raw_mapping.as_dict().items():
|
|
59
|
-
if aesthetic == 'name': # ignore FeatureSpec.name property
|
|
26
|
+
# fill mapping_meta_by_var, mappings and data_type_by_var.
|
|
27
|
+
if mapping_spec is not None:
|
|
28
|
+
for key, spec in mapping_spec.props().items():
|
|
29
|
+
# key is either an aesthetic name or 'name' (FeatureSpec.name property)
|
|
30
|
+
if key == 'name': # ignore FeatureSpec.name property
|
|
60
31
|
continue
|
|
61
|
-
|
|
62
|
-
if isinstance(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
else:
|
|
67
|
-
var_meta = VariableMeta()
|
|
68
|
-
var_meta.aesthetics.append(aesthetic)
|
|
69
|
-
if variable.levels is not None:
|
|
70
|
-
var_meta.levels = variable.levels
|
|
71
|
-
order = variable.parameters.get('order')
|
|
72
|
-
if order is not None:
|
|
73
|
-
var_meta.order = order
|
|
74
|
-
variables_meta[variable.variable] = var_meta
|
|
32
|
+
|
|
33
|
+
if isinstance(spec, MappingMeta):
|
|
34
|
+
mappings[key] = spec.variable
|
|
35
|
+
mapping_meta_by_var.setdefault(spec.variable, {})[key] = spec
|
|
36
|
+
data_type_by_var[spec.variable] = TYPE_UNKNOWN
|
|
75
37
|
else:
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
38
|
+
mappings[key] = spec # spec is a variable name
|
|
39
|
+
|
|
40
|
+
data_type_by_var.update(infer_type(data))
|
|
41
|
+
|
|
42
|
+
# fill series annotations
|
|
43
|
+
series_annotations = {} # var to series_annotation
|
|
44
|
+
for var_name, data_type in data_type_by_var.items():
|
|
45
|
+
series_annotation = {}
|
|
46
|
+
|
|
47
|
+
if data_type != TYPE_UNKNOWN:
|
|
48
|
+
series_annotation['type'] = data_type
|
|
49
|
+
|
|
50
|
+
if is_pandas_data_frame(data) and data[var_name].dtype.name == 'category' and data[var_name].dtype.ordered:
|
|
51
|
+
series_annotation['factor_levels'] = data[var_name].cat.categories.to_list()
|
|
52
|
+
elif var_name in mapping_meta_by_var:
|
|
53
|
+
levels = last_not_none(list(map(lambda mm: mm.levels, mapping_meta_by_var[var_name].values())))
|
|
54
|
+
if levels is not None:
|
|
55
|
+
series_annotation['factor_levels'] = levels
|
|
56
|
+
|
|
57
|
+
if 'factor_levels' in series_annotation and var_name in mapping_meta_by_var:
|
|
58
|
+
order = last_not_none(list(map(lambda mm: mm.parameters['order'], mapping_meta_by_var[var_name].values())))
|
|
59
|
+
if order is not None:
|
|
60
|
+
series_annotation['order'] = order
|
|
61
|
+
|
|
62
|
+
if len(series_annotation) > 0:
|
|
63
|
+
series_annotation['column'] = var_name
|
|
64
|
+
series_annotations[var_name] = series_annotation
|
|
65
|
+
|
|
66
|
+
# fill mapping annotations
|
|
67
|
+
mapping_annotations = []
|
|
68
|
+
for var_name, meta_data in mapping_meta_by_var.items():
|
|
69
|
+
for aesthetic, mapping_meta in meta_data.items():
|
|
70
|
+
if mapping_meta.annotation == 'as_discrete':
|
|
71
|
+
if 'factor_levels' in series_annotations.get(var_name, {}):
|
|
72
|
+
# there is a bug - if label is set then levels are not applied
|
|
73
|
+
continue
|
|
74
|
+
|
|
75
|
+
mapping_annotation = {}
|
|
76
|
+
|
|
77
|
+
# Note that the label is always set; otherwise, the scale title will appear as 'color.cyl'
|
|
78
|
+
label = mapping_meta.parameters.get('label')
|
|
79
|
+
if label is not None:
|
|
80
|
+
mapping_annotation.setdefault('parameters', {})['label'] = label
|
|
81
|
+
|
|
82
|
+
if mapping_meta.levels is not None:
|
|
83
|
+
mapping_annotation['levels'] = mapping_meta.levels
|
|
84
|
+
|
|
85
|
+
order_by = mapping_meta.parameters.get('order_by')
|
|
86
|
+
if order_by is not None:
|
|
87
|
+
mapping_annotation.setdefault('parameters', {})['order_by'] = order_by
|
|
88
|
+
|
|
89
|
+
order = mapping_meta.parameters.get('order')
|
|
90
|
+
if order is not None:
|
|
91
|
+
mapping_annotation.setdefault('parameters', {})['order'] = order
|
|
92
|
+
|
|
93
|
+
# add mapping meta if custom label is set or if series annotation for var doesn't contain order options
|
|
94
|
+
# otherwise don't add mapping meta - it's redundant, nothing unique compared to series annotation
|
|
95
|
+
if len(mapping_annotation):
|
|
96
|
+
mapping_annotation['aes'] = aesthetic
|
|
97
|
+
mapping_annotation['annotation'] = 'as_discrete'
|
|
98
|
+
mapping_annotations.append(mapping_annotation)
|
|
99
|
+
|
|
100
|
+
data_meta = {}
|
|
101
|
+
|
|
102
|
+
if len(series_annotations) > 0:
|
|
103
|
+
data_meta.update({'series_annotations': list(series_annotations.values())})
|
|
104
|
+
|
|
105
|
+
if len(mapping_annotations) > 0:
|
|
106
|
+
data_meta.update({'mapping_annotations': mapping_annotations})
|
|
107
|
+
|
|
108
|
+
return data, aes(**mappings), {'data_meta': data_meta}
|
|
120
109
|
|
|
121
110
|
|
|
122
111
|
def is_data_pub_stream(data: Any) -> bool:
|
|
@@ -147,7 +136,8 @@ def normalize_map_join(map_join):
|
|
|
147
136
|
data_names = [map_join[0]]
|
|
148
137
|
map_names = [map_join[1]]
|
|
149
138
|
elif len(map_join) > 2: # ['foo', 'bar', 'baz'] -> error
|
|
150
|
-
raise ValueError(
|
|
139
|
+
raise ValueError(
|
|
140
|
+
"map_join of type list[str] expected to have 1 or 2 items, but was {}".format(len(map_join)))
|
|
151
141
|
else:
|
|
152
142
|
raise invalid_map_join_format()
|
|
153
143
|
elif all(isinstance(v, Sequence) and not isinstance(v, str) for v in map_join): # all items are lists
|
|
@@ -217,17 +207,20 @@ def geo_data_frame_to_crs(gdf: 'GeoDataFrame', use_crs: Optional[str]):
|
|
|
217
207
|
return gdf.to_crs('EPSG:4326' if use_crs is None else use_crs)
|
|
218
208
|
|
|
219
209
|
|
|
220
|
-
def
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
return False
|
|
210
|
+
def key_int2str(data):
|
|
211
|
+
if is_pandas_data_frame(data):
|
|
212
|
+
if data.columns.inferred_type == 'integer' or data.columns.inferred_type == 'mixed-integer':
|
|
213
|
+
data.columns = data.columns.astype(str)
|
|
214
|
+
return data
|
|
226
215
|
|
|
216
|
+
if isinstance(data, dict):
|
|
217
|
+
return {(str(k) if isinstance(k, int) else k): v for k, v in data.items()}
|
|
227
218
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
219
|
+
return data
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def last_not_none(lst: List) -> Optional[Any]:
|
|
223
|
+
for i in reversed(lst):
|
|
224
|
+
if i is not None:
|
|
225
|
+
return i
|
|
226
|
+
return None
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
lets_plot_kotlin_bridge.cp310-win_amd64.pyd,sha256=
|
|
2
|
-
lets_plot/__init__.py,sha256=
|
|
1
|
+
lets_plot_kotlin_bridge.cp310-win_amd64.pyd,sha256=QMaNf06qYyE4cc5FUGJgQ3Cge3FNv0HLLYsosFJHZNI,6324736
|
|
2
|
+
lets_plot/__init__.py,sha256=JOsBQOnuJgUW3em6hRCx1bzQg9uOiTIhaAJmpJNJEwY,11480
|
|
3
3
|
lets_plot/_global_settings.py,sha256=dozwVWl2_Sg_-sWC08IYqVBhg4PCDAxHBERvD_XoWJU,7631
|
|
4
4
|
lets_plot/_kbridge.py,sha256=LCmRSeFeLFoESoVsm72QuZGFKDsRMMoN0lQF01xju1g,1250
|
|
5
|
-
lets_plot/_type_utils.py,sha256=
|
|
6
|
-
lets_plot/_version.py,sha256=
|
|
7
|
-
lets_plot/mapping.py,sha256=
|
|
5
|
+
lets_plot/_type_utils.py,sha256=ZO7os2Va772U2pJSkTA3OwDylN1QtXZo5y_EZpiWMdI,2789
|
|
6
|
+
lets_plot/_version.py,sha256=hpNRWDoTIbOQ_gh0KysEjir69e7rMM_M3YkDqT1BFjM,242
|
|
7
|
+
lets_plot/mapping.py,sha256=vWWGrVgzgo1u3R8djyshSoOEuaqlqSQpEVeQNqeKWk0,3691
|
|
8
8
|
lets_plot/settings_utils.py,sha256=vKrsXMuJHR88ZZhPtQFAC-xrWKCpCPiRetfx1GpBGKU,8678
|
|
9
9
|
lets_plot/tilesets.py,sha256=8LC_GsrZd1X12rII28W1XbO7A8YfeG1AjBR8L_PPFVk,10810
|
|
10
|
-
lets_plot/bistro/__init__.py,sha256=
|
|
10
|
+
lets_plot/bistro/__init__.py,sha256=0vjEBjuS3r4MR8ugQ1zIo1sks6K0ljSESJWH3pQcgYI,442
|
|
11
11
|
lets_plot/bistro/_plot2d_common.py,sha256=E6a2QghFF8CWwUzT-iCtgqYBepCCPuwUyxcDM8BJkL8,3712
|
|
12
|
-
lets_plot/bistro/corr.py,sha256=
|
|
13
|
-
lets_plot/bistro/im.py,sha256=
|
|
12
|
+
lets_plot/bistro/corr.py,sha256=GqBCneSccF8GBkJgkedpnUdIhXvmJ5Blhtej0qhKRrs,13015
|
|
13
|
+
lets_plot/bistro/im.py,sha256=HDpoNcqzYQrFfw40qAQQ8CvD0lQPzpNWkF93UrjLYjQ,5654
|
|
14
14
|
lets_plot/bistro/joint.py,sha256=bnr4Q-kyfgSoDnplpBq9jXi2XVpBEfcA0cZRVSZM3gU,6455
|
|
15
15
|
lets_plot/bistro/qq.py,sha256=LP5TSI7XZlUlPoJ92mAsFJl7Mnk5VQewqdcWXl1iAR4,6588
|
|
16
|
-
lets_plot/bistro/residual.py,sha256=
|
|
16
|
+
lets_plot/bistro/residual.py,sha256=QTKvMqSl7NoRQRTe85C36XyKBozyh9g6dNaMMMXG320,12357
|
|
17
|
+
lets_plot/bistro/waterfall.py,sha256=JD4wtHV0aFqOMp5oPZz87GJ2eWFwwGnZcnPqje7ApAU,9957
|
|
17
18
|
lets_plot/export/__init__.py,sha256=JloMKV4OAMgxBuYg8ObByZ3LJvqcUKed1G286WLA85E,194
|
|
18
19
|
lets_plot/export/ggsave_.py,sha256=BjpQ7eX_lDLPJpyeDr_G5zzPtUWVebvSoSLb-72xBPs,4001
|
|
19
20
|
lets_plot/frontend_context/__init__.py,sha256=LALJE-5rVdEcgCP-sWTwNAVoVZB-Pr2lG8CpVn04FrY,212
|
|
@@ -33,7 +34,7 @@ lets_plot/geo_data/core.py,sha256=8Kxp8hbMRJVItR-N3T_7Ml5TOIpCkYMUGb0vsfy9hUM,96
|
|
|
33
34
|
lets_plot/geo_data/geocoder.py,sha256=6clN2-uJN95YcMpKNUC4aLmegAJhRvMLsSc1TusgH3Q,34690
|
|
34
35
|
lets_plot/geo_data/geocodes.py,sha256=yuxj1FqhVCG0Vc3kUuxd21ShmL9WAtsu6tnmhackld4,18373
|
|
35
36
|
lets_plot/geo_data/livemap_helper.py,sha256=4169J6yeo3nftw3ynjPuUfCtrgw55a1mX7NxvNaLZo0,2150
|
|
36
|
-
lets_plot/geo_data/to_geo_data_frame.py,sha256=
|
|
37
|
+
lets_plot/geo_data/to_geo_data_frame.py,sha256=abVrGs6naP2hhJ6F2I0SodX82101pgkekV4pGM7BRJ8,5462
|
|
37
38
|
lets_plot/geo_data/type_assertion.py,sha256=9TThxe0Ojva6UH8MG_tGsIudKyIdRXmKJscNmBUULBc,809
|
|
38
39
|
lets_plot/geo_data/gis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
39
40
|
lets_plot/geo_data/gis/fluent_dict.py,sha256=qLzgPgancRe336CZUvXIpM96DRsBXvn3iVh6VvO7hwY,7231
|
|
@@ -46,39 +47,42 @@ lets_plot/geo_data/gis/response.py,sha256=MsAk10JQe0XC-h4Cv0w7uzYxAtlx3YaSrqYXA6
|
|
|
46
47
|
lets_plot/geo_data_internals/__init__.py,sha256=ZwcoMdyQ_k9589f2D3nXXjedJpyiDR8WyqaghTh_EVQ,238
|
|
47
48
|
lets_plot/geo_data_internals/constants.py,sha256=2dViytUZPiojilhWV3UWzBAXgdHl5OoIJsNMsI0V7yU,441
|
|
48
49
|
lets_plot/geo_data_internals/utils.py,sha256=8vfDa99yq1YpVNr-RDtpCJfbrON04rIG6cugpQXnJlU,1000
|
|
49
|
-
lets_plot/package_data/lets-plot.min.js,sha256=
|
|
50
|
-
lets_plot/plot/__init__.py,sha256=
|
|
50
|
+
lets_plot/package_data/lets-plot.min.js,sha256=VnxMHXgaCkf5bPIh1L0H-eU309SES04CZYZa4LTW0Ao,2155683
|
|
51
|
+
lets_plot/plot/__init__.py,sha256=bXUtxpS8UXqYn1uYIwdTY2a2koxJMjcCOdG4iUxutMM,1711
|
|
51
52
|
lets_plot/plot/_global_theme.py,sha256=eatwhJiiqnY6mrNW0Y1NMco2e7xxldhItgj1IOkhRuI,311
|
|
52
|
-
lets_plot/plot/annotation.py,sha256=
|
|
53
|
-
lets_plot/plot/coord.py,sha256=
|
|
54
|
-
lets_plot/plot/core.py,sha256=
|
|
53
|
+
lets_plot/plot/annotation.py,sha256=D6XKD85xQ6YqhedS8geHCWun_5xJZWOxyfFjeCo_6tU,7873
|
|
54
|
+
lets_plot/plot/coord.py,sha256=B4EEt6mqPERbYVwzl3VPkEym9hq_tO0LNtgdtluWQBQ,8218
|
|
55
|
+
lets_plot/plot/core.py,sha256=2LVaCx2wvJ6tSys0UW_fpgdb6V8uBF3Sg6sTPa_lC6E,32185
|
|
55
56
|
lets_plot/plot/facet.py,sha256=sybZ5dpXOpLlG9KrX2LRSeeoVgX8xL22-hToR-YnHy8,7034
|
|
56
57
|
lets_plot/plot/font_features.py,sha256=OInyzUmRbujBEeB2gxuD2O249-5htOQZi2Y_fujxpVY,2309
|
|
57
|
-
lets_plot/plot/geom.py,sha256=
|
|
58
|
+
lets_plot/plot/geom.py,sha256=m6BPCXIGNkxkQgzAeXVntCTMTzdz4UTdKoLJqfoGD9g,321342
|
|
58
59
|
lets_plot/plot/geom_extras.py,sha256=yJ9T5hAQWnhV-KwW-a55qbDOOrLF1D28VZsHpC4aC34,1747
|
|
59
|
-
lets_plot/plot/geom_function_.py,sha256=
|
|
60
|
-
lets_plot/plot/geom_imshow_.py,sha256=
|
|
61
|
-
lets_plot/plot/geom_livemap_.py,sha256=
|
|
60
|
+
lets_plot/plot/geom_function_.py,sha256=EvCp_kGeY1zbZMnlp-_WnjJioLQA9YX-aOMFlv8A0zg,7458
|
|
61
|
+
lets_plot/plot/geom_imshow_.py,sha256=vj6Z9U-1VWsi8Q7G-X5MtwW9KTdMFbyr25wX3CTjODg,15132
|
|
62
|
+
lets_plot/plot/geom_livemap_.py,sha256=X_zYxbRFQAxmGYGTWF8cemEY-eDIXSHBTvuTeWs2Yu4,12115
|
|
62
63
|
lets_plot/plot/gggrid_.py,sha256=EVx2zhlYmej8qXtdlhUzcCS6zTdbsjj3Nl5IwAD4hTI,4608
|
|
63
|
-
lets_plot/plot/
|
|
64
|
-
lets_plot/plot/
|
|
65
|
-
lets_plot/plot/
|
|
66
|
-
lets_plot/plot/
|
|
64
|
+
lets_plot/plot/ggtb_.py,sha256=lxt-e8iNXvNRBoENybRC_JY_bcGswfw8pSWhKedVkCw,266
|
|
65
|
+
lets_plot/plot/guide.py,sha256=iqdBgvtbTjzsTNJWmqfg7En9k-6EnMyBxbK5O6Y_8-w,6436
|
|
66
|
+
lets_plot/plot/label.py,sha256=xcqoGNARcYgSZHdA1h2EGUhJwtzBOax9nUOsz4CLhRQ,4736
|
|
67
|
+
lets_plot/plot/marginal_layer.py,sha256=auDAO5IiRpJVcqzqr31SnXJz7sQGIVbndx__qfr7JyY,6538
|
|
68
|
+
lets_plot/plot/plot.py,sha256=mS2vIt7tV1jUJI2atmerA6t1yCxGF2QqqugEqgUQPWo,8102
|
|
67
69
|
lets_plot/plot/pos.py,sha256=NxnuE--5hwQCWOEqnoQu8TFppZYXJG5m2cgWkPjmXIg,10703
|
|
68
70
|
lets_plot/plot/sampling.py,sha256=_f5kHZzTqrFRJhllMNeAhcPgHlKH7ZnZlLncC9C6yVI,8365
|
|
69
71
|
lets_plot/plot/sandbox_.py,sha256=5wp2bkIBsihw9aIoKr8FUQZmtZbInHPCULbG5uPsMYE,571
|
|
70
72
|
lets_plot/plot/scale.py,sha256=O-b5LJhrLW_3TtbUO3qiaC0vYTHLH-0w7dMrREFF9h4,138936
|
|
73
|
+
lets_plot/plot/scale_colormap_mpl.py,sha256=AoQ_oAcxbH6OMecUc_gvwJsqhJM52of435YIb-MP7lg,10428
|
|
71
74
|
lets_plot/plot/scale_convenience.py,sha256=UOXX07wP5aARYwsOZ-6rK_RR0szhdhnThPvia6LOqrE,4271
|
|
72
75
|
lets_plot/plot/scale_identity_.py,sha256=rMuiaI1wRleP_w8ExvaydlcWeznVJ60M-wzI2SPH7pY,24395
|
|
73
76
|
lets_plot/plot/scale_position.py,sha256=ntpPRO8PA46hGuL9GMcqq18rXqaX1hJF91cHIHxw998,47383
|
|
74
|
-
lets_plot/plot/
|
|
77
|
+
lets_plot/plot/series_meta.py,sha256=Nh-Vcq-zcg_oN4wdoHZxn6aKuScH3_5mVJ4D8LhbO3A,5621
|
|
78
|
+
lets_plot/plot/stat.py,sha256=HRumeeUrZNl8D_JWcukYAil87Uk2_94FcgGnYivkH6Q,21759
|
|
75
79
|
lets_plot/plot/subplots.py,sha256=vKW3BHI5YRM5CGAs_CCQ9bhu5oWqW0Oh1m6b-bClSUA,11620
|
|
76
|
-
lets_plot/plot/theme_.py,sha256=
|
|
80
|
+
lets_plot/plot/theme_.py,sha256=wrwRPMyQUfnAqnkLpV1LYX0vawXI47aECuH6WBzLJZA,24296
|
|
77
81
|
lets_plot/plot/theme_set.py,sha256=KLQSAihJU8_FmAU0at8WUAtgnIqCvU2Rd5awNhTZimo,9496
|
|
78
|
-
lets_plot/plot/tooltip.py,sha256=
|
|
79
|
-
lets_plot/plot/util.py,sha256=
|
|
80
|
-
lets_plot-4.
|
|
81
|
-
lets_plot-4.
|
|
82
|
-
lets_plot-4.
|
|
83
|
-
lets_plot-4.
|
|
84
|
-
lets_plot-4.
|
|
82
|
+
lets_plot/plot/tooltip.py,sha256=PTjUh-CwXmKrb36pNWT2re0K1mZ9kvWwdt4GhhF7VTY,16448
|
|
83
|
+
lets_plot/plot/util.py,sha256=w5PWWPPG_b3g8z9yxfodsd38Csu-qg6z_Zgmzbavsn0,8812
|
|
84
|
+
lets_plot-4.4.0rc1.dist-info/LICENSE,sha256=D7RdUBHyt0ua4vSZs8H7-HIcliPTSk9zY3sNzx8fejY,1087
|
|
85
|
+
lets_plot-4.4.0rc1.dist-info/METADATA,sha256=6z1XJzxwz0ll6kfqSHEQO8S5V0OxWtXIjYs1GOfrk4g,11575
|
|
86
|
+
lets_plot-4.4.0rc1.dist-info/WHEEL,sha256=fsW6--WFfuzX2scefE6JfcSZ5dXg5h59u8lqlpL5uuo,101
|
|
87
|
+
lets_plot-4.4.0rc1.dist-info/top_level.txt,sha256=ID-ORXUWN-oVZmD4YFy1rQVm2QT1D-MlGON3vdxqgpY,34
|
|
88
|
+
lets_plot-4.4.0rc1.dist-info/RECORD,,
|
|
Binary file
|
|
File without changes
|
|
File without changes
|