dtlpy 1.115.44__py3-none-any.whl → 1.117.6__py3-none-any.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.
- dtlpy/__init__.py +491 -491
- dtlpy/__version__.py +1 -1
- dtlpy/assets/__init__.py +26 -26
- dtlpy/assets/code_server/config.yaml +2 -2
- dtlpy/assets/code_server/installation.sh +24 -24
- dtlpy/assets/code_server/launch.json +13 -13
- dtlpy/assets/code_server/settings.json +2 -2
- dtlpy/assets/main.py +53 -53
- dtlpy/assets/main_partial.py +18 -18
- dtlpy/assets/mock.json +11 -11
- dtlpy/assets/model_adapter.py +83 -83
- dtlpy/assets/package.json +61 -61
- dtlpy/assets/package_catalog.json +29 -29
- dtlpy/assets/package_gitignore +307 -307
- dtlpy/assets/service_runners/__init__.py +33 -33
- dtlpy/assets/service_runners/converter.py +96 -96
- dtlpy/assets/service_runners/multi_method.py +49 -49
- dtlpy/assets/service_runners/multi_method_annotation.py +54 -54
- dtlpy/assets/service_runners/multi_method_dataset.py +55 -55
- dtlpy/assets/service_runners/multi_method_item.py +52 -52
- dtlpy/assets/service_runners/multi_method_json.py +52 -52
- dtlpy/assets/service_runners/single_method.py +37 -37
- dtlpy/assets/service_runners/single_method_annotation.py +43 -43
- dtlpy/assets/service_runners/single_method_dataset.py +43 -43
- dtlpy/assets/service_runners/single_method_item.py +41 -41
- dtlpy/assets/service_runners/single_method_json.py +42 -42
- dtlpy/assets/service_runners/single_method_multi_input.py +45 -45
- dtlpy/assets/voc_annotation_template.xml +23 -23
- dtlpy/caches/base_cache.py +32 -32
- dtlpy/caches/cache.py +473 -473
- dtlpy/caches/dl_cache.py +201 -201
- dtlpy/caches/filesystem_cache.py +89 -89
- dtlpy/caches/redis_cache.py +84 -84
- dtlpy/dlp/__init__.py +20 -20
- dtlpy/dlp/cli_utilities.py +367 -367
- dtlpy/dlp/command_executor.py +764 -764
- dtlpy/dlp/dlp +1 -1
- dtlpy/dlp/dlp.bat +1 -1
- dtlpy/dlp/dlp.py +128 -128
- dtlpy/dlp/parser.py +651 -651
- dtlpy/entities/__init__.py +83 -83
- dtlpy/entities/analytic.py +347 -347
- dtlpy/entities/annotation.py +1879 -1879
- dtlpy/entities/annotation_collection.py +699 -699
- dtlpy/entities/annotation_definitions/__init__.py +20 -20
- dtlpy/entities/annotation_definitions/base_annotation_definition.py +100 -100
- dtlpy/entities/annotation_definitions/box.py +195 -195
- dtlpy/entities/annotation_definitions/classification.py +67 -67
- dtlpy/entities/annotation_definitions/comparison.py +72 -72
- dtlpy/entities/annotation_definitions/cube.py +204 -204
- dtlpy/entities/annotation_definitions/cube_3d.py +149 -149
- dtlpy/entities/annotation_definitions/description.py +32 -32
- dtlpy/entities/annotation_definitions/ellipse.py +124 -124
- dtlpy/entities/annotation_definitions/free_text.py +62 -62
- dtlpy/entities/annotation_definitions/gis.py +69 -69
- dtlpy/entities/annotation_definitions/note.py +139 -139
- dtlpy/entities/annotation_definitions/point.py +117 -117
- dtlpy/entities/annotation_definitions/polygon.py +182 -182
- dtlpy/entities/annotation_definitions/polyline.py +111 -111
- dtlpy/entities/annotation_definitions/pose.py +92 -92
- dtlpy/entities/annotation_definitions/ref_image.py +86 -86
- dtlpy/entities/annotation_definitions/segmentation.py +240 -240
- dtlpy/entities/annotation_definitions/subtitle.py +34 -34
- dtlpy/entities/annotation_definitions/text.py +85 -85
- dtlpy/entities/annotation_definitions/undefined_annotation.py +74 -74
- dtlpy/entities/app.py +220 -220
- dtlpy/entities/app_module.py +107 -107
- dtlpy/entities/artifact.py +174 -174
- dtlpy/entities/assignment.py +399 -399
- dtlpy/entities/base_entity.py +214 -214
- dtlpy/entities/bot.py +113 -113
- dtlpy/entities/codebase.py +292 -292
- dtlpy/entities/collection.py +38 -38
- dtlpy/entities/command.py +169 -169
- dtlpy/entities/compute.py +449 -449
- dtlpy/entities/dataset.py +1299 -1299
- dtlpy/entities/directory_tree.py +44 -44
- dtlpy/entities/dpk.py +470 -470
- dtlpy/entities/driver.py +235 -235
- dtlpy/entities/execution.py +397 -397
- dtlpy/entities/feature.py +124 -124
- dtlpy/entities/feature_set.py +152 -145
- dtlpy/entities/filters.py +798 -798
- dtlpy/entities/gis_item.py +107 -107
- dtlpy/entities/integration.py +184 -184
- dtlpy/entities/item.py +975 -959
- dtlpy/entities/label.py +123 -123
- dtlpy/entities/links.py +85 -85
- dtlpy/entities/message.py +175 -175
- dtlpy/entities/model.py +684 -684
- dtlpy/entities/node.py +1005 -1005
- dtlpy/entities/ontology.py +810 -803
- dtlpy/entities/organization.py +287 -287
- dtlpy/entities/package.py +657 -657
- dtlpy/entities/package_defaults.py +5 -5
- dtlpy/entities/package_function.py +185 -185
- dtlpy/entities/package_module.py +113 -113
- dtlpy/entities/package_slot.py +118 -118
- dtlpy/entities/paged_entities.py +299 -299
- dtlpy/entities/pipeline.py +624 -624
- dtlpy/entities/pipeline_execution.py +279 -279
- dtlpy/entities/project.py +394 -394
- dtlpy/entities/prompt_item.py +505 -505
- dtlpy/entities/recipe.py +301 -301
- dtlpy/entities/reflect_dict.py +102 -102
- dtlpy/entities/resource_execution.py +138 -138
- dtlpy/entities/service.py +974 -963
- dtlpy/entities/service_driver.py +117 -117
- dtlpy/entities/setting.py +294 -294
- dtlpy/entities/task.py +495 -495
- dtlpy/entities/time_series.py +143 -143
- dtlpy/entities/trigger.py +426 -426
- dtlpy/entities/user.py +118 -118
- dtlpy/entities/webhook.py +124 -124
- dtlpy/examples/__init__.py +19 -19
- dtlpy/examples/add_labels.py +135 -135
- dtlpy/examples/add_metadata_to_item.py +21 -21
- dtlpy/examples/annotate_items_using_model.py +65 -65
- dtlpy/examples/annotate_video_using_model_and_tracker.py +75 -75
- dtlpy/examples/annotations_convert_to_voc.py +9 -9
- dtlpy/examples/annotations_convert_to_yolo.py +9 -9
- dtlpy/examples/convert_annotation_types.py +51 -51
- dtlpy/examples/converter.py +143 -143
- dtlpy/examples/copy_annotations.py +22 -22
- dtlpy/examples/copy_folder.py +31 -31
- dtlpy/examples/create_annotations.py +51 -51
- dtlpy/examples/create_video_annotations.py +83 -83
- dtlpy/examples/delete_annotations.py +26 -26
- dtlpy/examples/filters.py +113 -113
- dtlpy/examples/move_item.py +23 -23
- dtlpy/examples/play_video_annotation.py +13 -13
- dtlpy/examples/show_item_and_mask.py +53 -53
- dtlpy/examples/triggers.py +49 -49
- dtlpy/examples/upload_batch_of_items.py +20 -20
- dtlpy/examples/upload_items_and_custom_format_annotations.py +55 -55
- dtlpy/examples/upload_items_with_modalities.py +43 -43
- dtlpy/examples/upload_segmentation_annotations_from_mask_image.py +44 -44
- dtlpy/examples/upload_yolo_format_annotations.py +70 -70
- dtlpy/exceptions.py +125 -125
- dtlpy/miscellaneous/__init__.py +20 -20
- dtlpy/miscellaneous/dict_differ.py +95 -95
- dtlpy/miscellaneous/git_utils.py +217 -217
- dtlpy/miscellaneous/json_utils.py +14 -14
- dtlpy/miscellaneous/list_print.py +105 -105
- dtlpy/miscellaneous/zipping.py +130 -130
- dtlpy/ml/__init__.py +20 -20
- dtlpy/ml/base_feature_extractor_adapter.py +27 -27
- dtlpy/ml/base_model_adapter.py +1287 -1230
- dtlpy/ml/metrics.py +461 -461
- dtlpy/ml/predictions_utils.py +274 -274
- dtlpy/ml/summary_writer.py +57 -57
- dtlpy/ml/train_utils.py +60 -60
- dtlpy/new_instance.py +252 -252
- dtlpy/repositories/__init__.py +56 -56
- dtlpy/repositories/analytics.py +85 -85
- dtlpy/repositories/annotations.py +916 -916
- dtlpy/repositories/apps.py +383 -383
- dtlpy/repositories/artifacts.py +452 -452
- dtlpy/repositories/assignments.py +599 -599
- dtlpy/repositories/bots.py +213 -213
- dtlpy/repositories/codebases.py +559 -559
- dtlpy/repositories/collections.py +332 -332
- dtlpy/repositories/commands.py +152 -152
- dtlpy/repositories/compositions.py +61 -61
- dtlpy/repositories/computes.py +439 -439
- dtlpy/repositories/datasets.py +1585 -1504
- dtlpy/repositories/downloader.py +1157 -923
- dtlpy/repositories/dpks.py +433 -433
- dtlpy/repositories/drivers.py +482 -482
- dtlpy/repositories/executions.py +815 -815
- dtlpy/repositories/feature_sets.py +256 -226
- dtlpy/repositories/features.py +255 -255
- dtlpy/repositories/integrations.py +484 -484
- dtlpy/repositories/items.py +912 -912
- dtlpy/repositories/messages.py +94 -94
- dtlpy/repositories/models.py +1000 -1000
- dtlpy/repositories/nodes.py +80 -80
- dtlpy/repositories/ontologies.py +511 -511
- dtlpy/repositories/organizations.py +525 -525
- dtlpy/repositories/packages.py +1941 -1941
- dtlpy/repositories/pipeline_executions.py +451 -451
- dtlpy/repositories/pipelines.py +640 -640
- dtlpy/repositories/projects.py +539 -539
- dtlpy/repositories/recipes.py +429 -399
- dtlpy/repositories/resource_executions.py +137 -137
- dtlpy/repositories/schema.py +120 -120
- dtlpy/repositories/service_drivers.py +213 -213
- dtlpy/repositories/services.py +1704 -1704
- dtlpy/repositories/settings.py +339 -339
- dtlpy/repositories/tasks.py +1477 -1477
- dtlpy/repositories/times_series.py +278 -278
- dtlpy/repositories/triggers.py +536 -536
- dtlpy/repositories/upload_element.py +257 -257
- dtlpy/repositories/uploader.py +661 -661
- dtlpy/repositories/webhooks.py +249 -249
- dtlpy/services/__init__.py +22 -22
- dtlpy/services/aihttp_retry.py +131 -131
- dtlpy/services/api_client.py +1786 -1785
- dtlpy/services/api_reference.py +40 -40
- dtlpy/services/async_utils.py +133 -133
- dtlpy/services/calls_counter.py +44 -44
- dtlpy/services/check_sdk.py +68 -68
- dtlpy/services/cookie.py +115 -115
- dtlpy/services/create_logger.py +156 -156
- dtlpy/services/events.py +84 -84
- dtlpy/services/logins.py +235 -235
- dtlpy/services/reporter.py +256 -256
- dtlpy/services/service_defaults.py +91 -91
- dtlpy/utilities/__init__.py +20 -20
- dtlpy/utilities/annotations/__init__.py +16 -16
- dtlpy/utilities/annotations/annotation_converters.py +269 -269
- dtlpy/utilities/base_package_runner.py +285 -264
- dtlpy/utilities/converter.py +1650 -1650
- dtlpy/utilities/dataset_generators/__init__.py +1 -1
- dtlpy/utilities/dataset_generators/dataset_generator.py +670 -670
- dtlpy/utilities/dataset_generators/dataset_generator_tensorflow.py +23 -23
- dtlpy/utilities/dataset_generators/dataset_generator_torch.py +21 -21
- dtlpy/utilities/local_development/__init__.py +1 -1
- dtlpy/utilities/local_development/local_session.py +179 -179
- dtlpy/utilities/reports/__init__.py +2 -2
- dtlpy/utilities/reports/figures.py +343 -343
- dtlpy/utilities/reports/report.py +71 -71
- dtlpy/utilities/videos/__init__.py +17 -17
- dtlpy/utilities/videos/video_player.py +598 -598
- dtlpy/utilities/videos/videos.py +470 -470
- {dtlpy-1.115.44.data → dtlpy-1.117.6.data}/scripts/dlp +1 -1
- dtlpy-1.117.6.data/scripts/dlp.bat +2 -0
- {dtlpy-1.115.44.data → dtlpy-1.117.6.data}/scripts/dlp.py +128 -128
- {dtlpy-1.115.44.dist-info → dtlpy-1.117.6.dist-info}/METADATA +186 -186
- dtlpy-1.117.6.dist-info/RECORD +239 -0
- {dtlpy-1.115.44.dist-info → dtlpy-1.117.6.dist-info}/WHEEL +1 -1
- {dtlpy-1.115.44.dist-info → dtlpy-1.117.6.dist-info}/licenses/LICENSE +200 -200
- tests/features/environment.py +551 -551
- dtlpy/assets/__pycache__/__init__.cpython-310.pyc +0 -0
- dtlpy-1.115.44.data/scripts/dlp.bat +0 -2
- dtlpy-1.115.44.dist-info/RECORD +0 -240
- {dtlpy-1.115.44.dist-info → dtlpy-1.117.6.dist-info}/entry_points.txt +0 -0
- {dtlpy-1.115.44.dist-info → dtlpy-1.117.6.dist-info}/top_level.txt +0 -0
|
@@ -1,343 +1,343 @@
|
|
|
1
|
-
from typing import Optional, Union
|
|
2
|
-
import pandas as pd
|
|
3
|
-
import numpy as np
|
|
4
|
-
|
|
5
|
-
NUMBER_TYPES = (int, float, complex)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class FigOptions:
|
|
9
|
-
def __init__(self, rows_per_page: int = None, comments: dict = None, x_title: str = None, y_title: str = None,
|
|
10
|
-
colors: list = None, direction: str = None):
|
|
11
|
-
self.rows_per_page = rows_per_page
|
|
12
|
-
self.comments = comments
|
|
13
|
-
self.x_title = x_title
|
|
14
|
-
self.y_title = y_title
|
|
15
|
-
self.colors = colors
|
|
16
|
-
self.direction = direction
|
|
17
|
-
|
|
18
|
-
self._validate()
|
|
19
|
-
|
|
20
|
-
def _validate(self):
|
|
21
|
-
# Mapping attribute names to their expected types
|
|
22
|
-
expected_types = {
|
|
23
|
-
'rows_per_page': int,
|
|
24
|
-
'comments': dict,
|
|
25
|
-
'x_title': str,
|
|
26
|
-
'y_title': str,
|
|
27
|
-
'colors': list,
|
|
28
|
-
'direction': str
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
for attr, expected_type in expected_types.items():
|
|
32
|
-
value = getattr(self, attr)
|
|
33
|
-
if value is not None and not isinstance(value, expected_type):
|
|
34
|
-
raise ValueError(
|
|
35
|
-
f"{attr} must be of type {expected_type.__name__}")
|
|
36
|
-
|
|
37
|
-
def to_dict(self):
|
|
38
|
-
return {
|
|
39
|
-
"comments": self.comments,
|
|
40
|
-
"rowsPerPage": self.rows_per_page,
|
|
41
|
-
"xTitle": self.x_title,
|
|
42
|
-
"yTitle": self.y_title,
|
|
43
|
-
"colors": self.colors,
|
|
44
|
-
"direction": self.direction,
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
class Base:
|
|
49
|
-
def __init__(self,
|
|
50
|
-
title: str,
|
|
51
|
-
type: str,
|
|
52
|
-
labels: list,
|
|
53
|
-
data: Union[list, np.ndarray],
|
|
54
|
-
title_href: Optional[str] = None,
|
|
55
|
-
plot_id: Optional[str] = None,
|
|
56
|
-
options: Optional[FigOptions] = None):
|
|
57
|
-
self.title = title
|
|
58
|
-
self.type = type
|
|
59
|
-
self.title_href = title_href
|
|
60
|
-
self.plot_id = plot_id
|
|
61
|
-
self.labels = labels
|
|
62
|
-
self.data = data
|
|
63
|
-
self.options = options if options is not None and options != {} else FigOptions()
|
|
64
|
-
|
|
65
|
-
self._validate()
|
|
66
|
-
|
|
67
|
-
def _validate(self):
|
|
68
|
-
if not isinstance(self.title, str):
|
|
69
|
-
raise ValueError("title must be a string")
|
|
70
|
-
|
|
71
|
-
if self.title_href is not None and not isinstance(self.title_href, str):
|
|
72
|
-
raise ValueError("title_href must be a string or None")
|
|
73
|
-
|
|
74
|
-
if self.plot_id is not None and not isinstance(self.plot_id, str):
|
|
75
|
-
raise ValueError("plot_id must be a string or None")
|
|
76
|
-
|
|
77
|
-
if not isinstance(self.labels, list):
|
|
78
|
-
raise ValueError("labels must be a list")
|
|
79
|
-
|
|
80
|
-
if not isinstance(self.data, (list, np.ndarray)):
|
|
81
|
-
raise ValueError("data must be a list or a numpy ndarray")
|
|
82
|
-
|
|
83
|
-
if not isinstance(self.options, FigOptions):
|
|
84
|
-
raise ValueError("options must be an instance of FigOptions")
|
|
85
|
-
|
|
86
|
-
if not isinstance(self.type, str):
|
|
87
|
-
raise ValueError("type must be a string")
|
|
88
|
-
|
|
89
|
-
def to_dict(self):
|
|
90
|
-
options = self.options.to_dict()
|
|
91
|
-
if isinstance(self, ConfusionMatrix):
|
|
92
|
-
options["confusion"] = True
|
|
93
|
-
|
|
94
|
-
data = self.data
|
|
95
|
-
if isinstance(data, np.ndarray):
|
|
96
|
-
data = data.tolist()
|
|
97
|
-
|
|
98
|
-
return {
|
|
99
|
-
"type": self.type,
|
|
100
|
-
"title": self.title,
|
|
101
|
-
"href": self.title_href,
|
|
102
|
-
"labels": self.labels,
|
|
103
|
-
"id": self.plot_id,
|
|
104
|
-
"data": data,
|
|
105
|
-
"options": options
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
class Table(Base):
|
|
110
|
-
def __init__(self,
|
|
111
|
-
title: str,
|
|
112
|
-
labels: list,
|
|
113
|
-
data: Union[list, np.ndarray],
|
|
114
|
-
title_href: Optional[str] = None,
|
|
115
|
-
plot_id: Optional[str] = None,
|
|
116
|
-
options: Optional[FigOptions] = None):
|
|
117
|
-
super().__init__(title=title,
|
|
118
|
-
type="table",
|
|
119
|
-
labels=labels,
|
|
120
|
-
data=data,
|
|
121
|
-
title_href=title_href,
|
|
122
|
-
plot_id=plot_id,
|
|
123
|
-
options=options)
|
|
124
|
-
self._validate_table_data()
|
|
125
|
-
|
|
126
|
-
def _validate_table_data(self):
|
|
127
|
-
data, labels = self.data, self.labels
|
|
128
|
-
n_cols = len(labels)
|
|
129
|
-
|
|
130
|
-
if not isinstance(data, list):
|
|
131
|
-
raise ValueError(
|
|
132
|
-
'arg "data" must be a list of lists. got a {!r}'.format(type(data)))
|
|
133
|
-
|
|
134
|
-
for i_d, d in enumerate(data):
|
|
135
|
-
if not isinstance(d, list):
|
|
136
|
-
raise ValueError(
|
|
137
|
-
'"data" must be a list of lists. got type {!r} at index {}'.format(type(d), i_d))
|
|
138
|
-
if len(d) != n_cols:
|
|
139
|
-
raise ValueError(
|
|
140
|
-
'"data" rows must be the same size as "labels": {}. got size {} at index {}'.format(n_cols, len(d),
|
|
141
|
-
i_d))
|
|
142
|
-
|
|
143
|
-
@classmethod
|
|
144
|
-
def from_df(cls, df: pd.DataFrame, **kwargs):
|
|
145
|
-
labels = df.columns.to_list()
|
|
146
|
-
data = df.values.tolist()
|
|
147
|
-
return cls(labels=labels, data=data, **kwargs)
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
class Line(Base):
|
|
151
|
-
def __init__(self,
|
|
152
|
-
title: str,
|
|
153
|
-
labels: list,
|
|
154
|
-
data: Union[list, np.ndarray],
|
|
155
|
-
title_href: Optional[str] = None,
|
|
156
|
-
plot_id: Optional[str] = None,
|
|
157
|
-
options: Optional[FigOptions] = None):
|
|
158
|
-
super().__init__(title=title,
|
|
159
|
-
type="line",
|
|
160
|
-
labels=labels,
|
|
161
|
-
data=data,
|
|
162
|
-
title_href=title_href,
|
|
163
|
-
plot_id=plot_id,
|
|
164
|
-
options=options)
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
class Scatter(Base):
|
|
168
|
-
def __init__(self,
|
|
169
|
-
title: str,
|
|
170
|
-
data: Union[list, np.ndarray],
|
|
171
|
-
labels: list = None,
|
|
172
|
-
title_href: Optional[str] = None,
|
|
173
|
-
plot_id: Optional[str] = None,
|
|
174
|
-
options: Optional[FigOptions] = None):
|
|
175
|
-
if labels is None:
|
|
176
|
-
labels = list()
|
|
177
|
-
super().__init__(title=title,
|
|
178
|
-
type='scatter',
|
|
179
|
-
labels=labels,
|
|
180
|
-
data=data,
|
|
181
|
-
title_href=title_href,
|
|
182
|
-
plot_id=plot_id,
|
|
183
|
-
options=options)
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
class Bar(Base):
|
|
187
|
-
def __init__(self,
|
|
188
|
-
title: str,
|
|
189
|
-
labels: list,
|
|
190
|
-
data: Union[list, np.ndarray],
|
|
191
|
-
title_href: Optional[str] = None,
|
|
192
|
-
plot_id: Optional[str] = None,
|
|
193
|
-
options: Optional[FigOptions] = None):
|
|
194
|
-
super().__init__(title=title,
|
|
195
|
-
type="bar",
|
|
196
|
-
labels=labels,
|
|
197
|
-
data=data,
|
|
198
|
-
title_href=title_href,
|
|
199
|
-
plot_id=plot_id,
|
|
200
|
-
options=options)
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
class Doughnut(Base):
|
|
204
|
-
def __init__(self,
|
|
205
|
-
title: str,
|
|
206
|
-
labels: list,
|
|
207
|
-
data: Union[list, np.ndarray],
|
|
208
|
-
title_href: Optional[str] = None,
|
|
209
|
-
plot_id: Optional[str] = None,
|
|
210
|
-
options: Optional[FigOptions] = None):
|
|
211
|
-
super().__init__(title=title,
|
|
212
|
-
type="doughnut",
|
|
213
|
-
labels=labels,
|
|
214
|
-
data=data,
|
|
215
|
-
title_href=title_href,
|
|
216
|
-
plot_id=plot_id,
|
|
217
|
-
options=options)
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
class Pie(Base):
|
|
221
|
-
def __init__(self,
|
|
222
|
-
title: str,
|
|
223
|
-
labels: list,
|
|
224
|
-
data: Union[list, np.ndarray],
|
|
225
|
-
title_href: Optional[str] = None,
|
|
226
|
-
plot_id: Optional[str] = None,
|
|
227
|
-
options: Optional[FigOptions] = None):
|
|
228
|
-
super().__init__(title=title,
|
|
229
|
-
type='pie',
|
|
230
|
-
labels=labels,
|
|
231
|
-
data=data,
|
|
232
|
-
title_href=title_href,
|
|
233
|
-
plot_id=plot_id,
|
|
234
|
-
options=options)
|
|
235
|
-
self._validate_pie_data()
|
|
236
|
-
|
|
237
|
-
def _validate_pie_data(self):
|
|
238
|
-
data, labels = self.data, self.labels
|
|
239
|
-
if not isinstance(data, list):
|
|
240
|
-
raise ValueError(
|
|
241
|
-
'arg "data" must be a list. got a {!r}'.format(type(data)))
|
|
242
|
-
|
|
243
|
-
if len(data) != len(labels):
|
|
244
|
-
raise ValueError('"data" must be the same size as "labels". got size {} and {}'.format(len(data),
|
|
245
|
-
len(labels)))
|
|
246
|
-
for i_d, d in enumerate(data):
|
|
247
|
-
if not isinstance(d, NUMBER_TYPES):
|
|
248
|
-
raise ValueError(
|
|
249
|
-
'all "data" fields must be number. got type {!r} at index {}'.format(type(d), i_d))
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
class Hbar(Base):
|
|
253
|
-
def __init__(self,
|
|
254
|
-
title: str,
|
|
255
|
-
labels: list,
|
|
256
|
-
data: Union[list, np.ndarray],
|
|
257
|
-
title_href: Optional[str] = None,
|
|
258
|
-
plot_id: Optional[str] = None,
|
|
259
|
-
options: Optional[FigOptions] = None):
|
|
260
|
-
super().__init__(title=title,
|
|
261
|
-
type="hbar",
|
|
262
|
-
labels=labels,
|
|
263
|
-
data=data,
|
|
264
|
-
title_href=title_href,
|
|
265
|
-
plot_id=plot_id,
|
|
266
|
-
options=options)
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
class ConfusionMatrix(Base):
|
|
270
|
-
def __init__(self,
|
|
271
|
-
title: str,
|
|
272
|
-
labels: list,
|
|
273
|
-
data: Union[list, np.ndarray],
|
|
274
|
-
title_href: Optional[str] = None,
|
|
275
|
-
plot_id: Optional[str] = None,
|
|
276
|
-
options: Optional[FigOptions] = None,
|
|
277
|
-
href_map: Optional[list] = None,
|
|
278
|
-
color_map: Optional[np.ndarray] = None):
|
|
279
|
-
self.xlabel = 'Predicted'
|
|
280
|
-
self.ylabel = 'Actual'
|
|
281
|
-
self.color_map = color_map
|
|
282
|
-
self.href_map = href_map
|
|
283
|
-
super().__init__(title=title,
|
|
284
|
-
type="table",
|
|
285
|
-
labels=labels,
|
|
286
|
-
data=data,
|
|
287
|
-
title_href=title_href,
|
|
288
|
-
plot_id=plot_id,
|
|
289
|
-
options=options)
|
|
290
|
-
self._validate_confusion_matrix_data()
|
|
291
|
-
|
|
292
|
-
def _validate_confusion_matrix_data(self):
|
|
293
|
-
if self.color_map is not None and not isinstance(self.color_map, np.ndarray):
|
|
294
|
-
raise ValueError('arg "color_map" must be a numpy ndarray. got a {!r}'.format(
|
|
295
|
-
type(self.color_map)))
|
|
296
|
-
if self.href_map is not None and not isinstance(self.href_map, list):
|
|
297
|
-
raise ValueError(
|
|
298
|
-
'arg "href_map" must be a list. got a {!r}'.format(type(self.href_map)))
|
|
299
|
-
|
|
300
|
-
@staticmethod
|
|
301
|
-
def _rbgtohex(r, g, b):
|
|
302
|
-
return "#{0:02x}{1:02x}{2:02x}".format(int(255 * r), int(255 * g), int(255 * b))
|
|
303
|
-
|
|
304
|
-
def to_dict(self):
|
|
305
|
-
options = self.options.to_dict()
|
|
306
|
-
options["confusion"] = True
|
|
307
|
-
data = self.data.copy()
|
|
308
|
-
if isinstance(data, np.ndarray):
|
|
309
|
-
data = data.tolist()
|
|
310
|
-
labels = self.labels.copy()
|
|
311
|
-
for i_row, row in enumerate(data):
|
|
312
|
-
for i_col, val in enumerate(row):
|
|
313
|
-
color = self._rbgtohex(
|
|
314
|
-
*self.color_map[i_row, i_col, :3]) if self.color_map is not None else None
|
|
315
|
-
href = self.href_map[i_row][i_col] if self.href_map is not None else None
|
|
316
|
-
data[i_row][i_col] = {
|
|
317
|
-
'text': '{:.2f}'.format(val),
|
|
318
|
-
'href': href,
|
|
319
|
-
'color': color}
|
|
320
|
-
row.insert(0, labels[i_row])
|
|
321
|
-
labels.insert(0, f'{self.ylabel}//{self.xlabel}')
|
|
322
|
-
return {
|
|
323
|
-
"type": self.type,
|
|
324
|
-
"title": self.title,
|
|
325
|
-
"href": self.title_href,
|
|
326
|
-
"labels": labels,
|
|
327
|
-
"data": data,
|
|
328
|
-
"options": options
|
|
329
|
-
}
|
|
330
|
-
# def from_df(self, df: pd.DataFrame, **kwargs):
|
|
331
|
-
# data = []
|
|
332
|
-
# for i_label, g_label in enumerate(labels):
|
|
333
|
-
# line = list()
|
|
334
|
-
# line.append(g_label)
|
|
335
|
-
# for r_label in labels:
|
|
336
|
-
# if g_label == r_label:
|
|
337
|
-
# p = high_prob()
|
|
338
|
-
# else:
|
|
339
|
-
# p = low_prob()
|
|
340
|
-
# line.append({'text': str(p),
|
|
341
|
-
# 'href': "https://rc-con.dataloop.ai/projects/2cb9ae90-b6e8-4d15-9016-17bacc9b7bdf/datasets/607ed8107370454e4dd3b4c7/items?view=icons&dqlFilter=%7B%22filter%22%3A+%7B%22%24and%22%3A+%5B%7B%22hidden%22%3A+false%7D%2C+%7B%22type%22%3A+%22file%22%7D%2C+%7B%22dir%22%3A+%22booking%22%7D%5D%7D%2C+%22page%22%3A+0%2C+%22pageSize%22%3A+1000%2C+%22resource%22%3A+%22items%22%2C+%22join%22%3A+%7B%22on%22%3A+%7B%22resource%22%3A+%22annotations%22%2C+%22local%22%3A+%22itemId%22%2C+%22forigen%22%3A+%22id%22%7D%2C+%22filter%22%3A+%7B%22%24and%22%3A+%5B%7B%22label%22%3A+%22immigration%22%7D%5D%7D%7D%7D",
|
|
342
|
-
# 'color': rbgtohex(*colors(p)[:3])})
|
|
343
|
-
# data.append(line)
|
|
1
|
+
from typing import Optional, Union
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
NUMBER_TYPES = (int, float, complex)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FigOptions:
|
|
9
|
+
def __init__(self, rows_per_page: int = None, comments: dict = None, x_title: str = None, y_title: str = None,
|
|
10
|
+
colors: list = None, direction: str = None):
|
|
11
|
+
self.rows_per_page = rows_per_page
|
|
12
|
+
self.comments = comments
|
|
13
|
+
self.x_title = x_title
|
|
14
|
+
self.y_title = y_title
|
|
15
|
+
self.colors = colors
|
|
16
|
+
self.direction = direction
|
|
17
|
+
|
|
18
|
+
self._validate()
|
|
19
|
+
|
|
20
|
+
def _validate(self):
|
|
21
|
+
# Mapping attribute names to their expected types
|
|
22
|
+
expected_types = {
|
|
23
|
+
'rows_per_page': int,
|
|
24
|
+
'comments': dict,
|
|
25
|
+
'x_title': str,
|
|
26
|
+
'y_title': str,
|
|
27
|
+
'colors': list,
|
|
28
|
+
'direction': str
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
for attr, expected_type in expected_types.items():
|
|
32
|
+
value = getattr(self, attr)
|
|
33
|
+
if value is not None and not isinstance(value, expected_type):
|
|
34
|
+
raise ValueError(
|
|
35
|
+
f"{attr} must be of type {expected_type.__name__}")
|
|
36
|
+
|
|
37
|
+
def to_dict(self):
|
|
38
|
+
return {
|
|
39
|
+
"comments": self.comments,
|
|
40
|
+
"rowsPerPage": self.rows_per_page,
|
|
41
|
+
"xTitle": self.x_title,
|
|
42
|
+
"yTitle": self.y_title,
|
|
43
|
+
"colors": self.colors,
|
|
44
|
+
"direction": self.direction,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class Base:
|
|
49
|
+
def __init__(self,
|
|
50
|
+
title: str,
|
|
51
|
+
type: str,
|
|
52
|
+
labels: list,
|
|
53
|
+
data: Union[list, np.ndarray],
|
|
54
|
+
title_href: Optional[str] = None,
|
|
55
|
+
plot_id: Optional[str] = None,
|
|
56
|
+
options: Optional[FigOptions] = None):
|
|
57
|
+
self.title = title
|
|
58
|
+
self.type = type
|
|
59
|
+
self.title_href = title_href
|
|
60
|
+
self.plot_id = plot_id
|
|
61
|
+
self.labels = labels
|
|
62
|
+
self.data = data
|
|
63
|
+
self.options = options if options is not None and options != {} else FigOptions()
|
|
64
|
+
|
|
65
|
+
self._validate()
|
|
66
|
+
|
|
67
|
+
def _validate(self):
|
|
68
|
+
if not isinstance(self.title, str):
|
|
69
|
+
raise ValueError("title must be a string")
|
|
70
|
+
|
|
71
|
+
if self.title_href is not None and not isinstance(self.title_href, str):
|
|
72
|
+
raise ValueError("title_href must be a string or None")
|
|
73
|
+
|
|
74
|
+
if self.plot_id is not None and not isinstance(self.plot_id, str):
|
|
75
|
+
raise ValueError("plot_id must be a string or None")
|
|
76
|
+
|
|
77
|
+
if not isinstance(self.labels, list):
|
|
78
|
+
raise ValueError("labels must be a list")
|
|
79
|
+
|
|
80
|
+
if not isinstance(self.data, (list, np.ndarray)):
|
|
81
|
+
raise ValueError("data must be a list or a numpy ndarray")
|
|
82
|
+
|
|
83
|
+
if not isinstance(self.options, FigOptions):
|
|
84
|
+
raise ValueError("options must be an instance of FigOptions")
|
|
85
|
+
|
|
86
|
+
if not isinstance(self.type, str):
|
|
87
|
+
raise ValueError("type must be a string")
|
|
88
|
+
|
|
89
|
+
def to_dict(self):
|
|
90
|
+
options = self.options.to_dict()
|
|
91
|
+
if isinstance(self, ConfusionMatrix):
|
|
92
|
+
options["confusion"] = True
|
|
93
|
+
|
|
94
|
+
data = self.data
|
|
95
|
+
if isinstance(data, np.ndarray):
|
|
96
|
+
data = data.tolist()
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
"type": self.type,
|
|
100
|
+
"title": self.title,
|
|
101
|
+
"href": self.title_href,
|
|
102
|
+
"labels": self.labels,
|
|
103
|
+
"id": self.plot_id,
|
|
104
|
+
"data": data,
|
|
105
|
+
"options": options
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class Table(Base):
|
|
110
|
+
def __init__(self,
|
|
111
|
+
title: str,
|
|
112
|
+
labels: list,
|
|
113
|
+
data: Union[list, np.ndarray],
|
|
114
|
+
title_href: Optional[str] = None,
|
|
115
|
+
plot_id: Optional[str] = None,
|
|
116
|
+
options: Optional[FigOptions] = None):
|
|
117
|
+
super().__init__(title=title,
|
|
118
|
+
type="table",
|
|
119
|
+
labels=labels,
|
|
120
|
+
data=data,
|
|
121
|
+
title_href=title_href,
|
|
122
|
+
plot_id=plot_id,
|
|
123
|
+
options=options)
|
|
124
|
+
self._validate_table_data()
|
|
125
|
+
|
|
126
|
+
def _validate_table_data(self):
|
|
127
|
+
data, labels = self.data, self.labels
|
|
128
|
+
n_cols = len(labels)
|
|
129
|
+
|
|
130
|
+
if not isinstance(data, list):
|
|
131
|
+
raise ValueError(
|
|
132
|
+
'arg "data" must be a list of lists. got a {!r}'.format(type(data)))
|
|
133
|
+
|
|
134
|
+
for i_d, d in enumerate(data):
|
|
135
|
+
if not isinstance(d, list):
|
|
136
|
+
raise ValueError(
|
|
137
|
+
'"data" must be a list of lists. got type {!r} at index {}'.format(type(d), i_d))
|
|
138
|
+
if len(d) != n_cols:
|
|
139
|
+
raise ValueError(
|
|
140
|
+
'"data" rows must be the same size as "labels": {}. got size {} at index {}'.format(n_cols, len(d),
|
|
141
|
+
i_d))
|
|
142
|
+
|
|
143
|
+
@classmethod
|
|
144
|
+
def from_df(cls, df: pd.DataFrame, **kwargs):
|
|
145
|
+
labels = df.columns.to_list()
|
|
146
|
+
data = df.values.tolist()
|
|
147
|
+
return cls(labels=labels, data=data, **kwargs)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class Line(Base):
|
|
151
|
+
def __init__(self,
|
|
152
|
+
title: str,
|
|
153
|
+
labels: list,
|
|
154
|
+
data: Union[list, np.ndarray],
|
|
155
|
+
title_href: Optional[str] = None,
|
|
156
|
+
plot_id: Optional[str] = None,
|
|
157
|
+
options: Optional[FigOptions] = None):
|
|
158
|
+
super().__init__(title=title,
|
|
159
|
+
type="line",
|
|
160
|
+
labels=labels,
|
|
161
|
+
data=data,
|
|
162
|
+
title_href=title_href,
|
|
163
|
+
plot_id=plot_id,
|
|
164
|
+
options=options)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class Scatter(Base):
|
|
168
|
+
def __init__(self,
|
|
169
|
+
title: str,
|
|
170
|
+
data: Union[list, np.ndarray],
|
|
171
|
+
labels: list = None,
|
|
172
|
+
title_href: Optional[str] = None,
|
|
173
|
+
plot_id: Optional[str] = None,
|
|
174
|
+
options: Optional[FigOptions] = None):
|
|
175
|
+
if labels is None:
|
|
176
|
+
labels = list()
|
|
177
|
+
super().__init__(title=title,
|
|
178
|
+
type='scatter',
|
|
179
|
+
labels=labels,
|
|
180
|
+
data=data,
|
|
181
|
+
title_href=title_href,
|
|
182
|
+
plot_id=plot_id,
|
|
183
|
+
options=options)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
class Bar(Base):
|
|
187
|
+
def __init__(self,
|
|
188
|
+
title: str,
|
|
189
|
+
labels: list,
|
|
190
|
+
data: Union[list, np.ndarray],
|
|
191
|
+
title_href: Optional[str] = None,
|
|
192
|
+
plot_id: Optional[str] = None,
|
|
193
|
+
options: Optional[FigOptions] = None):
|
|
194
|
+
super().__init__(title=title,
|
|
195
|
+
type="bar",
|
|
196
|
+
labels=labels,
|
|
197
|
+
data=data,
|
|
198
|
+
title_href=title_href,
|
|
199
|
+
plot_id=plot_id,
|
|
200
|
+
options=options)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class Doughnut(Base):
|
|
204
|
+
def __init__(self,
|
|
205
|
+
title: str,
|
|
206
|
+
labels: list,
|
|
207
|
+
data: Union[list, np.ndarray],
|
|
208
|
+
title_href: Optional[str] = None,
|
|
209
|
+
plot_id: Optional[str] = None,
|
|
210
|
+
options: Optional[FigOptions] = None):
|
|
211
|
+
super().__init__(title=title,
|
|
212
|
+
type="doughnut",
|
|
213
|
+
labels=labels,
|
|
214
|
+
data=data,
|
|
215
|
+
title_href=title_href,
|
|
216
|
+
plot_id=plot_id,
|
|
217
|
+
options=options)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class Pie(Base):
|
|
221
|
+
def __init__(self,
|
|
222
|
+
title: str,
|
|
223
|
+
labels: list,
|
|
224
|
+
data: Union[list, np.ndarray],
|
|
225
|
+
title_href: Optional[str] = None,
|
|
226
|
+
plot_id: Optional[str] = None,
|
|
227
|
+
options: Optional[FigOptions] = None):
|
|
228
|
+
super().__init__(title=title,
|
|
229
|
+
type='pie',
|
|
230
|
+
labels=labels,
|
|
231
|
+
data=data,
|
|
232
|
+
title_href=title_href,
|
|
233
|
+
plot_id=plot_id,
|
|
234
|
+
options=options)
|
|
235
|
+
self._validate_pie_data()
|
|
236
|
+
|
|
237
|
+
def _validate_pie_data(self):
|
|
238
|
+
data, labels = self.data, self.labels
|
|
239
|
+
if not isinstance(data, list):
|
|
240
|
+
raise ValueError(
|
|
241
|
+
'arg "data" must be a list. got a {!r}'.format(type(data)))
|
|
242
|
+
|
|
243
|
+
if len(data) != len(labels):
|
|
244
|
+
raise ValueError('"data" must be the same size as "labels". got size {} and {}'.format(len(data),
|
|
245
|
+
len(labels)))
|
|
246
|
+
for i_d, d in enumerate(data):
|
|
247
|
+
if not isinstance(d, NUMBER_TYPES):
|
|
248
|
+
raise ValueError(
|
|
249
|
+
'all "data" fields must be number. got type {!r} at index {}'.format(type(d), i_d))
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class Hbar(Base):
|
|
253
|
+
def __init__(self,
|
|
254
|
+
title: str,
|
|
255
|
+
labels: list,
|
|
256
|
+
data: Union[list, np.ndarray],
|
|
257
|
+
title_href: Optional[str] = None,
|
|
258
|
+
plot_id: Optional[str] = None,
|
|
259
|
+
options: Optional[FigOptions] = None):
|
|
260
|
+
super().__init__(title=title,
|
|
261
|
+
type="hbar",
|
|
262
|
+
labels=labels,
|
|
263
|
+
data=data,
|
|
264
|
+
title_href=title_href,
|
|
265
|
+
plot_id=plot_id,
|
|
266
|
+
options=options)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class ConfusionMatrix(Base):
|
|
270
|
+
def __init__(self,
|
|
271
|
+
title: str,
|
|
272
|
+
labels: list,
|
|
273
|
+
data: Union[list, np.ndarray],
|
|
274
|
+
title_href: Optional[str] = None,
|
|
275
|
+
plot_id: Optional[str] = None,
|
|
276
|
+
options: Optional[FigOptions] = None,
|
|
277
|
+
href_map: Optional[list] = None,
|
|
278
|
+
color_map: Optional[np.ndarray] = None):
|
|
279
|
+
self.xlabel = 'Predicted'
|
|
280
|
+
self.ylabel = 'Actual'
|
|
281
|
+
self.color_map = color_map
|
|
282
|
+
self.href_map = href_map
|
|
283
|
+
super().__init__(title=title,
|
|
284
|
+
type="table",
|
|
285
|
+
labels=labels,
|
|
286
|
+
data=data,
|
|
287
|
+
title_href=title_href,
|
|
288
|
+
plot_id=plot_id,
|
|
289
|
+
options=options)
|
|
290
|
+
self._validate_confusion_matrix_data()
|
|
291
|
+
|
|
292
|
+
def _validate_confusion_matrix_data(self):
|
|
293
|
+
if self.color_map is not None and not isinstance(self.color_map, np.ndarray):
|
|
294
|
+
raise ValueError('arg "color_map" must be a numpy ndarray. got a {!r}'.format(
|
|
295
|
+
type(self.color_map)))
|
|
296
|
+
if self.href_map is not None and not isinstance(self.href_map, list):
|
|
297
|
+
raise ValueError(
|
|
298
|
+
'arg "href_map" must be a list. got a {!r}'.format(type(self.href_map)))
|
|
299
|
+
|
|
300
|
+
@staticmethod
|
|
301
|
+
def _rbgtohex(r, g, b):
|
|
302
|
+
return "#{0:02x}{1:02x}{2:02x}".format(int(255 * r), int(255 * g), int(255 * b))
|
|
303
|
+
|
|
304
|
+
def to_dict(self):
|
|
305
|
+
options = self.options.to_dict()
|
|
306
|
+
options["confusion"] = True
|
|
307
|
+
data = self.data.copy()
|
|
308
|
+
if isinstance(data, np.ndarray):
|
|
309
|
+
data = data.tolist()
|
|
310
|
+
labels = self.labels.copy()
|
|
311
|
+
for i_row, row in enumerate(data):
|
|
312
|
+
for i_col, val in enumerate(row):
|
|
313
|
+
color = self._rbgtohex(
|
|
314
|
+
*self.color_map[i_row, i_col, :3]) if self.color_map is not None else None
|
|
315
|
+
href = self.href_map[i_row][i_col] if self.href_map is not None else None
|
|
316
|
+
data[i_row][i_col] = {
|
|
317
|
+
'text': '{:.2f}'.format(val),
|
|
318
|
+
'href': href,
|
|
319
|
+
'color': color}
|
|
320
|
+
row.insert(0, labels[i_row])
|
|
321
|
+
labels.insert(0, f'{self.ylabel}//{self.xlabel}')
|
|
322
|
+
return {
|
|
323
|
+
"type": self.type,
|
|
324
|
+
"title": self.title,
|
|
325
|
+
"href": self.title_href,
|
|
326
|
+
"labels": labels,
|
|
327
|
+
"data": data,
|
|
328
|
+
"options": options
|
|
329
|
+
}
|
|
330
|
+
# def from_df(self, df: pd.DataFrame, **kwargs):
|
|
331
|
+
# data = []
|
|
332
|
+
# for i_label, g_label in enumerate(labels):
|
|
333
|
+
# line = list()
|
|
334
|
+
# line.append(g_label)
|
|
335
|
+
# for r_label in labels:
|
|
336
|
+
# if g_label == r_label:
|
|
337
|
+
# p = high_prob()
|
|
338
|
+
# else:
|
|
339
|
+
# p = low_prob()
|
|
340
|
+
# line.append({'text': str(p),
|
|
341
|
+
# 'href': "https://rc-con.dataloop.ai/projects/2cb9ae90-b6e8-4d15-9016-17bacc9b7bdf/datasets/607ed8107370454e4dd3b4c7/items?view=icons&dqlFilter=%7B%22filter%22%3A+%7B%22%24and%22%3A+%5B%7B%22hidden%22%3A+false%7D%2C+%7B%22type%22%3A+%22file%22%7D%2C+%7B%22dir%22%3A+%22booking%22%7D%5D%7D%2C+%22page%22%3A+0%2C+%22pageSize%22%3A+1000%2C+%22resource%22%3A+%22items%22%2C+%22join%22%3A+%7B%22on%22%3A+%7B%22resource%22%3A+%22annotations%22%2C+%22local%22%3A+%22itemId%22%2C+%22forigen%22%3A+%22id%22%7D%2C+%22filter%22%3A+%7B%22%24and%22%3A+%5B%7B%22label%22%3A+%22immigration%22%7D%5D%7D%7D%7D",
|
|
342
|
+
# 'color': rbgtohex(*colors(p)[:3])})
|
|
343
|
+
# data.append(line)
|