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,596 @@
|
|
|
1
|
+
import hvplot
|
|
2
|
+
import holoviews as hv
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import pytest
|
|
6
|
+
import xarray as xr
|
|
7
|
+
|
|
8
|
+
from holoviews import Store
|
|
9
|
+
from holoviews.core.options import Options, OptionTree
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@pytest.fixture(scope='class')
|
|
13
|
+
def load_pandas_accessor():
|
|
14
|
+
import hvplot.pandas # noqa
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@pytest.fixture(scope='class')
|
|
18
|
+
def load_xarray_accessor():
|
|
19
|
+
import hvplot.xarray # noqa
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@pytest.fixture(params=['bokeh', 'matplotlib', 'plotly'], scope='class')
|
|
23
|
+
def backend(request):
|
|
24
|
+
backend = request.param
|
|
25
|
+
backend_copy = Store.current_backend
|
|
26
|
+
if backend not in Store.registry:
|
|
27
|
+
hvplot.extension(backend, compatibility='bokeh')
|
|
28
|
+
Store.set_current_backend(backend)
|
|
29
|
+
store_copy = OptionTree(sorted(Store.options().items()), groups=Options._option_groups)
|
|
30
|
+
yield backend
|
|
31
|
+
Store.options(val=store_copy)
|
|
32
|
+
Store._custom_options = {k: {} for k in Store._custom_options.keys()}
|
|
33
|
+
Store.set_current_backend(backend_copy)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@pytest.fixture(scope='module')
|
|
37
|
+
def df():
|
|
38
|
+
return pd.DataFrame(
|
|
39
|
+
[[1, 2, 'A', 0.1], [3, 4, 'B', 0.2], [5, 6, 'C', 0.3]],
|
|
40
|
+
columns=['x', 'y', 'category', 'number'],
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@pytest.fixture(scope='module')
|
|
45
|
+
def symmetric_df():
|
|
46
|
+
return pd.DataFrame([[1, 2, -1], [3, 4, 0], [5, 6, 1]], columns=['x', 'y', 'number'])
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@pytest.mark.usefixtures('load_pandas_accessor')
|
|
50
|
+
class TestOptions:
|
|
51
|
+
@pytest.mark.parametrize(
|
|
52
|
+
'backend',
|
|
53
|
+
[
|
|
54
|
+
'bokeh',
|
|
55
|
+
pytest.param(
|
|
56
|
+
'matplotlib',
|
|
57
|
+
marks=pytest.mark.xfail(
|
|
58
|
+
reason='legend_position not supported w/ matplotlib for scatter'
|
|
59
|
+
),
|
|
60
|
+
),
|
|
61
|
+
pytest.param(
|
|
62
|
+
'plotly',
|
|
63
|
+
marks=pytest.mark.xfail(
|
|
64
|
+
reason='legend_position not supported w/ plotly for scatter'
|
|
65
|
+
),
|
|
66
|
+
),
|
|
67
|
+
],
|
|
68
|
+
indirect=True,
|
|
69
|
+
)
|
|
70
|
+
def test_scatter_legend_position(self, df, backend):
|
|
71
|
+
plot = df.hvplot.scatter('x', 'y', c='category', legend='left')
|
|
72
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
73
|
+
assert opts.kwargs['legend_position'] == 'left'
|
|
74
|
+
|
|
75
|
+
@pytest.mark.parametrize(
|
|
76
|
+
'backend',
|
|
77
|
+
[
|
|
78
|
+
'bokeh',
|
|
79
|
+
'matplotlib',
|
|
80
|
+
pytest.param(
|
|
81
|
+
'plotly',
|
|
82
|
+
marks=pytest.mark.xfail(reason='legend_position not supported w/ plotly for hist'),
|
|
83
|
+
),
|
|
84
|
+
],
|
|
85
|
+
indirect=True,
|
|
86
|
+
)
|
|
87
|
+
def test_histogram_by_category_legend_position(self, df, backend):
|
|
88
|
+
plot = df.hvplot.hist('y', by='category', legend='left')
|
|
89
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
90
|
+
assert opts.kwargs['legend_position'] == 'left'
|
|
91
|
+
|
|
92
|
+
@pytest.mark.parametrize('kind', ['scatter', 'points'])
|
|
93
|
+
def test_logz(self, df, kind, backend):
|
|
94
|
+
plot = df.hvplot('x', 'y', c='x', logz=True, kind=kind)
|
|
95
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
96
|
+
assert opts.kwargs['logz'] is True
|
|
97
|
+
|
|
98
|
+
@pytest.mark.parametrize('kind', ['scatter', 'points'])
|
|
99
|
+
def test_color_dim(self, df, kind, backend):
|
|
100
|
+
plot = df.hvplot('x', 'y', c='number', kind=kind)
|
|
101
|
+
opts = Store.lookup_options(backend, plot, 'style')
|
|
102
|
+
assert opts.kwargs['color'] == 'number'
|
|
103
|
+
assert 'number' in plot.vdims
|
|
104
|
+
|
|
105
|
+
@pytest.mark.parametrize('kind', ['scatter', 'points'])
|
|
106
|
+
def test_size_dim(self, df, kind, backend):
|
|
107
|
+
plot = df.hvplot('x', 'y', s='number', kind=kind)
|
|
108
|
+
opts = Store.lookup_options(backend, plot, 'style')
|
|
109
|
+
if backend in ['bokeh', 'plotly']:
|
|
110
|
+
param = 'size'
|
|
111
|
+
elif backend == 'matplotlib':
|
|
112
|
+
param = 's'
|
|
113
|
+
assert opts.kwargs[param] == 'number'
|
|
114
|
+
assert 'number' in plot.vdims
|
|
115
|
+
|
|
116
|
+
@pytest.mark.parametrize(
|
|
117
|
+
'backend',
|
|
118
|
+
[
|
|
119
|
+
'bokeh',
|
|
120
|
+
pytest.param(
|
|
121
|
+
'matplotlib',
|
|
122
|
+
marks=pytest.mark.xfail(reason='cannot map a dim to alpha w/ matplotlib'),
|
|
123
|
+
),
|
|
124
|
+
pytest.param(
|
|
125
|
+
'plotly', marks=pytest.mark.xfail(reason='cannot map a dim to alpha w/ plotly')
|
|
126
|
+
),
|
|
127
|
+
],
|
|
128
|
+
indirect=True,
|
|
129
|
+
)
|
|
130
|
+
@pytest.mark.parametrize('kind', ['scatter', 'points'])
|
|
131
|
+
def test_alpha_dim(self, df, kind, backend):
|
|
132
|
+
plot = df.hvplot('x', 'y', alpha='number', kind=kind)
|
|
133
|
+
opts = Store.lookup_options(backend, plot, 'style')
|
|
134
|
+
assert opts.kwargs['alpha'] == 'number'
|
|
135
|
+
assert 'number' in plot.vdims
|
|
136
|
+
# Special matplotlib code to trigger an error that happens on render
|
|
137
|
+
if backend == 'matplotlib':
|
|
138
|
+
mpl_renderer = hv.Store.renderers['matplotlib']
|
|
139
|
+
mpl_renderer.get_plot(plot)
|
|
140
|
+
|
|
141
|
+
@pytest.mark.parametrize('kind', ['scatter', 'points'])
|
|
142
|
+
def test_marker_dim(self, df, kind, backend):
|
|
143
|
+
plot = df.hvplot('x', 'y', marker='category', kind=kind)
|
|
144
|
+
opts = Store.lookup_options(backend, plot, 'style')
|
|
145
|
+
assert opts.kwargs['marker'] == 'category'
|
|
146
|
+
assert 'category' in plot.vdims
|
|
147
|
+
|
|
148
|
+
@pytest.mark.parametrize('kind', ['scatter', 'points'])
|
|
149
|
+
def test_color_dim_overlay(self, df, kind, backend):
|
|
150
|
+
plot = df.hvplot('x', 'y', c='number', by='category', kind=kind)
|
|
151
|
+
opts = Store.lookup_options(backend, plot.last, 'style')
|
|
152
|
+
assert opts.kwargs['color'] == 'number'
|
|
153
|
+
assert 'number' in plot.last.vdims
|
|
154
|
+
|
|
155
|
+
@pytest.mark.parametrize('kind', ['scatter', 'points'])
|
|
156
|
+
def test_size_dim_overlay(self, df, kind, backend):
|
|
157
|
+
plot = df.hvplot('x', 'y', s='number', by='category', kind=kind)
|
|
158
|
+
opts = Store.lookup_options(backend, plot.last, 'style')
|
|
159
|
+
if backend in ['bokeh', 'plotly']:
|
|
160
|
+
param = 'size'
|
|
161
|
+
elif backend == 'matplotlib':
|
|
162
|
+
param = 's'
|
|
163
|
+
assert opts.kwargs[param] == 'number'
|
|
164
|
+
assert 'number' in plot.last.vdims
|
|
165
|
+
|
|
166
|
+
@pytest.mark.parametrize(
|
|
167
|
+
'backend',
|
|
168
|
+
[
|
|
169
|
+
'bokeh',
|
|
170
|
+
'matplotlib',
|
|
171
|
+
pytest.param(
|
|
172
|
+
'plotly', marks=pytest.mark.xfail(reason='cannot map a dim to alpha w/ plotly')
|
|
173
|
+
),
|
|
174
|
+
],
|
|
175
|
+
indirect=True,
|
|
176
|
+
)
|
|
177
|
+
@pytest.mark.parametrize('kind', ['scatter', 'points'])
|
|
178
|
+
def test_alpha_dim_overlay(self, df, kind, backend):
|
|
179
|
+
plot = df.hvplot('x', 'y', alpha='number', by='category', kind=kind)
|
|
180
|
+
opts = Store.lookup_options(backend, plot.last, 'style')
|
|
181
|
+
assert opts.kwargs['alpha'] == 'number'
|
|
182
|
+
assert 'number' in plot.last.vdims
|
|
183
|
+
|
|
184
|
+
def test_hvplot_defaults(self, df, backend):
|
|
185
|
+
plot = df.hvplot.scatter('x', 'y', c='category')
|
|
186
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
187
|
+
if backend == 'bokeh':
|
|
188
|
+
assert opts.kwargs['height'] == 300
|
|
189
|
+
assert opts.kwargs['width'] == 700
|
|
190
|
+
elif backend == 'matplotlib':
|
|
191
|
+
assert opts.kwargs['aspect'] == pytest.approx(2.333333)
|
|
192
|
+
assert opts.kwargs['fig_size'] == pytest.approx(233.333333)
|
|
193
|
+
if backend == 'bokeh':
|
|
194
|
+
assert opts.kwargs['responsive'] is False
|
|
195
|
+
assert opts.kwargs['shared_axes'] is True
|
|
196
|
+
# legend_position shouldn't only be for Bokeh
|
|
197
|
+
assert opts.kwargs['legend_position'] == 'right'
|
|
198
|
+
assert opts.kwargs['show_grid'] is False
|
|
199
|
+
assert opts.kwargs['show_legend'] is True
|
|
200
|
+
assert opts.kwargs['logx'] is False
|
|
201
|
+
assert opts.kwargs['logy'] is False
|
|
202
|
+
assert opts.kwargs.get('logz') is None
|
|
203
|
+
|
|
204
|
+
@pytest.mark.parametrize(
|
|
205
|
+
'backend',
|
|
206
|
+
[
|
|
207
|
+
'bokeh',
|
|
208
|
+
pytest.param(
|
|
209
|
+
'matplotlib',
|
|
210
|
+
marks=pytest.mark.xfail(reason='default opts not supported w/ matplotlib'),
|
|
211
|
+
),
|
|
212
|
+
pytest.param(
|
|
213
|
+
'plotly', marks=pytest.mark.xfail(reason='default opts not supported w/ plotly')
|
|
214
|
+
),
|
|
215
|
+
],
|
|
216
|
+
indirect=True,
|
|
217
|
+
)
|
|
218
|
+
def test_holoviews_defined_default_opts(self, df, backend):
|
|
219
|
+
hv.opts.defaults(hv.opts.Scatter(height=400, width=900, show_grid=True))
|
|
220
|
+
plot = df.hvplot.scatter('x', 'y', c='category')
|
|
221
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
222
|
+
# legend_position shouldn't apply only to bokeh
|
|
223
|
+
if backend == 'bokeh':
|
|
224
|
+
assert opts.kwargs['legend_position'] == 'right'
|
|
225
|
+
assert opts.kwargs['show_grid'] is True
|
|
226
|
+
assert opts.kwargs['height'] == 400
|
|
227
|
+
assert opts.kwargs['width'] == 900
|
|
228
|
+
|
|
229
|
+
@pytest.mark.parametrize(
|
|
230
|
+
'backend',
|
|
231
|
+
[
|
|
232
|
+
'bokeh',
|
|
233
|
+
pytest.param(
|
|
234
|
+
'matplotlib',
|
|
235
|
+
marks=pytest.mark.xfail(reason='default opts not supported w/ matplotlib'),
|
|
236
|
+
),
|
|
237
|
+
pytest.param(
|
|
238
|
+
'plotly', marks=pytest.mark.xfail(reason='default opts not supported w/ plotly')
|
|
239
|
+
),
|
|
240
|
+
],
|
|
241
|
+
indirect=True,
|
|
242
|
+
)
|
|
243
|
+
def test_holoviews_defined_default_opts_overwritten_in_call(self, df, backend):
|
|
244
|
+
hv.opts.defaults(hv.opts.Scatter(height=400, width=900, show_grid=True))
|
|
245
|
+
plot = df.hvplot.scatter('x', 'y', c='category', width=300, legend='left')
|
|
246
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
247
|
+
# legend_position shouldn't apply only to bokeh
|
|
248
|
+
if backend == 'bokeh':
|
|
249
|
+
assert opts.kwargs['legend_position'] == 'left'
|
|
250
|
+
assert opts.kwargs['show_grid'] is True
|
|
251
|
+
assert opts.kwargs['height'] == 400
|
|
252
|
+
assert opts.kwargs['width'] == 300
|
|
253
|
+
|
|
254
|
+
@pytest.mark.parametrize(
|
|
255
|
+
'backend',
|
|
256
|
+
[
|
|
257
|
+
'bokeh',
|
|
258
|
+
pytest.param(
|
|
259
|
+
'matplotlib',
|
|
260
|
+
marks=pytest.mark.xfail(
|
|
261
|
+
reason='default opts not supported not supported w/ matplotlib'
|
|
262
|
+
),
|
|
263
|
+
),
|
|
264
|
+
pytest.param(
|
|
265
|
+
'plotly',
|
|
266
|
+
marks=pytest.mark.xfail(
|
|
267
|
+
reason='default opts not supported not supported w/ plotly'
|
|
268
|
+
),
|
|
269
|
+
),
|
|
270
|
+
],
|
|
271
|
+
indirect=True,
|
|
272
|
+
)
|
|
273
|
+
def test_holoviews_defined_default_opts_are_not_mutable(self, df, backend):
|
|
274
|
+
hv.opts.defaults(hv.opts.Scatter(tools=['tap']))
|
|
275
|
+
plot = df.hvplot.scatter('x', 'y', c='category')
|
|
276
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
277
|
+
assert opts.kwargs['tools'] == ['tap', 'hover']
|
|
278
|
+
default_opts = Store.options(backend=backend)['Scatter'].groups['plot'].options
|
|
279
|
+
assert default_opts['tools'] == ['tap']
|
|
280
|
+
|
|
281
|
+
def test_axis_set_to_visible_by_default(self, df, backend):
|
|
282
|
+
plot = df.hvplot.scatter('x', 'y', c='category')
|
|
283
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
284
|
+
assert 'xaxis' not in opts.kwargs
|
|
285
|
+
assert 'yaxis' not in opts.kwargs
|
|
286
|
+
|
|
287
|
+
def test_axis_set_to_none(self, df, backend):
|
|
288
|
+
plot = df.hvplot.scatter('x', 'y', c='category', xaxis=None, yaxis=None)
|
|
289
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
290
|
+
assert opts.kwargs['xaxis'] is None
|
|
291
|
+
assert opts.kwargs['yaxis'] is None
|
|
292
|
+
|
|
293
|
+
def test_axis_set_to_false(self, df, backend):
|
|
294
|
+
plot = df.hvplot.scatter('x', 'y', c='category', xaxis=False, yaxis=False)
|
|
295
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
296
|
+
assert opts.kwargs['xaxis'] is None
|
|
297
|
+
assert opts.kwargs['yaxis'] is None
|
|
298
|
+
|
|
299
|
+
def test_axis_set_to_none_in_holoviews_opts_default(self, df, backend):
|
|
300
|
+
hv.opts.defaults(hv.opts.Scatter(xaxis=None, yaxis=None))
|
|
301
|
+
plot = df.hvplot.scatter('x', 'y', c='category')
|
|
302
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
303
|
+
assert opts.kwargs['xaxis'] is None
|
|
304
|
+
assert opts.kwargs['yaxis'] is None
|
|
305
|
+
|
|
306
|
+
@pytest.mark.xfail
|
|
307
|
+
def test_axis_set_to_none_in_holoviews_opts_default_overwrite_in_call(self, df, backend):
|
|
308
|
+
hv.opts.defaults(hv.opts.Scatter(xaxis=None, yaxis=None))
|
|
309
|
+
plot = df.hvplot.scatter('x', 'y', c='category', xaxis=True, yaxis=True)
|
|
310
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
311
|
+
assert 'xaxis' not in opts.kwargs
|
|
312
|
+
assert 'yaxis' not in opts.kwargs
|
|
313
|
+
|
|
314
|
+
def test_loglog_opts(self, df, backend):
|
|
315
|
+
plot = df.hvplot.scatter('x', 'y', c='category', loglog=True)
|
|
316
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
317
|
+
assert opts.kwargs['logx'] is True
|
|
318
|
+
assert opts.kwargs['logy'] is True
|
|
319
|
+
assert opts.kwargs.get('logz') is None
|
|
320
|
+
|
|
321
|
+
def test_logy_opts(self, df, backend):
|
|
322
|
+
plot = df.hvplot.scatter('x', 'y', c='category', logy=True)
|
|
323
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
324
|
+
assert opts.kwargs['logx'] is False
|
|
325
|
+
assert opts.kwargs['logy'] is True
|
|
326
|
+
assert opts.kwargs.get('logz') is None
|
|
327
|
+
|
|
328
|
+
@pytest.mark.parametrize(
|
|
329
|
+
'backend',
|
|
330
|
+
[
|
|
331
|
+
'bokeh',
|
|
332
|
+
pytest.param(
|
|
333
|
+
'matplotlib',
|
|
334
|
+
marks=pytest.mark.xfail(reason='default opts not supported w/ matplotlib'),
|
|
335
|
+
),
|
|
336
|
+
pytest.param(
|
|
337
|
+
'plotly', marks=pytest.mark.xfail(reason='defaykt opts not supported w/ plotly')
|
|
338
|
+
),
|
|
339
|
+
],
|
|
340
|
+
indirect=True,
|
|
341
|
+
)
|
|
342
|
+
def test_holoviews_defined_default_opts_logx(self, df, backend):
|
|
343
|
+
hv.opts.defaults(hv.opts.Scatter(logx=True))
|
|
344
|
+
plot = df.hvplot.scatter('x', 'y', c='category')
|
|
345
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
346
|
+
assert opts.kwargs['logx'] is True
|
|
347
|
+
assert opts.kwargs['logy'] is False
|
|
348
|
+
assert opts.kwargs.get('logz') is None
|
|
349
|
+
|
|
350
|
+
def test_holoviews_defined_default_opts_logx_overwritten_in_call(self, df, backend):
|
|
351
|
+
hv.opts.defaults(hv.opts.Scatter(logx=True))
|
|
352
|
+
plot = df.hvplot.scatter('x', 'y', c='category', logx=False)
|
|
353
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
354
|
+
assert opts.kwargs['logx'] is False
|
|
355
|
+
assert opts.kwargs['logy'] is False
|
|
356
|
+
assert opts.kwargs.get('logz') is None
|
|
357
|
+
|
|
358
|
+
def test_hvplot_default_cat_cmap_opts(self, df, backend):
|
|
359
|
+
import colorcet as cc
|
|
360
|
+
|
|
361
|
+
plot = df.hvplot.scatter('x', 'y', c='category')
|
|
362
|
+
opts = Store.lookup_options(backend, plot, 'style')
|
|
363
|
+
assert opts.kwargs['cmap'] == cc.palette['glasbey_category10']
|
|
364
|
+
|
|
365
|
+
def test_hvplot_default_num_cmap_opts(self, df, backend):
|
|
366
|
+
plot = df.hvplot.scatter('x', 'y', c='number')
|
|
367
|
+
opts = Store.lookup_options(backend, plot, 'style')
|
|
368
|
+
assert opts.kwargs['cmap'] == 'kbc_r'
|
|
369
|
+
|
|
370
|
+
def test_cmap_opts_by_type(self, df, backend):
|
|
371
|
+
plot = df.hvplot.scatter('x', 'y', c='number', cmap='diverging')
|
|
372
|
+
opts = Store.lookup_options(backend, plot, 'style')
|
|
373
|
+
assert opts.kwargs['cmap'] == 'coolwarm'
|
|
374
|
+
|
|
375
|
+
def test_cmap_opts_by_name(self, df, backend):
|
|
376
|
+
plot = df.hvplot.scatter('x', 'y', c='number', cmap='fire')
|
|
377
|
+
opts = Store.lookup_options(backend, plot, 'style')
|
|
378
|
+
assert opts.kwargs['cmap'] == 'fire'
|
|
379
|
+
|
|
380
|
+
def test_colormap_opts_by_name(self, df, backend):
|
|
381
|
+
plot = df.hvplot.scatter('x', 'y', c='number', colormap='fire')
|
|
382
|
+
opts = Store.lookup_options(backend, plot, 'style')
|
|
383
|
+
assert opts.kwargs['cmap'] == 'fire'
|
|
384
|
+
|
|
385
|
+
def test_cmap_opts_as_a_list(self, df, backend):
|
|
386
|
+
plot = df.hvplot.scatter('x', 'y', c='number', cmap=['red', 'blue', 'green'])
|
|
387
|
+
opts = Store.lookup_options(backend, plot, 'style')
|
|
388
|
+
assert opts.kwargs['cmap'] == ['red', 'blue', 'green']
|
|
389
|
+
|
|
390
|
+
@pytest.mark.parametrize(
|
|
391
|
+
('opt', 'backend'),
|
|
392
|
+
[
|
|
393
|
+
('aspect', 'bokeh'),
|
|
394
|
+
('aspect', 'matplotlib'),
|
|
395
|
+
('aspect', 'plotly'),
|
|
396
|
+
('data_aspect', 'bokeh'),
|
|
397
|
+
('data_aspect', 'matplotlib'),
|
|
398
|
+
pytest.param(
|
|
399
|
+
'data_aspect',
|
|
400
|
+
'plotly',
|
|
401
|
+
marks=pytest.mark.xfail(reason='data_aspect not supported w/ plotly'),
|
|
402
|
+
),
|
|
403
|
+
],
|
|
404
|
+
indirect=['backend'],
|
|
405
|
+
)
|
|
406
|
+
def test_aspect(self, df, opt, backend):
|
|
407
|
+
plot = df.hvplot(x='x', y='y', **{opt: 2})
|
|
408
|
+
opts = Store.lookup_options(backend, plot, 'plot').kwargs
|
|
409
|
+
assert opts[opt] == 2
|
|
410
|
+
if backend in ['bokeh', 'matplotlib']:
|
|
411
|
+
assert opts.get('width') is None
|
|
412
|
+
assert opts.get('height') is None
|
|
413
|
+
elif backend == 'matplotlib':
|
|
414
|
+
assert opts.get('fig_size') is None
|
|
415
|
+
|
|
416
|
+
@pytest.mark.parametrize(
|
|
417
|
+
('opt', 'backend'),
|
|
418
|
+
[
|
|
419
|
+
('aspect', 'bokeh'),
|
|
420
|
+
('aspect', 'matplotlib'),
|
|
421
|
+
('aspect', 'plotly'),
|
|
422
|
+
('data_aspect', 'bokeh'),
|
|
423
|
+
('data_aspect', 'matplotlib'),
|
|
424
|
+
pytest.param(
|
|
425
|
+
'data_aspect',
|
|
426
|
+
'plotly',
|
|
427
|
+
marks=pytest.mark.xfail(reason='data_aspect not supported w/ plotly'),
|
|
428
|
+
),
|
|
429
|
+
],
|
|
430
|
+
indirect=['backend'],
|
|
431
|
+
)
|
|
432
|
+
def test_aspect_and_width(self, df, opt, backend):
|
|
433
|
+
plot = df.hvplot(x='x', y='y', width=150, **{opt: 2})
|
|
434
|
+
opts = hv.Store.lookup_options(backend, plot, 'plot').kwargs
|
|
435
|
+
assert opts[opt] == 2
|
|
436
|
+
if backend in ['bokeh', 'plotly']:
|
|
437
|
+
assert opts.get('width') == 150
|
|
438
|
+
assert opts.get('height') is None
|
|
439
|
+
elif backend == 'matplotlib':
|
|
440
|
+
assert opts.get('fig_size') == pytest.approx(50.0)
|
|
441
|
+
|
|
442
|
+
def test_symmetric_dataframe(self, backend):
|
|
443
|
+
import pandas as pd
|
|
444
|
+
|
|
445
|
+
df = pd.DataFrame([[1, 2, -1], [3, 4, 0], [5, 6, 1]], columns=['x', 'y', 'number'])
|
|
446
|
+
plot = df.hvplot.scatter('x', 'y', c='number')
|
|
447
|
+
plot_opts = Store.lookup_options(backend, plot, 'plot')
|
|
448
|
+
assert plot_opts.kwargs['symmetric'] is True
|
|
449
|
+
style_opts = Store.lookup_options(backend, plot, 'style')
|
|
450
|
+
assert style_opts.kwargs['cmap'] == 'coolwarm'
|
|
451
|
+
|
|
452
|
+
def test_symmetric_is_deduced_dataframe(self, symmetric_df, backend):
|
|
453
|
+
plot = symmetric_df.hvplot.scatter('x', 'y', c='number')
|
|
454
|
+
plot_opts = Store.lookup_options(backend, plot, 'plot')
|
|
455
|
+
assert plot_opts.kwargs['symmetric'] is True
|
|
456
|
+
style_opts = Store.lookup_options(backend, plot, 'style')
|
|
457
|
+
assert style_opts.kwargs['cmap'] == 'coolwarm'
|
|
458
|
+
|
|
459
|
+
def test_symmetric_from_opts(self, df, backend):
|
|
460
|
+
plot = df.hvplot.scatter('x', 'y', c='number', symmetric=True)
|
|
461
|
+
plot_opts = Store.lookup_options(backend, plot, 'plot')
|
|
462
|
+
assert plot_opts.kwargs['symmetric'] is True
|
|
463
|
+
style_opts = Store.lookup_options(backend, plot, 'style')
|
|
464
|
+
assert style_opts.kwargs['cmap'] == 'coolwarm'
|
|
465
|
+
|
|
466
|
+
def test_symmetric_from_opts_does_not_deduce(self, symmetric_df, backend):
|
|
467
|
+
plot = symmetric_df.hvplot.scatter('x', 'y', c='number', symmetric=False)
|
|
468
|
+
plot_opts = Store.lookup_options(backend, plot, 'plot')
|
|
469
|
+
assert plot_opts.kwargs['symmetric'] is False
|
|
470
|
+
style_opts = Store.lookup_options(backend, plot, 'style')
|
|
471
|
+
assert style_opts.kwargs['cmap'] == 'kbc_r'
|
|
472
|
+
|
|
473
|
+
def test_if_clim_is_set_symmetric_is_not_deduced(self, symmetric_df, backend):
|
|
474
|
+
plot = symmetric_df.hvplot.scatter('x', 'y', c='number', clim=(-1, 1))
|
|
475
|
+
plot_opts = Store.lookup_options(backend, plot, 'plot')
|
|
476
|
+
assert plot_opts.kwargs.get('symmetric') is None
|
|
477
|
+
style_opts = Store.lookup_options(backend, plot, 'style')
|
|
478
|
+
assert style_opts.kwargs['cmap'] == 'kbc_r'
|
|
479
|
+
|
|
480
|
+
@pytest.mark.parametrize(
|
|
481
|
+
'backend',
|
|
482
|
+
[
|
|
483
|
+
'bokeh',
|
|
484
|
+
'matplotlib',
|
|
485
|
+
pytest.param(
|
|
486
|
+
'plotly',
|
|
487
|
+
marks=pytest.mark.xfail(
|
|
488
|
+
reason='bandwidth, cut, levels not supported w/ plotly for bivariate'
|
|
489
|
+
),
|
|
490
|
+
),
|
|
491
|
+
],
|
|
492
|
+
indirect=True,
|
|
493
|
+
)
|
|
494
|
+
def test_bivariate_opts(self, df, backend):
|
|
495
|
+
plot = df.hvplot.bivariate('x', 'y', bandwidth=0.2, cut=1, levels=5, filled=True)
|
|
496
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
497
|
+
assert opts.kwargs['bandwidth'] == 0.2
|
|
498
|
+
assert opts.kwargs['cut'] == 1
|
|
499
|
+
assert opts.kwargs['levels'] == 5
|
|
500
|
+
assert opts.kwargs['filled'] is True
|
|
501
|
+
|
|
502
|
+
def test_kde_opts(self, df, backend):
|
|
503
|
+
plot = df.hvplot.kde('x', bandwidth=0.2, cut=1, filled=True)
|
|
504
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
505
|
+
assert opts.kwargs['bandwidth'] == 0.2
|
|
506
|
+
assert opts.kwargs['cut'] == 1
|
|
507
|
+
assert opts.kwargs['filled'] is True
|
|
508
|
+
|
|
509
|
+
def test_bgcolor(self, df, backend):
|
|
510
|
+
plot = df.hvplot.scatter('x', 'y', bgcolor='black')
|
|
511
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
512
|
+
assert opts.kwargs['bgcolor'] == 'black'
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
@pytest.fixture(scope='module')
|
|
516
|
+
def da():
|
|
517
|
+
return xr.DataArray(
|
|
518
|
+
data=np.arange(16).reshape((2, 2, 2, 2)),
|
|
519
|
+
coords={'time': [0, 1], 'y': [0, 1], 'x': [0, 1], 'band': [0, 1]},
|
|
520
|
+
dims=['time', 'y', 'x', 'band'],
|
|
521
|
+
name='test',
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
@pytest.fixture(scope='module')
|
|
526
|
+
def da2():
|
|
527
|
+
return xr.DataArray(
|
|
528
|
+
data=np.arange(27).reshape((3, 3, 3)),
|
|
529
|
+
coords={'y': [0, 1, 2], 'x': [0, 1, 2]},
|
|
530
|
+
dims=['y', 'x', 'other'],
|
|
531
|
+
name='test2',
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
@pytest.fixture(scope='module')
|
|
536
|
+
def ds1(da):
|
|
537
|
+
return xr.Dataset(dict(foo=da))
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
@pytest.fixture(scope='module')
|
|
541
|
+
def ds2(da, da2):
|
|
542
|
+
return xr.Dataset(dict(foo=da, bar=da2))
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
@pytest.mark.usefixtures('load_xarray_accessor')
|
|
546
|
+
class TestXarrayTitle:
|
|
547
|
+
def test_dataarray_2d_with_title(self, da, backend):
|
|
548
|
+
da_sel = da.sel(time=0, band=0)
|
|
549
|
+
plot = da_sel.hvplot() # Image plot
|
|
550
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
551
|
+
assert opts.kwargs['title'] == 'time = 0, band = 0'
|
|
552
|
+
|
|
553
|
+
def test_dataarray_1d_with_title(self, da, backend):
|
|
554
|
+
da_sel = da.sel(time=0, band=0, x=0)
|
|
555
|
+
plot = da_sel.hvplot() # Line plot
|
|
556
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
557
|
+
assert opts.kwargs['title'] == 'time = 0, x = 0, band = 0'
|
|
558
|
+
|
|
559
|
+
def test_dataarray_1d_and_by_with_title(self, da, backend):
|
|
560
|
+
da_sel = da.sel(time=0, band=0, x=[0, 1])
|
|
561
|
+
plot = da_sel.hvplot(by='x') # Line plot with hue/by
|
|
562
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
563
|
+
assert opts.kwargs['title'] == 'time = 0, band = 0'
|
|
564
|
+
|
|
565
|
+
def test_override_title(self, da, backend):
|
|
566
|
+
da_sel = da.sel(time=0, band=0)
|
|
567
|
+
plot = da_sel.hvplot(title='title') # Image plot
|
|
568
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
569
|
+
assert opts.kwargs['title'] == 'title'
|
|
570
|
+
|
|
571
|
+
def test_dataarray_4d_line_no_title(self, da, backend):
|
|
572
|
+
plot = da.hvplot.line(dynamic=False) # Line plot with widgets
|
|
573
|
+
opts = Store.lookup_options(backend, plot.last, 'plot')
|
|
574
|
+
assert 'title' not in opts.kwargs
|
|
575
|
+
|
|
576
|
+
def test_dataarray_3d_histogram_with_title(self, da, backend):
|
|
577
|
+
da_sel = da.sel(time=0)
|
|
578
|
+
plot = da_sel.hvplot() # Histogram and no widgets
|
|
579
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
580
|
+
assert opts.kwargs['title'] == 'time = 0'
|
|
581
|
+
|
|
582
|
+
def test_dataset_empty_raises(self, ds1, backend):
|
|
583
|
+
with pytest.raises(ValueError, match='empty xarray.Dataset'):
|
|
584
|
+
ds1.drop_vars('foo').hvplot()
|
|
585
|
+
|
|
586
|
+
def test_dataset_one_var_behaves_like_dataarray(self, ds1, backend):
|
|
587
|
+
ds_sel = ds1.sel(time=0, band=0)
|
|
588
|
+
plot = ds_sel.hvplot() # Image plot
|
|
589
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
590
|
+
assert opts.kwargs['title'] == 'time = 0, band = 0'
|
|
591
|
+
|
|
592
|
+
def test_dataset_scatter_with_title(self, ds2, backend):
|
|
593
|
+
ds_sel = ds2.sel(time=0, band=0, x=0, y=0)
|
|
594
|
+
plot = ds_sel.hvplot.scatter(x='foo', y='bar') # Image plot
|
|
595
|
+
opts = Store.lookup_options(backend, plot, 'plot')
|
|
596
|
+
assert opts.kwargs['title'] == 'time = 0, y = 0, x = 0, band = 0'
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from collections import OrderedDict
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import xarray as xr
|
|
6
|
+
|
|
7
|
+
from hvplot.plotting import hvPlot, hvPlotTabular
|
|
8
|
+
from holoviews import Store, Scatter
|
|
9
|
+
from holoviews.element.comparison import ComparisonTestCase
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TestOverrides(ComparisonTestCase):
|
|
13
|
+
def setUp(self):
|
|
14
|
+
import hvplot.pandas # noqa
|
|
15
|
+
|
|
16
|
+
self.df = pd.DataFrame([[1, 2], [3, 4], [5, 6]], columns=['x', 'y'])
|
|
17
|
+
|
|
18
|
+
def test_define_default_options(self):
|
|
19
|
+
hvplot = hvPlotTabular(self.df, width=42, height=42)
|
|
20
|
+
curve = hvplot(y='y')
|
|
21
|
+
opts = Store.lookup_options('bokeh', curve, 'plot')
|
|
22
|
+
self.assertEqual(opts.options.get('width'), 42)
|
|
23
|
+
self.assertEqual(opts.options.get('height'), 42)
|
|
24
|
+
|
|
25
|
+
def test_define_custom_method(self):
|
|
26
|
+
hvplot = hvPlotTabular(self.df, {'custom_scatter': {'width': 42, 'height': 42}})
|
|
27
|
+
custom_scatter = hvplot.custom_scatter(y='y')
|
|
28
|
+
scatter = hvplot.scatter(y='y')
|
|
29
|
+
custom_opts = Store.lookup_options('bokeh', custom_scatter, 'plot')
|
|
30
|
+
opts = Store.lookup_options('bokeh', scatter, 'plot')
|
|
31
|
+
self.assertEqual(custom_opts.options.get('width'), 42)
|
|
32
|
+
self.assertEqual(custom_opts.options.get('height'), 42)
|
|
33
|
+
self.assertNotEqual(opts.options.get('width'), 42)
|
|
34
|
+
self.assertNotEqual(opts.options.get('height'), 42)
|
|
35
|
+
|
|
36
|
+
def test_define_customize_method(self):
|
|
37
|
+
hvplot = hvPlotTabular(self.df, {'scatter': {'width': 42, 'height': 42}})
|
|
38
|
+
custom_scatter = hvplot.scatter(y='y')
|
|
39
|
+
curve = hvplot.line(y='y')
|
|
40
|
+
custom_opts = Store.lookup_options('bokeh', custom_scatter, 'plot')
|
|
41
|
+
opts = Store.lookup_options('bokeh', curve, 'plot')
|
|
42
|
+
self.assertEqual(custom_opts.options.get('width'), 42)
|
|
43
|
+
self.assertEqual(custom_opts.options.get('height'), 42)
|
|
44
|
+
self.assertNotEqual(opts.options.get('width'), 42)
|
|
45
|
+
self.assertNotEqual(opts.options.get('height'), 42)
|
|
46
|
+
|
|
47
|
+
def test_attempt_to_override_kind_on_method(self):
|
|
48
|
+
hvplot = hvPlotTabular(self.df, {'scatter': {'kind': 'line'}})
|
|
49
|
+
self.assertIsInstance(hvplot.scatter(y='y'), Scatter)
|
|
50
|
+
|
|
51
|
+
def test_pandas_query_metadata(self):
|
|
52
|
+
hvplot = hvPlotTabular(self.df, query='x>2')
|
|
53
|
+
assert len(hvplot._data) == 2
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class TestXArrayOverrides(ComparisonTestCase):
|
|
57
|
+
def setUp(self):
|
|
58
|
+
coords = OrderedDict([('time', [0, 1]), ('lat', [0, 1]), ('lon', [0, 1])])
|
|
59
|
+
self.da_img_by_time = xr.DataArray(
|
|
60
|
+
np.arange(8).reshape((2, 2, 2)), coords, ['time', 'lat', 'lon']
|
|
61
|
+
).assign_coords(lat1=xr.DataArray([2, 3], dims=['lat']))
|
|
62
|
+
|
|
63
|
+
def test_xarray_isel_scalar_metadata(self):
|
|
64
|
+
hvplot = hvPlot(self.da_img_by_time, isel={'time': 1})
|
|
65
|
+
assert hvplot._data.ndim == 2
|
|
66
|
+
|
|
67
|
+
def test_xarray_isel_nonscalar_metadata(self):
|
|
68
|
+
hvplot = hvPlot(self.da_img_by_time, isel={'time': [1]})
|
|
69
|
+
assert hvplot._data.ndim == 3
|
|
70
|
+
assert len(hvplot._data.time) == 1
|
|
71
|
+
|
|
72
|
+
def test_xarray_sel_metadata(self):
|
|
73
|
+
hvplot = hvPlot(self.da_img_by_time, sel={'time': 1})
|
|
74
|
+
assert hvplot._data.ndim == 2
|