perspective-python 3.0.0__cp39-abi3-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.
- perspective/__init__.py +81 -0
- perspective/extension/finos-perspective-nbextension.json +5 -0
- perspective/handlers/__init__.py +26 -0
- perspective/handlers/aiohttp.py +54 -0
- perspective/handlers/starlette.py +51 -0
- perspective/handlers/tornado.py +61 -0
- perspective/perspective.pyd +0 -0
- perspective/psp_cffi.py +127 -0
- perspective/templates/exported_widget.html.jinja +35 -0
- perspective/tests/__init__.py +11 -0
- perspective/tests/conftest.py +272 -0
- perspective/tests/core/__init__.py +11 -0
- perspective/tests/core/test_async.py +413 -0
- perspective/tests/core/test_threadpool.py +48 -0
- perspective/tests/server/__init__.py +11 -0
- perspective/tests/server/test_server.py +1058 -0
- perspective/tests/server/test_session.py +55 -0
- perspective/tests/single_threaded/test_single_threaded.py +61 -0
- perspective/tests/table/__init__.py +11 -0
- perspective/tests/table/arrow/date32.arrow +0 -0
- perspective/tests/table/arrow/date64.arrow +0 -0
- perspective/tests/table/arrow/dict.arrow +0 -0
- perspective/tests/table/arrow/dict_update.arrow +0 -0
- perspective/tests/table/arrow/int_float_str.arrow +0 -0
- perspective/tests/table/arrow/int_float_str_file.arrow +0 -0
- perspective/tests/table/arrow/int_float_str_update.arrow +0 -0
- perspective/tests/table/object_sequence.py +402 -0
- perspective/tests/table/test_delete.py +124 -0
- perspective/tests/table/test_exception.py +53 -0
- perspective/tests/table/test_leaks.py +54 -0
- perspective/tests/table/test_ports.py +178 -0
- perspective/tests/table/test_remove.py +102 -0
- perspective/tests/table/test_table.py +612 -0
- perspective/tests/table/test_table_arrow.py +452 -0
- perspective/tests/table/test_table_datetime.py +2409 -0
- perspective/tests/table/test_table_infer.py +201 -0
- perspective/tests/table/test_table_limit.py +41 -0
- perspective/tests/table/test_table_numpy.py +1022 -0
- perspective/tests/table/test_table_pandas.py +1018 -0
- perspective/tests/table/test_to_arrow.py +417 -0
- perspective/tests/table/test_to_arrow_lz4.py +32 -0
- perspective/tests/table/test_to_format.py +1024 -0
- perspective/tests/table/test_update.py +545 -0
- perspective/tests/table/test_update_arrow.py +980 -0
- perspective/tests/table/test_update_pandas.py +211 -0
- perspective/tests/table/test_view.py +2234 -0
- perspective/tests/table/test_view_expression.py +1940 -0
- perspective/tests/viewer/__init__.py +11 -0
- perspective/tests/viewer/test_validate.py +69 -0
- perspective/tests/viewer/test_viewer.py +245 -0
- perspective/tests/widget/__init__.py +11 -0
- perspective/tests/widget/test_widget.py +278 -0
- perspective/tests/widget/test_widget_pandas.py +453 -0
- perspective/viewer/__init__.py +15 -0
- perspective/viewer/validate.py +22 -0
- perspective/viewer/viewer.py +331 -0
- perspective/viewer/viewer_traitlets.py +101 -0
- perspective/widget/__init__.py +16 -0
- perspective/widget/widget.py +269 -0
- perspective/windows-x86_64-libpsp.dll +0 -0
- perspective_python-3.0.0.data/data/share/jupyter/labextensions/@finos/perspective-jupyterlab/install.json +5 -0
- perspective_python-3.0.0.data/data/share/jupyter/labextensions/@finos/perspective-jupyterlab/package.json +80 -0
- perspective_python-3.0.0.data/data/share/jupyter/labextensions/@finos/perspective-jupyterlab/static/253.3b3f5e7f7f7cce48f967.js +18 -0
- perspective_python-3.0.0.data/data/share/jupyter/labextensions/@finos/perspective-jupyterlab/static/253.3b3f5e7f7f7cce48f967.js.LICENSE.txt +59 -0
- perspective_python-3.0.0.data/data/share/jupyter/labextensions/@finos/perspective-jupyterlab/static/905.bda9d13f81bc1d4190ff.js +1 -0
- perspective_python-3.0.0.data/data/share/jupyter/labextensions/@finos/perspective-jupyterlab/static/remoteEntry.23914ddf681f7cbe24fe.js +1 -0
- perspective_python-3.0.0.data/data/share/jupyter/labextensions/@finos/perspective-jupyterlab/static/style.js +4 -0
- perspective_python-3.0.0.data/data/share/jupyter/labextensions/@finos/perspective-jupyterlab/static/third-party-licenses.json +16 -0
- perspective_python-3.0.0.dist-info/METADATA +30 -0
- perspective_python-3.0.0.dist-info/RECORD +71 -0
- perspective_python-3.0.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
2
|
+
# ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
|
3
|
+
# ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
|
4
|
+
# ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
|
5
|
+
# ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
|
6
|
+
# ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
7
|
+
# ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
|
8
|
+
# ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
|
9
|
+
# ┃ This file is part of the Perspective library, distributed under the terms ┃
|
|
10
|
+
# ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
|
+
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
|
+
|
|
13
|
+
import pandas as pd
|
|
14
|
+
import numpy as np
|
|
15
|
+
import pytest
|
|
16
|
+
from perspective import PerspectiveWidget
|
|
17
|
+
import perspective as psp
|
|
18
|
+
|
|
19
|
+
client = psp.Server().new_local_client()
|
|
20
|
+
Table = client.table
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
pytest.skip(allow_module_level=True)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class TestWidgetPandas:
|
|
27
|
+
def test_widget_load_table_df(self, superstore):
|
|
28
|
+
table = Table(superstore)
|
|
29
|
+
widget = PerspectiveWidget(table)
|
|
30
|
+
assert widget.table.schema() == {
|
|
31
|
+
"index": "integer",
|
|
32
|
+
"Country": "string",
|
|
33
|
+
"Region": "string",
|
|
34
|
+
"Category": "string",
|
|
35
|
+
"City": "string",
|
|
36
|
+
"Customer ID": "string",
|
|
37
|
+
"Discount": "float",
|
|
38
|
+
"Order Date": "date",
|
|
39
|
+
"Order ID": "string",
|
|
40
|
+
"Postal Code": "string",
|
|
41
|
+
"Product ID": "string",
|
|
42
|
+
"Profit": "float",
|
|
43
|
+
"Quantity": "integer",
|
|
44
|
+
"Row ID": "integer",
|
|
45
|
+
"Sales": "integer",
|
|
46
|
+
"Segment": "string",
|
|
47
|
+
"Ship Date": "date",
|
|
48
|
+
"Ship Mode": "string",
|
|
49
|
+
"State": "string",
|
|
50
|
+
"Sub-Category": "string",
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
assert sorted(widget.columns) == sorted(
|
|
54
|
+
[
|
|
55
|
+
"index",
|
|
56
|
+
"Category",
|
|
57
|
+
"City",
|
|
58
|
+
"Country",
|
|
59
|
+
"Customer ID",
|
|
60
|
+
"Discount",
|
|
61
|
+
"Order Date",
|
|
62
|
+
"Order ID",
|
|
63
|
+
"Postal Code",
|
|
64
|
+
"Product ID",
|
|
65
|
+
"Profit",
|
|
66
|
+
"Quantity",
|
|
67
|
+
"Region",
|
|
68
|
+
"Row ID",
|
|
69
|
+
"Sales",
|
|
70
|
+
"Segment",
|
|
71
|
+
"Ship Date",
|
|
72
|
+
"Ship Mode",
|
|
73
|
+
"State",
|
|
74
|
+
"Sub-Category",
|
|
75
|
+
]
|
|
76
|
+
)
|
|
77
|
+
view = widget.table.view()
|
|
78
|
+
assert view.num_rows() == len(superstore)
|
|
79
|
+
assert view.num_columns() == len(superstore.columns) + 1 # index
|
|
80
|
+
|
|
81
|
+
def test_widget_load_data_df(self, superstore):
|
|
82
|
+
widget = PerspectiveWidget(superstore)
|
|
83
|
+
assert sorted(widget.columns) == sorted(
|
|
84
|
+
[
|
|
85
|
+
"index",
|
|
86
|
+
"Category",
|
|
87
|
+
"City",
|
|
88
|
+
"Country",
|
|
89
|
+
"Customer ID",
|
|
90
|
+
"Discount",
|
|
91
|
+
"Order Date",
|
|
92
|
+
"Order ID",
|
|
93
|
+
"Postal Code",
|
|
94
|
+
"Product ID",
|
|
95
|
+
"Profit",
|
|
96
|
+
"Quantity",
|
|
97
|
+
"Region",
|
|
98
|
+
"Row ID",
|
|
99
|
+
"Sales",
|
|
100
|
+
"Segment",
|
|
101
|
+
"Ship Date",
|
|
102
|
+
"Ship Mode",
|
|
103
|
+
"State",
|
|
104
|
+
"Sub-Category",
|
|
105
|
+
]
|
|
106
|
+
)
|
|
107
|
+
view = widget.table.view()
|
|
108
|
+
assert view.num_rows() == len(superstore)
|
|
109
|
+
assert view.num_columns() == 20
|
|
110
|
+
|
|
111
|
+
def test_widget_load_series(self, superstore):
|
|
112
|
+
series = pd.Series(superstore["Profit"].values, name="profit")
|
|
113
|
+
widget = PerspectiveWidget(series)
|
|
114
|
+
assert widget.table.schema() == {"index": "integer", "profit": "float"}
|
|
115
|
+
|
|
116
|
+
assert sorted(widget.columns) == sorted(["index", "profit"])
|
|
117
|
+
view = widget.table.view()
|
|
118
|
+
assert view.num_rows() == len(superstore)
|
|
119
|
+
assert view.num_columns() == 2
|
|
120
|
+
|
|
121
|
+
def test_widget_load_pivot_table(self, superstore):
|
|
122
|
+
pivot_table = pd.pivot_table(
|
|
123
|
+
superstore,
|
|
124
|
+
values="Discount",
|
|
125
|
+
index=["Country", "Region"],
|
|
126
|
+
columns=["Category", "Segment"],
|
|
127
|
+
)
|
|
128
|
+
widget = PerspectiveWidget(pivot_table)
|
|
129
|
+
assert widget.group_by == ["Country", "Region"]
|
|
130
|
+
assert widget.split_by == ["Category", "Segment"]
|
|
131
|
+
assert widget.columns == ["value"]
|
|
132
|
+
# table should host flattened data
|
|
133
|
+
view = widget.table.view()
|
|
134
|
+
assert view.num_rows() == 60
|
|
135
|
+
assert view.num_columns() == 6
|
|
136
|
+
|
|
137
|
+
def test_widget_load_pivot_table_with_user_pivots(self, superstore):
|
|
138
|
+
pivot_table = pd.pivot_table(
|
|
139
|
+
superstore,
|
|
140
|
+
values="Discount",
|
|
141
|
+
index=["Country", "Region"],
|
|
142
|
+
columns="Category",
|
|
143
|
+
)
|
|
144
|
+
widget = PerspectiveWidget(pivot_table, group_by=["Category", "Segment"])
|
|
145
|
+
assert widget.group_by == ["Category", "Segment"]
|
|
146
|
+
assert widget.split_by == []
|
|
147
|
+
assert widget.columns == [
|
|
148
|
+
"index",
|
|
149
|
+
"Country",
|
|
150
|
+
"Region",
|
|
151
|
+
"Financials",
|
|
152
|
+
"Industrials",
|
|
153
|
+
"Technology",
|
|
154
|
+
]
|
|
155
|
+
# table should host flattened data
|
|
156
|
+
view = widget.table.view()
|
|
157
|
+
assert view.num_rows() == 5
|
|
158
|
+
assert view.num_columns() == 6
|
|
159
|
+
|
|
160
|
+
def test_widget_load_group_by(self, superstore):
|
|
161
|
+
df_pivoted = superstore.set_index(["Country", "Region"])
|
|
162
|
+
widget = PerspectiveWidget(df_pivoted)
|
|
163
|
+
assert widget.group_by == ["Country", "Region"]
|
|
164
|
+
assert widget.split_by == []
|
|
165
|
+
assert sorted(widget.columns) == sorted(
|
|
166
|
+
[
|
|
167
|
+
"index",
|
|
168
|
+
"Category",
|
|
169
|
+
"Country",
|
|
170
|
+
"City",
|
|
171
|
+
"Customer ID",
|
|
172
|
+
"Discount",
|
|
173
|
+
"Order Date",
|
|
174
|
+
"Order ID",
|
|
175
|
+
"Postal Code",
|
|
176
|
+
"Product ID",
|
|
177
|
+
"Profit",
|
|
178
|
+
"Quantity",
|
|
179
|
+
"Region",
|
|
180
|
+
"Row ID",
|
|
181
|
+
"Sales",
|
|
182
|
+
"Segment",
|
|
183
|
+
"Ship Date",
|
|
184
|
+
"Ship Mode",
|
|
185
|
+
"State",
|
|
186
|
+
"Sub-Category",
|
|
187
|
+
]
|
|
188
|
+
)
|
|
189
|
+
assert widget.table.size() == 100
|
|
190
|
+
view = widget.table.view()
|
|
191
|
+
assert view.num_rows() == len(superstore)
|
|
192
|
+
assert view.num_columns() == len(superstore.columns) + 1 # index
|
|
193
|
+
|
|
194
|
+
def test_widget_load_group_by_with_user_pivots(self, superstore):
|
|
195
|
+
df_pivoted = superstore.set_index(["Country", "Region"])
|
|
196
|
+
widget = PerspectiveWidget(df_pivoted, group_by=["Category", "Segment"])
|
|
197
|
+
assert widget.group_by == ["Category", "Segment"]
|
|
198
|
+
assert widget.split_by == []
|
|
199
|
+
assert sorted(widget.columns) == sorted(
|
|
200
|
+
[
|
|
201
|
+
"index",
|
|
202
|
+
"Category",
|
|
203
|
+
"Country",
|
|
204
|
+
"City",
|
|
205
|
+
"Customer ID",
|
|
206
|
+
"Discount",
|
|
207
|
+
"Order Date",
|
|
208
|
+
"Order ID",
|
|
209
|
+
"Postal Code",
|
|
210
|
+
"Product ID",
|
|
211
|
+
"Profit",
|
|
212
|
+
"Quantity",
|
|
213
|
+
"Region",
|
|
214
|
+
"Row ID",
|
|
215
|
+
"Sales",
|
|
216
|
+
"Segment",
|
|
217
|
+
"Ship Date",
|
|
218
|
+
"Ship Mode",
|
|
219
|
+
"State",
|
|
220
|
+
"Sub-Category",
|
|
221
|
+
]
|
|
222
|
+
)
|
|
223
|
+
assert widget.table.size() == 100
|
|
224
|
+
view = widget.table.view()
|
|
225
|
+
assert view.num_rows() == len(superstore)
|
|
226
|
+
assert view.num_columns() == len(superstore.columns) + 1 # index
|
|
227
|
+
|
|
228
|
+
def test_widget_load_split_by(self, superstore):
|
|
229
|
+
arrays = [
|
|
230
|
+
np.array(
|
|
231
|
+
[
|
|
232
|
+
"bar",
|
|
233
|
+
"bar",
|
|
234
|
+
"bar",
|
|
235
|
+
"bar",
|
|
236
|
+
"baz",
|
|
237
|
+
"baz",
|
|
238
|
+
"baz",
|
|
239
|
+
"baz",
|
|
240
|
+
"foo",
|
|
241
|
+
"foo",
|
|
242
|
+
"foo",
|
|
243
|
+
"foo",
|
|
244
|
+
"qux",
|
|
245
|
+
"qux",
|
|
246
|
+
"qux",
|
|
247
|
+
"qux",
|
|
248
|
+
]
|
|
249
|
+
),
|
|
250
|
+
np.array(
|
|
251
|
+
[
|
|
252
|
+
"one",
|
|
253
|
+
"one",
|
|
254
|
+
"two",
|
|
255
|
+
"two",
|
|
256
|
+
"one",
|
|
257
|
+
"one",
|
|
258
|
+
"two",
|
|
259
|
+
"two",
|
|
260
|
+
"one",
|
|
261
|
+
"one",
|
|
262
|
+
"two",
|
|
263
|
+
"two",
|
|
264
|
+
"one",
|
|
265
|
+
"one",
|
|
266
|
+
"two",
|
|
267
|
+
"two",
|
|
268
|
+
]
|
|
269
|
+
),
|
|
270
|
+
np.array(
|
|
271
|
+
[
|
|
272
|
+
"X",
|
|
273
|
+
"Y",
|
|
274
|
+
"X",
|
|
275
|
+
"Y",
|
|
276
|
+
"X",
|
|
277
|
+
"Y",
|
|
278
|
+
"X",
|
|
279
|
+
"Y",
|
|
280
|
+
"X",
|
|
281
|
+
"Y",
|
|
282
|
+
"X",
|
|
283
|
+
"Y",
|
|
284
|
+
"X",
|
|
285
|
+
"Y",
|
|
286
|
+
"X",
|
|
287
|
+
"Y",
|
|
288
|
+
]
|
|
289
|
+
),
|
|
290
|
+
]
|
|
291
|
+
tuples = list(zip(*arrays))
|
|
292
|
+
index = pd.MultiIndex.from_tuples(tuples, names=["first", "second", "third"])
|
|
293
|
+
df_both = pd.DataFrame(
|
|
294
|
+
np.random.randn(3, 16), index=["A", "B", "C"], columns=index
|
|
295
|
+
)
|
|
296
|
+
widget = PerspectiveWidget(df_both)
|
|
297
|
+
assert widget.columns == ["value"]
|
|
298
|
+
assert widget.split_by == ["first", "second", "third"]
|
|
299
|
+
assert widget.group_by == ["index"]
|
|
300
|
+
|
|
301
|
+
def test_widget_load_split_by_preserve_user_settings(self, superstore):
|
|
302
|
+
arrays = [
|
|
303
|
+
np.array(
|
|
304
|
+
[
|
|
305
|
+
"bar",
|
|
306
|
+
"bar",
|
|
307
|
+
"bar",
|
|
308
|
+
"bar",
|
|
309
|
+
"baz",
|
|
310
|
+
"baz",
|
|
311
|
+
"baz",
|
|
312
|
+
"baz",
|
|
313
|
+
"foo",
|
|
314
|
+
"foo",
|
|
315
|
+
"foo",
|
|
316
|
+
"foo",
|
|
317
|
+
"qux",
|
|
318
|
+
"qux",
|
|
319
|
+
"qux",
|
|
320
|
+
"qux",
|
|
321
|
+
]
|
|
322
|
+
),
|
|
323
|
+
np.array(
|
|
324
|
+
[
|
|
325
|
+
"one",
|
|
326
|
+
"one",
|
|
327
|
+
"two",
|
|
328
|
+
"two",
|
|
329
|
+
"one",
|
|
330
|
+
"one",
|
|
331
|
+
"two",
|
|
332
|
+
"two",
|
|
333
|
+
"one",
|
|
334
|
+
"one",
|
|
335
|
+
"two",
|
|
336
|
+
"two",
|
|
337
|
+
"one",
|
|
338
|
+
"one",
|
|
339
|
+
"two",
|
|
340
|
+
"two",
|
|
341
|
+
]
|
|
342
|
+
),
|
|
343
|
+
np.array(
|
|
344
|
+
[
|
|
345
|
+
"X",
|
|
346
|
+
"Y",
|
|
347
|
+
"X",
|
|
348
|
+
"Y",
|
|
349
|
+
"X",
|
|
350
|
+
"Y",
|
|
351
|
+
"X",
|
|
352
|
+
"Y",
|
|
353
|
+
"X",
|
|
354
|
+
"Y",
|
|
355
|
+
"X",
|
|
356
|
+
"Y",
|
|
357
|
+
"X",
|
|
358
|
+
"Y",
|
|
359
|
+
"X",
|
|
360
|
+
"Y",
|
|
361
|
+
]
|
|
362
|
+
),
|
|
363
|
+
]
|
|
364
|
+
tuples = list(zip(*arrays))
|
|
365
|
+
index = pd.MultiIndex.from_tuples(tuples, names=["first", "second", "third"])
|
|
366
|
+
df_both = pd.DataFrame(
|
|
367
|
+
np.random.randn(3, 16), index=["A", "B", "C"], columns=index
|
|
368
|
+
)
|
|
369
|
+
widget = PerspectiveWidget(df_both, columns=["first", "third"])
|
|
370
|
+
assert widget.columns == ["first", "third"]
|
|
371
|
+
assert widget.split_by == ["first", "second", "third"]
|
|
372
|
+
assert widget.group_by == ["index"]
|
|
373
|
+
|
|
374
|
+
def test_pivottable_values_index(self, superstore):
|
|
375
|
+
arrays = {
|
|
376
|
+
"A": [
|
|
377
|
+
"bar",
|
|
378
|
+
"bar",
|
|
379
|
+
"bar",
|
|
380
|
+
"bar",
|
|
381
|
+
"baz",
|
|
382
|
+
"baz",
|
|
383
|
+
"baz",
|
|
384
|
+
"baz",
|
|
385
|
+
"foo",
|
|
386
|
+
"foo",
|
|
387
|
+
"foo",
|
|
388
|
+
"foo",
|
|
389
|
+
"qux",
|
|
390
|
+
"qux",
|
|
391
|
+
"qux",
|
|
392
|
+
"qux",
|
|
393
|
+
],
|
|
394
|
+
"B": [
|
|
395
|
+
"one",
|
|
396
|
+
"one",
|
|
397
|
+
"two",
|
|
398
|
+
"two",
|
|
399
|
+
"one",
|
|
400
|
+
"one",
|
|
401
|
+
"two",
|
|
402
|
+
"two",
|
|
403
|
+
"one",
|
|
404
|
+
"one",
|
|
405
|
+
"two",
|
|
406
|
+
"two",
|
|
407
|
+
"one",
|
|
408
|
+
"one",
|
|
409
|
+
"two",
|
|
410
|
+
"two",
|
|
411
|
+
],
|
|
412
|
+
"C": [
|
|
413
|
+
"X",
|
|
414
|
+
"Y",
|
|
415
|
+
"X",
|
|
416
|
+
"Y",
|
|
417
|
+
"X",
|
|
418
|
+
"Y",
|
|
419
|
+
"X",
|
|
420
|
+
"Y",
|
|
421
|
+
"X",
|
|
422
|
+
"Y",
|
|
423
|
+
"X",
|
|
424
|
+
"Y",
|
|
425
|
+
"X",
|
|
426
|
+
"Y",
|
|
427
|
+
"X",
|
|
428
|
+
"Y",
|
|
429
|
+
],
|
|
430
|
+
"D": np.arange(16),
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
df = pd.DataFrame(arrays)
|
|
434
|
+
df_pivot = df.pivot_table(
|
|
435
|
+
values=["D"], index=["A"], columns=["B", "C"], aggfunc={"D": "count"}
|
|
436
|
+
)
|
|
437
|
+
widget = PerspectiveWidget(df_pivot)
|
|
438
|
+
assert widget.columns == ["value"]
|
|
439
|
+
assert widget.split_by == ["B", "C"]
|
|
440
|
+
assert widget.group_by == ["A"]
|
|
441
|
+
|
|
442
|
+
def test_pivottable_multi_values(self, superstore):
|
|
443
|
+
pt = pd.pivot_table(
|
|
444
|
+
superstore,
|
|
445
|
+
values=["Discount", "Sales"],
|
|
446
|
+
index=["Country", "Region"],
|
|
447
|
+
aggfunc={"Discount": "count", "Sales": "sum"},
|
|
448
|
+
columns=["State", "Quantity"],
|
|
449
|
+
)
|
|
450
|
+
widget = PerspectiveWidget(pt)
|
|
451
|
+
assert widget.columns == ["Discount", "Sales"]
|
|
452
|
+
assert widget.split_by == ["State", "Quantity"]
|
|
453
|
+
assert widget.group_by == ["Country", "Region"]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
2
|
+
# ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
|
3
|
+
# ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
|
4
|
+
# ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
|
5
|
+
# ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
|
6
|
+
# ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
7
|
+
# ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
|
8
|
+
# ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
|
9
|
+
# ┃ This file is part of the Perspective library, distributed under the terms ┃
|
|
10
|
+
# ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
|
+
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
|
+
|
|
13
|
+
from .viewer import PerspectiveViewer
|
|
14
|
+
|
|
15
|
+
__all__ = ["PerspectiveViewer"]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
2
|
+
# ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
|
3
|
+
# ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
|
4
|
+
# ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
|
5
|
+
# ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
|
6
|
+
# ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
7
|
+
# ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
|
8
|
+
# ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
|
9
|
+
# ┃ This file is part of the Perspective library, distributed under the terms ┃
|
|
10
|
+
# ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
|
+
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def validate_version(version):
|
|
15
|
+
# basic semver of form \d+\.\d+\.\d+(\+.+)?
|
|
16
|
+
spl = version.split(".", 2)
|
|
17
|
+
return (
|
|
18
|
+
len(spl) == 3
|
|
19
|
+
and spl[0].isdigit()
|
|
20
|
+
and spl[1].isdigit()
|
|
21
|
+
and (spl[2].split("+")[0]).isdigit()
|
|
22
|
+
)
|