hvplot 0.9.3a1__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.
- hvplot/__init__.py +322 -0
- hvplot/_version.py +16 -0
- hvplot/backend_transforms.py +329 -0
- hvplot/converter.py +2855 -0
- hvplot/cudf.py +26 -0
- hvplot/dask.py +42 -0
- hvplot/data/crime.csv +56 -0
- hvplot/datasets.yaml +48 -0
- hvplot/fugue.py +64 -0
- hvplot/ibis.py +21 -0
- hvplot/intake.py +32 -0
- hvplot/interactive.py +968 -0
- hvplot/networkx.py +625 -0
- hvplot/pandas.py +30 -0
- hvplot/plotting/__init__.py +63 -0
- hvplot/plotting/andrews_curves.py +99 -0
- hvplot/plotting/core.py +2288 -0
- hvplot/plotting/lag_plot.py +34 -0
- hvplot/plotting/parallel_coordinates.py +85 -0
- hvplot/plotting/scatter_matrix.py +220 -0
- hvplot/polars.py +21 -0
- hvplot/sample_data.py +30 -0
- hvplot/streamz.py +21 -0
- hvplot/tests/__init__.py +0 -0
- hvplot/tests/conftest.py +44 -0
- hvplot/tests/data/README.md +5 -0
- hvplot/tests/data/RGB-red.byte.tif +0 -0
- hvplot/tests/plotting/__init__.py +0 -0
- hvplot/tests/plotting/testcore.py +108 -0
- hvplot/tests/plotting/testohlc.py +34 -0
- hvplot/tests/plotting/testscattermatrix.py +138 -0
- hvplot/tests/test_links.py +99 -0
- hvplot/tests/testbackend_transforms.py +89 -0
- hvplot/tests/testcharts.py +452 -0
- hvplot/tests/testfugue.py +46 -0
- hvplot/tests/testgeo.py +468 -0
- hvplot/tests/testgeowithoutgv.py +60 -0
- hvplot/tests/testgridplots.py +259 -0
- hvplot/tests/testhelp.py +75 -0
- hvplot/tests/testibis.py +17 -0
- hvplot/tests/testinteractive.py +1442 -0
- hvplot/tests/testnetworkx.py +26 -0
- hvplot/tests/testoperations.py +385 -0
- hvplot/tests/testoptions.py +596 -0
- hvplot/tests/testoverrides.py +74 -0
- hvplot/tests/testpanel.py +70 -0
- hvplot/tests/testpatch.py +135 -0
- hvplot/tests/testplotting.py +69 -0
- hvplot/tests/teststreaming.py +28 -0
- hvplot/tests/testtransforms.py +39 -0
- hvplot/tests/testui.py +383 -0
- hvplot/tests/testutil.py +378 -0
- hvplot/tests/util.py +82 -0
- hvplot/ui.py +1032 -0
- hvplot/util.py +677 -0
- hvplot/utilities.py +129 -0
- hvplot/xarray.py +62 -0
- hvplot-0.9.3a1.dist-info/LICENSE +29 -0
- hvplot-0.9.3a1.dist-info/METADATA +243 -0
- hvplot-0.9.3a1.dist-info/RECORD +63 -0
- hvplot-0.9.3a1.dist-info/WHEEL +5 -0
- hvplot-0.9.3a1.dist-info/entry_points.txt +2 -0
- hvplot-0.9.3a1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1442 @@
|
|
|
1
|
+
from packaging.version import Version
|
|
2
|
+
|
|
3
|
+
import holoviews as hv
|
|
4
|
+
import hvplot.pandas # noqa
|
|
5
|
+
import hvplot.xarray # noqa
|
|
6
|
+
import matplotlib
|
|
7
|
+
import numpy as np
|
|
8
|
+
import pandas as pd
|
|
9
|
+
import panel as pn
|
|
10
|
+
import pytest
|
|
11
|
+
import xarray as xr
|
|
12
|
+
|
|
13
|
+
from holoviews.util.transform import dim
|
|
14
|
+
from hvplot import bind
|
|
15
|
+
from hvplot.interactive import Interactive
|
|
16
|
+
from hvplot.tests.util import makeDataFrame, makeMixedDataFrame
|
|
17
|
+
from hvplot.xarray import XArrayInteractive
|
|
18
|
+
from hvplot.util import bokeh3, param2
|
|
19
|
+
|
|
20
|
+
is_bokeh2 = pytest.mark.skipif(bokeh3, reason='requires bokeh 2.x')
|
|
21
|
+
is_bokeh3 = pytest.mark.skipif(not bokeh3, reason='requires bokeh 3.x')
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@pytest.fixture(scope='module')
|
|
25
|
+
def series():
|
|
26
|
+
return pd.Series(np.arange(5.0), name='A')
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@pytest.fixture(scope='module')
|
|
30
|
+
def df():
|
|
31
|
+
return makeMixedDataFrame()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@pytest.fixture(scope='module')
|
|
35
|
+
def dataset():
|
|
36
|
+
return xr.tutorial.load_dataset('air_temperature')
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@pytest.fixture(scope='module')
|
|
40
|
+
def dataarray(dataset):
|
|
41
|
+
return dataset.air
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class CallCtxt:
|
|
45
|
+
def __init__(self, call_args, call_kwargs, **kwargs):
|
|
46
|
+
for k, v in kwargs.items():
|
|
47
|
+
if k in ['args', 'kwargs']:
|
|
48
|
+
raise ValueError("**kwargs passed to CallCtxt can't be named args or kwargs")
|
|
49
|
+
setattr(self, k, v)
|
|
50
|
+
self.args = call_args
|
|
51
|
+
self.kwargs = call_kwargs
|
|
52
|
+
|
|
53
|
+
def __repr__(self):
|
|
54
|
+
inner = ''
|
|
55
|
+
for attr in vars(self):
|
|
56
|
+
inner += f'{attr}={getattr(self, attr)!r}, '
|
|
57
|
+
return f'CallCtxt({inner}args={self.args!r}, kwargs={self.kwargs!r})'
|
|
58
|
+
|
|
59
|
+
def is_empty(self):
|
|
60
|
+
return not self.args and not self.kwargs
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class Spy:
|
|
64
|
+
def __init__(self):
|
|
65
|
+
self.count = 0
|
|
66
|
+
self.calls = {}
|
|
67
|
+
|
|
68
|
+
def __repr__(self):
|
|
69
|
+
return f'Spy(count={self.count!r}, calls={self.calls!r})'
|
|
70
|
+
|
|
71
|
+
def register_call(self, called_args, called_kwargs, **kwargs):
|
|
72
|
+
self.calls[self.count] = CallCtxt(called_args, called_kwargs, **kwargs)
|
|
73
|
+
self.count += 1
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@pytest.fixture
|
|
77
|
+
def clone_spy():
|
|
78
|
+
# In hindsight it might have been a better idea to spy __init__
|
|
79
|
+
# rather than _clone, to check exactly how new instances are created
|
|
80
|
+
# and not depend on the implementation of _clone.
|
|
81
|
+
spy = Spy()
|
|
82
|
+
|
|
83
|
+
_clone = Interactive._clone
|
|
84
|
+
|
|
85
|
+
def clone_bis(inst, *args, **kwargs):
|
|
86
|
+
cloned = _clone(inst, *args, **kwargs)
|
|
87
|
+
spy.register_call(args, kwargs, depth=cloned._depth)
|
|
88
|
+
return cloned
|
|
89
|
+
|
|
90
|
+
Interactive._clone = clone_bis
|
|
91
|
+
|
|
92
|
+
yield spy
|
|
93
|
+
|
|
94
|
+
Interactive._clone = _clone
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_spy(clone_spy, series):
|
|
98
|
+
si = Interactive(series)
|
|
99
|
+
si._clone()
|
|
100
|
+
|
|
101
|
+
assert clone_spy.count == 1
|
|
102
|
+
assert clone_spy.calls[0].is_empty()
|
|
103
|
+
|
|
104
|
+
si._clone(x='X')
|
|
105
|
+
|
|
106
|
+
assert clone_spy.count == 2
|
|
107
|
+
assert not clone_spy.calls[1].is_empty()
|
|
108
|
+
assert clone_spy.calls[1].kwargs == dict(x='X')
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_interactive_pandas_dataframe(df):
|
|
112
|
+
dfi = Interactive(df)
|
|
113
|
+
|
|
114
|
+
assert type(dfi) is Interactive
|
|
115
|
+
assert dfi._obj is df
|
|
116
|
+
assert dfi._fn is None
|
|
117
|
+
assert dfi._transform == dim('*')
|
|
118
|
+
assert dfi._method is None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def test_interactive_pandas_series(series):
|
|
122
|
+
si = Interactive(series)
|
|
123
|
+
|
|
124
|
+
assert type(si) is Interactive
|
|
125
|
+
assert si._obj is series
|
|
126
|
+
assert si._fn is None
|
|
127
|
+
assert si._transform == dim('*')
|
|
128
|
+
assert si._method is None
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def test_interactive_xarray_dataarray(dataarray):
|
|
132
|
+
dai = Interactive(dataarray)
|
|
133
|
+
|
|
134
|
+
assert type(dai) is XArrayInteractive
|
|
135
|
+
assert (dai._obj == dataarray).all()
|
|
136
|
+
assert dai._fn is None
|
|
137
|
+
assert dai._transform == dim('air')
|
|
138
|
+
assert dai._method is None
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def test_interactive_xarray_dataarray_no_name():
|
|
142
|
+
dataarray = xr.DataArray(np.random.rand(2, 2))
|
|
143
|
+
with pytest.raises(ValueError, match='Cannot use interactive API on DataArray without name'):
|
|
144
|
+
Interactive(dataarray)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def test_interactive_xarray_dataset(dataset):
|
|
148
|
+
dsi = Interactive(dataset)
|
|
149
|
+
|
|
150
|
+
assert type(dsi) is XArrayInteractive
|
|
151
|
+
assert dsi._obj is dataset
|
|
152
|
+
assert dsi._fn is None
|
|
153
|
+
assert dsi._transform == dim('*')
|
|
154
|
+
assert dsi._method is None
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def test_interactive_pandas_function(df):
|
|
158
|
+
select = pn.widgets.Select(options=list(df.columns))
|
|
159
|
+
|
|
160
|
+
def sel_col(col):
|
|
161
|
+
return df[col]
|
|
162
|
+
|
|
163
|
+
dfi = Interactive(bind(sel_col, select))
|
|
164
|
+
assert type(dfi) is Interactive
|
|
165
|
+
assert dfi._obj is df.A
|
|
166
|
+
assert isinstance(dfi._fn, pn.param.ParamFunction)
|
|
167
|
+
assert dfi._transform == dim('*')
|
|
168
|
+
assert dfi._method is None
|
|
169
|
+
|
|
170
|
+
select.value = 'B'
|
|
171
|
+
assert dfi._obj is df.B
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def test_interactive_xarray_function(dataset):
|
|
175
|
+
ds = dataset.copy()
|
|
176
|
+
ds['air2'] = ds.air * 2
|
|
177
|
+
|
|
178
|
+
select = pn.widgets.Select(options=list(ds))
|
|
179
|
+
|
|
180
|
+
def sel_col(sel):
|
|
181
|
+
return ds[sel]
|
|
182
|
+
|
|
183
|
+
dsi = Interactive(bind(sel_col, select))
|
|
184
|
+
|
|
185
|
+
assert type(dsi) is XArrayInteractive
|
|
186
|
+
assert isinstance(dsi._fn, pn.param.ParamFunction)
|
|
187
|
+
assert dsi._transform == dim('air')
|
|
188
|
+
assert dsi._method is None
|
|
189
|
+
|
|
190
|
+
select.value = 'air2'
|
|
191
|
+
assert (dsi._obj == ds.air2).all()
|
|
192
|
+
assert dsi._transform == dim('air2')
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def test_interactive_nested_widgets():
|
|
196
|
+
df = makeDataFrame()
|
|
197
|
+
w = pn.widgets.RadioButtonGroup(value='A', options=list('ABC'))
|
|
198
|
+
|
|
199
|
+
idf = Interactive(df)
|
|
200
|
+
pipeline = idf.groupby(['D', w]).mean()
|
|
201
|
+
ioutput = pipeline.panel().object().object
|
|
202
|
+
iw = pipeline.widgets()
|
|
203
|
+
|
|
204
|
+
output = df.groupby(['D', 'A']).mean()
|
|
205
|
+
|
|
206
|
+
pd.testing.assert_frame_equal(ioutput, output)
|
|
207
|
+
assert len(iw) == 1
|
|
208
|
+
assert iw[0] == w
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@pytest.mark.skipif(
|
|
212
|
+
Version(hv.__version__) < Version('1.15.1'),
|
|
213
|
+
reason='Needs holoviews 1.15.1',
|
|
214
|
+
)
|
|
215
|
+
def test_interactive_slice():
|
|
216
|
+
df = makeDataFrame()
|
|
217
|
+
w = pn.widgets.IntSlider(start=10, end=40)
|
|
218
|
+
|
|
219
|
+
idf = Interactive(df)
|
|
220
|
+
pipeline = idf.iloc[:w]
|
|
221
|
+
ioutput = pipeline.panel().object().object
|
|
222
|
+
iw = pipeline.widgets()
|
|
223
|
+
|
|
224
|
+
output = df.iloc[:10]
|
|
225
|
+
|
|
226
|
+
pd.testing.assert_frame_equal(ioutput, output)
|
|
227
|
+
assert len(iw) == 1
|
|
228
|
+
assert iw[0] == w
|
|
229
|
+
|
|
230
|
+
w.value = 15
|
|
231
|
+
ioutput = pipeline.panel().object().object
|
|
232
|
+
output = df.iloc[:15]
|
|
233
|
+
pd.testing.assert_frame_equal(ioutput, output)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def test_interactive_pandas_dataframe_hvplot_accessor(df):
|
|
237
|
+
dfi = df.interactive()
|
|
238
|
+
|
|
239
|
+
assert dfi.hvplot(kind='scatter')._transform == dfi.hvplot.scatter()._transform
|
|
240
|
+
|
|
241
|
+
with pytest.raises(TypeError):
|
|
242
|
+
dfi.hvplot.scatter(kind='area')
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def test_interactive_xarray_dataset_hvplot_accessor(dataarray):
|
|
246
|
+
dai = dataarray.interactive
|
|
247
|
+
|
|
248
|
+
assert dai.hvplot(kind='line')._transform == dai.hvplot.line()._transform
|
|
249
|
+
|
|
250
|
+
with pytest.raises(TypeError):
|
|
251
|
+
dai.hvplot.line(kind='area')
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def test_interactive_pandas_dataframe_hvplot_accessor_dmap(df):
|
|
255
|
+
dfi = df.interactive()
|
|
256
|
+
dfi = dfi.hvplot.line(y='A')
|
|
257
|
+
|
|
258
|
+
# TODO: Not sure about the logic
|
|
259
|
+
assert dfi._dmap is True
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def test_interactive_pandas_dataframe_hvplot_accessor_dmap_kind_widget(df):
|
|
263
|
+
w = pn.widgets.Select(options=['line', 'scatter'])
|
|
264
|
+
dfi = df.interactive()
|
|
265
|
+
dfi = dfi.hvplot(kind=w, y='A')
|
|
266
|
+
|
|
267
|
+
assert dfi._dmap is False
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def test_interactive_with_bound_function_calls():
|
|
271
|
+
df = pd.DataFrame({'species': [1, 1, 1, 2, 2, 2], 'sex': 3 * ['MALE', 'FEMALE']})
|
|
272
|
+
|
|
273
|
+
w_species = pn.widgets.Select(name='Species', options=[1, 2])
|
|
274
|
+
w_sex = pn.widgets.MultiSelect(name='Sex', value=['MALE'], options=['MALE', 'FEMALE'])
|
|
275
|
+
|
|
276
|
+
def load_data(species, watch=True):
|
|
277
|
+
if watch:
|
|
278
|
+
load_data.COUNT += 1
|
|
279
|
+
return df.loc[df['species'] == species]
|
|
280
|
+
|
|
281
|
+
load_data.COUNT = 0
|
|
282
|
+
|
|
283
|
+
# Setting up interactive with a function
|
|
284
|
+
dfi = bind(load_data, w_species).interactive()
|
|
285
|
+
dfi = dfi.loc[dfi['sex'].isin(w_sex)]
|
|
286
|
+
|
|
287
|
+
out = dfi.output()
|
|
288
|
+
|
|
289
|
+
assert isinstance(out, pn.param.ParamFunction)
|
|
290
|
+
assert isinstance(out._pane, pn.pane.DataFrame)
|
|
291
|
+
pd.testing.assert_frame_equal(
|
|
292
|
+
out._pane.object, load_data(w_species.value, watch=False).loc[df['sex'].isin(w_sex.value)]
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
(dfi.loc[dfi['sex'].isin(w_sex)])
|
|
296
|
+
assert load_data.COUNT == 1
|
|
297
|
+
|
|
298
|
+
w_species.value = 2
|
|
299
|
+
|
|
300
|
+
pd.testing.assert_frame_equal(
|
|
301
|
+
out._pane.object, load_data(w_species.value, watch=False).loc[df['sex'].isin(w_sex.value)]
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
assert load_data.COUNT == 2
|
|
305
|
+
|
|
306
|
+
dfi = dfi.head(1)
|
|
307
|
+
|
|
308
|
+
assert load_data.COUNT == 2
|
|
309
|
+
|
|
310
|
+
out = dfi.output()
|
|
311
|
+
|
|
312
|
+
pd.testing.assert_frame_equal(
|
|
313
|
+
out._pane.object,
|
|
314
|
+
load_data(w_species.value, watch=False).loc[df['sex'].isin(w_sex.value)].head(1),
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
w_species.value = 1
|
|
318
|
+
|
|
319
|
+
pd.testing.assert_frame_equal(
|
|
320
|
+
out._pane.object,
|
|
321
|
+
load_data(w_species.value, watch=False).loc[df['sex'].isin(w_sex.value)].head(1),
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
assert load_data.COUNT == 3
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def test_interactive_pandas_series_init(series, clone_spy):
|
|
328
|
+
si = Interactive(series)
|
|
329
|
+
|
|
330
|
+
assert clone_spy.count == 0
|
|
331
|
+
|
|
332
|
+
assert si._obj is series
|
|
333
|
+
assert repr(si._transform) == "dim('*')"
|
|
334
|
+
assert isinstance(si._current, pd.DataFrame)
|
|
335
|
+
pd.testing.assert_series_equal(si._current.A, series)
|
|
336
|
+
assert si._depth == 0
|
|
337
|
+
assert si._method is None
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def test_interactive_pandas_series_accessor(series, clone_spy):
|
|
341
|
+
si = series.interactive()
|
|
342
|
+
|
|
343
|
+
assert isinstance(si, Interactive)
|
|
344
|
+
assert clone_spy.count == 1
|
|
345
|
+
assert clone_spy.calls[0].is_empty()
|
|
346
|
+
|
|
347
|
+
assert si._obj is series
|
|
348
|
+
assert repr(si._transform) == "dim('*')"
|
|
349
|
+
assert isinstance(si._current, pd.DataFrame)
|
|
350
|
+
pd.testing.assert_series_equal(si._current.A, series)
|
|
351
|
+
assert si._depth == 1
|
|
352
|
+
assert si._method is None
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def test_interactive_pandas_series_operator(series, clone_spy):
|
|
356
|
+
si = Interactive(series)
|
|
357
|
+
si = si + 2
|
|
358
|
+
|
|
359
|
+
assert isinstance(si, Interactive)
|
|
360
|
+
assert isinstance(si._current, pd.DataFrame)
|
|
361
|
+
pd.testing.assert_series_equal(si._current.A, series + 2)
|
|
362
|
+
assert si._obj is series
|
|
363
|
+
assert repr(si._transform) == "dim('*').pd+2"
|
|
364
|
+
assert si._depth == 2
|
|
365
|
+
assert si._method is None
|
|
366
|
+
|
|
367
|
+
assert clone_spy.count == 2
|
|
368
|
+
|
|
369
|
+
# _clone(copy=True) in _resolve_accessor in _apply_operator
|
|
370
|
+
assert clone_spy.calls[0].depth == 1
|
|
371
|
+
assert not clone_spy.calls[0].args
|
|
372
|
+
assert clone_spy.calls[0].kwargs == {'copy': True}
|
|
373
|
+
|
|
374
|
+
# _clone in _apply_operator
|
|
375
|
+
assert clone_spy.calls[1].depth == 2
|
|
376
|
+
assert len(clone_spy.calls[1].args) == 1
|
|
377
|
+
assert repr(clone_spy.calls[1].args[0]) == "dim('*').pd+2"
|
|
378
|
+
assert not clone_spy.calls[1].kwargs
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def test_interactive_pandas_series_method_args(series, clone_spy):
|
|
382
|
+
si = Interactive(series)
|
|
383
|
+
si = si.head(2)
|
|
384
|
+
|
|
385
|
+
assert isinstance(si, Interactive)
|
|
386
|
+
assert isinstance(si._current, pd.DataFrame)
|
|
387
|
+
pd.testing.assert_series_equal(si._current.A, series.head(2))
|
|
388
|
+
assert si._obj is series
|
|
389
|
+
assert repr(si._transform) == "dim('*').pd.head(2)"
|
|
390
|
+
assert si._depth == 3
|
|
391
|
+
assert si._method is None
|
|
392
|
+
|
|
393
|
+
assert clone_spy.count == 3
|
|
394
|
+
|
|
395
|
+
# _clone in _resolve_accessor in __getattribute__(name='head')
|
|
396
|
+
assert clone_spy.calls[0].depth == 1
|
|
397
|
+
assert not clone_spy.calls[0].args
|
|
398
|
+
assert clone_spy.calls[0].kwargs == {'copy': True}
|
|
399
|
+
|
|
400
|
+
# 1st _clone(copy=True) in __call__
|
|
401
|
+
assert clone_spy.calls[1].depth == 2
|
|
402
|
+
assert not clone_spy.calls[1].args
|
|
403
|
+
assert clone_spy.calls[1].kwargs == {'copy': True}
|
|
404
|
+
|
|
405
|
+
# 2nd _clone in __call__
|
|
406
|
+
assert clone_spy.calls[2].depth == 3
|
|
407
|
+
assert len(clone_spy.calls[2].args) == 1
|
|
408
|
+
assert repr(clone_spy.calls[2].args[0]) == "dim('*').pd.head(2)"
|
|
409
|
+
assert clone_spy.calls[2].kwargs == {'plot': False}
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def test_interactive_pandas_series_method_kwargs(series, clone_spy):
|
|
413
|
+
si = Interactive(series)
|
|
414
|
+
si = si.head(n=2)
|
|
415
|
+
|
|
416
|
+
assert isinstance(si, Interactive)
|
|
417
|
+
assert isinstance(si._current, pd.DataFrame)
|
|
418
|
+
pd.testing.assert_series_equal(si._current.A, series.head(2))
|
|
419
|
+
assert si._obj is series
|
|
420
|
+
assert repr(si._transform) == "dim('*').pd.head(n=2)"
|
|
421
|
+
assert si._depth == 3
|
|
422
|
+
assert si._method is None
|
|
423
|
+
|
|
424
|
+
assert clone_spy.count == 3
|
|
425
|
+
|
|
426
|
+
# _clone in _resolve_accessor in __getattribute__(name='head')
|
|
427
|
+
assert clone_spy.calls[0].depth == 1
|
|
428
|
+
assert not clone_spy.calls[0].args
|
|
429
|
+
assert clone_spy.calls[0].kwargs == {'copy': True}
|
|
430
|
+
|
|
431
|
+
# 1st _clone(copy=True) in __call__
|
|
432
|
+
assert clone_spy.calls[1].depth == 2
|
|
433
|
+
assert not clone_spy.calls[1].args
|
|
434
|
+
assert clone_spy.calls[1].kwargs == {'copy': True}
|
|
435
|
+
|
|
436
|
+
# 2nd _clone in __call__
|
|
437
|
+
assert clone_spy.calls[2].depth == 3
|
|
438
|
+
assert len(clone_spy.calls[2].args) == 1
|
|
439
|
+
assert repr(clone_spy.calls[2].args[0]) == "dim('*').pd.head(n=2)"
|
|
440
|
+
assert clone_spy.calls[2].kwargs == {'plot': False}
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def test_interactive_pandas_series_method_not_called(series, clone_spy):
|
|
444
|
+
si = Interactive(series)
|
|
445
|
+
si = si.head
|
|
446
|
+
|
|
447
|
+
assert isinstance(si, Interactive)
|
|
448
|
+
assert isinstance(si._current, pd.DataFrame)
|
|
449
|
+
pd.testing.assert_series_equal(si._current.A, si._obj)
|
|
450
|
+
assert si._obj is series
|
|
451
|
+
assert repr(si._transform) == "dim('*')"
|
|
452
|
+
assert si._depth == 1
|
|
453
|
+
assert si._method == 'head'
|
|
454
|
+
|
|
455
|
+
assert clone_spy.count == 1
|
|
456
|
+
|
|
457
|
+
# _clone in _resolve_accessor in __getattribute__(name='head')
|
|
458
|
+
assert clone_spy.calls[0].depth == 1
|
|
459
|
+
assert not clone_spy.calls[0].args
|
|
460
|
+
assert clone_spy.calls[0].kwargs == {'copy': True}
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
def test_interactive_pandas_frame_attrib(df, clone_spy):
|
|
464
|
+
dfi = Interactive(df)
|
|
465
|
+
dfi = dfi.A
|
|
466
|
+
|
|
467
|
+
assert isinstance(dfi, Interactive)
|
|
468
|
+
assert isinstance(dfi._current, pd.DataFrame)
|
|
469
|
+
pd.testing.assert_frame_equal(dfi._current, dfi._obj)
|
|
470
|
+
assert dfi._obj is df
|
|
471
|
+
assert repr(dfi._transform) == "dim('*')"
|
|
472
|
+
assert dfi._depth == 1
|
|
473
|
+
assert dfi._method == 'A'
|
|
474
|
+
|
|
475
|
+
assert clone_spy.count == 1
|
|
476
|
+
|
|
477
|
+
# _clone in _resolve_accessor in __getattribute__(name='A')
|
|
478
|
+
assert clone_spy.calls[0].depth == 1
|
|
479
|
+
assert not clone_spy.calls[0].args
|
|
480
|
+
assert clone_spy.calls[0].kwargs == {'copy': True}
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
def test_interactive_pandas_series_operator_and_method(series, clone_spy):
|
|
484
|
+
si = Interactive(series)
|
|
485
|
+
|
|
486
|
+
si = (si + 2).head(2)
|
|
487
|
+
|
|
488
|
+
assert isinstance(si, Interactive)
|
|
489
|
+
assert isinstance(si._current, pd.DataFrame)
|
|
490
|
+
pd.testing.assert_series_equal(si._current.A, (series + 2).head(2))
|
|
491
|
+
assert si._obj is series
|
|
492
|
+
assert repr(si._transform) == "(dim('*').pd+2).head(2)"
|
|
493
|
+
assert si._depth == 5
|
|
494
|
+
assert si._method is None
|
|
495
|
+
|
|
496
|
+
assert clone_spy.count == 5
|
|
497
|
+
|
|
498
|
+
# _clone(copy=True) in _resolve_accessor in _apply_operator
|
|
499
|
+
assert clone_spy.calls[0].depth == 1
|
|
500
|
+
assert not clone_spy.calls[0].args
|
|
501
|
+
assert clone_spy.calls[0].kwargs == {'copy': True}
|
|
502
|
+
|
|
503
|
+
# _clone in _apply_operator
|
|
504
|
+
assert clone_spy.calls[1].depth == 2
|
|
505
|
+
assert len(clone_spy.calls[1].args) == 1
|
|
506
|
+
assert repr(clone_spy.calls[1].args[0]) == "dim('*').pd+2"
|
|
507
|
+
assert not clone_spy.calls[1].kwargs
|
|
508
|
+
|
|
509
|
+
# _clone in _resolve_accessor in __getattribute__(name='head')
|
|
510
|
+
assert clone_spy.calls[2].depth == 3
|
|
511
|
+
assert not clone_spy.calls[2].args
|
|
512
|
+
assert clone_spy.calls[2].kwargs == {'copy': True}
|
|
513
|
+
|
|
514
|
+
# 1st _clone(copy=True) in __call__
|
|
515
|
+
assert clone_spy.calls[3].depth == 4
|
|
516
|
+
assert not clone_spy.calls[3].args
|
|
517
|
+
assert clone_spy.calls[3].kwargs == {'copy': True}
|
|
518
|
+
|
|
519
|
+
# 2nd _clone in __call__
|
|
520
|
+
assert clone_spy.calls[4].depth == 5
|
|
521
|
+
assert len(clone_spy.calls[4].args) == 1
|
|
522
|
+
assert repr(clone_spy.calls[4].args[0]) == "(dim('*').pd+2).head(2)"
|
|
523
|
+
assert clone_spy.calls[4].kwargs == {'plot': False}
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def test_interactive_pandas_series_operator_widget(series):
|
|
527
|
+
w = pn.widgets.FloatSlider(value=2.0, start=1.0, end=5.0)
|
|
528
|
+
|
|
529
|
+
si = Interactive(series)
|
|
530
|
+
|
|
531
|
+
si = si + w
|
|
532
|
+
|
|
533
|
+
assert isinstance(si, Interactive)
|
|
534
|
+
assert isinstance(si._current, pd.DataFrame)
|
|
535
|
+
pd.testing.assert_series_equal(si._current.A, series + w.value)
|
|
536
|
+
assert si._obj is series
|
|
537
|
+
assert repr(si._transform) == "dim('*').pd+FloatSlider(end=5.0, start=1.0, value=2.0)"
|
|
538
|
+
assert si._depth == 2
|
|
539
|
+
assert si._method is None
|
|
540
|
+
|
|
541
|
+
assert len(si._params) == 1
|
|
542
|
+
assert si._params[0] is w.param.value
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
def test_interactive_pandas_series_method_widget(series):
|
|
546
|
+
w = pn.widgets.IntSlider(value=2, start=1, end=5)
|
|
547
|
+
|
|
548
|
+
si = Interactive(series)
|
|
549
|
+
|
|
550
|
+
si = si.head(w)
|
|
551
|
+
|
|
552
|
+
assert isinstance(si, Interactive)
|
|
553
|
+
assert isinstance(si._current, pd.DataFrame)
|
|
554
|
+
pd.testing.assert_series_equal(si._current.A, series.head(w.value))
|
|
555
|
+
assert si._obj is series
|
|
556
|
+
assert repr(si._transform) == "dim('*').pd.head(IntSlider(end=5, start=1, value=2))"
|
|
557
|
+
assert si._depth == 3
|
|
558
|
+
assert si._method is None
|
|
559
|
+
|
|
560
|
+
assert len(si._params) == 1
|
|
561
|
+
assert si._params[0] is w.param.value
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
def test_interactive_pandas_series_operator_and_method_widget(series):
|
|
565
|
+
w1 = pn.widgets.FloatSlider(value=2.0, start=1.0, end=5.0)
|
|
566
|
+
w2 = pn.widgets.IntSlider(value=2, start=1, end=5)
|
|
567
|
+
|
|
568
|
+
si = Interactive(series)
|
|
569
|
+
|
|
570
|
+
si = (si + w1).head(w2)
|
|
571
|
+
|
|
572
|
+
assert isinstance(si, Interactive)
|
|
573
|
+
assert isinstance(si._current, pd.DataFrame)
|
|
574
|
+
pd.testing.assert_series_equal(si._current.A, (series + w1.value).head(w2.value))
|
|
575
|
+
assert si._obj is series
|
|
576
|
+
assert (
|
|
577
|
+
repr(si._transform)
|
|
578
|
+
== "(dim('*').pd+FloatSlider(end=5.0, start=1.0, value=2.0)).head(IntSlider(end=5, start=1, value=2))"
|
|
579
|
+
)
|
|
580
|
+
assert si._depth == 5
|
|
581
|
+
assert si._method is None
|
|
582
|
+
|
|
583
|
+
assert len(si._params) == 2
|
|
584
|
+
assert si._params[0] is w1.param.value
|
|
585
|
+
assert si._params[1] is w2.param.value
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
def test_interactive_pandas_series_operator_ipywidgets(series):
|
|
589
|
+
ipywidgets = pytest.importorskip('ipywidgets')
|
|
590
|
+
|
|
591
|
+
w = ipywidgets.FloatSlider(value=2.0, min=1.0, max=5.0)
|
|
592
|
+
|
|
593
|
+
si = Interactive(series)
|
|
594
|
+
|
|
595
|
+
si = si + w
|
|
596
|
+
|
|
597
|
+
assert isinstance(si, Interactive)
|
|
598
|
+
assert isinstance(si._current, pd.DataFrame)
|
|
599
|
+
pd.testing.assert_series_equal(si._current.A, series + w.value)
|
|
600
|
+
assert si._obj is series
|
|
601
|
+
assert repr(si._transform) == "dim('*').pd+FloatSlider(value=2.0, max=5.0, min=1.0)"
|
|
602
|
+
assert si._depth == 2
|
|
603
|
+
assert si._method is None
|
|
604
|
+
|
|
605
|
+
# TODO: Isn't that a bug?
|
|
606
|
+
assert len(si._params) == 0
|
|
607
|
+
|
|
608
|
+
widgets = si.widgets()
|
|
609
|
+
|
|
610
|
+
assert isinstance(widgets, pn.Column)
|
|
611
|
+
assert len(widgets) == 1
|
|
612
|
+
assert isinstance(widgets[0], pn.pane.IPyWidget)
|
|
613
|
+
assert widgets[0].object is w
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
def test_interactive_pandas_series_operator_out_widgets(series):
|
|
617
|
+
w = pn.widgets.FloatSlider(value=2.0, start=1.0, end=5.0)
|
|
618
|
+
si = Interactive(series)
|
|
619
|
+
si = si + w
|
|
620
|
+
|
|
621
|
+
widgets = si.widgets()
|
|
622
|
+
|
|
623
|
+
assert isinstance(widgets, pn.Column)
|
|
624
|
+
assert len(widgets) == 1
|
|
625
|
+
assert widgets[0] is w
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
def test_interactive_pandas_series_method_out_widgets(series):
|
|
629
|
+
w = pn.widgets.IntSlider(value=2, start=1, end=5)
|
|
630
|
+
si = Interactive(series)
|
|
631
|
+
si = si.head(w)
|
|
632
|
+
|
|
633
|
+
widgets = si.widgets()
|
|
634
|
+
|
|
635
|
+
assert isinstance(widgets, pn.Column)
|
|
636
|
+
assert len(widgets) == 1
|
|
637
|
+
assert widgets[0] is w
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
def test_interactive_pandas_series_operator_and_method_out_widgets(series):
|
|
641
|
+
w1 = pn.widgets.FloatSlider(value=2.0, start=1.0, end=5.0)
|
|
642
|
+
w2 = pn.widgets.IntSlider(value=2, start=1, end=5)
|
|
643
|
+
si = Interactive(series)
|
|
644
|
+
|
|
645
|
+
si = (si + w1).head(w2)
|
|
646
|
+
|
|
647
|
+
widgets = si.widgets()
|
|
648
|
+
|
|
649
|
+
assert isinstance(widgets, pn.Column)
|
|
650
|
+
assert len(widgets) == 2
|
|
651
|
+
assert widgets[0] is w1
|
|
652
|
+
assert widgets[1] is w2
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
def test_interactive_pandas_frame_bind_out_widgets(df):
|
|
656
|
+
select = pn.widgets.Select(options=list(df.columns))
|
|
657
|
+
|
|
658
|
+
def sel_col(col):
|
|
659
|
+
return df[col]
|
|
660
|
+
|
|
661
|
+
dfi = Interactive(bind(sel_col, select))
|
|
662
|
+
|
|
663
|
+
widgets = dfi.widgets()
|
|
664
|
+
|
|
665
|
+
assert isinstance(widgets, pn.Column)
|
|
666
|
+
assert len(widgets) == 1
|
|
667
|
+
assert widgets[0] is select
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
def test_interactive_pandas_frame_bind_operator_out_widgets(df):
|
|
671
|
+
select = pn.widgets.Select(value='A', options=list(df.columns))
|
|
672
|
+
|
|
673
|
+
def sel_col(col):
|
|
674
|
+
return df[col]
|
|
675
|
+
|
|
676
|
+
dfi = Interactive(bind(sel_col, select))
|
|
677
|
+
|
|
678
|
+
w = pn.widgets.FloatSlider(value=2.0, start=1.0, end=5.0)
|
|
679
|
+
dfi = dfi + w
|
|
680
|
+
|
|
681
|
+
widgets = dfi.widgets()
|
|
682
|
+
|
|
683
|
+
assert isinstance(widgets, pn.Column)
|
|
684
|
+
assert len(widgets) == 2
|
|
685
|
+
assert widgets[0] is select
|
|
686
|
+
assert widgets[1] is w
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
def test_interactive_reevaluate_uses_cached_value(series):
|
|
690
|
+
w = pn.widgets.FloatSlider(value=2.0, start=1.0, end=5.0)
|
|
691
|
+
si = Interactive(series)
|
|
692
|
+
si = si + w
|
|
693
|
+
|
|
694
|
+
w.value = 3.0
|
|
695
|
+
assert repr(si._transform) == "dim('*').pd+FloatSlider(end=5.0, start=1.0, value=3.0)"
|
|
696
|
+
|
|
697
|
+
assert si._callback().object is si._callback().object
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
def test_interactive_pandas_series_operator_widget_update(series):
|
|
701
|
+
w = pn.widgets.FloatSlider(value=2.0, start=1.0, end=5.0)
|
|
702
|
+
si = Interactive(series)
|
|
703
|
+
si = si + w
|
|
704
|
+
|
|
705
|
+
w.value = 3.0
|
|
706
|
+
assert repr(si._transform) == "dim('*').pd+FloatSlider(end=5.0, start=1.0, value=3.0)"
|
|
707
|
+
|
|
708
|
+
out = si._callback()
|
|
709
|
+
assert out.object is si.eval()
|
|
710
|
+
assert isinstance(out, pn.pane.DataFrame)
|
|
711
|
+
pd.testing.assert_series_equal(out.object.A, series + 3.0)
|
|
712
|
+
|
|
713
|
+
|
|
714
|
+
def test_interactive_pandas_series_method_widget_update(series):
|
|
715
|
+
w = pn.widgets.IntSlider(value=2, start=1, end=5)
|
|
716
|
+
si = Interactive(series)
|
|
717
|
+
si = si.head(w)
|
|
718
|
+
|
|
719
|
+
w.value = 3
|
|
720
|
+
assert repr(si._transform) == "dim('*').pd.head(IntSlider(end=5, start=1, value=3))"
|
|
721
|
+
|
|
722
|
+
out = si._callback()
|
|
723
|
+
assert out.object is si.eval()
|
|
724
|
+
assert isinstance(out, pn.pane.DataFrame)
|
|
725
|
+
pd.testing.assert_series_equal(out.object.A, series.head(3))
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
def test_interactive_pandas_series_operator_and_method_widget_update(series):
|
|
729
|
+
w1 = pn.widgets.FloatSlider(value=2.0, start=1.0, end=5.0)
|
|
730
|
+
w2 = pn.widgets.IntSlider(value=2, start=1, end=5)
|
|
731
|
+
si = Interactive(series)
|
|
732
|
+
si = (si + w1).head(w2)
|
|
733
|
+
|
|
734
|
+
w1.value = 3.0
|
|
735
|
+
w2.value = 3
|
|
736
|
+
|
|
737
|
+
assert (
|
|
738
|
+
repr(si._transform)
|
|
739
|
+
== "(dim('*').pd+FloatSlider(end=5.0, start=1.0, value=3.0)).head(IntSlider(end=5, start=1, value=3))"
|
|
740
|
+
)
|
|
741
|
+
|
|
742
|
+
out = si._callback()
|
|
743
|
+
assert out.object is si.eval()
|
|
744
|
+
assert isinstance(out, pn.pane.DataFrame)
|
|
745
|
+
pd.testing.assert_series_equal(out.object.A, (series + 3.0).head(3))
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
def test_interactive_pandas_frame_loc(df):
|
|
749
|
+
dfi = Interactive(df)
|
|
750
|
+
|
|
751
|
+
dfi = dfi.loc[:, 'A']
|
|
752
|
+
|
|
753
|
+
assert isinstance(dfi, Interactive)
|
|
754
|
+
|
|
755
|
+
assert dfi._obj is df
|
|
756
|
+
assert isinstance(dfi._current, pd.Series)
|
|
757
|
+
pd.testing.assert_series_equal(dfi._current, df.loc[:, 'A'])
|
|
758
|
+
assert repr(dfi._transform) == "dim('*').pd.loc, getitem, (slice(None, None, None), 'A')"
|
|
759
|
+
assert dfi._depth == 3
|
|
760
|
+
assert dfi._method is None
|
|
761
|
+
|
|
762
|
+
|
|
763
|
+
def test_interactive_pandas_frame_filtering(df, clone_spy):
|
|
764
|
+
dfi = Interactive(df)
|
|
765
|
+
|
|
766
|
+
dfi = dfi[dfi.A > 1]
|
|
767
|
+
|
|
768
|
+
assert isinstance(dfi, Interactive)
|
|
769
|
+
|
|
770
|
+
assert dfi._obj is df
|
|
771
|
+
assert isinstance(dfi._current, pd.DataFrame)
|
|
772
|
+
pd.testing.assert_frame_equal(dfi._current, df[df.A > 1])
|
|
773
|
+
assert repr(dfi._transform) == "dim('*', getitem, dim('*').pd.A)>1"
|
|
774
|
+
# The depth of that Interactive instance is 2 because the last part of
|
|
775
|
+
# the chain executed, i.e. dfi[], leads to two clones being created,
|
|
776
|
+
# incrementing the _depth up to 2.
|
|
777
|
+
assert dfi._depth == 2
|
|
778
|
+
assert dfi._method is None
|
|
779
|
+
|
|
780
|
+
assert clone_spy.count == 5
|
|
781
|
+
|
|
782
|
+
# _clone(True) in _resolve_accessor in __getattribute__(name='A')
|
|
783
|
+
assert clone_spy.calls[0].depth == 1
|
|
784
|
+
assert not clone_spy.calls[0].args
|
|
785
|
+
assert clone_spy.calls[0].kwargs == {'copy': True}
|
|
786
|
+
|
|
787
|
+
# _clone in _resolve_accessor in _apply_operator(__gt__)
|
|
788
|
+
assert clone_spy.calls[1].depth == 2
|
|
789
|
+
assert len(clone_spy.calls[1].args) == 1
|
|
790
|
+
assert repr(clone_spy.calls[1].args[0]) == "dim('*').pd.A()"
|
|
791
|
+
assert clone_spy.calls[1].kwargs == {'inherit_kwargs': {}}
|
|
792
|
+
|
|
793
|
+
# _clone in _apply_operator(__gt__)
|
|
794
|
+
assert clone_spy.calls[2].depth == 3
|
|
795
|
+
assert len(clone_spy.calls[2].args) == 1
|
|
796
|
+
assert repr(clone_spy.calls[2].args[0]) == "(dim('*').pd.A())>1"
|
|
797
|
+
assert not clone_spy.calls[2].kwargs
|
|
798
|
+
|
|
799
|
+
# _clone(True) in _resolve_accessor in _apply_operator(getitem)
|
|
800
|
+
assert clone_spy.calls[3].depth == 1
|
|
801
|
+
assert not clone_spy.calls[3].args
|
|
802
|
+
assert clone_spy.calls[3].kwargs == {'copy': True}
|
|
803
|
+
|
|
804
|
+
# _clone in _apply_operator(getitem)
|
|
805
|
+
assert clone_spy.calls[4].depth == 2
|
|
806
|
+
assert len(clone_spy.calls[4].args) == 1
|
|
807
|
+
assert repr(clone_spy.calls[4].args[0]) == "dim('*', getitem, (dim('*').pd.A())>1)"
|
|
808
|
+
assert not clone_spy.calls[4].kwargs
|
|
809
|
+
|
|
810
|
+
|
|
811
|
+
def test_interactive_pandas_frame_chained_attrs(df, clone_spy):
|
|
812
|
+
dfi = Interactive(df)
|
|
813
|
+
|
|
814
|
+
dfi = dfi.A.max()
|
|
815
|
+
|
|
816
|
+
assert isinstance(dfi, Interactive)
|
|
817
|
+
|
|
818
|
+
assert dfi._obj is df
|
|
819
|
+
assert isinstance(dfi._current, float)
|
|
820
|
+
assert dfi._current == pytest.approx(df.A.max())
|
|
821
|
+
# This is a weird repr! Bug?
|
|
822
|
+
assert repr(dfi._transform) == "dim('*').pd.A).max("
|
|
823
|
+
assert dfi._depth == 4
|
|
824
|
+
assert dfi._method is None
|
|
825
|
+
|
|
826
|
+
assert clone_spy.count == 4
|
|
827
|
+
|
|
828
|
+
# _clone(True) in _resolve_accessor in __getattribute__(name='A')
|
|
829
|
+
assert clone_spy.calls[0].depth == 1
|
|
830
|
+
assert not clone_spy.calls[0].args
|
|
831
|
+
assert clone_spy.calls[0].kwargs == {'copy': True}
|
|
832
|
+
|
|
833
|
+
# _clone(True) in _resolve_accessor in __getattribute__(name='max')
|
|
834
|
+
assert clone_spy.calls[1].depth == 2
|
|
835
|
+
assert len(clone_spy.calls[1].args) == 1
|
|
836
|
+
assert repr(clone_spy.calls[1].args[0]) == "dim('*').pd.A()"
|
|
837
|
+
assert clone_spy.calls[1].kwargs == {'inherit_kwargs': {}}
|
|
838
|
+
|
|
839
|
+
# 1st _clone(copy=True) in __call__
|
|
840
|
+
assert clone_spy.calls[2].depth == 3
|
|
841
|
+
assert not clone_spy.calls[2].args
|
|
842
|
+
assert clone_spy.calls[2].kwargs == {'copy': True}
|
|
843
|
+
|
|
844
|
+
# 2nd _clone in __call__
|
|
845
|
+
assert clone_spy.calls[3].depth == 4
|
|
846
|
+
assert len(clone_spy.calls[3].args) == 1
|
|
847
|
+
assert repr(clone_spy.calls[3].args[0]) == "(dim('*').pd.A()).max()"
|
|
848
|
+
assert clone_spy.calls[3].kwargs == {'plot': False}
|
|
849
|
+
|
|
850
|
+
|
|
851
|
+
def test_interactive_pandas_out_repr(series):
|
|
852
|
+
si = Interactive(series)
|
|
853
|
+
si = si.max()
|
|
854
|
+
|
|
855
|
+
assert isinstance(si, Interactive)
|
|
856
|
+
assert isinstance(si._current, pd.Series)
|
|
857
|
+
assert si._current.A == pytest.approx(series.max())
|
|
858
|
+
assert si._obj is series
|
|
859
|
+
assert repr(si._transform) == "dim('*').pd.max()"
|
|
860
|
+
# One _clone from _resolve_accessor, two from _clone
|
|
861
|
+
assert si._depth == 3
|
|
862
|
+
assert si._method is None
|
|
863
|
+
|
|
864
|
+
# Equivalent to eval
|
|
865
|
+
out = si._callback()
|
|
866
|
+
|
|
867
|
+
assert isinstance(out, pd.Series)
|
|
868
|
+
assert out.A == pytest.approx(series.max())
|
|
869
|
+
|
|
870
|
+
|
|
871
|
+
def test_interactive_xarray_dataarray_out_repr(dataarray):
|
|
872
|
+
dai = Interactive(dataarray)
|
|
873
|
+
|
|
874
|
+
assert isinstance(dai._current, xr.DataArray)
|
|
875
|
+
assert dai._obj is dataarray
|
|
876
|
+
assert repr(dai._transform) == "dim('air')"
|
|
877
|
+
assert dai._depth == 0
|
|
878
|
+
assert dai._method is None
|
|
879
|
+
|
|
880
|
+
# Equivalent to eval
|
|
881
|
+
out = dai._callback()
|
|
882
|
+
|
|
883
|
+
assert isinstance(out, xr.DataArray)
|
|
884
|
+
|
|
885
|
+
|
|
886
|
+
def test_interactive_pandas_out_frame(series):
|
|
887
|
+
si = Interactive(series)
|
|
888
|
+
si = si.head(2)
|
|
889
|
+
|
|
890
|
+
assert isinstance(si, Interactive)
|
|
891
|
+
assert isinstance(si._current, pd.DataFrame)
|
|
892
|
+
pd.testing.assert_series_equal(si._current.A, series.head(2))
|
|
893
|
+
assert si._obj is series
|
|
894
|
+
assert repr(si._transform) == "dim('*').pd.head(2)"
|
|
895
|
+
assert si._depth == 3
|
|
896
|
+
assert si._method is None
|
|
897
|
+
|
|
898
|
+
# Equivalent to eval
|
|
899
|
+
out = si._callback()
|
|
900
|
+
|
|
901
|
+
assert isinstance(out, pn.pane.DataFrame)
|
|
902
|
+
pd.testing.assert_frame_equal(out.object, si._current)
|
|
903
|
+
|
|
904
|
+
|
|
905
|
+
def test_interactive_pandas_out_frame_max_rows(series):
|
|
906
|
+
si = Interactive(series, max_rows=5)
|
|
907
|
+
si = si.head(2)
|
|
908
|
+
|
|
909
|
+
# Equivalent to eval
|
|
910
|
+
out = si._callback()
|
|
911
|
+
|
|
912
|
+
assert isinstance(out, pn.pane.DataFrame)
|
|
913
|
+
assert out.max_rows == 5
|
|
914
|
+
|
|
915
|
+
|
|
916
|
+
def test_interactive_pandas_out_frame_max_rows_accessor_called(series):
|
|
917
|
+
si = series.interactive(max_rows=5)
|
|
918
|
+
si = si.head(2)
|
|
919
|
+
|
|
920
|
+
# Equivalent to eval
|
|
921
|
+
out = si._callback()
|
|
922
|
+
|
|
923
|
+
assert isinstance(out, pn.pane.DataFrame)
|
|
924
|
+
assert out.max_rows == 5
|
|
925
|
+
|
|
926
|
+
|
|
927
|
+
def test_interactive_pandas_out_frame_kwargs(series):
|
|
928
|
+
si = Interactive(series, width=111)
|
|
929
|
+
si = si.head(2)
|
|
930
|
+
|
|
931
|
+
# Equivalent to eval
|
|
932
|
+
out = si._callback()
|
|
933
|
+
|
|
934
|
+
assert isinstance(out, pn.pane.DataFrame)
|
|
935
|
+
assert out.width == 111
|
|
936
|
+
|
|
937
|
+
|
|
938
|
+
def test_interactive_pandas_out_frame_kwargs_accessor_called(series):
|
|
939
|
+
si = series.interactive(width=111)
|
|
940
|
+
si = si.head(2)
|
|
941
|
+
|
|
942
|
+
# Equivalent to eval
|
|
943
|
+
out = si._callback()
|
|
944
|
+
|
|
945
|
+
assert isinstance(out, pn.pane.DataFrame)
|
|
946
|
+
assert out.width == 111
|
|
947
|
+
|
|
948
|
+
|
|
949
|
+
def test_interactive_pandas_out_frame_attrib(df):
|
|
950
|
+
dfi = Interactive(df)
|
|
951
|
+
dfi = dfi.A
|
|
952
|
+
|
|
953
|
+
assert dfi._method == 'A'
|
|
954
|
+
|
|
955
|
+
# Equivalent to eval
|
|
956
|
+
out = dfi._callback()
|
|
957
|
+
|
|
958
|
+
# In that case the default behavior is to return the object transformed
|
|
959
|
+
# and to which _method is applied
|
|
960
|
+
assert isinstance(out, pd.Series)
|
|
961
|
+
pd.testing.assert_series_equal(df.A, out)
|
|
962
|
+
|
|
963
|
+
|
|
964
|
+
@pytest.mark.parametrize(
|
|
965
|
+
'op',
|
|
966
|
+
[
|
|
967
|
+
'-', # __neg__
|
|
968
|
+
# Doesn't any of the supported data implement __not__?
|
|
969
|
+
# e.g. `not series` raises an error.
|
|
970
|
+
# 'not', # __not__
|
|
971
|
+
'+', # _pos__
|
|
972
|
+
],
|
|
973
|
+
)
|
|
974
|
+
def test_interactive_pandas_series_operator_unary(series, op):
|
|
975
|
+
if op == '~':
|
|
976
|
+
series = pd.Series([True, False, True], name='A')
|
|
977
|
+
si = Interactive(series)
|
|
978
|
+
si = eval(f'{op} si')
|
|
979
|
+
|
|
980
|
+
assert isinstance(si, Interactive)
|
|
981
|
+
assert isinstance(si._current, pd.DataFrame)
|
|
982
|
+
pd.testing.assert_series_equal(si._current.A, eval(f'{op} series'))
|
|
983
|
+
assert si._obj is series
|
|
984
|
+
assert repr(si._transform) == f"{op}dim('*')"
|
|
985
|
+
assert si._depth == 2
|
|
986
|
+
assert si._method is None
|
|
987
|
+
|
|
988
|
+
|
|
989
|
+
def test_interactive_pandas_series_operator_unary_invert(): # __invert__
|
|
990
|
+
series = pd.Series([True, False, True], name='A')
|
|
991
|
+
si = Interactive(series)
|
|
992
|
+
si = ~si
|
|
993
|
+
|
|
994
|
+
assert isinstance(si, Interactive)
|
|
995
|
+
assert isinstance(si._current, pd.DataFrame)
|
|
996
|
+
pd.testing.assert_series_equal(si._current.A, ~series)
|
|
997
|
+
assert si._obj is series
|
|
998
|
+
assert repr(si._transform) == "dim('*', inv)"
|
|
999
|
+
assert si._depth == 2
|
|
1000
|
+
assert si._method is None
|
|
1001
|
+
|
|
1002
|
+
|
|
1003
|
+
@pytest.mark.parametrize(
|
|
1004
|
+
'op',
|
|
1005
|
+
[
|
|
1006
|
+
'+', # __add__
|
|
1007
|
+
'&', # __and__
|
|
1008
|
+
'/', # __div__
|
|
1009
|
+
'==', # __eq__
|
|
1010
|
+
'//', # __floordiv__
|
|
1011
|
+
'>=', # __ge__
|
|
1012
|
+
'>', # __gt__
|
|
1013
|
+
'<=', # __le__
|
|
1014
|
+
'<', # __lt__
|
|
1015
|
+
'<', # __lt__
|
|
1016
|
+
# '<<', # __lshift__
|
|
1017
|
+
'%', # __mod__
|
|
1018
|
+
'*', # __mul__
|
|
1019
|
+
'!=', # __ne__
|
|
1020
|
+
'|', # __or__
|
|
1021
|
+
# '>>', # __rshift__
|
|
1022
|
+
'**', # __pow__
|
|
1023
|
+
'-', # __sub__
|
|
1024
|
+
'/', # __truediv__
|
|
1025
|
+
],
|
|
1026
|
+
)
|
|
1027
|
+
def test_interactive_pandas_series_operator_binary(series, op):
|
|
1028
|
+
if op in ['&', '|']:
|
|
1029
|
+
series = pd.Series([True, False, True], name='A')
|
|
1030
|
+
val = True
|
|
1031
|
+
else:
|
|
1032
|
+
val = 2.0
|
|
1033
|
+
si = Interactive(series)
|
|
1034
|
+
si = eval(f'si {op} {val}')
|
|
1035
|
+
|
|
1036
|
+
assert isinstance(si, Interactive)
|
|
1037
|
+
assert isinstance(si._current, pd.DataFrame)
|
|
1038
|
+
pd.testing.assert_series_equal(si._current.A, eval(f'series {op} {val}'))
|
|
1039
|
+
assert si._obj is series
|
|
1040
|
+
val_repr = '2.0' if isinstance(val, float) else 'True'
|
|
1041
|
+
assert repr(si._transform) == f"dim('*').pd{op}{val_repr}"
|
|
1042
|
+
assert si._depth == 2
|
|
1043
|
+
assert si._method is None
|
|
1044
|
+
|
|
1045
|
+
|
|
1046
|
+
@pytest.mark.parametrize(
|
|
1047
|
+
'op',
|
|
1048
|
+
[
|
|
1049
|
+
'+', # __radd__
|
|
1050
|
+
'&', # __rand__
|
|
1051
|
+
'/', # __rdiv__
|
|
1052
|
+
'//', # __rfloordiv__
|
|
1053
|
+
# '<<', # __rlshift__
|
|
1054
|
+
'%', # __rmod__
|
|
1055
|
+
'*', # __rmul__
|
|
1056
|
+
'|', # __ror__
|
|
1057
|
+
'**', # __rpow__
|
|
1058
|
+
# '>>', # __rshift__
|
|
1059
|
+
'-', # __rsub__
|
|
1060
|
+
'/', # __rtruediv__
|
|
1061
|
+
],
|
|
1062
|
+
)
|
|
1063
|
+
def test_interactive_pandas_series_operator_reverse_binary(op):
|
|
1064
|
+
if op in ['&', '|']:
|
|
1065
|
+
series = pd.Series([True, False, True], name='A')
|
|
1066
|
+
val = True
|
|
1067
|
+
else:
|
|
1068
|
+
series = pd.Series([1.0, 2.0, 3.0], name='A')
|
|
1069
|
+
val = 2.0
|
|
1070
|
+
si = Interactive(series)
|
|
1071
|
+
si = eval(f'{val} {op} si')
|
|
1072
|
+
|
|
1073
|
+
assert isinstance(si, Interactive)
|
|
1074
|
+
assert isinstance(si._current, pd.DataFrame)
|
|
1075
|
+
pd.testing.assert_series_equal(si._current.A, eval(f'{val} {op} series'))
|
|
1076
|
+
assert si._obj is series
|
|
1077
|
+
val_repr = '2.0' if isinstance(val, float) else 'True'
|
|
1078
|
+
assert repr(si._transform) == f"{val_repr}{op}dim('*')"
|
|
1079
|
+
assert si._depth == 2
|
|
1080
|
+
assert si._method is None
|
|
1081
|
+
|
|
1082
|
+
|
|
1083
|
+
def test_interactive_pandas_series_operator_abs(series):
|
|
1084
|
+
si = Interactive(series)
|
|
1085
|
+
si = abs(si)
|
|
1086
|
+
|
|
1087
|
+
assert isinstance(si, Interactive)
|
|
1088
|
+
assert isinstance(si._current, pd.DataFrame)
|
|
1089
|
+
pd.testing.assert_series_equal(si._current.A, abs(series))
|
|
1090
|
+
assert si._obj is series
|
|
1091
|
+
assert repr(si._transform) == "absdim('*')"
|
|
1092
|
+
assert si._depth == 2
|
|
1093
|
+
assert si._method is None
|
|
1094
|
+
|
|
1095
|
+
|
|
1096
|
+
def test_interactive_pandas_series_operator_round(series):
|
|
1097
|
+
si = Interactive(series)
|
|
1098
|
+
si = round(si)
|
|
1099
|
+
|
|
1100
|
+
assert isinstance(si, Interactive)
|
|
1101
|
+
assert isinstance(si._current, pd.DataFrame)
|
|
1102
|
+
pd.testing.assert_series_equal(si._current.A, round(series))
|
|
1103
|
+
assert si._obj is series
|
|
1104
|
+
assert repr(si._transform) == "dim('*', round)"
|
|
1105
|
+
assert si._depth == 2
|
|
1106
|
+
assert si._method is None
|
|
1107
|
+
|
|
1108
|
+
|
|
1109
|
+
def test_interactive_pandas_series_plot(series, clone_spy):
|
|
1110
|
+
si = Interactive(series)
|
|
1111
|
+
|
|
1112
|
+
si = si.plot()
|
|
1113
|
+
|
|
1114
|
+
assert isinstance(si, Interactive)
|
|
1115
|
+
assert isinstance(si._current, matplotlib.axes.Axes)
|
|
1116
|
+
assert si._obj is series
|
|
1117
|
+
assert "dim('*').pd.plot(ax=<function Interactive._get_ax_fn.<locals>.get_ax" in repr(
|
|
1118
|
+
si._transform
|
|
1119
|
+
)
|
|
1120
|
+
assert si._depth == 3
|
|
1121
|
+
assert si._method is None
|
|
1122
|
+
|
|
1123
|
+
assert clone_spy.count == 3
|
|
1124
|
+
|
|
1125
|
+
# _clone(True) in _resolve_accessor in __getattribute__(name='plot')
|
|
1126
|
+
assert clone_spy.calls[0].depth == 1
|
|
1127
|
+
assert not clone_spy.calls[0].args
|
|
1128
|
+
assert clone_spy.calls[0].kwargs == {'copy': True}
|
|
1129
|
+
|
|
1130
|
+
# 1st _clone(copy=True) in __call__
|
|
1131
|
+
assert clone_spy.calls[1].depth == 2
|
|
1132
|
+
assert not clone_spy.calls[1].args
|
|
1133
|
+
assert clone_spy.calls[1].kwargs == {'copy': True}
|
|
1134
|
+
|
|
1135
|
+
# 2nd _clone in __call__
|
|
1136
|
+
assert clone_spy.calls[2].depth == 3
|
|
1137
|
+
assert len(clone_spy.calls[2].args) == 1
|
|
1138
|
+
# Not the complete repr as the function doesn't have a nice repr,
|
|
1139
|
+
# its repr displays the memory address.
|
|
1140
|
+
assert "dim('*').pd.plot(ax=<function Interactive._get_ax_fn.<locals>.get_ax" in repr(
|
|
1141
|
+
clone_spy.calls[2].args[0]
|
|
1142
|
+
)
|
|
1143
|
+
assert clone_spy.calls[2].kwargs == {'plot': True}
|
|
1144
|
+
|
|
1145
|
+
assert not si._dmap
|
|
1146
|
+
assert isinstance(si._fig, matplotlib.figure.Figure)
|
|
1147
|
+
|
|
1148
|
+
# Just test that it doesn't raise any error.
|
|
1149
|
+
si.output()
|
|
1150
|
+
|
|
1151
|
+
|
|
1152
|
+
def test_interactive_pandas_series_plot_kind_attr(series, clone_spy):
|
|
1153
|
+
# TODO: Not checking the dim reprs in this test as that was
|
|
1154
|
+
# affecting the pipeline.
|
|
1155
|
+
si = Interactive(series)
|
|
1156
|
+
|
|
1157
|
+
si = si.plot.line()
|
|
1158
|
+
|
|
1159
|
+
assert isinstance(si, Interactive)
|
|
1160
|
+
assert isinstance(si._current, matplotlib.axes.Axes)
|
|
1161
|
+
assert si._obj is series
|
|
1162
|
+
# assert "dim('*').pd.plot).line(ax=<function Interactive._get_ax_fn.<locals>.get_ax" in repr(si._transform)
|
|
1163
|
+
assert si._depth == 4
|
|
1164
|
+
assert si._method is None
|
|
1165
|
+
|
|
1166
|
+
assert clone_spy.count == 4
|
|
1167
|
+
|
|
1168
|
+
# _clone(True) in _resolve_accessor in __getattribute__(name='plot')
|
|
1169
|
+
assert clone_spy.calls[0].depth == 1
|
|
1170
|
+
assert not clone_spy.calls[0].args
|
|
1171
|
+
assert clone_spy.calls[0].kwargs == {'copy': True}
|
|
1172
|
+
|
|
1173
|
+
# _clone in _resolve_accessor in __getattribute__(name='line')
|
|
1174
|
+
assert clone_spy.calls[1].depth == 2
|
|
1175
|
+
assert len(clone_spy.calls[1].args) == 1
|
|
1176
|
+
# assert repr(clone_spy.calls[1].args[0]) == "dim('*').pd.plot()"
|
|
1177
|
+
assert len(clone_spy.calls[1].kwargs) == 1
|
|
1178
|
+
assert 'inherit_kwargs' in clone_spy.calls[1].kwargs
|
|
1179
|
+
assert 'ax' in clone_spy.calls[1].kwargs['inherit_kwargs']
|
|
1180
|
+
|
|
1181
|
+
# 1st _clone(copy=True) in __call__
|
|
1182
|
+
assert clone_spy.calls[2].depth == 3
|
|
1183
|
+
assert not clone_spy.calls[2].args
|
|
1184
|
+
assert clone_spy.calls[2].kwargs == {'copy': True}
|
|
1185
|
+
|
|
1186
|
+
# 2nd _clone in __call__
|
|
1187
|
+
assert clone_spy.calls[3].depth == 4
|
|
1188
|
+
assert len(clone_spy.calls[3].args) == 1
|
|
1189
|
+
# assert "(dim('*').pd.plot()).line(ax=<function Interactive._get_ax_fn.<locals>.get_ax" in repr(clone_spy.calls[3].args[0])
|
|
1190
|
+
# TODO: Looks like a bug, 'plot' should be True?
|
|
1191
|
+
assert clone_spy.calls[3].kwargs == {'plot': False}
|
|
1192
|
+
|
|
1193
|
+
assert not si._dmap
|
|
1194
|
+
assert isinstance(si._fig, matplotlib.figure.Figure)
|
|
1195
|
+
|
|
1196
|
+
# Just test that it doesn't raise any error.
|
|
1197
|
+
si.output()
|
|
1198
|
+
|
|
1199
|
+
|
|
1200
|
+
def test_interactive_pandas_dir_no_type_change(df):
|
|
1201
|
+
dfi = Interactive(df)
|
|
1202
|
+
dfi = dfi.head()
|
|
1203
|
+
|
|
1204
|
+
attrs = dir(dfi)
|
|
1205
|
+
|
|
1206
|
+
assert all(col in attrs for col in list(df.columns))
|
|
1207
|
+
assert 'describe' in attrs
|
|
1208
|
+
|
|
1209
|
+
|
|
1210
|
+
def test_interactive_pandas_dir_with_type_change(df):
|
|
1211
|
+
dfi = Interactive(df)
|
|
1212
|
+
dfi = dfi.head().A.head()
|
|
1213
|
+
|
|
1214
|
+
attrs = dir(dfi)
|
|
1215
|
+
|
|
1216
|
+
assert not any(col in attrs for col in list(df.columns))
|
|
1217
|
+
assert 'T' in attrs
|
|
1218
|
+
|
|
1219
|
+
|
|
1220
|
+
@pytest.mark.xfail(
|
|
1221
|
+
reason='hvplot.util.check_library expects the obj to have __module__, which is not true for a float'
|
|
1222
|
+
)
|
|
1223
|
+
def test_interactive_pandas_dir_with_type_change_to_float(df):
|
|
1224
|
+
dfi = Interactive(df)
|
|
1225
|
+
dfi = dfi.head().A.max()
|
|
1226
|
+
|
|
1227
|
+
attrs = dir(dfi)
|
|
1228
|
+
|
|
1229
|
+
assert not any(col in attrs for col in list(df.columns))
|
|
1230
|
+
assert 'describe' not in attrs
|
|
1231
|
+
assert 'real' not in attrs
|
|
1232
|
+
|
|
1233
|
+
|
|
1234
|
+
def test_interactive_pandas_dir_attrib(df):
|
|
1235
|
+
dfi = Interactive(df)
|
|
1236
|
+
dfi = dfi.head().A
|
|
1237
|
+
|
|
1238
|
+
attrs = dir(dfi)
|
|
1239
|
+
|
|
1240
|
+
assert not any(col in attrs for col in list(df.columns))
|
|
1241
|
+
assert 'T' in attrs
|
|
1242
|
+
|
|
1243
|
+
|
|
1244
|
+
def test_interactive_pandas_layout_default_no_widgets(df):
|
|
1245
|
+
dfi = Interactive(df)
|
|
1246
|
+
dfi = dfi.head()
|
|
1247
|
+
|
|
1248
|
+
assert dfi._center is False
|
|
1249
|
+
assert dfi._loc == 'top_left'
|
|
1250
|
+
|
|
1251
|
+
layout = dfi.layout()
|
|
1252
|
+
|
|
1253
|
+
assert isinstance(layout, pn.Row)
|
|
1254
|
+
assert len(layout) == 1
|
|
1255
|
+
|
|
1256
|
+
|
|
1257
|
+
def test_interactive_pandas_layout_default_no_widgets_kwargs(df):
|
|
1258
|
+
dfi = Interactive(df)
|
|
1259
|
+
dfi = dfi.head()
|
|
1260
|
+
|
|
1261
|
+
layout = dfi.layout(width=200)
|
|
1262
|
+
|
|
1263
|
+
assert isinstance(layout, pn.Row)
|
|
1264
|
+
assert layout.width == 200
|
|
1265
|
+
|
|
1266
|
+
|
|
1267
|
+
@is_bokeh2
|
|
1268
|
+
def test_interactive_pandas_layout_default_with_widgets(df):
|
|
1269
|
+
w = pn.widgets.IntSlider(value=2, start=1, end=5)
|
|
1270
|
+
dfi = Interactive(df)
|
|
1271
|
+
dfi = dfi.head(w)
|
|
1272
|
+
|
|
1273
|
+
assert dfi._center is False
|
|
1274
|
+
assert dfi._loc == 'top_left'
|
|
1275
|
+
|
|
1276
|
+
layout = dfi.layout()
|
|
1277
|
+
|
|
1278
|
+
assert isinstance(layout, pn.Row)
|
|
1279
|
+
assert len(layout) == 1
|
|
1280
|
+
assert isinstance(layout[0], pn.Column)
|
|
1281
|
+
assert len(layout[0]) == 2
|
|
1282
|
+
assert isinstance(layout[0][0], pn.Row)
|
|
1283
|
+
assert isinstance(layout[0][1], pn.pane.PaneBase)
|
|
1284
|
+
assert len(layout[0][0]) == 2
|
|
1285
|
+
assert isinstance(layout[0][0][0], pn.Column)
|
|
1286
|
+
assert len(layout[0][0][0]) == 1
|
|
1287
|
+
assert isinstance(layout[0][0][0][0], pn.widgets.Widget)
|
|
1288
|
+
assert isinstance(layout[0][0][1], pn.layout.HSpacer)
|
|
1289
|
+
|
|
1290
|
+
|
|
1291
|
+
@is_bokeh3
|
|
1292
|
+
def test_interactive_pandas_layout_default_with_widgets_bk3(df):
|
|
1293
|
+
w = pn.widgets.IntSlider(value=2, start=1, end=5)
|
|
1294
|
+
dfi = Interactive(df)
|
|
1295
|
+
dfi = dfi.head(w)
|
|
1296
|
+
|
|
1297
|
+
assert dfi._center is False
|
|
1298
|
+
assert dfi._loc == 'top_left'
|
|
1299
|
+
|
|
1300
|
+
layout = dfi.layout()
|
|
1301
|
+
|
|
1302
|
+
assert isinstance(layout, pn.Row)
|
|
1303
|
+
assert len(layout) == 1
|
|
1304
|
+
assert isinstance(layout[0], pn.Column)
|
|
1305
|
+
assert len(layout[0]) == 2
|
|
1306
|
+
assert isinstance(layout[0][0], pn.Column)
|
|
1307
|
+
assert isinstance(layout[0][1], pn.pane.PaneBase)
|
|
1308
|
+
assert len(layout[0][0]) == 1
|
|
1309
|
+
assert isinstance(layout[0][0][0], pn.widgets.IntSlider)
|
|
1310
|
+
|
|
1311
|
+
|
|
1312
|
+
@is_bokeh2
|
|
1313
|
+
def test_interactive_pandas_layout_center_with_widgets(df):
|
|
1314
|
+
w = pn.widgets.IntSlider(value=2, start=1, end=5)
|
|
1315
|
+
dfi = df.interactive(center=True)
|
|
1316
|
+
dfi = dfi.head(w)
|
|
1317
|
+
|
|
1318
|
+
assert dfi._center is True
|
|
1319
|
+
assert dfi._loc == 'top_left'
|
|
1320
|
+
|
|
1321
|
+
layout = dfi.layout()
|
|
1322
|
+
|
|
1323
|
+
assert isinstance(layout, pn.Row)
|
|
1324
|
+
assert len(layout) == 3
|
|
1325
|
+
assert isinstance(layout[0], pn.layout.HSpacer)
|
|
1326
|
+
assert isinstance(layout[1], pn.Column)
|
|
1327
|
+
assert isinstance(layout[2], pn.layout.HSpacer)
|
|
1328
|
+
assert len(layout[1]) == 2
|
|
1329
|
+
assert isinstance(layout[1][0], pn.Row)
|
|
1330
|
+
assert isinstance(layout[1][1], pn.Row)
|
|
1331
|
+
assert len(layout[1][0]) == 2
|
|
1332
|
+
assert len(layout[1][1]) == 3
|
|
1333
|
+
assert isinstance(layout[1][0][0], pn.Column)
|
|
1334
|
+
assert len(layout[1][0][0]) == 1
|
|
1335
|
+
assert isinstance(layout[1][0][0][0], pn.widgets.Widget)
|
|
1336
|
+
assert isinstance(layout[1][1][0], pn.layout.HSpacer)
|
|
1337
|
+
assert isinstance(layout[1][1][1], pn.pane.PaneBase)
|
|
1338
|
+
assert isinstance(layout[1][1][2], pn.layout.HSpacer)
|
|
1339
|
+
|
|
1340
|
+
|
|
1341
|
+
@is_bokeh2
|
|
1342
|
+
def test_interactive_pandas_layout_loc_with_widgets(df):
|
|
1343
|
+
w = pn.widgets.IntSlider(value=2, start=1, end=5)
|
|
1344
|
+
dfi = df.interactive(loc='top_right')
|
|
1345
|
+
dfi = dfi.head(w)
|
|
1346
|
+
|
|
1347
|
+
assert dfi._center is False
|
|
1348
|
+
assert dfi._loc == 'top_right'
|
|
1349
|
+
|
|
1350
|
+
layout = dfi.layout()
|
|
1351
|
+
|
|
1352
|
+
assert isinstance(layout, pn.Row)
|
|
1353
|
+
assert len(layout) == 1
|
|
1354
|
+
assert isinstance(layout[0], pn.Column)
|
|
1355
|
+
assert len(layout[0]) == 2
|
|
1356
|
+
assert isinstance(layout[0][0], pn.Row)
|
|
1357
|
+
assert isinstance(layout[0][1], pn.pane.PaneBase)
|
|
1358
|
+
assert len(layout[0][0]) == 2
|
|
1359
|
+
assert isinstance(layout[0][0][0], pn.layout.HSpacer)
|
|
1360
|
+
assert isinstance(layout[0][0][1], pn.Column)
|
|
1361
|
+
assert len(layout[0][0][1]) == 1
|
|
1362
|
+
assert isinstance(layout[0][0][1][0], pn.widgets.Widget)
|
|
1363
|
+
|
|
1364
|
+
|
|
1365
|
+
def test_interactive_pandas_eval(df):
|
|
1366
|
+
dfi = Interactive(df)
|
|
1367
|
+
dfi = dfi.head(2)
|
|
1368
|
+
|
|
1369
|
+
pd.testing.assert_frame_equal(dfi.eval(), df.head(2))
|
|
1370
|
+
|
|
1371
|
+
|
|
1372
|
+
def test_interactive_pandas_eval_attrib(df):
|
|
1373
|
+
dfi = Interactive(df)
|
|
1374
|
+
dfi = dfi.head(2).A
|
|
1375
|
+
|
|
1376
|
+
pd.testing.assert_series_equal(dfi.eval(), df.head(2).A)
|
|
1377
|
+
|
|
1378
|
+
|
|
1379
|
+
def test_interactive_pandas_eval_hvplot(df):
|
|
1380
|
+
dfi = Interactive(df)
|
|
1381
|
+
dfi = dfi.head(2).hvplot(y='A')
|
|
1382
|
+
|
|
1383
|
+
evaled = dfi.eval()
|
|
1384
|
+
|
|
1385
|
+
assert isinstance(evaled, hv.Curve)
|
|
1386
|
+
|
|
1387
|
+
|
|
1388
|
+
def test_interactive_pandas_series_widget_value(series):
|
|
1389
|
+
w = pn.widgets.FloatSlider(value=2.0, start=1.0, end=5.0)
|
|
1390
|
+
|
|
1391
|
+
si = Interactive(series)
|
|
1392
|
+
|
|
1393
|
+
si = si + w.param.value
|
|
1394
|
+
|
|
1395
|
+
assert isinstance(si, Interactive)
|
|
1396
|
+
assert isinstance(si._current, pd.DataFrame)
|
|
1397
|
+
pd.testing.assert_series_equal(si._current.A, series + w.value)
|
|
1398
|
+
assert si._obj is series
|
|
1399
|
+
if param2:
|
|
1400
|
+
assert "dim('*').pd+<param.parameters.Number object" in repr(si._transform)
|
|
1401
|
+
else:
|
|
1402
|
+
assert "dim('*').pd+<param.Number object" in repr(si._transform)
|
|
1403
|
+
assert si._depth == 2
|
|
1404
|
+
assert si._method is None
|
|
1405
|
+
|
|
1406
|
+
assert len(si._params) == 1
|
|
1407
|
+
assert si._params[0] is w.param.value
|
|
1408
|
+
|
|
1409
|
+
widgets = si.widgets()
|
|
1410
|
+
|
|
1411
|
+
assert isinstance(widgets, pn.Column)
|
|
1412
|
+
assert len(widgets) == 1
|
|
1413
|
+
assert widgets[0] is w
|
|
1414
|
+
|
|
1415
|
+
|
|
1416
|
+
def test_clones_dont_reexecute_transforms():
|
|
1417
|
+
# Fixes https://github.com/holoviz/hvplot/issues/832
|
|
1418
|
+
df = pd.DataFrame()
|
|
1419
|
+
msgs = []
|
|
1420
|
+
|
|
1421
|
+
def piped(df, msg):
|
|
1422
|
+
msgs.append(msg)
|
|
1423
|
+
return df
|
|
1424
|
+
|
|
1425
|
+
df.interactive.pipe(piped, msg='1').pipe(piped, msg='2')
|
|
1426
|
+
|
|
1427
|
+
assert len(msgs) == 3
|
|
1428
|
+
|
|
1429
|
+
|
|
1430
|
+
def test_interactive_accept_non_str_columnar_data():
|
|
1431
|
+
df = pd.DataFrame(np.random.random((10, 2)))
|
|
1432
|
+
assert all(not isinstance(col, str) for col in df.columns)
|
|
1433
|
+
dfi = Interactive(df)
|
|
1434
|
+
|
|
1435
|
+
w = pn.widgets.FloatSlider(start=0, end=1, step=0.05)
|
|
1436
|
+
|
|
1437
|
+
# Column names converted as string so can no longer use dfi[1]
|
|
1438
|
+
dfi = dfi['1'] + w.param.value
|
|
1439
|
+
|
|
1440
|
+
w.value = 0.5
|
|
1441
|
+
|
|
1442
|
+
pytest.approx(dfi.eval().sum(), (df[1] + 0.5).sum())
|