iqplot 0.3.8__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.
- iqplot/__init__.py +10 -0
- iqplot/cat.py +1838 -0
- iqplot/dist.py +2764 -0
- iqplot/utils.py +516 -0
- iqplot-0.3.8.dist-info/METADATA +41 -0
- iqplot-0.3.8.dist-info/RECORD +8 -0
- iqplot-0.3.8.dist-info/WHEEL +4 -0
- iqplot-0.3.8.dist-info/licenses/LICENSE +21 -0
iqplot/utils.py
ADDED
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
"""Utility functions for parsing inputs."""
|
|
2
|
+
|
|
3
|
+
import copy
|
|
4
|
+
import warnings
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pandas as pd
|
|
8
|
+
|
|
9
|
+
import bokeh.core.enums
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _fig_dimensions(kwargs):
|
|
13
|
+
if (
|
|
14
|
+
"width" not in kwargs
|
|
15
|
+
and "plot_width" not in kwargs
|
|
16
|
+
and "frame_width" not in kwargs
|
|
17
|
+
):
|
|
18
|
+
kwargs["frame_width"] = 375
|
|
19
|
+
if (
|
|
20
|
+
"height" not in kwargs
|
|
21
|
+
and "plot_height" not in kwargs
|
|
22
|
+
and "frame_height" not in kwargs
|
|
23
|
+
):
|
|
24
|
+
kwargs["frame_height"] = 275
|
|
25
|
+
|
|
26
|
+
if "toolbar_location" not in kwargs:
|
|
27
|
+
kwargs["toolbar_location"] = "above"
|
|
28
|
+
|
|
29
|
+
return kwargs
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _parse_deprecations(
|
|
33
|
+
q,
|
|
34
|
+
q_axis,
|
|
35
|
+
val,
|
|
36
|
+
horizontal,
|
|
37
|
+
horiz_q_axis,
|
|
38
|
+
click_policy,
|
|
39
|
+
legend_click_policy,
|
|
40
|
+
conf_int_kwargs,
|
|
41
|
+
fill_kwargs,
|
|
42
|
+
):
|
|
43
|
+
if q_axis not in ("x", "y"):
|
|
44
|
+
raise RuntimeError("Invalid `q_axis`. Must be 'x' or 'y'.")
|
|
45
|
+
|
|
46
|
+
if horizontal is not None:
|
|
47
|
+
if (horizontal and q_axis != horiz_q_axis) or (
|
|
48
|
+
not horizontal and q_axis == horiz_q_axis
|
|
49
|
+
):
|
|
50
|
+
raise RuntimeError(
|
|
51
|
+
"`horizontal` and `q_axis` kwargs in disagreement. "
|
|
52
|
+
"Use `q_axis`; `horizontal` is deprecated."
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
warnings.warn(f"`horizontal` is deprecated. Use `q_axis`.", DeprecationWarning)
|
|
56
|
+
|
|
57
|
+
if val is not None:
|
|
58
|
+
if q is None:
|
|
59
|
+
q = val
|
|
60
|
+
elif q != val:
|
|
61
|
+
raise RuntimeError(
|
|
62
|
+
"`val` and `q` in disagreement. Use `q`; `val` is deprecated."
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
warnings.warn(f"`val` is deprecated. Use `q`. Using q={q}.", DeprecationWarning)
|
|
66
|
+
|
|
67
|
+
if click_policy is not None:
|
|
68
|
+
if legend_click_policy is None:
|
|
69
|
+
legend_click_policy = click_policy
|
|
70
|
+
elif click_policy != legend_click_policy:
|
|
71
|
+
raise RuntimeError(
|
|
72
|
+
"`click_policy` and `legend_click_policy` in disagreement. Use `legend_click_policy`; `click_policy` is deprecated."
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
warnings.warn(
|
|
76
|
+
f"`click_policy` is deprecated. Use `legend_click_policy`. Using legend_click_policy='{legend_click_policy}'."
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
if conf_int_kwargs is not None:
|
|
80
|
+
if fill_kwargs is None:
|
|
81
|
+
fill_kwargs = copy.copy(conf_int_kwargs)
|
|
82
|
+
elif conf_int_kwargs != fill_kwargs:
|
|
83
|
+
raise RuntimeError(
|
|
84
|
+
"`fill_kwargs` and `conf_int_kwargs` in disagreement. Use `fill_kwargs`; `conf_int_kwargs` is deprecated."
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
warnings.warn(f"`conf_int_kwargs is deprecated. Use `fill_kwargs`.")
|
|
88
|
+
|
|
89
|
+
return q, legend_click_policy, fill_kwargs
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _check_cats_none(cats, order, show_legend=False, legend_label=None):
|
|
93
|
+
"""Check for kwargs that are disallowed when `cats` is None.
|
|
94
|
+
|
|
95
|
+
Must be called before `_data_cats()`, which replaces a `cats` of
|
|
96
|
+
None with a dummy categorical variable. `show_legend` and
|
|
97
|
+
`legend_label` are omitted for plots whose legend is not built from
|
|
98
|
+
`cats`, in which case only `order` is checked.
|
|
99
|
+
"""
|
|
100
|
+
if cats is None:
|
|
101
|
+
if show_legend and legend_label is None:
|
|
102
|
+
raise RuntimeError(
|
|
103
|
+
"No legend to show if `cats` and `legend_label` are None."
|
|
104
|
+
)
|
|
105
|
+
if order is not None:
|
|
106
|
+
raise RuntimeError("No `order` is allowed if `cats` is None.")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _data_cats(data, q, cats, show_legend, legend_label):
|
|
110
|
+
if "xarray.core.dataarray.DataArray" in str(type(data)):
|
|
111
|
+
if q is None:
|
|
112
|
+
if data.name is None:
|
|
113
|
+
q = "x"
|
|
114
|
+
else:
|
|
115
|
+
q = data.name
|
|
116
|
+
data = pd.DataFrame({q: data.squeeze().values})
|
|
117
|
+
elif isinstance(data, np.ndarray):
|
|
118
|
+
if q is None:
|
|
119
|
+
q = "x"
|
|
120
|
+
data = pd.DataFrame({q: data.squeeze()})
|
|
121
|
+
if cats is not None:
|
|
122
|
+
raise RuntimeError("If `data` is a Numpy array, `cats` must be None.")
|
|
123
|
+
elif "polars.dataframe.frame.DataFrame" in str(type(data)):
|
|
124
|
+
# For now, we just convert to Pandas. We will add functionality
|
|
125
|
+
# to work with polars data frames to take advantage of their
|
|
126
|
+
# performance in the future.
|
|
127
|
+
data = data.to_pandas()
|
|
128
|
+
elif "polars.series.series.Series" in str(type(data)) or isinstance(
|
|
129
|
+
data, pd.Series
|
|
130
|
+
):
|
|
131
|
+
# For now, just convert to Pandas series if it's a Polars series
|
|
132
|
+
if "polars.series.series.Series" in str(type(data)):
|
|
133
|
+
data = data.to_pandas()
|
|
134
|
+
if q is None:
|
|
135
|
+
if data.name is None:
|
|
136
|
+
q = "x"
|
|
137
|
+
else:
|
|
138
|
+
q = data.name
|
|
139
|
+
data = pd.DataFrame({q: data})
|
|
140
|
+
if cats is not None:
|
|
141
|
+
raise RuntimeError("If `data` is a Pandas series, `cats` must be None.")
|
|
142
|
+
elif not isinstance(data, pd.DataFrame):
|
|
143
|
+
raise RuntimeError(
|
|
144
|
+
f"Data type {type(data)} for argument `data` is not supported."
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
# Make a copy of the data frame
|
|
148
|
+
data = data.copy()
|
|
149
|
+
|
|
150
|
+
# A tuple or Index of column names is converted to a list. Pandas
|
|
151
|
+
# takes a tuple to be a single column name and an Index to be a
|
|
152
|
+
# grouping vector, neither of which is meant here.
|
|
153
|
+
if isinstance(cats, (tuple, pd.Index)):
|
|
154
|
+
cats = list(cats)
|
|
155
|
+
|
|
156
|
+
if cats is None:
|
|
157
|
+
if legend_label is None:
|
|
158
|
+
data["__dummy_cat"] = " "
|
|
159
|
+
show_legend = False
|
|
160
|
+
else:
|
|
161
|
+
data["__dummy_cat"] = legend_label
|
|
162
|
+
cats = "__dummy_cat"
|
|
163
|
+
|
|
164
|
+
# Ensure the categorical columns are present and have data type str
|
|
165
|
+
for cat in cats if isinstance(cats, (list, tuple)) else [cats]:
|
|
166
|
+
if cat not in data.columns:
|
|
167
|
+
raise RuntimeError(f"{cat} is not a column in the inputted data frame")
|
|
168
|
+
data[cat] = data[cat].astype(str)
|
|
169
|
+
# data.loc[:, cat] = data.loc[:, cat].astype(str)
|
|
170
|
+
|
|
171
|
+
return data, q, cats, show_legend
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _order_to_str(order):
|
|
175
|
+
"""Convert entries in `order` to strings."""
|
|
176
|
+
if order is None:
|
|
177
|
+
return order
|
|
178
|
+
|
|
179
|
+
order = list(order)
|
|
180
|
+
|
|
181
|
+
for i, item in enumerate(order):
|
|
182
|
+
if not isinstance(item, (list, tuple, np.ndarray)):
|
|
183
|
+
order[i] = str(order[i])
|
|
184
|
+
else:
|
|
185
|
+
order[i] = tuple([str(x) for x in item])
|
|
186
|
+
|
|
187
|
+
return order
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _fill_between(p, x1=None, y1=None, x2=None, y2=None, **kwargs):
|
|
191
|
+
"""
|
|
192
|
+
Create a filled region between two curves.
|
|
193
|
+
|
|
194
|
+
Parameters
|
|
195
|
+
----------
|
|
196
|
+
p : bokeh.plotting.Figure instance
|
|
197
|
+
Figure to be populated.
|
|
198
|
+
x1 : array_like
|
|
199
|
+
Array of x-values for first curve
|
|
200
|
+
y1 : array_like
|
|
201
|
+
Array of y-values for first curve
|
|
202
|
+
x2 : array_like
|
|
203
|
+
Array of x-values for second curve
|
|
204
|
+
y2 : array_like
|
|
205
|
+
Array of y-values for second curve
|
|
206
|
+
kwargs
|
|
207
|
+
Any kwargs passed to p.patch.
|
|
208
|
+
|
|
209
|
+
Returns
|
|
210
|
+
-------
|
|
211
|
+
output : bokeh.plotting.Figure instance
|
|
212
|
+
Plot populated with fill-between.
|
|
213
|
+
|
|
214
|
+
"""
|
|
215
|
+
x = list(x1) + list(x2)[::-1]
|
|
216
|
+
y = list(y1) + list(y2)[::-1]
|
|
217
|
+
patch = p.patch(x=x, y=y, **kwargs)
|
|
218
|
+
|
|
219
|
+
# Old way; only works for Numpy arrays
|
|
220
|
+
# patch = p.patch(
|
|
221
|
+
# x=np.concatenate((x1, x2[::-1])), y=np.concatenate((y1, y2[::-1])), **kwargs
|
|
222
|
+
# )
|
|
223
|
+
|
|
224
|
+
return p, patch
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _check_marker_kwargs(marker_kwargs):
|
|
228
|
+
if marker_kwargs is None:
|
|
229
|
+
marker_kwargs = {}
|
|
230
|
+
elif not isinstance(marker_kwargs, dict):
|
|
231
|
+
raise RuntimeError("`marker_kwargs` must be a dict.")
|
|
232
|
+
|
|
233
|
+
if "marker" in marker_kwargs:
|
|
234
|
+
raise RuntimeError(
|
|
235
|
+
"'marker' cannot be a key in `marker_kwargs`. Specify using the `marker` kwargs instead."
|
|
236
|
+
)
|
|
237
|
+
if "source" in marker_kwargs:
|
|
238
|
+
raise RuntimeError("'source' cannot be a key in `marker_kwargs`.")
|
|
239
|
+
if "x" in marker_kwargs:
|
|
240
|
+
raise RuntimeError("'x' cannot be a key in `marker_kwargs`.")
|
|
241
|
+
if "y" in marker_kwargs:
|
|
242
|
+
raise RuntimeError("'y' cannot be a key in `marker_kwargs`.")
|
|
243
|
+
if "cat" in marker_kwargs:
|
|
244
|
+
raise RuntimeError("'cat' cannot be a key in `marker_kwargs`.")
|
|
245
|
+
if "legend" in marker_kwargs:
|
|
246
|
+
raise RuntimeError("'legend' cannot be a key in `marker_kwargs`.")
|
|
247
|
+
if "legend_label" in marker_kwargs:
|
|
248
|
+
raise RuntimeError("'legend_label' cannot be a key in `marker_kwargs`.")
|
|
249
|
+
|
|
250
|
+
return marker_kwargs
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _check_marker(marker):
|
|
254
|
+
if marker not in bokeh.core.enums.MarkerType:
|
|
255
|
+
err_str = (
|
|
256
|
+
f"{marker} is an invalid marker specification. Acceptable values are ["
|
|
257
|
+
)
|
|
258
|
+
for marker in list(bokeh.core.enums.MarkerType)[:-1]:
|
|
259
|
+
err_str += f"{marker}, "
|
|
260
|
+
err_str += list(bokeh.core.enums.MarkerType)[-1] + "]."
|
|
261
|
+
|
|
262
|
+
raise RuntimeError(err_str)
|
|
263
|
+
|
|
264
|
+
return marker
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _source_and_labels_from_cats(df, cats):
|
|
268
|
+
if isinstance(cats, (list, tuple)):
|
|
269
|
+
cat_source = list(zip(*tuple([df[cat].astype(str) for cat in cats])))
|
|
270
|
+
return cat_source, [", ".join(cat) for cat in cat_source]
|
|
271
|
+
else:
|
|
272
|
+
cat_source = list(df[cats].astype(str).values)
|
|
273
|
+
return cat_source, cat_source
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _tooltip_cols(tooltips):
|
|
277
|
+
if tooltips is None:
|
|
278
|
+
return []
|
|
279
|
+
if not isinstance(tooltips, (list, tuple)):
|
|
280
|
+
raise RuntimeError("`tooltips` must be a list or tuple of two-tuples.")
|
|
281
|
+
|
|
282
|
+
cols = []
|
|
283
|
+
for tip in tooltips:
|
|
284
|
+
if not isinstance(tip, (list, tuple)) or len(tip) != 2:
|
|
285
|
+
raise RuntimeError("Invalid tooltip.")
|
|
286
|
+
if tip[1][0] == "@":
|
|
287
|
+
if tip[1][1] == "{":
|
|
288
|
+
cols.append(tip[1][2 : tip[1].find("}")])
|
|
289
|
+
elif "{" in tip[1]:
|
|
290
|
+
cols.append(tip[1][1 : tip[1].find("{")])
|
|
291
|
+
else:
|
|
292
|
+
cols.append(tip[1][1:])
|
|
293
|
+
|
|
294
|
+
return cols
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def _cols_to_keep(cats, q, color_column, tooltips):
|
|
298
|
+
cols = _tooltip_cols(tooltips)
|
|
299
|
+
cols += [q]
|
|
300
|
+
|
|
301
|
+
if isinstance(cats, (list, tuple)):
|
|
302
|
+
cols += list(cats)
|
|
303
|
+
else:
|
|
304
|
+
cols += [cats]
|
|
305
|
+
|
|
306
|
+
if color_column is not None:
|
|
307
|
+
cols += [color_column]
|
|
308
|
+
|
|
309
|
+
return list(set(cols))
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def _check_cat_input(
|
|
313
|
+
df, cats, q, color_column, parcoord_column, tooltips, palette, order, kwargs
|
|
314
|
+
):
|
|
315
|
+
if df is None:
|
|
316
|
+
raise RuntimeError("`df` argument must be provided.")
|
|
317
|
+
if cats is None:
|
|
318
|
+
raise RuntimeError("`cats` argument must be provided.")
|
|
319
|
+
if q is None:
|
|
320
|
+
raise RuntimeError("`q` argument must be provided.")
|
|
321
|
+
|
|
322
|
+
if not isinstance(palette, (list, tuple, str)):
|
|
323
|
+
raise RuntimeError("`palette` must be a list, tuple or string.")
|
|
324
|
+
|
|
325
|
+
if q not in df.columns:
|
|
326
|
+
raise RuntimeError(f"{q} is not a column in the inputted data frame")
|
|
327
|
+
|
|
328
|
+
cats_array = isinstance(cats, (list, tuple))
|
|
329
|
+
if cats_array and len(cats) == 1:
|
|
330
|
+
cats = cats[0]
|
|
331
|
+
cats_array = False
|
|
332
|
+
|
|
333
|
+
if cats_array:
|
|
334
|
+
for cat in cats:
|
|
335
|
+
if cat not in df.columns:
|
|
336
|
+
raise RuntimeError(f"{cat} is not a column in the inputted data frame")
|
|
337
|
+
else:
|
|
338
|
+
if isinstance(cats, tuple):
|
|
339
|
+
raise RuntimeError(
|
|
340
|
+
"Cannot have tuples as data frame column names if there is only one categorical variable."
|
|
341
|
+
)
|
|
342
|
+
if cats not in df.columns:
|
|
343
|
+
raise RuntimeError(f"{cats} is not a column in the inputted data frame")
|
|
344
|
+
|
|
345
|
+
if color_column is not None and color_column not in df.columns:
|
|
346
|
+
raise RuntimeError(f"{color_column} is not a column in the inputted data frame")
|
|
347
|
+
|
|
348
|
+
cols = _cols_to_keep(cats, q, color_column, tooltips)
|
|
349
|
+
|
|
350
|
+
for col in cols:
|
|
351
|
+
if col not in df.columns:
|
|
352
|
+
raise RuntimeError(f"{col} is not a column in the inputted data frame")
|
|
353
|
+
|
|
354
|
+
bad_kwargs = ["x", "y", "source", "cat", "legend"]
|
|
355
|
+
if kwargs is not None and any([key in kwargs for key in bad_kwargs]):
|
|
356
|
+
raise RuntimeError(", ".join(bad_kwargs) + " are not allowed kwargs.")
|
|
357
|
+
|
|
358
|
+
if q == "cat":
|
|
359
|
+
raise RuntimeError("`'cat'` cannot be used as `q`.")
|
|
360
|
+
|
|
361
|
+
if q == "__label" or (cats == "__label" or (cats_array and "__label" in cats)):
|
|
362
|
+
raise RuntimeError("'__label' cannot be used for `q` or `cats`.")
|
|
363
|
+
|
|
364
|
+
if order is not None:
|
|
365
|
+
grouped = df.groupby(cats)
|
|
366
|
+
if grouped.ngroups > len(order):
|
|
367
|
+
raise RuntimeError(
|
|
368
|
+
"`order` must have at least as many elements as the number of unique groups in `cats`."
|
|
369
|
+
)
|
|
370
|
+
for entry in order:
|
|
371
|
+
if entry not in grouped.groups.keys():
|
|
372
|
+
raise RuntimeError(
|
|
373
|
+
f"Entry {entry} in `order` but not present as a group in the inputted data."
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
if parcoord_column is not None:
|
|
377
|
+
if parcoord_column not in df.columns:
|
|
378
|
+
raise RuntimeError(
|
|
379
|
+
f"{parcoord_column} is not a column in the inputted data frame"
|
|
380
|
+
)
|
|
381
|
+
if cats == "__dummy_cat":
|
|
382
|
+
raise RuntimeError(
|
|
383
|
+
"`cats` must be provided in `parcoord_column` is provided."
|
|
384
|
+
)
|
|
385
|
+
grouped = df.groupby(parcoord_column)
|
|
386
|
+
|
|
387
|
+
return cats, cols
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def _specific_fill_and_color_kwargs(kwargs, kwarg_type):
|
|
391
|
+
if "color" in kwargs:
|
|
392
|
+
if "fill_color" in kwargs or "line_color" in kwargs:
|
|
393
|
+
raise RuntimeError(
|
|
394
|
+
"Specifing both color and fill_color or line_color in a set of kwargs is ambiguous."
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
kwargs["line_color"] = kwargs["color"]
|
|
398
|
+
|
|
399
|
+
if kwarg_type != "line":
|
|
400
|
+
kwargs["fill_color"] = kwargs["color"]
|
|
401
|
+
|
|
402
|
+
del kwargs["color"]
|
|
403
|
+
|
|
404
|
+
if "alpha" in kwargs:
|
|
405
|
+
if "fill_alpha" in kwargs or "line_alpha" in kwargs:
|
|
406
|
+
raise RuntimeError(
|
|
407
|
+
"Specifing both alpha and fill_alpha or line_alpha in a set of kwargs is ambiguous."
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
if kwarg_type != "fill":
|
|
411
|
+
kwargs["line_alpha"] = kwargs["alpha"]
|
|
412
|
+
|
|
413
|
+
if kwarg_type != "line":
|
|
414
|
+
kwargs["fill_alpha"] = kwargs["alpha"]
|
|
415
|
+
|
|
416
|
+
del kwargs["alpha"]
|
|
417
|
+
|
|
418
|
+
return kwargs
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def _convert_data(data, inf_ok=False, min_len=1):
|
|
422
|
+
"""
|
|
423
|
+
Convert inputted 1D data set into NumPy array of floats.
|
|
424
|
+
All NaNs are dropped.
|
|
425
|
+
|
|
426
|
+
Parameters
|
|
427
|
+
----------
|
|
428
|
+
data : int, float, or array_like
|
|
429
|
+
Input data, to be converted.
|
|
430
|
+
inf_ok : bool, default False
|
|
431
|
+
If True, np.inf values are allowed in the arrays.
|
|
432
|
+
min_len : int, default 1
|
|
433
|
+
Minimum length of array.
|
|
434
|
+
|
|
435
|
+
Returns
|
|
436
|
+
-------
|
|
437
|
+
output : ndarray
|
|
438
|
+
`data` as a one-dimensional NumPy array, dtype float.
|
|
439
|
+
"""
|
|
440
|
+
# If it's scalar, convert to array
|
|
441
|
+
if np.isscalar(data):
|
|
442
|
+
data = np.array([data], dtype=np.float64)
|
|
443
|
+
|
|
444
|
+
# Convert data to NumPy array
|
|
445
|
+
data = np.array(data, dtype=np.float64)
|
|
446
|
+
|
|
447
|
+
# Make sure it is 1D
|
|
448
|
+
if len(data.shape) != 1:
|
|
449
|
+
raise RuntimeError("Input must be a 1D array or Pandas series.")
|
|
450
|
+
|
|
451
|
+
# Remove NaNs
|
|
452
|
+
data = data[~np.isnan(data)]
|
|
453
|
+
|
|
454
|
+
# Check for infinite entries
|
|
455
|
+
if not inf_ok and np.isinf(data).any():
|
|
456
|
+
raise RuntimeError("All entries must be finite.")
|
|
457
|
+
|
|
458
|
+
# Check for minimal length
|
|
459
|
+
if len(data) < min_len:
|
|
460
|
+
raise RuntimeError(
|
|
461
|
+
"Array must have at least {0:d} non-NaN entries.".format(min_len)
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
return data
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def _edge_value_given(p_edge_value):
|
|
468
|
+
|
|
469
|
+
ret_val = True
|
|
470
|
+
|
|
471
|
+
if p_edge_value is None:
|
|
472
|
+
ret_val = False
|
|
473
|
+
else:
|
|
474
|
+
try:
|
|
475
|
+
if np.isnan(p_edge_value):
|
|
476
|
+
ret_val = False
|
|
477
|
+
else:
|
|
478
|
+
ret_val = True
|
|
479
|
+
except:
|
|
480
|
+
ret_val = True
|
|
481
|
+
|
|
482
|
+
return ret_val
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
def _range_specified(axis_range):
|
|
486
|
+
"""
|
|
487
|
+
Missing x_range and y_range start and end values are None in
|
|
488
|
+
Bokeh 2.x and np.nan in Bokeh 3.x. This checks to see if the start
|
|
489
|
+
and end attributes of a Range1d instance are None or nan and returns
|
|
490
|
+
True if not.
|
|
491
|
+
"""
|
|
492
|
+
return _edge_value_given(axis_range.start), _edge_value_given(axis_range.end)
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def _dummy_jit(*args, **kwargs):
|
|
496
|
+
"""Dummy wrapper for jitting if numba not applicable."""
|
|
497
|
+
|
|
498
|
+
def wrapper(f):
|
|
499
|
+
return f
|
|
500
|
+
|
|
501
|
+
def marker(*args, **kwargs):
|
|
502
|
+
return marker
|
|
503
|
+
|
|
504
|
+
if (
|
|
505
|
+
len(args) > 0
|
|
506
|
+
and (args[0] is marker or not callable(args[0]))
|
|
507
|
+
or len(kwargs) > 0
|
|
508
|
+
):
|
|
509
|
+
# @jit(int32(int32, int32)), @jit(signature="void(int32)")
|
|
510
|
+
return wrapper
|
|
511
|
+
elif len(args) == 0:
|
|
512
|
+
# @jit()
|
|
513
|
+
return wrapper
|
|
514
|
+
else:
|
|
515
|
+
# @jit
|
|
516
|
+
return args[0]
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: iqplot
|
|
3
|
+
Version: 0.3.8
|
|
4
|
+
Summary: Generate Bokeh plots for data sets with one quantitative variable.
|
|
5
|
+
Project-URL: Homepage, https://github.com/justinbois/iqplot
|
|
6
|
+
Project-URL: Documentation, http://iqplot.github.io/
|
|
7
|
+
Project-URL: Repository, https://github.com/justinbois/iqplot
|
|
8
|
+
Project-URL: Issues, https://github.com/justinbois/iqplot/issues
|
|
9
|
+
Author-email: Justin Bois <bois@caltech.edu>
|
|
10
|
+
Maintainer-email: Justin Bois <bois@caltech.edu>
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: bokeh,box plot,ecdf,histogram,plotting,visualization
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: Science/Research
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: bokeh>=3.0
|
|
25
|
+
Requires-Dist: colorcet
|
|
26
|
+
Requires-Dist: numpy
|
|
27
|
+
Requires-Dist: pandas
|
|
28
|
+
Provides-Extra: data
|
|
29
|
+
Requires-Dist: polars; extra == 'data'
|
|
30
|
+
Requires-Dist: xarray; extra == 'data'
|
|
31
|
+
Provides-Extra: speedups
|
|
32
|
+
Requires-Dist: numba; extra == 'speedups'
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# iqplot
|
|
36
|
+
|
|
37
|
+
[](https://doi.org/10.22002/D1.20286)
|
|
38
|
+
|
|
39
|
+
A utility to use Bokeh to generate plots for data sets containing one quantitative variable and an arbitrary many categorical variables. It generates strip plots, box plots, histograms, and ECDFs.
|
|
40
|
+
|
|
41
|
+
Read the [documentation](http://iqplot.github.io/) for details.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
iqplot/__init__.py,sha256=8FGWpx8kin9c9ZYyEdyWrhQh3L_RB_9S1Y5G0DmVCVs,186
|
|
2
|
+
iqplot/cat.py,sha256=A-skxz_LmnXPqWo-mc8y8tW_sQZu3cYM2o8PI5MuDgY,66255
|
|
3
|
+
iqplot/dist.py,sha256=yuFl97IxcuhdnCQE3pG_AwLm4rkEZul5atgbDWWBjAY,96684
|
|
4
|
+
iqplot/utils.py,sha256=_SkYNlna-XDrPzcmWuN0h8FUbw5VJj58GqOK4U6PemA,16040
|
|
5
|
+
iqplot-0.3.8.dist-info/METADATA,sha256=ciPIjiBStfdatwvSvmtZBiT330SR_YPEPrwEBqb7trM,1718
|
|
6
|
+
iqplot-0.3.8.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
7
|
+
iqplot-0.3.8.dist-info/licenses/LICENSE,sha256=odDJ-SW1-H_SrEZaQx1g7hqC4aaujIVEhPOcH93C8Jo,1068
|
|
8
|
+
iqplot-0.3.8.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 Justin Bois
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|