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
lessPy/radar_plotly.py
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# radar_plotly.py — analog of radar.plotly.R (.radar.plotly)
|
|
2
|
+
#
|
|
3
|
+
# Renders a radar (spider) chart from ALREADY-TABULATED input:
|
|
4
|
+
# 1-D pandas Series: index = x categories (the axes) -> one
|
|
5
|
+
# polygon
|
|
6
|
+
# 2-D pandas DataFrame: rows = `by` levels, columns = x
|
|
7
|
+
# categories -> one polygon per by level, with a legend
|
|
8
|
+
# facets= ordered dict {facet level: Series or DataFrame}, all
|
|
9
|
+
# sharing the same x categories (and by levels) -> a grid
|
|
10
|
+
# of radar panels on a common radial scale
|
|
11
|
+
#
|
|
12
|
+
# The R renderer consumes the .radar_aggregate() bundle, whose
|
|
13
|
+
# facet="(All)"/by="(All)" slices collapse to exactly this
|
|
14
|
+
# Series/crosstab shape, so Chart()'s standard table feeds it
|
|
15
|
+
# directly.
|
|
16
|
+
|
|
17
|
+
import numpy as np
|
|
18
|
+
import pandas as pd
|
|
19
|
+
import plotly.graph_objects as go
|
|
20
|
+
|
|
21
|
+
from .utils import get_option
|
|
22
|
+
from .plotly_utils import (
|
|
23
|
+
BASE_COLORS, facet_layout, make_trans, plotly_style, to_hex,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _theta_from_levels(k):
|
|
28
|
+
"""Angular tick positions, degrees, one per category axis.
|
|
29
|
+
R analog: .plotly_theta_from_levels()"""
|
|
30
|
+
if k < 1:
|
|
31
|
+
raise ValueError("x must have at least 1 category")
|
|
32
|
+
return [i * 360 / k for i in range(k)]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def radar_plotly(x=None, x_name=None, by_name=None, main=None,
|
|
36
|
+
fill=None, opacity=1.0, digits_d=2,
|
|
37
|
+
val_label="Count", facets=None, facet_name=None,
|
|
38
|
+
n_col=None, style_opts=None):
|
|
39
|
+
|
|
40
|
+
# normalize input to an ordered list of (facet level, table);
|
|
41
|
+
# a non-faceted call is the single panel (None, x)
|
|
42
|
+
if facets is not None:
|
|
43
|
+
fac_levels = [str(k) for k in facets.keys()]
|
|
44
|
+
tabs = list(facets.values())
|
|
45
|
+
else:
|
|
46
|
+
fac_levels = [None]
|
|
47
|
+
tabs = [x]
|
|
48
|
+
n_fac = len(tabs)
|
|
49
|
+
first = tabs[0]
|
|
50
|
+
|
|
51
|
+
two_d = isinstance(first, pd.DataFrame)
|
|
52
|
+
for t in tabs:
|
|
53
|
+
if not isinstance(t, pd.DataFrame if two_d
|
|
54
|
+
else pd.Series):
|
|
55
|
+
raise TypeError(
|
|
56
|
+
"each radar table must be a pandas Series (1-D) "
|
|
57
|
+
"or DataFrame (2-D, rows = by levels), the same "
|
|
58
|
+
"shape in every facet panel")
|
|
59
|
+
|
|
60
|
+
if x_name is None:
|
|
61
|
+
x_name = (first.columns.name if two_d
|
|
62
|
+
else first.index.name) or "x"
|
|
63
|
+
if two_d and by_name is None:
|
|
64
|
+
by_name = first.index.name or "by"
|
|
65
|
+
if style_opts is None:
|
|
66
|
+
style_opts = plotly_style()
|
|
67
|
+
if fill is None:
|
|
68
|
+
fill = BASE_COLORS
|
|
69
|
+
|
|
70
|
+
if two_d:
|
|
71
|
+
groups = [str(g) for g in first.index]
|
|
72
|
+
cats = [str(c) for c in first.columns]
|
|
73
|
+
mats = [t.to_numpy(dtype=float) for t in tabs]
|
|
74
|
+
else:
|
|
75
|
+
groups = ["(All)"]
|
|
76
|
+
cats = [str(c) for c in first.index]
|
|
77
|
+
mats = [t.to_numpy(dtype=float).reshape(1, -1)
|
|
78
|
+
for t in tabs]
|
|
79
|
+
|
|
80
|
+
n_by = len(groups)
|
|
81
|
+
rmax = np.nanmax([np.nanmax(m) for m in mats])
|
|
82
|
+
if not np.isfinite(rmax):
|
|
83
|
+
raise ValueError("radar: all values are missing; "
|
|
84
|
+
"nothing to plot")
|
|
85
|
+
|
|
86
|
+
title_size = round(16 * get_option("main_size", 1))
|
|
87
|
+
|
|
88
|
+
theta_deg = _theta_from_levels(len(cats))
|
|
89
|
+
theta_closed = theta_deg + [theta_deg[0]]
|
|
90
|
+
text_labels = cats + [cats[0]]
|
|
91
|
+
|
|
92
|
+
fill_vec = [fill[i % len(fill)]
|
|
93
|
+
if isinstance(fill, (list, tuple)) else fill
|
|
94
|
+
for i in range(n_by)]
|
|
95
|
+
cols_hex = to_hex(fill_vec)
|
|
96
|
+
fills_rgba = make_trans(cols_hex, opacity)
|
|
97
|
+
|
|
98
|
+
val_spec = f":.{max(0, int(digits_d or 0))}f"
|
|
99
|
+
show_leg = n_by > 1
|
|
100
|
+
|
|
101
|
+
# ---- facet domains + panel title annotations -------------------
|
|
102
|
+
# single panel: most of the space, with breathing room (as in R)
|
|
103
|
+
if n_fac == 1:
|
|
104
|
+
domains = [dict(x=(0.08, 0.95), y=(0.05, 0.88))]
|
|
105
|
+
anns = []
|
|
106
|
+
else:
|
|
107
|
+
# compress panels: shift down and shrink so the main title
|
|
108
|
+
# has clear space (R analog: .radar.plotly domain_adjust)
|
|
109
|
+
def dom_adj(d):
|
|
110
|
+
span = (d["y"][1] - d["y"][0]) * 0.72
|
|
111
|
+
y1 = min(0.78, d["y"][1] - 0.10)
|
|
112
|
+
y0 = max(0.0, y1 - span)
|
|
113
|
+
d["y"] = (y0, y1)
|
|
114
|
+
return d
|
|
115
|
+
|
|
116
|
+
def ann_adj(i, a, d):
|
|
117
|
+
a["y"] = min(d["y"][1] + 0.12, 0.97)
|
|
118
|
+
return a
|
|
119
|
+
|
|
120
|
+
domains, anns = facet_layout(
|
|
121
|
+
fac_levels, facet_name or "",
|
|
122
|
+
domain_adjust=dom_adj,
|
|
123
|
+
y_base=-0.018, y_row_shift=-0.003, yanchor="bottom",
|
|
124
|
+
ann_adjust=ann_adj, n_col=n_col)
|
|
125
|
+
|
|
126
|
+
fig = go.Figure()
|
|
127
|
+
for i, (lv, mat) in enumerate(zip(fac_levels, mats)):
|
|
128
|
+
subplot_name = "polar" if i == 0 else f"polar{i + 1}"
|
|
129
|
+
fac_line = (f"{facet_name or 'Facet'}: {lv}<br>"
|
|
130
|
+
if n_fac > 1 else "")
|
|
131
|
+
for j, grp in enumerate(groups):
|
|
132
|
+
r_j = mat[j].tolist()
|
|
133
|
+
r_closed = r_j + [r_j[0]]
|
|
134
|
+
by_line = (f"{by_name}: {grp}<br>" if n_by > 1 else "")
|
|
135
|
+
fig.add_trace(go.Scatterpolar(
|
|
136
|
+
theta=theta_closed,
|
|
137
|
+
thetaunit="degrees",
|
|
138
|
+
r=r_closed,
|
|
139
|
+
mode="lines+markers",
|
|
140
|
+
line=dict(width=1.5, color=cols_hex[j]),
|
|
141
|
+
marker=dict(size=4, color=cols_hex[j]),
|
|
142
|
+
fill="toself",
|
|
143
|
+
fillcolor=fills_rgba[j],
|
|
144
|
+
name=grp,
|
|
145
|
+
text=text_labels,
|
|
146
|
+
hovertemplate=(
|
|
147
|
+
f"{x_name}: %{{text}}<br>"
|
|
148
|
+
f"{by_line}"
|
|
149
|
+
f"{fac_line}"
|
|
150
|
+
f"{val_label}: %{{r{val_spec}}}"
|
|
151
|
+
"<extra></extra>"),
|
|
152
|
+
subplot=subplot_name,
|
|
153
|
+
showlegend=show_leg and i == 0,
|
|
154
|
+
))
|
|
155
|
+
|
|
156
|
+
# ---- per-panel polar layout blocks ------------------------------
|
|
157
|
+
# R analog: .plotly_apply_polar_layout()
|
|
158
|
+
lab_cex = get_option("lab_size", 1.0)
|
|
159
|
+
ax_cex = get_option("axis_size", 0.9)
|
|
160
|
+
for i, d in enumerate(domains):
|
|
161
|
+
nm = "polar" if i == 0 else f"polar{i + 1}"
|
|
162
|
+
fig.update_layout(**{nm: dict(
|
|
163
|
+
domain=dict(x=list(d["x"]), y=list(d["y"])),
|
|
164
|
+
radialaxis=dict(
|
|
165
|
+
visible=True, showline=True,
|
|
166
|
+
range=[0, rmax * 1.08],
|
|
167
|
+
tickfont=dict(size=round(16.0 * ax_cex)),
|
|
168
|
+
),
|
|
169
|
+
angularaxis=dict(
|
|
170
|
+
type="linear", direction="clockwise",
|
|
171
|
+
rotation=10, tickmode="array",
|
|
172
|
+
tickvals=theta_deg, ticktext=cats,
|
|
173
|
+
tickfont=dict(size=round(14.5 * lab_cex)),
|
|
174
|
+
),
|
|
175
|
+
)})
|
|
176
|
+
|
|
177
|
+
# legend OUTSIDE the paper (x > 1) so plotly never auto-shifts
|
|
178
|
+
# the polar domain to avoid legend overlap
|
|
179
|
+
r_margin = 110 if show_leg else 20
|
|
180
|
+
fig.update_layout(
|
|
181
|
+
title=dict(
|
|
182
|
+
text=main, x=0.5, xanchor="center",
|
|
183
|
+
y=0.97, yanchor="top",
|
|
184
|
+
font=dict(size=title_size,
|
|
185
|
+
color=to_hex(get_option("lab_color",
|
|
186
|
+
"black")))),
|
|
187
|
+
margin=dict(t=round(title_size * 2.2), b=10,
|
|
188
|
+
l=20 if n_fac == 1 else r_margin,
|
|
189
|
+
r=r_margin),
|
|
190
|
+
showlegend=show_leg,
|
|
191
|
+
annotations=anns,
|
|
192
|
+
legend=dict(
|
|
193
|
+
x=1.02, xanchor="left", y=1.0, yanchor="top",
|
|
194
|
+
bordercolor=to_hex(style_opts["panel_border"]),
|
|
195
|
+
borderwidth=1,
|
|
196
|
+
font=dict(size=round(18 * ax_cex)),
|
|
197
|
+
),
|
|
198
|
+
template=None,
|
|
199
|
+
paper_bgcolor=to_hex(style_opts["window_fill"]),
|
|
200
|
+
)
|
|
201
|
+
return fig
|