lessPython 0.1.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 +680 -0
- lessPy/Chart.py +1055 -0
- lessPy/Correlation.py +236 -0
- lessPy/Flows.py +116 -0
- lessPy/Logit.py +615 -0
- lessPy/Prop_test.py +267 -0
- lessPy/Regression.py +1491 -0
- lessPy/VariableLabels.py +119 -0
- lessPy/X.py +426 -0
- lessPy/XY.py +2007 -0
- lessPy/__init__.py +60 -0
- lessPy/anova_rmd.py +227 -0
- lessPy/bc_plotly.py +575 -0
- lessPy/bubble_plotly.py +470 -0
- lessPy/corCFA.py +316 -0
- lessPy/corEFA.py +220 -0
- lessPy/corPrint.py +45 -0
- lessPy/corProp.py +73 -0
- lessPy/corRead.py +48 -0
- lessPy/corReflect.py +72 -0
- lessPy/corReorder.py +161 -0
- lessPy/corScree.py +87 -0
- lessPy/data/Anova_1way.csv +25 -0
- lessPy/data/Anova_2way.csv +49 -0
- lessPy/data/Anova_rb.csv +8 -0
- lessPy/data/Anova_rbf.csv +49 -0
- lessPy/data/Anova_sp.csv +57 -0
- lessPy/data/BodyMeas.csv +341 -0
- lessPy/data/Cars93.csv +94 -0
- lessPy/data/Employee.csv +38 -0
- lessPy/data/Employee_lbl.csv +9 -0
- lessPy/data/FreqTable99.csv +5 -0
- lessPy/data/Jackets.csv +1026 -0
- lessPy/data/Learn.csv +35 -0
- lessPy/data/Mach4.csv +352 -0
- lessPy/data/Mach4_lbl.csv +21 -0
- lessPy/data/Reading.csv +101 -0
- lessPy/data/StockPrice.csv +1489 -0
- lessPy/data/WeightLoss.csv +11 -0
- lessPy/datasets.py +46 -0
- lessPy/date_infer.py +112 -0
- lessPy/details.py +314 -0
- lessPy/dn_plotly.py +495 -0
- lessPy/dot_plotly.py +385 -0
- lessPy/freq_poly_plotly.py +324 -0
- lessPy/getColors.py +399 -0
- lessPy/hier_plotly.py +352 -0
- lessPy/hs_plotly.py +395 -0
- lessPy/logit_rmd.py +410 -0
- lessPy/order_by.py +94 -0
- lessPy/pie_plotly.py +292 -0
- lessPy/pivot.py +158 -0
- lessPy/plotly_utils.py +787 -0
- lessPy/plt_add.py +129 -0
- lessPy/plt_contour.py +192 -0
- lessPy/plt_contour_facet.py +194 -0
- lessPy/plt_forecast.py +677 -0
- lessPy/plt_mat_plotly.py +201 -0
- lessPy/plt_plotly.py +216 -0
- lessPy/plt_smooth.py +170 -0
- lessPy/plt_time.py +143 -0
- lessPy/prob_norm.py +111 -0
- lessPy/prob_tcut.py +131 -0
- lessPy/prob_znorm.py +110 -0
- lessPy/radar_plotly.py +201 -0
- lessPy/reg_rmd.py +754 -0
- lessPy/rename.py +33 -0
- lessPy/reshape.py +95 -0
- lessPy/showColors.py +130 -0
- lessPy/simCImean.py +165 -0
- lessPy/simCLT.py +265 -0
- lessPy/simFlips.py +104 -0
- lessPy/simMeans.py +146 -0
- lessPy/stats_out.py +189 -0
- lessPy/ttest.py +641 -0
- lessPy/utils.py +235 -0
- lessPy/vbs_plotly.py +545 -0
- lesspython-0.1.0.dist-info/METADATA +93 -0
- lesspython-0.1.0.dist-info/RECORD +82 -0
- lesspython-0.1.0.dist-info/WHEEL +5 -0
- lesspython-0.1.0.dist-info/licenses/LICENSE +338 -0
- lesspython-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
# freq_poly_plotly.py — analog of freq_poly.plotly.R
|
|
2
|
+
#
|
|
3
|
+
# Frequency polygon: per-group bin counts on SHARED breaks (the
|
|
4
|
+
# bins arrive from X() computed over all the data, so every
|
|
5
|
+
# group's polygon is comparable), vertices at the bin midpoints,
|
|
6
|
+
# closed to zero one bin-width beyond the first and last
|
|
7
|
+
# midpoints. Modeled on dn_plotly, as freq_poly.plotly() is
|
|
8
|
+
# modeled on dn.plotly(); binning is local (pd.cut), no
|
|
9
|
+
# dependency on the histogram code path. fill_area=False (X's
|
|
10
|
+
# area_fill "off"): lines only, with a marker at each vertex but
|
|
11
|
+
# not at the two zero-closing endpoints.
|
|
12
|
+
#
|
|
13
|
+
# facet= draws one panel per level on the shared breaks and a
|
|
14
|
+
# shared count scale, first level in the bottom panel — an
|
|
15
|
+
# EXTENSION beyond R, where X() still stops with "Facets not yet
|
|
16
|
+
# working with density or freq_poly plots". With by=, each panel
|
|
17
|
+
# overlays one polygon per group present in it; proportions are
|
|
18
|
+
# per group within a panel.
|
|
19
|
+
|
|
20
|
+
import math
|
|
21
|
+
|
|
22
|
+
import numpy as np
|
|
23
|
+
import pandas as pd
|
|
24
|
+
import plotly.graph_objects as go
|
|
25
|
+
|
|
26
|
+
from .utils import get_option, pretty
|
|
27
|
+
from .plotly_utils import (
|
|
28
|
+
auto_opacity, axis_format, axis_num, facet_fig,
|
|
29
|
+
facet_panels, finish_facet, get_tick_fmt,
|
|
30
|
+
legend_style, make_trans, plot_border, plotly_style, to_hex,
|
|
31
|
+
x_grid,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _poly_counts(xg, breaks, proportion):
|
|
36
|
+
"""Bin counts of xg on the shared breaks (right-closed, as in
|
|
37
|
+
R's hist); proportions are within-group."""
|
|
38
|
+
cats = pd.cut(pd.Series(xg), breaks, right=True,
|
|
39
|
+
include_lowest=True)
|
|
40
|
+
yv = cats.value_counts(sort=False).to_numpy(dtype=float)
|
|
41
|
+
if proportion and yv.sum() > 0:
|
|
42
|
+
yv = yv / yv.sum()
|
|
43
|
+
return yv
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def freq_poly_plotly(x, by=None, x_name=None, by_name=None,
|
|
47
|
+
facet=None, facet_order=None, facet_name=None,
|
|
48
|
+
facet2=None, facet2_order=None,
|
|
49
|
+
facet2_name=None,
|
|
50
|
+
breaks=None, proportion=False,
|
|
51
|
+
fill=None, fill_area=True,
|
|
52
|
+
x_lab=None, y_lab=None, main=None,
|
|
53
|
+
n_col=1, axis_fmt="K", axis_x_pre="",
|
|
54
|
+
axis_y_pre="", digits_d=3,
|
|
55
|
+
style_opts=None):
|
|
56
|
+
|
|
57
|
+
if breaks is None or len(breaks) < 2:
|
|
58
|
+
raise ValueError("breaks (bin edges) required, length >= 2")
|
|
59
|
+
if style_opts is None:
|
|
60
|
+
style_opts = plotly_style()
|
|
61
|
+
if x_name is None:
|
|
62
|
+
x_name = getattr(x, "name", None) or "x"
|
|
63
|
+
if x_lab is None:
|
|
64
|
+
x_lab = x_name
|
|
65
|
+
if y_lab is None:
|
|
66
|
+
y_lab = (f"Proportion of {x_name}" if proportion
|
|
67
|
+
else f"Count of {x_name}")
|
|
68
|
+
|
|
69
|
+
if facet is not None:
|
|
70
|
+
return _fp_facet(x, facet, facet_order, x_name,
|
|
71
|
+
facet_name, breaks, proportion, fill,
|
|
72
|
+
fill_area, x_lab, y_lab, digits_d, main,
|
|
73
|
+
style_opts, by=by, by_name=by_name,
|
|
74
|
+
n_col=n_col, facet2=facet2,
|
|
75
|
+
facet2_order=facet2_order,
|
|
76
|
+
facet2_name=facet2_name,
|
|
77
|
+
axis_fmt=axis_fmt,
|
|
78
|
+
axis_x_pre=axis_x_pre)
|
|
79
|
+
|
|
80
|
+
x = np.asarray(x, dtype=float)
|
|
81
|
+
|
|
82
|
+
# --- groups ---
|
|
83
|
+
if by is None:
|
|
84
|
+
groups = ["Series 1"]
|
|
85
|
+
else:
|
|
86
|
+
by = pd.Series(by).astype(str)
|
|
87
|
+
groups = sorted(by.dropna().unique().tolist())
|
|
88
|
+
G = len(groups)
|
|
89
|
+
|
|
90
|
+
# groups need distinguishable hues: default to the palette
|
|
91
|
+
if fill is None:
|
|
92
|
+
if G > 1:
|
|
93
|
+
from .plotly_utils import BASE_COLORS
|
|
94
|
+
fill = BASE_COLORS
|
|
95
|
+
else:
|
|
96
|
+
fill = get_option("bar_fill_cont", "#96AAC3")
|
|
97
|
+
|
|
98
|
+
# --- bin geometry, counts per group on the shared breaks ---
|
|
99
|
+
breaks = [float(b) for b in breaks]
|
|
100
|
+
left = np.array(breaks[:-1])
|
|
101
|
+
right = np.array(breaks[1:])
|
|
102
|
+
mids = (left + right) / 2
|
|
103
|
+
step = mids[1] - mids[0] if len(mids) > 1 else 1.0
|
|
104
|
+
|
|
105
|
+
polys, ymax = [], 0.0
|
|
106
|
+
for gname in groups:
|
|
107
|
+
xg = x if by is None else x[(by == gname).to_numpy()]
|
|
108
|
+
xg = xg[np.isfinite(xg)]
|
|
109
|
+
yv = _poly_counts(xg, breaks, proportion)
|
|
110
|
+
polys.append(yv)
|
|
111
|
+
ymax = max(ymax, float(yv.max()))
|
|
112
|
+
|
|
113
|
+
# --- ticks ---
|
|
114
|
+
gridT1 = pretty(float(mids.min()), float(mids.max()))
|
|
115
|
+
gridL1 = axis_format(gridT1, digits_d, axis_fmt,
|
|
116
|
+
axis_x_pre)
|
|
117
|
+
gridT2 = pretty(0, ymax, n=6)
|
|
118
|
+
fmt2 = get_tick_fmt(gridT2, digits_d)
|
|
119
|
+
gridL2 = [f"{v:.{digits_d}f}" if fmt2 else f"{v:g}"
|
|
120
|
+
for v in gridT2]
|
|
121
|
+
|
|
122
|
+
# --- colors ---
|
|
123
|
+
alpha_fill = auto_opacity(G, "fill")
|
|
124
|
+
alpha_line = auto_opacity(G, "lines")
|
|
125
|
+
fill_list = [fill[g % len(fill)]
|
|
126
|
+
if isinstance(fill, (list, tuple)) else fill
|
|
127
|
+
for g in range(G)]
|
|
128
|
+
fill_rgba = [make_trans(c, alpha_fill) for c in fill_list]
|
|
129
|
+
line_rgba = [make_trans(c, alpha_line) for c in fill_list]
|
|
130
|
+
pt_rgba = [make_trans(c, 1) for c in fill_list] # vertices
|
|
131
|
+
|
|
132
|
+
fig = go.Figure()
|
|
133
|
+
for g, gname in enumerate(groups):
|
|
134
|
+
xx = np.concatenate([[mids[0] - step], mids,
|
|
135
|
+
[mids[-1] + step]])
|
|
136
|
+
yy = np.concatenate([[0], polys[g], [0]])
|
|
137
|
+
hover = [
|
|
138
|
+
(f"{x_name}: {xv:.6g}"
|
|
139
|
+
f"<br>{y_lab}: {round(float(yv), digits_d):g}")
|
|
140
|
+
for xv, yv in zip(xx, yy)
|
|
141
|
+
]
|
|
142
|
+
if G > 1 and by_name:
|
|
143
|
+
hover = [f"{by_name}: {gname}<br>{h}" for h in hover]
|
|
144
|
+
# lines only: mark the vertices, not the zero endpoints
|
|
145
|
+
marker = (None if fill_area
|
|
146
|
+
else dict(color=pt_rgba[g],
|
|
147
|
+
size=[0] + [7.5] * len(mids) + [0],
|
|
148
|
+
line=dict(width=0)))
|
|
149
|
+
fig.add_trace(go.Scatter(
|
|
150
|
+
x=xx, y=yy,
|
|
151
|
+
name=gname if G > 1 else None,
|
|
152
|
+
mode="lines" if fill_area else "lines+markers",
|
|
153
|
+
line=dict(color=line_rgba[g], width=2.2),
|
|
154
|
+
marker=marker,
|
|
155
|
+
fill="tozeroy" if fill_area else "none",
|
|
156
|
+
fillcolor=fill_rgba[g] if fill_area else None,
|
|
157
|
+
hoverinfo="text", hovertext=hover,
|
|
158
|
+
showlegend=G > 1,
|
|
159
|
+
))
|
|
160
|
+
|
|
161
|
+
# --- axes, grid, layout ---
|
|
162
|
+
ax_x = axis_num(x_lab, gridT1, gridL1)
|
|
163
|
+
ax_y = axis_num(y_lab, gridT2, gridL2)
|
|
164
|
+
ax_y.update(showgrid=True,
|
|
165
|
+
gridcolor=to_hex(style_opts["grid_col"]),
|
|
166
|
+
gridwidth=1, griddash="dot")
|
|
167
|
+
|
|
168
|
+
fig.update_layout(
|
|
169
|
+
xaxis=ax_x,
|
|
170
|
+
yaxis=ax_y,
|
|
171
|
+
shapes=x_grid(gridT1) + plot_border(),
|
|
172
|
+
template=None,
|
|
173
|
+
plot_bgcolor=to_hex(style_opts["panel_fill"]),
|
|
174
|
+
paper_bgcolor=to_hex(style_opts["window_fill"]),
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
if G > 1:
|
|
178
|
+
fig.update_layout(legend=legend_style(by_name, style_opts))
|
|
179
|
+
|
|
180
|
+
if main:
|
|
181
|
+
title_size = round(16 * get_option("main_size", 1))
|
|
182
|
+
fig.update_layout(
|
|
183
|
+
title=dict(text=main, x=0.5, xanchor="center",
|
|
184
|
+
y=0.99, yanchor="top",
|
|
185
|
+
font=dict(size=title_size,
|
|
186
|
+
color=to_hex(get_option(
|
|
187
|
+
"lab_color", "black")))),
|
|
188
|
+
margin=dict(t=round(title_size * 2.2)))
|
|
189
|
+
return fig
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _fp_facet(x, facet, facet_order, x_name, facet_name, breaks,
|
|
193
|
+
proportion, fill, fill_area, x_lab, y_lab, digits_d,
|
|
194
|
+
main, style_opts, by=None, by_name=None, n_col=1,
|
|
195
|
+
facet2=None, facet2_order=None, facet2_name=None,
|
|
196
|
+
axis_fmt="K", axis_x_pre=""):
|
|
197
|
+
"""One frequency-polygon panel per facet level: shared breaks,
|
|
198
|
+
shared count scale, per-group proportions. With by=, one
|
|
199
|
+
polygon per group within each panel, legend from the first
|
|
200
|
+
panel showing a group; a group absent from a panel draws no
|
|
201
|
+
polygon there. facet2: the two-facet grid, rows = facet2
|
|
202
|
+
levels."""
|
|
203
|
+
x = np.asarray(x, dtype=float)
|
|
204
|
+
ok = np.isfinite(x)
|
|
205
|
+
x, facet = x[ok], np.asarray(facet)[ok]
|
|
206
|
+
if facet2 is not None:
|
|
207
|
+
facet2 = np.asarray(facet2)[ok]
|
|
208
|
+
if facet_order is None:
|
|
209
|
+
facet_order = sorted(set(facet))
|
|
210
|
+
labels, pos, panel_sel, n_row_g, n_col = facet_panels(
|
|
211
|
+
facet, facet_order, facet2, facet2_order,
|
|
212
|
+
facet_name, facet2_name, n_col)
|
|
213
|
+
n_f = len(labels)
|
|
214
|
+
|
|
215
|
+
if by is None:
|
|
216
|
+
groups = [None]
|
|
217
|
+
else:
|
|
218
|
+
by = pd.Series(by).astype(str).to_numpy()[ok]
|
|
219
|
+
groups = sorted(set(by))
|
|
220
|
+
G = len(groups)
|
|
221
|
+
|
|
222
|
+
if fill is None:
|
|
223
|
+
if G > 1:
|
|
224
|
+
from .plotly_utils import BASE_COLORS
|
|
225
|
+
fill = BASE_COLORS
|
|
226
|
+
else:
|
|
227
|
+
fill = get_option("bar_fill_cont", "#96AAC3")
|
|
228
|
+
|
|
229
|
+
breaks = [float(b) for b in breaks]
|
|
230
|
+
left = np.array(breaks[:-1])
|
|
231
|
+
right = np.array(breaks[1:])
|
|
232
|
+
mids = (left + right) / 2
|
|
233
|
+
step = mids[1] - mids[0] if len(mids) > 1 else 1.0
|
|
234
|
+
|
|
235
|
+
# polys[i][g] is None for an absent (facet, group) cell
|
|
236
|
+
polys = [[None] * G for _ in range(n_f)]
|
|
237
|
+
ymax = 0.0
|
|
238
|
+
for i in range(n_f):
|
|
239
|
+
in_lvl = panel_sel[i]
|
|
240
|
+
for g, gname in enumerate(groups):
|
|
241
|
+
sel = (in_lvl if gname is None
|
|
242
|
+
else in_lvl & (by == gname))
|
|
243
|
+
if not sel.any():
|
|
244
|
+
continue
|
|
245
|
+
yv = _poly_counts(x[sel], breaks, proportion)
|
|
246
|
+
polys[i][g] = yv
|
|
247
|
+
ymax = max(ymax, float(yv.max()))
|
|
248
|
+
|
|
249
|
+
axT1 = pretty(float(mids.min()), float(mids.max()))
|
|
250
|
+
axT2 = pretty(0, ymax, n=6)
|
|
251
|
+
fmt2 = get_tick_fmt(axT2, digits_d)
|
|
252
|
+
ax = dict(axT1=axT1,
|
|
253
|
+
axL1=axis_format(axT1, digits_d, axis_fmt,
|
|
254
|
+
axis_x_pre),
|
|
255
|
+
axT2=axT2,
|
|
256
|
+
axL2=[f"{v:.{digits_d}f}" if fmt2 else f"{v:g}"
|
|
257
|
+
for v in axT2])
|
|
258
|
+
|
|
259
|
+
alpha_fill = auto_opacity(G, "fill")
|
|
260
|
+
alpha_line = auto_opacity(G, "lines")
|
|
261
|
+
fill_list = [fill[g % len(fill)]
|
|
262
|
+
if isinstance(fill, (list, tuple)) else fill
|
|
263
|
+
for g in range(G)]
|
|
264
|
+
fill_rgba = [make_trans(c, alpha_fill) for c in fill_list]
|
|
265
|
+
line_rgba = [make_trans(c, alpha_line) for c in fill_list]
|
|
266
|
+
pt_rgba = [make_trans(c, 1) for c in fill_list]
|
|
267
|
+
|
|
268
|
+
fig = facet_fig(n_row_g, n_col)
|
|
269
|
+
seen = set() # legend once per group
|
|
270
|
+
for i, lab in enumerate(labels):
|
|
271
|
+
row, p_col = pos[i]
|
|
272
|
+
panel_txt = (lab if facet2 is not None
|
|
273
|
+
else f"{facet_name}: {lab}")
|
|
274
|
+
for g, gname in enumerate(groups):
|
|
275
|
+
yv = polys[i][g]
|
|
276
|
+
if yv is None:
|
|
277
|
+
continue
|
|
278
|
+
xx = np.concatenate([[mids[0] - step], mids,
|
|
279
|
+
[mids[-1] + step]])
|
|
280
|
+
yy = np.concatenate([[0], yv, [0]])
|
|
281
|
+
hover = [
|
|
282
|
+
(f"{x_name}: {xv:.6g}"
|
|
283
|
+
f"<br>{y_lab}: {round(float(hv), digits_d):g}"
|
|
284
|
+
f"<br>{panel_txt}")
|
|
285
|
+
for xv, hv in zip(xx, yy)
|
|
286
|
+
]
|
|
287
|
+
if gname is not None and by_name:
|
|
288
|
+
hover = [f"{by_name}: {gname}<br>{h}"
|
|
289
|
+
for h in hover]
|
|
290
|
+
marker = (None if fill_area
|
|
291
|
+
else dict(color=pt_rgba[g],
|
|
292
|
+
size=[0] + [7.5] * len(mids)
|
|
293
|
+
+ [0],
|
|
294
|
+
line=dict(width=0)))
|
|
295
|
+
show = G > 1 and gname not in seen
|
|
296
|
+
seen.add(gname)
|
|
297
|
+
fig.add_trace(go.Scatter(
|
|
298
|
+
x=xx, y=yy,
|
|
299
|
+
name=gname if G > 1 else None,
|
|
300
|
+
legendgroup=gname if G > 1 else None,
|
|
301
|
+
mode="lines" if fill_area else "lines+markers",
|
|
302
|
+
line=dict(color=line_rgba[g], width=2.2),
|
|
303
|
+
marker=marker,
|
|
304
|
+
fill="tozeroy" if fill_area else "none",
|
|
305
|
+
fillcolor=fill_rgba[g] if fill_area else None,
|
|
306
|
+
hoverinfo="text", hovertext=hover,
|
|
307
|
+
showlegend=show,
|
|
308
|
+
), row=row, col=p_col)
|
|
309
|
+
|
|
310
|
+
finish_facet(fig, labels, ax, x_lab, y_lab, n_col=n_col,
|
|
311
|
+
gridT1=axT1, style_opts=style_opts, pos=pos)
|
|
312
|
+
if G > 1:
|
|
313
|
+
fig.update_layout(legend=legend_style(by_name,
|
|
314
|
+
style_opts))
|
|
315
|
+
if main:
|
|
316
|
+
title_size = round(16 * get_option("main_size", 1))
|
|
317
|
+
fig.update_layout(
|
|
318
|
+
title=dict(text=main, x=0.5, xanchor="center",
|
|
319
|
+
y=0.99, yanchor="top",
|
|
320
|
+
font=dict(size=title_size,
|
|
321
|
+
color=to_hex(get_option(
|
|
322
|
+
"lab_color", "black")))),
|
|
323
|
+
margin=dict(t=round(title_size * 2.2)))
|
|
324
|
+
return fig
|