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.
Files changed (82) hide show
  1. lessPy/ANOVA.py +680 -0
  2. lessPy/Chart.py +1055 -0
  3. lessPy/Correlation.py +236 -0
  4. lessPy/Flows.py +116 -0
  5. lessPy/Logit.py +615 -0
  6. lessPy/Prop_test.py +267 -0
  7. lessPy/Regression.py +1491 -0
  8. lessPy/VariableLabels.py +119 -0
  9. lessPy/X.py +426 -0
  10. lessPy/XY.py +2007 -0
  11. lessPy/__init__.py +60 -0
  12. lessPy/anova_rmd.py +227 -0
  13. lessPy/bc_plotly.py +575 -0
  14. lessPy/bubble_plotly.py +470 -0
  15. lessPy/corCFA.py +316 -0
  16. lessPy/corEFA.py +220 -0
  17. lessPy/corPrint.py +45 -0
  18. lessPy/corProp.py +73 -0
  19. lessPy/corRead.py +48 -0
  20. lessPy/corReflect.py +72 -0
  21. lessPy/corReorder.py +161 -0
  22. lessPy/corScree.py +87 -0
  23. lessPy/data/Anova_1way.csv +25 -0
  24. lessPy/data/Anova_2way.csv +49 -0
  25. lessPy/data/Anova_rb.csv +8 -0
  26. lessPy/data/Anova_rbf.csv +49 -0
  27. lessPy/data/Anova_sp.csv +57 -0
  28. lessPy/data/BodyMeas.csv +341 -0
  29. lessPy/data/Cars93.csv +94 -0
  30. lessPy/data/Employee.csv +38 -0
  31. lessPy/data/Employee_lbl.csv +9 -0
  32. lessPy/data/FreqTable99.csv +5 -0
  33. lessPy/data/Jackets.csv +1026 -0
  34. lessPy/data/Learn.csv +35 -0
  35. lessPy/data/Mach4.csv +352 -0
  36. lessPy/data/Mach4_lbl.csv +21 -0
  37. lessPy/data/Reading.csv +101 -0
  38. lessPy/data/StockPrice.csv +1489 -0
  39. lessPy/data/WeightLoss.csv +11 -0
  40. lessPy/datasets.py +46 -0
  41. lessPy/date_infer.py +112 -0
  42. lessPy/details.py +314 -0
  43. lessPy/dn_plotly.py +495 -0
  44. lessPy/dot_plotly.py +385 -0
  45. lessPy/freq_poly_plotly.py +324 -0
  46. lessPy/getColors.py +399 -0
  47. lessPy/hier_plotly.py +352 -0
  48. lessPy/hs_plotly.py +395 -0
  49. lessPy/logit_rmd.py +410 -0
  50. lessPy/order_by.py +94 -0
  51. lessPy/pie_plotly.py +292 -0
  52. lessPy/pivot.py +158 -0
  53. lessPy/plotly_utils.py +787 -0
  54. lessPy/plt_add.py +129 -0
  55. lessPy/plt_contour.py +192 -0
  56. lessPy/plt_contour_facet.py +194 -0
  57. lessPy/plt_forecast.py +677 -0
  58. lessPy/plt_mat_plotly.py +201 -0
  59. lessPy/plt_plotly.py +216 -0
  60. lessPy/plt_smooth.py +170 -0
  61. lessPy/plt_time.py +143 -0
  62. lessPy/prob_norm.py +111 -0
  63. lessPy/prob_tcut.py +131 -0
  64. lessPy/prob_znorm.py +110 -0
  65. lessPy/radar_plotly.py +201 -0
  66. lessPy/reg_rmd.py +754 -0
  67. lessPy/rename.py +33 -0
  68. lessPy/reshape.py +95 -0
  69. lessPy/showColors.py +130 -0
  70. lessPy/simCImean.py +165 -0
  71. lessPy/simCLT.py +265 -0
  72. lessPy/simFlips.py +104 -0
  73. lessPy/simMeans.py +146 -0
  74. lessPy/stats_out.py +189 -0
  75. lessPy/ttest.py +641 -0
  76. lessPy/utils.py +235 -0
  77. lessPy/vbs_plotly.py +545 -0
  78. lesspython-0.1.0.dist-info/METADATA +93 -0
  79. lesspython-0.1.0.dist-info/RECORD +82 -0
  80. lesspython-0.1.0.dist-info/WHEEL +5 -0
  81. lesspython-0.1.0.dist-info/licenses/LICENSE +338 -0
  82. lesspython-0.1.0.dist-info/top_level.txt +1 -0
lessPy/hs_plotly.py ADDED
@@ -0,0 +1,395 @@
1
+ # hs_plotly.py — analog of hs.plotly.R
2
+ #
3
+ # Renders a histogram from RAW numeric data plus pre-computed bin
4
+ # breaks (binning policy — Sturges, bin_width, etc. — is decided
5
+ # upstream in X(), as in lessR where .hst.main supplies h$breaks).
6
+ # Optional by= draws one translucent series per group, overlaid
7
+ # (default) or stacked.
8
+ #
9
+ # Small extension over the R source: proportion=True scales each
10
+ # group's counts to proportions (R's plotly path always passes
11
+ # counts and handles proportions only in the base-R renderer).
12
+ #
13
+ # facet= draws one panel per level on shared bins and a shared
14
+ # count scale (~ .bar.lattice T.type="hist"), first level in the
15
+ # bottom panel; proportions are per panel. With by=, each panel
16
+ # overlays (or stacks) one translucent series per group, and
17
+ # proportions and hover shares are per group within a panel.
18
+
19
+ import math
20
+
21
+ import numpy as np
22
+ import pandas as pd
23
+ import plotly.graph_objects as go
24
+
25
+ from .utils import get_option, pretty
26
+ from .plotly_utils import (
27
+ auto_opacity, axis_format, axis_num, facet_fig,
28
+ facet_panels, finish_facet, get_tick_fmt,
29
+ legend_style, make_trans, plot_border, plotly_style, to_hex,
30
+ x_grid,
31
+ )
32
+
33
+
34
+ def hs_plotly(x, by=None, x_name=None, by_name=None,
35
+ facet=None, facet_order=None, facet_name=None,
36
+ facet2=None, facet2_order=None, facet2_name=None,
37
+ breaks=None, freq=True, proportion=False,
38
+ fill=None, border=None,
39
+ cumulate="off", reg="snow2", counts=False,
40
+ x_lab=None, y_lab=None,
41
+ ax=None, gridT1=None,
42
+ digits_d=2, position="overlay", main=None,
43
+ n_col=1, axis_fmt="K", axis_x_pre="",
44
+ axis_y_pre="", style_opts=None):
45
+
46
+ if position not in ("overlay", "stack"):
47
+ raise ValueError('position must be "overlay" or "stack"')
48
+ if breaks is None or len(breaks) < 2:
49
+ raise ValueError("breaks (bin edges) required, length >= 2")
50
+ if style_opts is None:
51
+ style_opts = plotly_style()
52
+ if facet is not None:
53
+ return _hs_facet(x, facet, facet_order, x_name,
54
+ facet_name, breaks, proportion, fill,
55
+ border, x_lab, y_lab, digits_d, main,
56
+ style_opts, by=by, by_name=by_name,
57
+ position=position, n_col=n_col,
58
+ axis_fmt=axis_fmt,
59
+ axis_x_pre=axis_x_pre,
60
+ axis_y_pre=axis_y_pre,
61
+ facet2=facet2,
62
+ facet2_order=facet2_order,
63
+ facet2_name=facet2_name)
64
+ fill_miss = fill is None
65
+ if fill_miss:
66
+ fill = get_option("bar_fill_cont", "#96AAC3")
67
+ if border is None:
68
+ border = get_option("bar_color_cont", "#8496AF")
69
+ if x_name is None:
70
+ x_name = getattr(x, "name", None) or "x"
71
+ if x_lab is None:
72
+ x_lab = x_name
73
+ if y_lab is None:
74
+ y_lab = f"Count of {x_name}"
75
+
76
+ x = np.asarray(x, dtype=float)
77
+ breaks = [float(b) for b in breaks]
78
+
79
+ # --- bin geometry ---
80
+ left = np.array(breaks[:-1])
81
+ right = np.array(breaks[1:])
82
+ mids = (left + right) / 2
83
+ widths = right - left
84
+ n_bins = len(mids)
85
+
86
+ # --- groups ---
87
+ if by is None:
88
+ groups = ["Series 1"]
89
+ else:
90
+ by = pd.Series(by).astype(str)
91
+ groups = sorted(by.dropna().unique().tolist())
92
+ G = len(groups)
93
+
94
+ # groups need distinguishable hues: default to the palette
95
+ if fill_miss and G > 1:
96
+ from .plotly_utils import BASE_COLORS
97
+ fill = BASE_COLORS
98
+
99
+ # --- counts per bin (right-closed bins, as in R's hist) ---
100
+ def bin_counts(xg):
101
+ cats = pd.cut(xg, breaks, right=True, include_lowest=True)
102
+ return cats.value_counts(sort=False).to_numpy(dtype=float)
103
+
104
+ y_mat = np.zeros((n_bins, G))
105
+ for g, gname in enumerate(groups):
106
+ xg = x if by is None else x[(by == gname).to_numpy()]
107
+ xg = xg[np.isfinite(xg)]
108
+ y_mat[:, g] = bin_counts(pd.Series(xg))
109
+
110
+ # density: normalize each group to unit area; proportion: to 1
111
+ if not freq:
112
+ for g in range(G):
113
+ n_g = y_mat[:, g].sum()
114
+ if n_g > 0:
115
+ y_mat[:, g] = y_mat[:, g] / (n_g * widths)
116
+ elif proportion:
117
+ for g in range(G):
118
+ n_g = y_mat[:, g].sum()
119
+ if n_g > 0:
120
+ y_mat[:, g] = y_mat[:, g] / n_g
121
+
122
+ # cumulative histogram: cumsum the (single) series; "both"
123
+ # overdraws the regular bars in the reg color (hst.main.R)
124
+ y_orig = None
125
+ if cumulate in ("on", "both"):
126
+ y_orig = y_mat[:, 0].copy()
127
+ y_mat[:, 0] = np.cumsum(y_mat[:, 0])
128
+ y_lab = f"Cumulative {y_lab}"
129
+
130
+ rng_txt = [f"({lv:g}, {rv:g}]" for lv, rv in zip(left, right)]
131
+
132
+ def hover_group(yv):
133
+ if freq and not proportion:
134
+ amt = yv
135
+ cum_lbl = "Cumulative"
136
+ else:
137
+ amt = yv * widths if not freq else yv
138
+ cum_lbl = ("Cumulative (area)" if not freq
139
+ else "Cumulative")
140
+ total = amt.sum()
141
+ share = amt / total if total > 0 else np.full(n_bins, np.nan)
142
+ cum = np.cumsum(amt)
143
+ cum_pct = cum / total if total > 0 else share
144
+ return [
145
+ (f"Bin: {rng_txt[i]}"
146
+ f"<br>{y_lab}: {round(yv[i], digits_d):g}"
147
+ f"<br>% of total: {100 * share[i]:.1f}%"
148
+ f"<br>{cum_lbl}: {round(cum[i], digits_d):g}"
149
+ f"<br>Cumulative %: {100 * cum_pct[i]:.1f}%")
150
+ for i in range(n_bins)
151
+ ]
152
+
153
+ # colors
154
+ alpha_fill = auto_opacity(
155
+ G if G > 1 else 1,
156
+ "stack" if (G > 1 and position == "stack") else "overlay")
157
+ fills = [make_trans(
158
+ fill[g % len(fill)] if isinstance(fill, (list, tuple))
159
+ else fill, alpha_fill) for g in range(G)]
160
+ borders = [to_hex(
161
+ border[g % len(border)] if isinstance(border, (list, tuple))
162
+ else border) for g in range(G)]
163
+
164
+ fig = go.Figure()
165
+ for g, gname in enumerate(groups):
166
+ yv = y_mat[:, g]
167
+ if cumulate != "off": # shares of a cumsum are
168
+ hover = [ # not meaningful
169
+ f"Bin: {rng_txt[i]}<br>{y_lab}: "
170
+ f"{round(yv[i], digits_d):g}"
171
+ for i in range(n_bins)]
172
+ else:
173
+ hover = hover_group(yv)
174
+ if G > 1 and by_name:
175
+ hover = [f"{by_name}: {gname}<br>{h}" for h in hover]
176
+ extra = {}
177
+ if counts: # labels above the bars
178
+ extra = dict(
179
+ text=[f"{round(v, digits_d):g}" for v in yv],
180
+ textposition="outside", cliponaxis=False,
181
+ textfont=dict(color=to_hex(get_option(
182
+ "lab_color", "black"))))
183
+ fig.add_trace(go.Bar(
184
+ x=mids,
185
+ y=yv,
186
+ name=gname if G > 1 else None,
187
+ width=widths * 0.98,
188
+ marker=dict(
189
+ color=fills[g],
190
+ line=dict(color=borders[g], width=1),
191
+ ),
192
+ hoverinfo="text",
193
+ hovertext=hover,
194
+ showlegend=G > 1,
195
+ **extra,
196
+ ))
197
+ if cumulate == "both": # regular bars on top
198
+ reg_hex = "#EEE9E9" if reg == "snow2" else to_hex(reg)
199
+ fig.add_trace(go.Bar(
200
+ x=mids, y=y_orig, width=widths * 0.98,
201
+ marker=dict(color=reg_hex,
202
+ line=dict(color=borders[0], width=1)),
203
+ hoverinfo="skip", showlegend=False))
204
+
205
+ # axes, grid, layout
206
+ if ax is None:
207
+ from .utils import pretty
208
+ fx = x[np.isfinite(x)]
209
+ axT1 = pretty(float(fx.min()), float(fx.max()))
210
+ ymax = (y_mat.sum(axis=1).max()
211
+ if (G > 1 and position == "stack")
212
+ else y_mat.max())
213
+ axT2 = pretty(0, float(ymax))
214
+ ax = dict(
215
+ axT1=axT1,
216
+ axL1=axis_format(axT1, digits_d, axis_fmt,
217
+ axis_x_pre),
218
+ axT2=axT2,
219
+ axL2=axis_format(axT2, digits_d, axis_fmt,
220
+ axis_y_pre))
221
+ if gridT1 is None:
222
+ gridT1 = breaks # grid at the bin boundaries
223
+
224
+ ax_x = axis_num(x_lab, ax["axT1"], ax["axL1"])
225
+ ax_y = axis_num(y_lab, ax["axT2"], ax["axL2"])
226
+ ax_y.update(showgrid=True,
227
+ gridcolor=to_hex(style_opts["grid_col"]),
228
+ gridwidth=1, griddash="dot")
229
+
230
+ fig.update_layout(
231
+ xaxis=ax_x,
232
+ yaxis=ax_y,
233
+ shapes=x_grid(gridT1) + plot_border(),
234
+ template=None,
235
+ barmode=("stack" if (G > 1 and position == "stack")
236
+ else "overlay"),
237
+ bargap=0,
238
+ plot_bgcolor=to_hex(style_opts["panel_fill"]),
239
+ paper_bgcolor=to_hex(style_opts["window_fill"]),
240
+ )
241
+
242
+ if G > 1:
243
+ fig.update_layout(legend=legend_style(by_name, style_opts))
244
+
245
+ if main:
246
+ title_size = round(16 * get_option("main_size", 1))
247
+ fig.update_layout(
248
+ title=dict(text=main, x=0.5, xanchor="center",
249
+ y=0.99, yanchor="top",
250
+ font=dict(size=title_size,
251
+ color=to_hex(get_option(
252
+ "lab_color", "black")))),
253
+ margin=dict(t=round(title_size * 2.2)))
254
+ return fig
255
+
256
+
257
+ def _hs_facet(x, facet, facet_order, x_name, facet_name, breaks,
258
+ proportion, fill, border, x_lab, y_lab, digits_d,
259
+ main, style_opts, by=None, by_name=None,
260
+ position="overlay", n_col=1, axis_fmt="K",
261
+ axis_x_pre="", axis_y_pre="",
262
+ facet2=None, facet2_order=None, facet2_name=None):
263
+ """One histogram panel per facet level: shared bins, shared
264
+ count scale, per-panel proportions. ~ .bar.lattice hist.
265
+ With by=, one translucent series per group within each panel,
266
+ overlaid (default) or stacked, legend from the bottom panel;
267
+ proportions are then per group within a panel. facet2: the
268
+ two-facet grid, rows = facet2 levels."""
269
+ x = np.asarray(x, dtype=float)
270
+ ok = np.isfinite(x)
271
+ x, facet = x[ok], np.asarray(facet)[ok]
272
+ if facet2 is not None:
273
+ facet2 = np.asarray(facet2)[ok]
274
+ if facet_order is None:
275
+ facet_order = sorted(set(facet))
276
+ labels, pos, sel, n_row_g, n_col = facet_panels(
277
+ facet, facet_order, facet2, facet2_order,
278
+ facet_name, facet2_name, n_col)
279
+
280
+ if by is None:
281
+ groups = [None]
282
+ else:
283
+ by = pd.Series(by).astype(str).to_numpy()[ok]
284
+ groups = sorted(set(by))
285
+ G = len(groups)
286
+ stack = G > 1 and position == "stack"
287
+
288
+ if fill is None:
289
+ if G > 1:
290
+ from .plotly_utils import BASE_COLORS
291
+ fill = BASE_COLORS
292
+ else:
293
+ fill = get_option("bar_fill_cont", "#96AAC3")
294
+ if border is None:
295
+ border = get_option("bar_color_cont", "#8496AF")
296
+ if x_lab is None:
297
+ x_lab = x_name
298
+ if y_lab is None:
299
+ y_lab = (f"Proportion of {x_name}" if proportion
300
+ else f"Count of {x_name}")
301
+
302
+ breaks = [float(b) for b in breaks]
303
+ left = np.array(breaks[:-1])
304
+ right = np.array(breaks[1:])
305
+ mids = (left + right) / 2
306
+ widths = right - left
307
+ n_bins = len(mids)
308
+ rng_txt = [f"({lv:g}, {rv:g}]"
309
+ for lv, rv in zip(left, right)]
310
+
311
+ # y_mats[i][:, g]: bin heights for facet panel i, group g
312
+ y_mats = []
313
+ for in_lvl in sel:
314
+ y_mat = np.zeros((n_bins, G))
315
+ for g, gname in enumerate(groups):
316
+ sel = (in_lvl if gname is None
317
+ else in_lvl & (by == gname))
318
+ cats = pd.cut(pd.Series(x[sel]), breaks, right=True,
319
+ include_lowest=True)
320
+ yv = cats.value_counts(sort=False).to_numpy(
321
+ dtype=float)
322
+ if proportion and yv.sum() > 0:
323
+ yv = yv / yv.sum()
324
+ y_mat[:, g] = yv
325
+ y_mats.append(y_mat)
326
+
327
+ ymax = max(float(m.sum(axis=1).max() if stack else m.max())
328
+ for m in y_mats)
329
+ axT1 = pretty(float(x.min()), float(x.max()))
330
+ axT2 = pretty(0, ymax)
331
+ fmt2 = get_tick_fmt(axT2, digits_d)
332
+ ax = dict(axT1=axT1,
333
+ axL1=axis_format(axT1, digits_d, axis_fmt,
334
+ axis_x_pre),
335
+ axT2=axT2,
336
+ axL2=[f"{v:.{digits_d}f}" if fmt2 else f"{v:g}"
337
+ for v in axT2])
338
+
339
+ alpha_fill = auto_opacity(G if G > 1 else 1,
340
+ "stack" if stack else "overlay")
341
+ fills = [make_trans(
342
+ fill[g % len(fill)] if isinstance(fill, (list, tuple))
343
+ else fill, alpha_fill) for g in range(G)]
344
+ borders = [to_hex(
345
+ border[g % len(border)] if isinstance(border, (list, tuple))
346
+ else border) for g in range(G)]
347
+
348
+ pct_lbl = "% of panel" if G == 1 else "% of group"
349
+ fig = facet_fig(n_row_g, n_col)
350
+ for i, lab in enumerate(labels):
351
+ p_row, p_col = pos[i]
352
+ panel_txt = (lab if facet2 is not None
353
+ else f"{facet_name}: {lab}")
354
+ for g, gname in enumerate(groups):
355
+ yv = y_mats[i][:, g]
356
+ total = yv.sum()
357
+ hover = [
358
+ (f"Bin: {rng_txt[j]}"
359
+ f"<br>{y_lab}: {round(yv[j], digits_d):g}"
360
+ f"<br>{pct_lbl}: "
361
+ f"{100 * yv[j] / total if total else 0:.1f}%"
362
+ f"<br>{panel_txt}")
363
+ for j in range(n_bins)
364
+ ]
365
+ if gname is not None and by_name:
366
+ hover = [f"{by_name}: {gname}<br>{h}"
367
+ for h in hover]
368
+ fig.add_trace(go.Bar(
369
+ x=mids, y=yv, width=widths * 0.98,
370
+ name=gname if G > 1 else None,
371
+ legendgroup=gname if G > 1 else None,
372
+ marker=dict(color=fills[g],
373
+ line=dict(color=borders[g], width=1)),
374
+ hoverinfo="text", hovertext=hover,
375
+ showlegend=G > 1 and i == 0,
376
+ ), row=p_row, col=p_col) # first level bottom
377
+
378
+ fig.update_layout(bargap=0,
379
+ barmode="stack" if stack else "overlay")
380
+ finish_facet(fig, labels, ax, x_lab, y_lab,
381
+ gridT1=breaks, style_opts=style_opts,
382
+ n_col=n_col, pos=pos)
383
+ if G > 1:
384
+ fig.update_layout(legend=legend_style(by_name,
385
+ style_opts))
386
+ if main:
387
+ title_size = round(16 * get_option("main_size", 1))
388
+ fig.update_layout(
389
+ title=dict(text=main, x=0.5, xanchor="center",
390
+ y=0.99, yanchor="top",
391
+ font=dict(size=title_size,
392
+ color=to_hex(get_option(
393
+ "lab_color", "black")))),
394
+ margin=dict(t=round(title_size * 2.2)))
395
+ return fig