lessPython 0.2.0__py3-none-any.whl → 0.3.1__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 +74 -15
- lessPy/Chart.py +431 -88
- lessPy/Correlation.py +40 -11
- lessPy/Flows.py +3 -3
- lessPy/Logit.py +15 -11
- lessPy/Regression.py +27 -21
- lessPy/X.py +280 -42
- lessPy/XY.py +1332 -239
- lessPy/__init__.py +12 -2
- lessPy/_themes.py +5007 -0
- lessPy/bc_items_plotly.py +33 -20
- lessPy/bc_plotly.py +27 -10
- lessPy/bubble_plotly.py +33 -17
- lessPy/corCFA.py +9 -5
- lessPy/corProp.py +8 -4
- lessPy/corReflect.py +8 -4
- lessPy/corReorder.py +8 -4
- lessPy/corScree.py +12 -8
- lessPy/data/StockPrice.csv +1401 -1386
- lessPy/details.py +17 -4
- lessPy/dn_plotly.py +74 -54
- lessPy/dot_plotly.py +9 -8
- lessPy/freq_poly_plotly.py +18 -11
- lessPy/getColors.py +247 -20
- lessPy/hier_plotly.py +41 -14
- lessPy/hs_plotly.py +23 -11
- lessPy/pie_plotly.py +1 -1
- lessPy/pivot.py +39 -9
- lessPy/plotly_utils.py +162 -29
- lessPy/plt_add.py +2 -7
- lessPy/plt_contour.py +6 -6
- lessPy/plt_contour_facet.py +3 -3
- lessPy/plt_mat_plotly.py +85 -12
- lessPy/plt_plotly.py +34 -18
- lessPy/plt_smooth.py +4 -4
- lessPy/plt_time.py +10 -3
- lessPy/prob_norm.py +7 -3
- lessPy/prob_tcut.py +7 -3
- lessPy/prob_znorm.py +7 -1
- lessPy/profile_plotly.py +5 -4
- lessPy/radar_plotly.py +4 -3
- lessPy/rcolors.py +461 -0
- lessPy/simCImean.py +7 -3
- lessPy/simCLT.py +7 -3
- lessPy/simFlips.py +7 -3
- lessPy/simMeans.py +7 -3
- lessPy/stats_out.py +257 -11
- lessPy/style.py +167 -0
- lessPy/sunflower_plotly.py +141 -0
- lessPy/ttest.py +17 -13
- lessPy/utils.py +146 -6
- lessPy/vbs_plotly.py +39 -27
- lessPy/x_console.py +763 -0
- {lesspython-0.2.0.dist-info → lesspython-0.3.1.dist-info}/METADATA +13 -2
- lesspython-0.3.1.dist-info/RECORD +89 -0
- {lesspython-0.2.0.dist-info → lesspython-0.3.1.dist-info}/WHEEL +1 -1
- lesspython-0.2.0.dist-info/RECORD +0 -84
- {lesspython-0.2.0.dist-info → lesspython-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {lesspython-0.2.0.dist-info → lesspython-0.3.1.dist-info}/top_level.txt +0 -0
lessPy/details.py
CHANGED
|
@@ -129,6 +129,11 @@ def details(data=None, n_mcut=1, max_lines=30, miss_show=30,
|
|
|
129
129
|
|
|
130
130
|
names = list(data.columns)
|
|
131
131
|
types = [_type_label(data[c]) for c in names]
|
|
132
|
+
# whole numbers that pandas stores as float64 (a pandas integer
|
|
133
|
+
# column cannot hold NaN): reported as integer, as lessR does,
|
|
134
|
+
# but flagged so the report states what pandas actually stores
|
|
135
|
+
stored_float = [t == "integer" and pdt.is_float_dtype(data[c].dtype)
|
|
136
|
+
for c, t in zip(names, types)]
|
|
132
137
|
n_val = [int(data[c].notna().sum()) for c in names]
|
|
133
138
|
n_miss = [int(data[c].isna().sum()) for c in names]
|
|
134
139
|
n_uniq = [int(data[c].dropna().nunique()) for c in names]
|
|
@@ -137,7 +142,8 @@ def details(data=None, n_mcut=1, max_lines=30, miss_show=30,
|
|
|
137
142
|
_print_types(types)
|
|
138
143
|
first_last = [_first_last(data[c], n_obs) for c in names]
|
|
139
144
|
maybe_id, id_col = _print_table(names, types, n_val, n_miss,
|
|
140
|
-
n_uniq, first_last, n_var
|
|
145
|
+
n_uniq, first_last, n_var,
|
|
146
|
+
stored_float)
|
|
141
147
|
|
|
142
148
|
if maybe_id is not None and not var_labels:
|
|
143
149
|
_print_id_note(maybe_id, id_col)
|
|
@@ -196,10 +202,13 @@ def _print_types(types):
|
|
|
196
202
|
|
|
197
203
|
|
|
198
204
|
def _print_table(names, types, n_val, n_miss, n_uniq, first_last,
|
|
199
|
-
n_var):
|
|
205
|
+
n_var, stored_float=None):
|
|
206
|
+
if stored_float is None:
|
|
207
|
+
stored_float = [False] * n_var
|
|
208
|
+
shown = [t + "*" if f else t for t, f in zip(types, stored_float)]
|
|
200
209
|
w_num = max(2, len(str(n_var)))
|
|
201
210
|
w_nam = max(len("Variable"), *(len(x) for x in names))
|
|
202
|
-
w_typ = max(len("Type"), *(len(t) for t in
|
|
211
|
+
w_typ = max(len("Type"), *(len(t) for t in shown))
|
|
203
212
|
w_val = max(len("Values"), *(len(str(x)) for x in n_val))
|
|
204
213
|
w_mis = max(len("Missing"), *(len(str(x)) for x in n_miss))
|
|
205
214
|
w_uni = max(len("Unique"), *(len(str(x)) for x in n_uniq))
|
|
@@ -215,7 +224,7 @@ def _print_table(names, types, n_val, n_miss, n_uniq, first_last,
|
|
|
215
224
|
maybe_id, id_col = None, 0
|
|
216
225
|
for i in range(n_var):
|
|
217
226
|
print(f"{i + 1:>{w_num}} {names[i]:<{w_nam}} "
|
|
218
|
-
f"{
|
|
227
|
+
f"{shown[i]:<{w_typ}} {n_val[i]:>{w_val}} "
|
|
219
228
|
f"{n_miss[i]:>{w_mis}} {n_uniq[i]:>{w_uni}} "
|
|
220
229
|
f"{first_last[i]}")
|
|
221
230
|
if (n_uniq[i] == n_val[i]
|
|
@@ -223,6 +232,10 @@ def _print_table(names, types, n_val, n_miss, n_uniq, first_last,
|
|
|
223
232
|
"character")):
|
|
224
233
|
maybe_id, id_col = names[i], i + 1
|
|
225
234
|
_dash(len(hdr))
|
|
235
|
+
if any(stored_float):
|
|
236
|
+
print("* Whole numbers stored by Pandas as float64 (double), "
|
|
237
|
+
"usually because\n the column had missing values, NaN, "
|
|
238
|
+
"which a Pandas integer column\n cannot hold.")
|
|
226
239
|
return maybe_id, id_col
|
|
227
240
|
|
|
228
241
|
|
lessPy/dn_plotly.py
CHANGED
|
@@ -58,11 +58,13 @@ def dn_plotly(x, by=None, x_name=None, by_name=None,
|
|
|
58
58
|
from .utils import bw_nrd0
|
|
59
59
|
bw = bw_nrd0(x)
|
|
60
60
|
h = bw * adjust
|
|
61
|
+
from .stats_out import record_stats
|
|
62
|
+
record_stats(bw=float(h))
|
|
61
63
|
|
|
62
64
|
if facet is not None:
|
|
63
65
|
if fill_miss and by is not None:
|
|
64
|
-
from .plotly_utils import
|
|
65
|
-
fill =
|
|
66
|
+
from .plotly_utils import by_colors
|
|
67
|
+
fill = by_colors(len(pd.Series(by).dropna().unique()))
|
|
66
68
|
return _dn_facet(x, facet, facet_order, x_name,
|
|
67
69
|
facet_name, fill, x_lab, y_lab, main,
|
|
68
70
|
h, n, fill_area, style_opts,
|
|
@@ -91,8 +93,8 @@ def dn_plotly(x, by=None, x_name=None, by_name=None,
|
|
|
91
93
|
|
|
92
94
|
# groups need distinguishable hues: default to the palette
|
|
93
95
|
if fill_miss and G > 1:
|
|
94
|
-
from .plotly_utils import
|
|
95
|
-
fill =
|
|
96
|
+
from .plotly_utils import by_colors
|
|
97
|
+
fill = by_colors(G)
|
|
96
98
|
|
|
97
99
|
def group_x(gname):
|
|
98
100
|
xg = x if by is None else x[(by == gname).to_numpy()]
|
|
@@ -124,6 +126,11 @@ def dn_plotly(x, by=None, x_name=None, by_name=None,
|
|
|
124
126
|
dx = grid[1] - grid[0]
|
|
125
127
|
|
|
126
128
|
# --- densities per group -------------------------------------
|
|
129
|
+
# R analog: dn.plotly.R — kind "normal"/"both" adds the normal
|
|
130
|
+
# fit to each group by its own mean and sd on the shared grid;
|
|
131
|
+
# a group of identical values has no normal to draw
|
|
132
|
+
do_gen = kind in ("general", "both")
|
|
133
|
+
do_nrm = kind in ("normal", "both")
|
|
127
134
|
dens = []
|
|
128
135
|
ymax = 0.0
|
|
129
136
|
for gname in groups:
|
|
@@ -133,19 +140,21 @@ def dn_plotly(x, by=None, x_name=None, by_name=None,
|
|
|
133
140
|
[[0], np.cumsum((y[1:] + y[:-1]) * 0.5 * dx)])
|
|
134
141
|
if cum[-1] > 0:
|
|
135
142
|
cum = cum / cum[-1]
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
143
|
+
sd = float(xg.std(ddof=1))
|
|
144
|
+
nrm = None
|
|
145
|
+
if do_nrm and np.isfinite(sd) and sd > 0:
|
|
146
|
+
z = (grid - float(xg.mean())) / sd
|
|
147
|
+
nrm = np.exp(-0.5 * z * z) / (sd * np.sqrt(2 * np.pi))
|
|
148
|
+
dens.append(dict(y=y, cum=cum, mn=float(xg.mean()), nrm=nrm))
|
|
149
|
+
# only a curve that is drawn sets the height of the y-axis
|
|
150
|
+
if do_gen:
|
|
151
|
+
ymax = max(ymax, float(y.max()))
|
|
152
|
+
if nrm is not None:
|
|
153
|
+
ymax = max(ymax, float(nrm.max()))
|
|
154
|
+
|
|
155
|
+
# --- background histogram -------------------------------------
|
|
156
|
+
# show_histogram draws a faint density-scaled histogram behind
|
|
157
|
+
# the curves (dn.main.R)
|
|
149
158
|
hist_y = None
|
|
150
159
|
if show_histogram and hist_edges is not None:
|
|
151
160
|
e = np.asarray(hist_edges, dtype=float)
|
|
@@ -155,10 +164,6 @@ def dn_plotly(x, by=None, x_name=None, by_name=None,
|
|
|
155
164
|
hist_w = np.diff(e)
|
|
156
165
|
hist_y = cnt / (tot * hist_w) # density scale
|
|
157
166
|
hist_mids = (e[:-1] + e[1:]) / 2
|
|
158
|
-
if kind == "normal": # general curve not drawn
|
|
159
|
-
ymax = 0.0
|
|
160
|
-
if d_nrm is not None:
|
|
161
|
-
ymax = max(ymax, float(d_nrm.max()))
|
|
162
167
|
if hist_y is not None:
|
|
163
168
|
ymax = max(ymax, float(hist_y.max()))
|
|
164
169
|
|
|
@@ -195,12 +200,16 @@ def dn_plotly(x, by=None, x_name=None, by_name=None,
|
|
|
195
200
|
if fill_hist is None else fill_hist),
|
|
196
201
|
line=dict(width=0)),
|
|
197
202
|
hoverinfo="skip", showlegend=False))
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
203
|
+
# the normal curve: its own color for a single distribution, but
|
|
204
|
+
# the group's color when there are groups to keep apart, dashed
|
|
205
|
+
# beside that group's general curve (dn.plotly.R)
|
|
206
|
+
nrm_fill = (None if fill_normal in (None, "transparent")
|
|
207
|
+
else as_plotly_color(fill_normal))
|
|
208
|
+
for g, gname in enumerate(groups):
|
|
209
|
+
d = dens[g]
|
|
210
|
+
pre = (f"{by_name}: {gname}<br>"
|
|
211
|
+
if G > 1 and by_name else "")
|
|
212
|
+
if do_gen:
|
|
204
213
|
fig.add_trace(go.Scatter(
|
|
205
214
|
mode="lines",
|
|
206
215
|
x=grid, y=d["y"],
|
|
@@ -209,29 +218,40 @@ def dn_plotly(x, by=None, x_name=None, by_name=None,
|
|
|
209
218
|
fill="tozeroy" if fill_area else "none",
|
|
210
219
|
fillcolor=fill_rgba[g] if fill_area else None,
|
|
211
220
|
hoverinfo="text",
|
|
212
|
-
hovertext=
|
|
221
|
+
hovertext=[pre + t for t in hover(d)],
|
|
213
222
|
showlegend=G > 1,
|
|
214
223
|
))
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
width=1.4 if fill_area else 0.7)
|
|
224
|
+
if d["nrm"] is not None:
|
|
225
|
+
nrm_col = line_rgba[g] if G > 1 else to_hex(color_normal)
|
|
226
|
+
filled = fill_area and G == 1 and nrm_fill is not None
|
|
219
227
|
fig.add_trace(go.Scatter(
|
|
220
|
-
mode="lines",
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
228
|
+
mode="lines", x=grid, y=d["nrm"],
|
|
229
|
+
name=(None if G == 1 else
|
|
230
|
+
f"{gname} normal" if do_gen else gname),
|
|
231
|
+
showlegend=G > 1 and not do_gen,
|
|
232
|
+
line=dict(color=nrm_col, width=1.4,
|
|
233
|
+
dash="dash" if do_gen and G > 1
|
|
234
|
+
else "solid"),
|
|
235
|
+
fill="tozeroy" if filled else "none",
|
|
236
|
+
fillcolor=nrm_fill if filled else None,
|
|
237
|
+
hoverinfo="text",
|
|
238
|
+
hovertext=[
|
|
239
|
+
f"{pre}{x_name}: {grid[i]:.6g}"
|
|
240
|
+
f"<br>Normal density="
|
|
241
|
+
f"{round(float(d['nrm'][i]), 6):g}"
|
|
242
|
+
for i in range(n)],
|
|
224
243
|
))
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
244
|
+
# vertical line at the group mean, up to whichever curve
|
|
245
|
+
# is drawn
|
|
246
|
+
curve = d["y"] if do_gen or d["nrm"] is None else d["nrm"]
|
|
247
|
+
y_mn = float(np.interp(d["mn"], grid, curve))
|
|
228
248
|
fig.add_trace(go.Scatter(
|
|
229
|
-
mode="lines",
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
249
|
+
mode="lines",
|
|
250
|
+
x=[d["mn"], d["mn"]], y=[0, y_mn],
|
|
251
|
+
line=dict(color=line_rgba[g], dash="dash",
|
|
252
|
+
width=1.4 if fill_area else 0.7),
|
|
253
|
+
hoverinfo="skip", showlegend=False,
|
|
254
|
+
))
|
|
235
255
|
if rug: # ticks at each data value
|
|
236
256
|
xr, yr = [], []
|
|
237
257
|
y1r = -0.05 * ymax
|
|
@@ -240,7 +260,7 @@ def dn_plotly(x, by=None, x_name=None, by_name=None,
|
|
|
240
260
|
yr += [0.0, y1r, None]
|
|
241
261
|
fig.add_trace(go.Scatter(
|
|
242
262
|
mode="lines", x=xr, y=yr,
|
|
243
|
-
line=dict(color=
|
|
263
|
+
line=dict(color=as_plotly_color(color_rug),
|
|
244
264
|
width=max(0.5, float(size_rug))),
|
|
245
265
|
hoverinfo="skip", showlegend=False))
|
|
246
266
|
|
|
@@ -250,12 +270,12 @@ def dn_plotly(x, by=None, x_name=None, by_name=None,
|
|
|
250
270
|
axis_shapes = [dict(
|
|
251
271
|
type="line", xref="paper", yref="paper",
|
|
252
272
|
x0=0, x1=1, y0=0, y1=0,
|
|
253
|
-
line=dict(color=
|
|
273
|
+
line=dict(color=as_plotly_color("gray50"), width=1))]
|
|
254
274
|
|
|
255
275
|
ax_x = axis_num(x_lab, gridT1, gridL1)
|
|
256
276
|
ax_y = axis_num(y_lab, gridT2, gridL2)
|
|
257
277
|
ax_y.update(showgrid=True,
|
|
258
|
-
gridcolor=
|
|
278
|
+
gridcolor=as_plotly_color(style_opts["grid_col"]),
|
|
259
279
|
gridwidth=1, griddash="dot")
|
|
260
280
|
|
|
261
281
|
fig.update_layout(
|
|
@@ -264,8 +284,8 @@ def dn_plotly(x, by=None, x_name=None, by_name=None,
|
|
|
264
284
|
shapes=x_grid(gridT1) + plot_border() + axis_shapes,
|
|
265
285
|
template=None,
|
|
266
286
|
bargap=0,
|
|
267
|
-
plot_bgcolor=
|
|
268
|
-
paper_bgcolor=
|
|
287
|
+
plot_bgcolor=as_plotly_color(style_opts["panel_fill"]),
|
|
288
|
+
paper_bgcolor=as_plotly_color(style_opts["window_fill"]),
|
|
269
289
|
)
|
|
270
290
|
|
|
271
291
|
if G > 1:
|
|
@@ -277,7 +297,7 @@ def dn_plotly(x, by=None, x_name=None, by_name=None,
|
|
|
277
297
|
title=dict(text=main, x=0.5, xanchor="center",
|
|
278
298
|
y=0.99, yanchor="top",
|
|
279
299
|
font=dict(size=title_size,
|
|
280
|
-
color=
|
|
300
|
+
color=as_plotly_color(get_option(
|
|
281
301
|
"lab_color", "black")))),
|
|
282
302
|
margin=dict(t=round(title_size * 2.2)))
|
|
283
303
|
return fig
|
|
@@ -455,7 +475,7 @@ def _dn_facet(x, facet, facet_order, x_name, facet_name, fill,
|
|
|
455
475
|
else as_plotly_color(fill_normal))
|
|
456
476
|
fig.add_trace(go.Scatter(
|
|
457
477
|
mode="lines", x=grid, y=d_nrms[i],
|
|
458
|
-
line=dict(color=
|
|
478
|
+
line=dict(color=as_plotly_color(color_normal),
|
|
459
479
|
width=1.35 if nrm_fill is None else 1),
|
|
460
480
|
fill="none" if nrm_fill is None else "tozeroy",
|
|
461
481
|
fillcolor=nrm_fill,
|
|
@@ -469,7 +489,7 @@ def _dn_facet(x, facet, facet_order, x_name, facet_name, fill,
|
|
|
469
489
|
yr += [0.0, y1r, None]
|
|
470
490
|
fig.add_trace(go.Scatter(
|
|
471
491
|
mode="lines", x=xr, y=yr,
|
|
472
|
-
line=dict(color=
|
|
492
|
+
line=dict(color=as_plotly_color(color_rug),
|
|
473
493
|
width=max(0.5, float(size_rug))),
|
|
474
494
|
hoverinfo="skip", showlegend=False,
|
|
475
495
|
), row=row, col=p_col)
|
|
@@ -489,7 +509,7 @@ def _dn_facet(x, facet, facet_order, x_name, facet_name, fill,
|
|
|
489
509
|
title=dict(text=main, x=0.5, xanchor="center",
|
|
490
510
|
y=0.99, yanchor="top",
|
|
491
511
|
font=dict(size=title_size,
|
|
492
|
-
color=
|
|
512
|
+
color=as_plotly_color(get_option(
|
|
493
513
|
"lab_color", "black")))),
|
|
494
514
|
margin=dict(t=round(title_size * 2.2)))
|
|
495
515
|
return fig
|
lessPy/dot_plotly.py
CHANGED
|
@@ -19,6 +19,7 @@ import plotly.graph_objects as go
|
|
|
19
19
|
|
|
20
20
|
from .utils import get_option
|
|
21
21
|
from .plotly_utils import (
|
|
22
|
+
as_plotly_color,
|
|
22
23
|
BASE_COLORS, axis_cat, axis_format, axis_num, facet_layout,
|
|
23
24
|
get_tick_fmt, make_trans, plot_border, plotly_style, to_hex,
|
|
24
25
|
x_grid, y_grid,
|
|
@@ -128,7 +129,7 @@ def _dot_title(main):
|
|
|
128
129
|
return dict(text=main, x=0.5, xanchor="center",
|
|
129
130
|
y=0.98, yanchor="top",
|
|
130
131
|
font=dict(size=round(16 * get_option("main_size", 1)),
|
|
131
|
-
color=
|
|
132
|
+
color=as_plotly_color(get_option("lab_color",
|
|
132
133
|
"black"))))
|
|
133
134
|
|
|
134
135
|
|
|
@@ -169,7 +170,7 @@ def dot_plotly(x, y, orientation="v",
|
|
|
169
170
|
else np.asarray(list(vals), dtype=float))
|
|
170
171
|
flat = flat[np.isfinite(flat)]
|
|
171
172
|
val_max = float(flat.max()) if flat.size else None
|
|
172
|
-
seg_color =
|
|
173
|
+
seg_color = as_plotly_color(style_opts["segment_color"])
|
|
173
174
|
|
|
174
175
|
def val_ticks(orient):
|
|
175
176
|
# value-axis labels through the axis_fmt policies ("60K"),
|
|
@@ -240,8 +241,8 @@ def dot_plotly(x, y, orientation="v",
|
|
|
240
241
|
font=dict(size=round(
|
|
241
242
|
16 * get_option("axis_size", 0.9)))),
|
|
242
243
|
template=None,
|
|
243
|
-
plot_bgcolor=
|
|
244
|
-
paper_bgcolor=
|
|
244
|
+
plot_bgcolor=as_plotly_color(style_opts["panel_fill"]),
|
|
245
|
+
paper_bgcolor=as_plotly_color(style_opts["window_fill"]),
|
|
245
246
|
)
|
|
246
247
|
return fig
|
|
247
248
|
|
|
@@ -374,8 +375,8 @@ def dot_plotly(x, y, orientation="v",
|
|
|
374
375
|
16 * get_option("axis_size", 0.9)))),
|
|
375
376
|
title=_dot_title(main),
|
|
376
377
|
template=None,
|
|
377
|
-
plot_bgcolor=
|
|
378
|
-
paper_bgcolor=
|
|
378
|
+
plot_bgcolor=as_plotly_color(style_opts["panel_fill"]),
|
|
379
|
+
paper_bgcolor=as_plotly_color(style_opts["window_fill"]),
|
|
379
380
|
**axes,
|
|
380
381
|
)
|
|
381
382
|
return fig
|
|
@@ -450,7 +451,7 @@ def dot_plotly(x, y, orientation="v",
|
|
|
450
451
|
margin=margin,
|
|
451
452
|
title=_dot_title(main),
|
|
452
453
|
template=None,
|
|
453
|
-
plot_bgcolor=
|
|
454
|
-
paper_bgcolor=
|
|
454
|
+
plot_bgcolor=as_plotly_color(style_opts["panel_fill"]),
|
|
455
|
+
paper_bgcolor=as_plotly_color(style_opts["window_fill"]),
|
|
455
456
|
)
|
|
456
457
|
return fig
|
lessPy/freq_poly_plotly.py
CHANGED
|
@@ -25,7 +25,7 @@ import plotly.graph_objects as go
|
|
|
25
25
|
|
|
26
26
|
from .utils import get_option, pretty
|
|
27
27
|
from .plotly_utils import (
|
|
28
|
-
auto_opacity, axis_format, axis_num, facet_fig,
|
|
28
|
+
as_plotly_color, auto_opacity, axis_format, axis_num, facet_fig,
|
|
29
29
|
facet_panels, finish_facet, get_tick_fmt,
|
|
30
30
|
legend_style, make_trans, plot_border, plotly_style, to_hex,
|
|
31
31
|
x_grid,
|
|
@@ -48,6 +48,7 @@ def freq_poly_plotly(x, by=None, x_name=None, by_name=None,
|
|
|
48
48
|
facet2=None, facet2_order=None,
|
|
49
49
|
facet2_name=None,
|
|
50
50
|
breaks=None, proportion=False,
|
|
51
|
+
cumulate=False,
|
|
51
52
|
fill=None, fill_area=True,
|
|
52
53
|
x_lab=None, y_lab=None, main=None,
|
|
53
54
|
n_col=1, axis_fmt="K", axis_x_pre="",
|
|
@@ -65,6 +66,8 @@ def freq_poly_plotly(x, by=None, x_name=None, by_name=None,
|
|
|
65
66
|
if y_lab is None:
|
|
66
67
|
y_lab = (f"Proportion of {x_name}" if proportion
|
|
67
68
|
else f"Count of {x_name}")
|
|
69
|
+
if cumulate:
|
|
70
|
+
y_lab = f"Cumulative {y_lab}"
|
|
68
71
|
|
|
69
72
|
if facet is not None:
|
|
70
73
|
return _fp_facet(x, facet, facet_order, x_name,
|
|
@@ -90,8 +93,8 @@ def freq_poly_plotly(x, by=None, x_name=None, by_name=None,
|
|
|
90
93
|
# groups need distinguishable hues: default to the palette
|
|
91
94
|
if fill is None:
|
|
92
95
|
if G > 1:
|
|
93
|
-
from .plotly_utils import
|
|
94
|
-
fill =
|
|
96
|
+
from .plotly_utils import by_colors
|
|
97
|
+
fill = by_colors(G)
|
|
95
98
|
else:
|
|
96
99
|
fill = get_option("bar_fill_cont", "#96AAC3")
|
|
97
100
|
|
|
@@ -107,6 +110,8 @@ def freq_poly_plotly(x, by=None, x_name=None, by_name=None,
|
|
|
107
110
|
xg = x if by is None else x[(by == gname).to_numpy()]
|
|
108
111
|
xg = xg[np.isfinite(xg)]
|
|
109
112
|
yv = _poly_counts(xg, breaks, proportion)
|
|
113
|
+
if cumulate: # running total over the bins
|
|
114
|
+
yv = np.cumsum(yv)
|
|
110
115
|
polys.append(yv)
|
|
111
116
|
ymax = max(ymax, float(yv.max()))
|
|
112
117
|
|
|
@@ -133,7 +138,9 @@ def freq_poly_plotly(x, by=None, x_name=None, by_name=None,
|
|
|
133
138
|
for g, gname in enumerate(groups):
|
|
134
139
|
xx = np.concatenate([[mids[0] - step], mids,
|
|
135
140
|
[mids[-1] + step]])
|
|
136
|
-
|
|
141
|
+
# a cumulative polygon (ogive) holds its total at the right
|
|
142
|
+
yy = np.concatenate([[0], polys[g],
|
|
143
|
+
[polys[g][-1] if cumulate else 0]])
|
|
137
144
|
hover = [
|
|
138
145
|
(f"{x_name}: {xv:.6g}"
|
|
139
146
|
f"<br>{y_lab}: {round(float(yv), digits_d):g}")
|
|
@@ -162,7 +169,7 @@ def freq_poly_plotly(x, by=None, x_name=None, by_name=None,
|
|
|
162
169
|
ax_x = axis_num(x_lab, gridT1, gridL1)
|
|
163
170
|
ax_y = axis_num(y_lab, gridT2, gridL2)
|
|
164
171
|
ax_y.update(showgrid=True,
|
|
165
|
-
gridcolor=
|
|
172
|
+
gridcolor=as_plotly_color(style_opts["grid_col"]),
|
|
166
173
|
gridwidth=1, griddash="dot")
|
|
167
174
|
|
|
168
175
|
fig.update_layout(
|
|
@@ -170,8 +177,8 @@ def freq_poly_plotly(x, by=None, x_name=None, by_name=None,
|
|
|
170
177
|
yaxis=ax_y,
|
|
171
178
|
shapes=x_grid(gridT1) + plot_border(),
|
|
172
179
|
template=None,
|
|
173
|
-
plot_bgcolor=
|
|
174
|
-
paper_bgcolor=
|
|
180
|
+
plot_bgcolor=as_plotly_color(style_opts["panel_fill"]),
|
|
181
|
+
paper_bgcolor=as_plotly_color(style_opts["window_fill"]),
|
|
175
182
|
)
|
|
176
183
|
|
|
177
184
|
if G > 1:
|
|
@@ -183,7 +190,7 @@ def freq_poly_plotly(x, by=None, x_name=None, by_name=None,
|
|
|
183
190
|
title=dict(text=main, x=0.5, xanchor="center",
|
|
184
191
|
y=0.99, yanchor="top",
|
|
185
192
|
font=dict(size=title_size,
|
|
186
|
-
color=
|
|
193
|
+
color=as_plotly_color(get_option(
|
|
187
194
|
"lab_color", "black")))),
|
|
188
195
|
margin=dict(t=round(title_size * 2.2)))
|
|
189
196
|
return fig
|
|
@@ -221,8 +228,8 @@ def _fp_facet(x, facet, facet_order, x_name, facet_name, breaks,
|
|
|
221
228
|
|
|
222
229
|
if fill is None:
|
|
223
230
|
if G > 1:
|
|
224
|
-
from .plotly_utils import
|
|
225
|
-
fill =
|
|
231
|
+
from .plotly_utils import by_colors
|
|
232
|
+
fill = by_colors(G)
|
|
226
233
|
else:
|
|
227
234
|
fill = get_option("bar_fill_cont", "#96AAC3")
|
|
228
235
|
|
|
@@ -318,7 +325,7 @@ def _fp_facet(x, facet, facet_order, x_name, facet_name, breaks,
|
|
|
318
325
|
title=dict(text=main, x=0.5, xanchor="center",
|
|
319
326
|
y=0.99, yanchor="top",
|
|
320
327
|
font=dict(size=title_size,
|
|
321
|
-
color=
|
|
328
|
+
color=as_plotly_color(get_option(
|
|
322
329
|
"lab_color", "black")))),
|
|
323
330
|
margin=dict(t=round(title_size * 2.2)))
|
|
324
331
|
return fig
|