lessPython 0.2.0__py3-none-any.whl → 0.3.0__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.
- lessPy/ANOVA.py +5 -5
- lessPy/Chart.py +386 -88
- lessPy/Correlation.py +2 -2
- lessPy/Logit.py +2 -2
- lessPy/Regression.py +6 -4
- lessPy/X.py +279 -42
- lessPy/XY.py +814 -158
- lessPy/__init__.py +12 -2
- lessPy/_themes.py +5007 -0
- lessPy/bc_items_plotly.py +31 -18
- lessPy/bc_plotly.py +23 -7
- lessPy/bubble_plotly.py +17 -2
- lessPy/corCFA.py +3 -3
- lessPy/corProp.py +2 -2
- lessPy/corReflect.py +2 -2
- lessPy/corReorder.py +2 -2
- lessPy/corScree.py +3 -3
- lessPy/data/StockPrice.csv +1401 -1386
- lessPy/details.py +17 -4
- lessPy/dn_plotly.py +65 -45
- lessPy/freq_poly_plotly.py +12 -5
- lessPy/getColors.py +247 -20
- lessPy/hier_plotly.py +36 -9
- lessPy/hs_plotly.py +16 -4
- lessPy/plotly_utils.py +78 -9
- lessPy/plt_mat_plotly.py +74 -2
- lessPy/plt_time.py +10 -3
- lessPy/prob_norm.py +3 -3
- lessPy/prob_tcut.py +3 -3
- lessPy/prob_znorm.py +3 -1
- lessPy/rcolors.py +461 -0
- lessPy/simCImean.py +3 -3
- lessPy/simCLT.py +3 -3
- lessPy/simFlips.py +3 -3
- lessPy/simMeans.py +3 -3
- lessPy/stats_out.py +257 -11
- lessPy/style.py +160 -0
- lessPy/sunflower_plotly.py +140 -0
- lessPy/ttest.py +5 -5
- lessPy/utils.py +43 -6
- lessPy/vbs_plotly.py +16 -5
- lessPy/x_console.py +763 -0
- {lesspython-0.2.0.dist-info → lesspython-0.3.0.dist-info}/METADATA +6 -2
- lesspython-0.3.0.dist-info/RECORD +89 -0
- lesspython-0.2.0.dist-info/RECORD +0 -84
- {lesspython-0.2.0.dist-info → lesspython-0.3.0.dist-info}/WHEEL +0 -0
- {lesspython-0.2.0.dist-info → lesspython-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {lesspython-0.2.0.dist-info → lesspython-0.3.0.dist-info}/top_level.txt +0 -0
lessPy/Correlation.py
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import numpy as np
|
|
13
13
|
import pandas as pd
|
|
14
14
|
|
|
15
|
-
from .utils import fmt, get_column, get_option
|
|
15
|
+
from .utils import fmt, get_column, get_option, show_plots
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class CorrelationResults:
|
|
@@ -222,7 +222,7 @@ def _matrix(df, miss, show, method, digits_d, heat_map, main):
|
|
|
222
222
|
if heat_map:
|
|
223
223
|
plots["heatmap"] = _heatmap(crs, main)
|
|
224
224
|
crs.attrs["plots"] = plots
|
|
225
|
-
return crs
|
|
225
|
+
return show_plots(crs)
|
|
226
226
|
|
|
227
227
|
|
|
228
228
|
def _heatmap(df, main):
|
lessPy/Logit.py
CHANGED
|
@@ -42,7 +42,7 @@ from .plt_mat_plotly import scatter_matrix
|
|
|
42
42
|
from .logit_rmd import logit_rmd
|
|
43
43
|
from .Regression import (
|
|
44
44
|
_expand_indicators, _parse_formula, _prntbl)
|
|
45
|
-
from .utils import fmt, get_column, get_option, pretty
|
|
45
|
+
from .utils import fmt, get_column, get_option, pretty, show_plots
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
class LogitResults:
|
|
@@ -484,7 +484,7 @@ def Logit(my_formula, data=None, filter=None, ref_group=None,
|
|
|
484
484
|
list(data.columns), Rmd, Rmd_data, Rmd_format,
|
|
485
485
|
Rmd_browser, results, explain, interpret, code)
|
|
486
486
|
|
|
487
|
-
return out
|
|
487
|
+
return show_plots(out)
|
|
488
488
|
|
|
489
489
|
|
|
490
490
|
def _logit_confuse(lines, y01, fitv, pc, y_name, lv2_txt,
|
lessPy/Regression.py
CHANGED
|
@@ -31,8 +31,9 @@
|
|
|
31
31
|
# Numerics through statsmodels OLS (imported lazily, as
|
|
32
32
|
# plt_forecast does); influence measures from OLSInfluence.
|
|
33
33
|
# Returns a RegressionResults object holding the tables as
|
|
34
|
-
# DataFrames and the plotly figures in .plots
|
|
35
|
-
#
|
|
34
|
+
# DataFrames and the plotly figures in .plots. In a notebook the
|
|
35
|
+
# figures also display as the analysis runs, as lessR draws them;
|
|
36
|
+
# in a script they are not shown (utils.show_plots).
|
|
36
37
|
|
|
37
38
|
import math
|
|
38
39
|
import re
|
|
@@ -49,7 +50,8 @@ from .plotly_utils import (
|
|
|
49
50
|
BASE_COLORS, as_plotly_color, axis_format, axis_num,
|
|
50
51
|
make_trans, plot_border, plotly_style, to_hex, x_grid)
|
|
51
52
|
from .utils import (
|
|
52
|
-
category_order, fmt, get_column, get_option, pretty
|
|
53
|
+
category_order, fmt, get_column, get_option, pretty,
|
|
54
|
+
show_plots)
|
|
53
55
|
from .X import _breaks_from_args
|
|
54
56
|
|
|
55
57
|
|
|
@@ -1001,7 +1003,7 @@ def Regression(my_formula, data=None, filter=None, digits_d=None,
|
|
|
1001
1003
|
Rmd_browser, results, explain, interpret, code,
|
|
1002
1004
|
n_res_rows, n_pred_rows, res_sort, d)
|
|
1003
1005
|
|
|
1004
|
-
return out
|
|
1006
|
+
return show_plots(out)
|
|
1005
1007
|
|
|
1006
1008
|
|
|
1007
1009
|
def _reg_kfold(yv, Xd, y_name, pred_names, n_keep, formula,
|
lessPy/X.py
CHANGED
|
@@ -49,13 +49,16 @@ from scipy import stats as sps
|
|
|
49
49
|
from .dn_plotly import dn_plotly
|
|
50
50
|
from .freq_poly_plotly import freq_poly_plotly
|
|
51
51
|
from .hs_plotly import hs_plotly
|
|
52
|
-
from .plotly_utils import
|
|
52
|
+
from .plotly_utils import (
|
|
53
|
+
BASE_COLORS, axis_format, by_colors, font_scaled,
|
|
54
|
+
)
|
|
53
55
|
from .plt_add import plt_add
|
|
54
|
-
from .
|
|
56
|
+
from . import x_console as xc
|
|
57
|
+
from .stats_out import CALL_STATS, record_stats, resolve_quiet, x_stats
|
|
55
58
|
from .vbs_plotly import vbs_plotly
|
|
56
59
|
from .utils import (
|
|
57
|
-
|
|
58
|
-
resolve_facet,
|
|
60
|
+
band_width, category_order, get_column, get_option, pretty,
|
|
61
|
+
resolve_facet, set_option,
|
|
59
62
|
)
|
|
60
63
|
|
|
61
64
|
# the VBS family: single-element v/b/s, and the composites. "vb"
|
|
@@ -63,7 +66,20 @@ from .utils import (
|
|
|
63
66
|
# (deprecated) vbs_plot= subset string; here they are just forms.
|
|
64
67
|
_VBS_FORMS = ("violin", "box", "strip", "vb", "vs", "bs", "vbs")
|
|
65
68
|
_FORMS = ("histogram", "freq_poly", "density") + _VBS_FORMS
|
|
66
|
-
_STATS = ("count", "proportion"
|
|
69
|
+
_STATS = ("count", "proportion")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _norm_form(form):
|
|
73
|
+
"""Any one to three of v b s, in any order and any case, names
|
|
74
|
+
those VBS layers: normalize to the canonical order v b s, a
|
|
75
|
+
lone letter to the full name of its layer (X.R)."""
|
|
76
|
+
if isinstance(form, str):
|
|
77
|
+
ltr = list(form.strip().lower())
|
|
78
|
+
if (1 <= len(ltr) <= 3 and set(ltr) <= set("vbs")
|
|
79
|
+
and len(set(ltr)) == len(ltr)):
|
|
80
|
+
f = "".join(c for c in "vbs" if c in ltr)
|
|
81
|
+
return {"v": "violin", "b": "box", "s": "strip"}.get(f, f)
|
|
82
|
+
return form
|
|
67
83
|
|
|
68
84
|
|
|
69
85
|
def _breaks_from_args(fx, bin_start, bin_width, bin_end, breaks):
|
|
@@ -102,7 +118,7 @@ def _x_finish(fig, rotate_x, rotate_y, scale_x, axis_fmt,
|
|
|
102
118
|
angles. R analogs: scale_x, rotate_x, rotate_y."""
|
|
103
119
|
if scale_x is not None:
|
|
104
120
|
tv = np.linspace(float(scale_x[0]), float(scale_x[1]),
|
|
105
|
-
int(scale_x[2]))
|
|
121
|
+
int(scale_x[2]) + 1) # n intervals, as R
|
|
106
122
|
fig.update_xaxes(
|
|
107
123
|
tickvals=tv,
|
|
108
124
|
ticktext=axis_format(tv, digits_d, axis_fmt,
|
|
@@ -117,7 +133,7 @@ def _x_finish(fig, rotate_x, rotate_y, scale_x, axis_fmt,
|
|
|
117
133
|
|
|
118
134
|
def X(x, by=None, facet=None, data=None, filter=None,
|
|
119
135
|
form="histogram",
|
|
120
|
-
stat=
|
|
136
|
+
stat=None,
|
|
121
137
|
fill=None, color=None,
|
|
122
138
|
bin_start=None, bin_width=None, bin_end=None,
|
|
123
139
|
breaks="Sturges",
|
|
@@ -142,23 +158,44 @@ def X(x, by=None, facet=None, data=None, filter=None,
|
|
|
142
158
|
quiet=None):
|
|
143
159
|
"""Analytic view of the distribution of one numerical variable,
|
|
144
160
|
optionally grouped (by=). Variables are strings naming columns
|
|
145
|
-
of the DataFrame `data`. Returns a plotly Figure
|
|
161
|
+
of the DataFrame `data`. Returns a plotly Figure, or a list of
|
|
162
|
+
Figures, one per variable, when x is a list of names.
|
|
146
163
|
"""
|
|
147
164
|
|
|
165
|
+
form = _norm_form(form)
|
|
148
166
|
if form not in _FORMS:
|
|
149
|
-
raise ValueError(
|
|
150
|
-
|
|
151
|
-
|
|
167
|
+
raise ValueError(
|
|
168
|
+
"form must be one of: "
|
|
169
|
+
+ ", ".join(f'"{f}"' for f in _FORMS) + "\n\n"
|
|
170
|
+
"Any subset of the violin, box, and strip layers is a "
|
|
171
|
+
"form, named\nby its letters in any order and any case, "
|
|
172
|
+
'as in form="vb" or\nform="SV". A single layer may also '
|
|
173
|
+
'be named in full, as in\nform="violin".')
|
|
174
|
+
if stat is not None:
|
|
175
|
+
if stat not in _STATS:
|
|
176
|
+
raise ValueError(f"stat must be one of {_STATS}")
|
|
177
|
+
# stat names a frequency of the binned data, which only the
|
|
178
|
+
# forms that plot a frequency have (X.R)
|
|
179
|
+
if form not in ("histogram", "freq_poly"):
|
|
180
|
+
why = ("A density is an area, already scaled to a "
|
|
181
|
+
"proportion,\nso a count and a proportion do not "
|
|
182
|
+
"distinguish two\ndisplays."
|
|
183
|
+
if form == "density" else
|
|
184
|
+
f'form="{form}" plots the values themselves and\n'
|
|
185
|
+
"the quantiles of their distribution, not a "
|
|
186
|
+
"frequency,\nso a count and a proportion do not "
|
|
187
|
+
"distinguish two\ndisplays.")
|
|
188
|
+
raise ValueError(
|
|
189
|
+
"stat sets the plotted value to a count of the cases\n"
|
|
190
|
+
"in each bin or to their proportion, so it applies to "
|
|
191
|
+
"the forms\nthat plot a frequency: form=\"histogram\" "
|
|
192
|
+
'and\nform="freq_poly".\n\n' + why)
|
|
193
|
+
else:
|
|
194
|
+
stat = "count"
|
|
152
195
|
if data is None:
|
|
153
196
|
raise ValueError(
|
|
154
197
|
"data= is required: a pandas DataFrame containing the "
|
|
155
198
|
"named columns")
|
|
156
|
-
if stat == "density":
|
|
157
|
-
if form == "freq_poly":
|
|
158
|
-
stat = "count" # density is not a freq poly stat (X.R)
|
|
159
|
-
else:
|
|
160
|
-
form = "density" # R analog: X.R stat="density"
|
|
161
|
-
|
|
162
199
|
if cumulate not in ("off", "on", "both"):
|
|
163
200
|
raise ValueError('cumulate: "off", "on", or "both"')
|
|
164
201
|
if kind not in ("general", "normal", "both"):
|
|
@@ -171,16 +208,41 @@ def X(x, by=None, facet=None, data=None, filter=None,
|
|
|
171
208
|
if cumulate != "off" and (by is not None
|
|
172
209
|
or facet is not None):
|
|
173
210
|
raise ValueError(
|
|
174
|
-
"cumulate applies to a single histogram
|
|
175
|
-
"no by= or facet=")
|
|
211
|
+
"cumulate applies to a single histogram or frequency "
|
|
212
|
+
"polygon: no by= or facet=")
|
|
213
|
+
if cumulate != "off" and form not in ("histogram", "freq_poly"):
|
|
214
|
+
raise ValueError(
|
|
215
|
+
"cumulate accumulates the bin counts of a histogram or "
|
|
216
|
+
f'frequency polygon, which form="{form}" does not plot')
|
|
176
217
|
if counts and (by is not None or facet is not None):
|
|
177
218
|
raise ValueError(
|
|
178
219
|
"counts labels apply to a single histogram: "
|
|
179
220
|
"no by= or facet=")
|
|
180
|
-
if
|
|
221
|
+
if rug and by is not None:
|
|
222
|
+
raise ValueError(
|
|
223
|
+
"rug draws the data values of a single series: no by=")
|
|
224
|
+
if kind != "general" and by is not None and facet is not None:
|
|
225
|
+
raise ValueError(
|
|
226
|
+
"kind with by= fits a normal curve to each group of a "
|
|
227
|
+
"single panel: no facet=")
|
|
228
|
+
# by overlays the groups within a panel, marked on the points
|
|
229
|
+
# of the strip layer; a violin or box drawn over another is
|
|
230
|
+
# not readable, so without a strip there is no layer to carry
|
|
231
|
+
# the groups, and facet separates them (X.R)
|
|
232
|
+
vbs_ltr = {"violin": "v", "box": "b",
|
|
233
|
+
"strip": "s"}.get(form, form)
|
|
234
|
+
if (form in _VBS_FORMS and by is not None
|
|
235
|
+
and "s" not in vbs_ltr):
|
|
181
236
|
raise ValueError(
|
|
182
|
-
"
|
|
183
|
-
"
|
|
237
|
+
"The groups of by are overlaid within a panel, marked "
|
|
238
|
+
"on the\npoints of the strip layer, which "
|
|
239
|
+
f'form="{form}" does\nnot include. A violin or a box '
|
|
240
|
+
"drawn over another is not\nreadable, so there is no "
|
|
241
|
+
"layer here to carry the groups.\n\n"
|
|
242
|
+
"To mark the groups, add the strip layer:\n"
|
|
243
|
+
f' X("{x}", by="{by}", form="{vbs_ltr}s")\n\n'
|
|
244
|
+
f"To draw a separate {form} for each group:\n"
|
|
245
|
+
f' X("{x}", facet="{by}", form="{form}")')
|
|
184
246
|
if (out_cut > 0 or box_adj) and form not in _VBS_FORMS:
|
|
185
247
|
raise ValueError(
|
|
186
248
|
"out_cut and box_adj apply to the VBS forms: "
|
|
@@ -211,6 +273,18 @@ def X(x, by=None, facet=None, data=None, filter=None,
|
|
|
211
273
|
"the plotly VBS draws facet levels as bands on one "
|
|
212
274
|
"panel, so n_row and n_col do not apply")
|
|
213
275
|
|
|
276
|
+
if isinstance(by, (list, tuple)):
|
|
277
|
+
raise ValueError(
|
|
278
|
+
"Only one by variable is permitted, but more than one "
|
|
279
|
+
f"specified:\n by = {list(by)}\n\n"
|
|
280
|
+
"The groups of by are overlaid within the panel, "
|
|
281
|
+
"distinguished by\ncolor and optionally symbol. That one "
|
|
282
|
+
"channel is carried by the\nfirst variable, so a second "
|
|
283
|
+
"has no encoding left by which to\nseparate its levels.\n"
|
|
284
|
+
"To stratify by a second variable, use facet, which draws "
|
|
285
|
+
"each of\nits groups in a panel of its own.")
|
|
286
|
+
|
|
287
|
+
data_in = data # the caller's, for suggestions
|
|
214
288
|
if filter is not None:
|
|
215
289
|
data = data.query(filter)
|
|
216
290
|
|
|
@@ -224,6 +298,15 @@ def X(x, by=None, facet=None, data=None, filter=None,
|
|
|
224
298
|
(facet_ser, facet_name,
|
|
225
299
|
facet2_ser, facet2_name) = resolve_facet(data, facet, "X")
|
|
226
300
|
|
|
301
|
+
# before casewise deletion: the one-variable summary counts the
|
|
302
|
+
# missing values of x, the panel table those within each panel
|
|
303
|
+
x_raw = x_ser.to_numpy(dtype=float)
|
|
304
|
+
grp_ok = ~pd.concat([s for s in (by_ser, facet_ser, facet2_ser)
|
|
305
|
+
if s is not None] or [x_ser.notna()],
|
|
306
|
+
axis=1).isna().any(axis=1).to_numpy()
|
|
307
|
+
by_raw = by_ser.to_numpy() if by_ser is not None else None
|
|
308
|
+
f_raw = facet_ser.to_numpy() if facet_ser is not None else None
|
|
309
|
+
|
|
227
310
|
used = [s for s in (x_ser, by_ser, facet_ser, facet2_ser)
|
|
228
311
|
if s is not None]
|
|
229
312
|
keep = ~pd.concat(used, axis=1).isna().any(axis=1)
|
|
@@ -240,24 +323,60 @@ def X(x, by=None, facet=None, data=None, filter=None,
|
|
|
240
323
|
if len(fx) == 0:
|
|
241
324
|
raise ValueError(f"'{x}' has no finite values")
|
|
242
325
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
2 if digits_d is None else digits_d)
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
326
|
+
# the statistics for fig.stats; the console report, ports of
|
|
327
|
+
# R's printers (x_console), follows the display it accompanies
|
|
328
|
+
x_stats(x_ser.to_numpy(dtype=float), x,
|
|
329
|
+
2 if digits_d is None else digits_d)
|
|
330
|
+
show = not resolve_quiet(quiet)
|
|
331
|
+
pre, dname = xc.call_names(data_in)
|
|
332
|
+
x_lbl = ((getattr(data_in, "attrs", {}) or {})
|
|
333
|
+
.get("variable_labels", {}) or {}).get(x)
|
|
334
|
+
# parameters named in the call, which the suggestions skip
|
|
335
|
+
given = {nm for nm, v in (
|
|
336
|
+
("bin_width", bin_width), ("bin_start", bin_start),
|
|
337
|
+
("bin_end", bin_end), ("out_cut", out_cut or None),
|
|
338
|
+
("fences", fences or None), ("vbs_mean", vbs_mean or None),
|
|
339
|
+
("box_adj", box_adj or None)) if v is not None}
|
|
340
|
+
|
|
341
|
+
def say(*comps):
|
|
342
|
+
if show:
|
|
343
|
+
print(xc.report(*comps))
|
|
344
|
+
|
|
345
|
+
def one_var(sug):
|
|
346
|
+
"""suggestions, summary, outliers: the single-group report"""
|
|
347
|
+
return [sug, xc.ss_numeric(x_raw, x, x_lbl, digits_d),
|
|
348
|
+
xc.outliers(fx, 2)]
|
|
349
|
+
|
|
350
|
+
def panel_table():
|
|
351
|
+
"""the statistics of each panel (one facet variable)"""
|
|
352
|
+
ok = grp_ok
|
|
353
|
+
cells = f_raw[ok].astype(str)
|
|
354
|
+
name = facet_name
|
|
355
|
+
d = 2
|
|
356
|
+
if by_raw is not None:
|
|
357
|
+
cells = np.char.add(np.char.add(cells, ", "),
|
|
358
|
+
by_raw[ok].astype(str))
|
|
359
|
+
name = f"{facet_name}, {by}"
|
|
360
|
+
d = max(xc.max_dd(fx) + 1, 2) if digits_d is None \
|
|
361
|
+
else digits_d
|
|
362
|
+
return xc.facet_table(x_raw[ok], cells, x, name, d)
|
|
363
|
+
|
|
364
|
+
def bins(edges):
|
|
365
|
+
cnt = pd.cut(pd.Series(fx), edges, right=True,
|
|
366
|
+
include_lowest=True).value_counts(sort=False)
|
|
367
|
+
return xc.bin_table(edges, cnt.to_numpy(), len(fx),
|
|
368
|
+
xc.max_dd(fx) if digits_d is None
|
|
369
|
+
else digits_d)
|
|
256
370
|
|
|
257
371
|
# ----- violin / box / strip (VBS) ----------------------------
|
|
258
372
|
if form in _VBS_FORMS:
|
|
259
373
|
vbs_plot = {"violin": "v", "box": "b",
|
|
260
374
|
"strip": "s"}.get(form, form)
|
|
375
|
+
# the violin's smoothing is bw, the density curve's is
|
|
376
|
+
# bandwidth; both name the same quantity, so a bandwidth
|
|
377
|
+
# given without a bw is honored here (X.R)
|
|
378
|
+
if bw is None and bandwidth is not None:
|
|
379
|
+
bw = bandwidth
|
|
261
380
|
fin = np.isfinite(x_ser.to_numpy(dtype=float))
|
|
262
381
|
by_arr = by_order = None
|
|
263
382
|
facet_arr = facet_order = None
|
|
@@ -279,8 +398,7 @@ def X(x, by=None, facet=None, data=None, filter=None,
|
|
|
279
398
|
by_arr = by_ser.to_numpy()[fin]
|
|
280
399
|
by_order = category_order(by_ser)
|
|
281
400
|
n_g = len(by_order)
|
|
282
|
-
pt_fill =
|
|
283
|
-
for i in range(n_g)]
|
|
401
|
+
pt_fill = by_colors(n_g) # theme's palette, as R
|
|
284
402
|
pt_color = pt_fill
|
|
285
403
|
pt_trans = get_option("trans_pt_fill", 0.10)
|
|
286
404
|
# strip-point colors per vbs_pt_fill, X.R VBS section
|
|
@@ -303,7 +421,7 @@ def X(x, by=None, facet=None, data=None, filter=None,
|
|
|
303
421
|
get_column(data, ID, "ID")[keep]
|
|
304
422
|
if ID is not None
|
|
305
423
|
else x_ser.index).astype(str)[fin]
|
|
306
|
-
|
|
424
|
+
fig = _x_finish(vbs_plotly(
|
|
307
425
|
fx, x_name=x, vbs_plot=vbs_plot,
|
|
308
426
|
by=by_arr, by_order=by_order, by_name=by,
|
|
309
427
|
facet=facet_arr, facet_order=facet_order,
|
|
@@ -325,6 +443,49 @@ def X(x, by=None, facet=None, data=None, filter=None,
|
|
|
325
443
|
), rotate_x, rotate_y, scale_x, axis_fmt, axis_x_pre,
|
|
326
444
|
2 if digits_d is None else digits_d)
|
|
327
445
|
|
|
446
|
+
# report, ~ .param.VBS() and the pivot tables of X.R, from
|
|
447
|
+
# the data as read rather than as jittered for the strip
|
|
448
|
+
prm = CALL_STATS.get() or {}
|
|
449
|
+
comps = [xc.suggest_vbs(
|
|
450
|
+
x, pre, dname, given,
|
|
451
|
+
n_by=len(by_order) if by_order is not None else 0,
|
|
452
|
+
by_name=by,
|
|
453
|
+
n_facet=len(facet_order) if facet_order is not None else 0,
|
|
454
|
+
facet_name=facet_name)]
|
|
455
|
+
ids_out = (np.asarray(get_column(data, ID, "ID")[keep])
|
|
456
|
+
.astype(str)[fin] if ID is not None else None)
|
|
457
|
+
if by is None and facet is None:
|
|
458
|
+
comps += [
|
|
459
|
+
xc.box_summary(fx, x, x_lbl, digits_d, k_iqr=k,
|
|
460
|
+
box_adj=box_adj, a=a, b=b),
|
|
461
|
+
xc.outliers(fx, digits_d, ids=ids_out, k_iqr=k,
|
|
462
|
+
box_adj=box_adj, a=a, b=b),
|
|
463
|
+
xc.dup_values(fx)]
|
|
464
|
+
elif facet is not None:
|
|
465
|
+
comps.append(xc.dup_values(fx, facet_arr, facet_order))
|
|
466
|
+
else:
|
|
467
|
+
comps.append(xc.dup_values(fx, by_arr, by_order))
|
|
468
|
+
comps.append(xc.vbs_params(
|
|
469
|
+
vbs_plot, prm,
|
|
470
|
+
show_bw=prm.get("bw") is not None))
|
|
471
|
+
sd = max(xc.max_dd(fx), 2) # integer data still needs
|
|
472
|
+
summ = ["", f"---------- Summary Statistics for {x}"]
|
|
473
|
+
if by is not None:
|
|
474
|
+
summ += [""] + xc.vbs_summary(fx, by_arr, by, sd,
|
|
475
|
+
order=by_order)
|
|
476
|
+
if facet_arr is not None:
|
|
477
|
+
summ += [""] + xc.vbs_summary(fx, facet_arr, facet_name,
|
|
478
|
+
sd, order=facet_order)
|
|
479
|
+
if facet2_arr is not None:
|
|
480
|
+
summ += [""] + xc.vbs_summary(fx, facet2_arr,
|
|
481
|
+
facet2_name, sd,
|
|
482
|
+
order=facet2_order)
|
|
483
|
+
if by is None and facet is None:
|
|
484
|
+
summ += [""] + xc.vbs_summary(fx, None, x, sd)
|
|
485
|
+
comps.append(summ)
|
|
486
|
+
say(*comps)
|
|
487
|
+
return fig
|
|
488
|
+
|
|
328
489
|
# facet for histogram/density: aligned array + level order
|
|
329
490
|
facet_arr = (facet_ser.to_numpy()
|
|
330
491
|
if facet_ser is not None else None)
|
|
@@ -348,13 +509,14 @@ def X(x, by=None, facet=None, data=None, filter=None,
|
|
|
348
509
|
if form == "freq_poly":
|
|
349
510
|
edges = _breaks_from_args(fx, bin_start, bin_width,
|
|
350
511
|
bin_end, breaks)
|
|
351
|
-
|
|
512
|
+
fig = _x_finish(freq_poly_plotly(
|
|
352
513
|
x_ser, by=by_ser, x_name=x, by_name=by,
|
|
353
514
|
facet=facet_arr, facet_order=f_order,
|
|
354
515
|
facet_name=facet_name,
|
|
355
516
|
facet2=facet2_arr, facet2_order=f2_order,
|
|
356
517
|
facet2_name=facet2_name,
|
|
357
518
|
breaks=edges, proportion=stat == "proportion",
|
|
519
|
+
cumulate=cumulate != "off",
|
|
358
520
|
fill=fill,
|
|
359
521
|
fill_area=area_fill not in ("off", "transparent"),
|
|
360
522
|
x_lab=x if xlab is None else xlab, y_lab=ylab,
|
|
@@ -364,12 +526,22 @@ def X(x, by=None, facet=None, data=None, filter=None,
|
|
|
364
526
|
digits_d=3 if digits_d is None else digits_d,
|
|
365
527
|
), rotate_x, rotate_y, scale_x, axis_fmt, axis_x_pre,
|
|
366
528
|
3 if digits_d is None else digits_d)
|
|
529
|
+
if facet_ser is not None:
|
|
530
|
+
if facet2_ser is None:
|
|
531
|
+
say(panel_table())
|
|
532
|
+
elif by_ser is not None: # X.R: summary and outliers
|
|
533
|
+
say(*one_var(None)[1:])
|
|
534
|
+
else:
|
|
535
|
+
say(*one_var(xc.suggest_freq_poly(x, pre, dname, given)),
|
|
536
|
+
bins(edges))
|
|
537
|
+
return fig
|
|
367
538
|
|
|
368
539
|
# ----- density ---------------------------------------------
|
|
369
540
|
if form == "density":
|
|
370
|
-
|
|
541
|
+
# lessR's .band.width(): bw.nrd0 widened toward one peak
|
|
542
|
+
bw = bandwidth if bandwidth is not None else band_width(fx, 25)
|
|
371
543
|
show_h = show_histogram and by is None
|
|
372
|
-
|
|
544
|
+
fig = _x_finish(dn_plotly(
|
|
373
545
|
x_ser, by=by_ser, x_name=x, by_name=by,
|
|
374
546
|
facet=facet_arr, facet_order=f_order,
|
|
375
547
|
facet_name=facet_name,
|
|
@@ -393,6 +565,30 @@ def X(x, by=None, facet=None, data=None, filter=None,
|
|
|
393
565
|
axis_fmt=axis_fmt, axis_x_pre=axis_x_pre,
|
|
394
566
|
), rotate_x, rotate_y, scale_x, axis_fmt, axis_x_pre,
|
|
395
567
|
3 if digits_d is None else digits_d)
|
|
568
|
+
if facet_ser is not None:
|
|
569
|
+
if facet2_ser is None:
|
|
570
|
+
say(panel_table())
|
|
571
|
+
elif by_ser is not None:
|
|
572
|
+
say(xc.by_table(x_ser.to_numpy(dtype=float),
|
|
573
|
+
by_ser.to_numpy(), x, by, pre, dname,
|
|
574
|
+
order=category_order(by_ser)))
|
|
575
|
+
else: # dn.main.R
|
|
576
|
+
st = [f"--- Bandwidth --- for general curve: "
|
|
577
|
+
f"{bw:.4f}", ""]
|
|
578
|
+
if kind in ("normal", "both"):
|
|
579
|
+
if 2 < len(fx) < 5000:
|
|
580
|
+
W, p = sps.shapiro(fx)
|
|
581
|
+
record_stats(shapiro_W=float(W),
|
|
582
|
+
shapiro_p=float(p))
|
|
583
|
+
st += [" ", "Null hypothesis is a normal population",
|
|
584
|
+
f"Shapiro-Wilk normality test: W = {W:.4f},"
|
|
585
|
+
f" p-value = {p:.4f}"]
|
|
586
|
+
else:
|
|
587
|
+
st.append("Sample size out of range for "
|
|
588
|
+
"Shapiro-Wilk normality test.")
|
|
589
|
+
sug, ss, otl = one_var(xc.suggest_density(x, pre, dname))
|
|
590
|
+
say(sug, st, ss, otl)
|
|
591
|
+
return fig
|
|
396
592
|
|
|
397
593
|
# ----- histogram ---------------------------------------------
|
|
398
594
|
edges = _breaks_from_args(fx, bin_start, bin_width, bin_end,
|
|
@@ -419,8 +615,49 @@ def X(x, by=None, facet=None, data=None, filter=None,
|
|
|
419
615
|
axis_x_pre, 2 if digits_d is None else digits_d)
|
|
420
616
|
if add is not None:
|
|
421
617
|
plt_add(fig, add, x1=x1, x2=x2, y1=y1, y2=y2)
|
|
618
|
+
if facet_ser is not None:
|
|
619
|
+
if facet2_ser is None: # as .bar.lattice()
|
|
620
|
+
say(panel_table())
|
|
621
|
+
elif by_ser is not None:
|
|
622
|
+
say(xc.by_table(x_ser.to_numpy(dtype=float), by_ser.to_numpy(),
|
|
623
|
+
x, by, pre, dname,
|
|
624
|
+
order=category_order(by_ser)))
|
|
625
|
+
else:
|
|
626
|
+
say(*one_var(xc.suggest_hist(x, pre, dname, given)),
|
|
627
|
+
bins(edges))
|
|
422
628
|
return fig
|
|
423
629
|
|
|
424
630
|
|
|
631
|
+
def _per_variable(func):
|
|
632
|
+
"""A list of x variables is one display per variable, as in R,
|
|
633
|
+
returned as a list of Figures; suggestions are not repeated for
|
|
634
|
+
each, as R gives none for several variables."""
|
|
635
|
+
import functools
|
|
636
|
+
|
|
637
|
+
@functools.wraps(func)
|
|
638
|
+
def wrapper(*args, **kwargs):
|
|
639
|
+
xs = kwargs["x"] if "x" in kwargs else (args[0] if args
|
|
640
|
+
else None)
|
|
641
|
+
if not isinstance(xs, (list, tuple)):
|
|
642
|
+
return func(*args, **kwargs)
|
|
643
|
+
sug = get_option("suggest", True)
|
|
644
|
+
set_option("suggest", False)
|
|
645
|
+
try:
|
|
646
|
+
figs = []
|
|
647
|
+
for xi in xs:
|
|
648
|
+
if "x" in kwargs:
|
|
649
|
+
figs.append(func(*args, **{**kwargs, "x": xi}))
|
|
650
|
+
else:
|
|
651
|
+
figs.append(func(xi, *args[1:], **kwargs))
|
|
652
|
+
return figs
|
|
653
|
+
finally:
|
|
654
|
+
set_option("suggest", sug)
|
|
655
|
+
|
|
656
|
+
return wrapper
|
|
657
|
+
|
|
658
|
+
|
|
425
659
|
# font_size= scales all text of the returned figure
|
|
426
|
-
|
|
660
|
+
from .stats_out import attach_stats as _attach_stats # noqa: E402
|
|
661
|
+
from .style import with_theme as _with_theme # noqa: E402
|
|
662
|
+
X = _per_variable(font_scaled(_attach_stats(_with_theme(X),
|
|
663
|
+
capture=True)))
|