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/dot_plotly.py
ADDED
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
# dot_plotly.py — analog of dot.plotly.R
|
|
2
|
+
#
|
|
3
|
+
# Renders a Cleveland dot chart from ALREADY-AGGREGATED input:
|
|
4
|
+
# single-series cats + 1-D vals: one dot per category, with an
|
|
5
|
+
# origin->value stem ("lollipop") per dot
|
|
6
|
+
# paired cats + DataFrame vals (2+ numeric columns): one
|
|
7
|
+
# series per column, horizontal, with a legend
|
|
8
|
+
# (e.g., x=Name, y=[Pre, Post])
|
|
9
|
+
# faceted cats + vals + facet= (one level per
|
|
10
|
+
# observation): a grid of single-series panels,
|
|
11
|
+
# one per facet level, on a shared value axis
|
|
12
|
+
# with dot hues keyed by category name
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
import pandas as pd
|
|
16
|
+
import plotly.graph_objects as go
|
|
17
|
+
|
|
18
|
+
from .utils import get_option
|
|
19
|
+
from .plotly_utils import (
|
|
20
|
+
BASE_COLORS, axis_cat, axis_format, axis_num, facet_layout,
|
|
21
|
+
get_tick_fmt, make_trans, plot_border, plotly_style, to_hex,
|
|
22
|
+
x_grid, y_grid,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# R plotting shape codes -> plotly symbol names
|
|
26
|
+
_SYMBOLS = {21: "circle", 22: "square", 23: "diamond",
|
|
27
|
+
24: "triangle-up", 25: "triangle-down"}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _marker_style(shape, pt_size, fill, border, pt_opacity):
|
|
31
|
+
"""Per-point marker geometry. R analog: .dot_marker_style()"""
|
|
32
|
+
px = float(pt_size) * 10
|
|
33
|
+
if not np.isfinite(px) or px <= 0:
|
|
34
|
+
px = 5
|
|
35
|
+
fill1 = fill[0] if isinstance(fill, (list, tuple)) else fill
|
|
36
|
+
border1 = (border[0] if isinstance(border, (list, tuple))
|
|
37
|
+
else border)
|
|
38
|
+
return {
|
|
39
|
+
"symbol": _SYMBOLS.get(shape, "circle"),
|
|
40
|
+
"px": px,
|
|
41
|
+
"color": make_trans(fill1, pt_opacity),
|
|
42
|
+
"border": to_hex(border1),
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _marker_list(ms, n, color_override=None):
|
|
47
|
+
"""marker= dict for a scatter trace. R analog: .dot_marker_list()"""
|
|
48
|
+
return dict(
|
|
49
|
+
symbol=ms["symbol"],
|
|
50
|
+
size=[ms["px"]] * n,
|
|
51
|
+
sizemode="diameter",
|
|
52
|
+
color=(ms["color"] if color_override is None
|
|
53
|
+
else color_override),
|
|
54
|
+
opacity=1,
|
|
55
|
+
line=dict(color=ms["border"], width=1),
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _add_marker_trace(fig, cats, vals, marker, orientation,
|
|
60
|
+
hover_tmpl=None, name=None, showlegend=False,
|
|
61
|
+
axis_suf=""):
|
|
62
|
+
"""R analog: .dot_marker_trace()"""
|
|
63
|
+
xv, yv = (cats, vals) if orientation == "v" else (vals, cats)
|
|
64
|
+
kw = dict(mode="markers", x=xv, y=yv, marker=marker,
|
|
65
|
+
showlegend=showlegend,
|
|
66
|
+
xaxis=f"x{axis_suf}", yaxis=f"y{axis_suf}")
|
|
67
|
+
if hover_tmpl is not None:
|
|
68
|
+
kw["hovertemplate"] = hover_tmpl
|
|
69
|
+
if name is not None:
|
|
70
|
+
kw["name"] = name
|
|
71
|
+
fig.add_trace(go.Scatter(**kw))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _add_segment_trace(fig, cats, vals, seg_color, orientation,
|
|
75
|
+
seg_start, axis_suf=""):
|
|
76
|
+
"""None-separated origin->value stems; cats/vals must be
|
|
77
|
+
pre-filtered to finite values. R analog: .dot_segment_trace()"""
|
|
78
|
+
n = len(vals)
|
|
79
|
+
if n == 0:
|
|
80
|
+
return
|
|
81
|
+
seg_cat, seg_val = [], []
|
|
82
|
+
for c, v in zip(cats, vals):
|
|
83
|
+
seg_cat += [c, c, None]
|
|
84
|
+
seg_val += [seg_start, v, None]
|
|
85
|
+
if orientation == "v":
|
|
86
|
+
seg_x, seg_y = seg_cat, seg_val
|
|
87
|
+
else:
|
|
88
|
+
seg_x, seg_y = seg_val, seg_cat
|
|
89
|
+
fig.add_trace(go.Scatter(
|
|
90
|
+
mode="lines", x=seg_x, y=seg_y,
|
|
91
|
+
xaxis=f"x{axis_suf}", yaxis=f"y{axis_suf}",
|
|
92
|
+
line=dict(color=seg_color, width=1),
|
|
93
|
+
hoverinfo="skip", showlegend=False,
|
|
94
|
+
))
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _val_axis(label, gridT, origin_x, ticktext=None):
|
|
98
|
+
"""Numeric (value) axis spec. R analog: .dot_val_axis()"""
|
|
99
|
+
ax = axis_num(label, gridT, ticktext)
|
|
100
|
+
if origin_x is not None and gridT:
|
|
101
|
+
step = gridT[1] - gridT[0] if len(gridT) > 1 else 0
|
|
102
|
+
ax["range"] = [min(origin_x, min(gridT)), max(gridT) + step]
|
|
103
|
+
ax["autorange"] = False
|
|
104
|
+
return ax
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _cat_axis(label, cats):
|
|
108
|
+
"""Category axis, order frozen. R analog: .dot_cat_axis()"""
|
|
109
|
+
ax = axis_cat(label)
|
|
110
|
+
ax.update(categoryorder="array", categoryarray=cats,
|
|
111
|
+
tickmode="array", tickvals=cats, ticktext=cats)
|
|
112
|
+
return ax
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _dot_title(main):
|
|
116
|
+
"""R analog: .dot_title()"""
|
|
117
|
+
if not main:
|
|
118
|
+
return None
|
|
119
|
+
return dict(text=main, x=0.5, xanchor="center",
|
|
120
|
+
y=0.98, yanchor="top",
|
|
121
|
+
font=dict(size=round(16 * get_option("main_size", 1)),
|
|
122
|
+
color=to_hex(get_option("lab_color",
|
|
123
|
+
"black"))))
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _hover(orientation, x_lab, y_lab, val_spec):
|
|
127
|
+
if orientation == "v":
|
|
128
|
+
cat_label = x_lab or "Category"
|
|
129
|
+
val_label = y_lab or "Value"
|
|
130
|
+
return (f"{cat_label}: %{{x}}<br>"
|
|
131
|
+
f"{val_label}: %{{y{val_spec}}}<extra></extra>")
|
|
132
|
+
cat_label = y_lab or "Category"
|
|
133
|
+
val_label = x_lab or "Value"
|
|
134
|
+
return (f"{val_label}: %{{x{val_spec}}}<br>"
|
|
135
|
+
f"{cat_label}: %{{y}}<extra></extra>")
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def dot_plotly(x, y, orientation="v",
|
|
139
|
+
fill=None, border=None, shape=21, pt_size=1,
|
|
140
|
+
x_lab="", y_lab="", digits_d=2, pt_opacity=0.95,
|
|
141
|
+
gridT=None, origin_x=None, main=None,
|
|
142
|
+
facet=None, facet_name=None, n_col=None,
|
|
143
|
+
axis_fmt="K", axis_x_pre="", axis_y_pre="",
|
|
144
|
+
rotate_x=0, rotate_y=0,
|
|
145
|
+
segments_x=True, segments_y=True, style_opts=None):
|
|
146
|
+
|
|
147
|
+
cats, vals = x, y
|
|
148
|
+
if style_opts is None:
|
|
149
|
+
style_opts = plotly_style()
|
|
150
|
+
if fill is None:
|
|
151
|
+
fill = BASE_COLORS
|
|
152
|
+
if border is None:
|
|
153
|
+
border = get_option("pt_color", "#324E5C")
|
|
154
|
+
|
|
155
|
+
title_size = round(16 * get_option("main_size", 1))
|
|
156
|
+
seg_color = to_hex(style_opts["segment_color"])
|
|
157
|
+
|
|
158
|
+
def val_ticks(orient):
|
|
159
|
+
# value-axis labels through the axis_fmt policies ("60K"),
|
|
160
|
+
# prefixed for the physical axis the values land on
|
|
161
|
+
if not gridT:
|
|
162
|
+
return None
|
|
163
|
+
return axis_format(gridT, digits_d, axis_fmt,
|
|
164
|
+
axis_x_pre if orient == "h"
|
|
165
|
+
else axis_y_pre)
|
|
166
|
+
|
|
167
|
+
def apply_rotate(x_ax, y_ax):
|
|
168
|
+
if rotate_x:
|
|
169
|
+
x_ax["tickangle"] = -rotate_x
|
|
170
|
+
if rotate_y:
|
|
171
|
+
y_ax["tickangle"] = -rotate_y
|
|
172
|
+
|
|
173
|
+
# ---- paired dot plot (vals is a DataFrame, 2+ columns) --------
|
|
174
|
+
# Single panel, cats on y, values on x; one legend entry per
|
|
175
|
+
# y column.
|
|
176
|
+
if isinstance(vals, pd.DataFrame):
|
|
177
|
+
cats_all = [str(c) for c in cats]
|
|
178
|
+
ms = _marker_style(21, pt_size, fill, border, pt_opacity)
|
|
179
|
+
seg_start = 0 if origin_x is None else origin_x
|
|
180
|
+
|
|
181
|
+
fig = go.Figure()
|
|
182
|
+
for si, col in enumerate(vals.columns):
|
|
183
|
+
vals_si = vals[col].to_numpy(dtype=float)
|
|
184
|
+
fill_si = (fill[si % len(fill)]
|
|
185
|
+
if isinstance(fill, (list, tuple)) else fill)
|
|
186
|
+
clr = make_trans(fill_si, pt_opacity)
|
|
187
|
+
if segments_x:
|
|
188
|
+
fin = np.isfinite(vals_si)
|
|
189
|
+
_add_segment_trace(
|
|
190
|
+
fig, [c for c, f in zip(cats_all, fin) if f],
|
|
191
|
+
vals_si[fin].tolist(), seg_color, "h",
|
|
192
|
+
seg_start)
|
|
193
|
+
_add_marker_trace(
|
|
194
|
+
fig, cats_all, vals_si.tolist(),
|
|
195
|
+
marker=_marker_list(ms, len(vals_si),
|
|
196
|
+
color_override=clr),
|
|
197
|
+
orientation="h", name=str(col), showlegend=True)
|
|
198
|
+
|
|
199
|
+
x_ax = _val_axis(x_lab, gridT, origin_x, val_ticks("h"))
|
|
200
|
+
y_ax = _cat_axis(y_lab, cats_all)
|
|
201
|
+
apply_rotate(x_ax, y_ax)
|
|
202
|
+
fig.update_layout(
|
|
203
|
+
xaxis=x_ax,
|
|
204
|
+
yaxis=y_ax,
|
|
205
|
+
shapes=(x_grid(gridT) if gridT else []) + plot_border(),
|
|
206
|
+
margin=dict(t=round(title_size * 2.2) if main else 30),
|
|
207
|
+
title=_dot_title(main),
|
|
208
|
+
legend=dict(font=dict(size=round(
|
|
209
|
+
16 * get_option("axis_size", 0.9)))),
|
|
210
|
+
template=None,
|
|
211
|
+
plot_bgcolor=to_hex(style_opts["panel_fill"]),
|
|
212
|
+
paper_bgcolor=to_hex(style_opts["window_fill"]),
|
|
213
|
+
)
|
|
214
|
+
return fig
|
|
215
|
+
|
|
216
|
+
# ---- faceted dot plot --------------------------------------------
|
|
217
|
+
# Grid of single-series panels, one per facet level, on a shared
|
|
218
|
+
# value axis. Per-panel segment start is clamped at-or-below the
|
|
219
|
+
# panel minimum so dots never sit to the left of (or below)
|
|
220
|
+
# their stem. R analog: dot.plotly.R faceted path
|
|
221
|
+
if facet is not None:
|
|
222
|
+
fac_char = [None if f is None
|
|
223
|
+
or (isinstance(f, float) and np.isnan(f))
|
|
224
|
+
else str(f) for f in facet]
|
|
225
|
+
fac_levels = sorted({f for f in fac_char if f is not None})
|
|
226
|
+
|
|
227
|
+
cats_in = [str(c) for c in cats]
|
|
228
|
+
vals_in = np.asarray(list(vals), dtype=float)
|
|
229
|
+
|
|
230
|
+
ms = _marker_style(shape, pt_size, fill, border,
|
|
231
|
+
pt_opacity)
|
|
232
|
+
# color keyed by category name so hues match across panels
|
|
233
|
+
cat_levels = list(dict.fromkeys(cats_in))
|
|
234
|
+
col_map = {c: make_trans(
|
|
235
|
+
to_hex(fill[i % len(fill)]
|
|
236
|
+
if isinstance(fill, (list, tuple))
|
|
237
|
+
else fill),
|
|
238
|
+
pt_opacity)
|
|
239
|
+
for i, c in enumerate(cat_levels)}
|
|
240
|
+
|
|
241
|
+
val_fmt = get_tick_fmt(vals_in[np.isfinite(vals_in)],
|
|
242
|
+
digits_d)
|
|
243
|
+
val_spec = f":{val_fmt}" if val_fmt else ""
|
|
244
|
+
hover_tmpl = _hover(orientation, x_lab, y_lab, val_spec)
|
|
245
|
+
draw_seg = segments_x if orientation == "h" else segments_y
|
|
246
|
+
|
|
247
|
+
def ann_adj(i, a, d):
|
|
248
|
+
a["y"] = min(d["y"][1] + 0.04, 0.99)
|
|
249
|
+
return a
|
|
250
|
+
domains, anns = facet_layout(
|
|
251
|
+
fac_levels, facet_name or "",
|
|
252
|
+
yanchor="bottom", ann_adjust=ann_adj, n_col=n_col)
|
|
253
|
+
|
|
254
|
+
fig = go.Figure()
|
|
255
|
+
axes = {}
|
|
256
|
+
for i, lv in enumerate(fac_levels):
|
|
257
|
+
suf = "" if i == 0 else str(i + 1)
|
|
258
|
+
keep = [k for k, f in enumerate(fac_char) if f == lv]
|
|
259
|
+
cats_i = [cats_in[k] for k in keep]
|
|
260
|
+
vals_i = vals_in[keep]
|
|
261
|
+
fin = np.isfinite(vals_i)
|
|
262
|
+
|
|
263
|
+
if fin.any() and draw_seg:
|
|
264
|
+
# never above the panel's data minimum — avoid
|
|
265
|
+
# backward stems
|
|
266
|
+
panel_min = float(vals_i[fin].min())
|
|
267
|
+
seg_start = (min(origin_x, panel_min)
|
|
268
|
+
if origin_x is not None else 0)
|
|
269
|
+
_add_segment_trace(
|
|
270
|
+
fig, [c for c, f in zip(cats_i, fin) if f],
|
|
271
|
+
vals_i[fin].tolist(), seg_color, orientation,
|
|
272
|
+
seg_start, axis_suf=suf)
|
|
273
|
+
_add_marker_trace(
|
|
274
|
+
fig, cats_i,
|
|
275
|
+
[None if not np.isfinite(v) else v
|
|
276
|
+
for v in vals_i],
|
|
277
|
+
marker=_marker_list(
|
|
278
|
+
ms, len(vals_i),
|
|
279
|
+
color_override=[col_map[c] for c in cats_i]),
|
|
280
|
+
orientation=orientation, hover_tmpl=hover_tmpl,
|
|
281
|
+
axis_suf=suf)
|
|
282
|
+
|
|
283
|
+
val_lbl = x_lab if orientation == "h" else y_lab
|
|
284
|
+
cat_lbl = y_lab if orientation == "h" else x_lab
|
|
285
|
+
val_ax = _val_axis(val_lbl if i == 0 else "",
|
|
286
|
+
gridT, origin_x,
|
|
287
|
+
val_ticks(orientation))
|
|
288
|
+
cat_ax = _cat_axis(cat_lbl, cats_i)
|
|
289
|
+
x_ax, y_ax = ((val_ax, cat_ax)
|
|
290
|
+
if orientation == "h"
|
|
291
|
+
else (cat_ax, val_ax))
|
|
292
|
+
apply_rotate(x_ax, y_ax)
|
|
293
|
+
x_ax.update(domain=list(domains[i]["x"]),
|
|
294
|
+
anchor=f"y{suf}")
|
|
295
|
+
y_ax.update(domain=list(domains[i]["y"]),
|
|
296
|
+
anchor=f"x{suf}")
|
|
297
|
+
axes[f"xaxis{suf}"] = x_ax
|
|
298
|
+
axes[f"yaxis{suf}"] = y_ax
|
|
299
|
+
|
|
300
|
+
fig.update_layout(
|
|
301
|
+
annotations=anns,
|
|
302
|
+
margin=dict(t=round(title_size * 2.2) if main else 40,
|
|
303
|
+
b=8, l=20, r=20),
|
|
304
|
+
showlegend=False,
|
|
305
|
+
title=_dot_title(main),
|
|
306
|
+
template=None,
|
|
307
|
+
plot_bgcolor=to_hex(style_opts["panel_fill"]),
|
|
308
|
+
paper_bgcolor=to_hex(style_opts["window_fill"]),
|
|
309
|
+
**axes,
|
|
310
|
+
)
|
|
311
|
+
return fig
|
|
312
|
+
|
|
313
|
+
# ---- single-series dot plot ------------------------------------
|
|
314
|
+
pt_opacity = float(pt_opacity)
|
|
315
|
+
if not np.isfinite(pt_opacity):
|
|
316
|
+
pt_opacity = 0.95
|
|
317
|
+
pt_opacity = max(0.0, min(1.0, pt_opacity))
|
|
318
|
+
|
|
319
|
+
pairs = [(str(c), v) for c, v in zip(cats, vals)
|
|
320
|
+
if c is not None and not (isinstance(c, float)
|
|
321
|
+
and np.isnan(c))]
|
|
322
|
+
if not pairs:
|
|
323
|
+
return go.Figure()
|
|
324
|
+
cats = [p[0] for p in pairs]
|
|
325
|
+
vals = np.array([p[1] for p in pairs], dtype=float)
|
|
326
|
+
cats_all = list(cats) # full list for categoryarray
|
|
327
|
+
|
|
328
|
+
ms = _marker_style(shape, pt_size, fill, border, pt_opacity)
|
|
329
|
+
|
|
330
|
+
val_fmt = get_tick_fmt(vals[np.isfinite(vals)], digits_d)
|
|
331
|
+
val_spec = f":{val_fmt}" if val_fmt else ""
|
|
332
|
+
hover_tmpl = _hover(orientation, x_lab, y_lab, val_spec)
|
|
333
|
+
|
|
334
|
+
fig = go.Figure()
|
|
335
|
+
# segments first so dots sit on top
|
|
336
|
+
draw_seg = segments_x if orientation == "h" else segments_y
|
|
337
|
+
if draw_seg:
|
|
338
|
+
fin = np.isfinite(vals)
|
|
339
|
+
_add_segment_trace(
|
|
340
|
+
fig, [c for c, f in zip(cats, fin) if f],
|
|
341
|
+
vals[fin].tolist(), seg_color, orientation,
|
|
342
|
+
seg_start=0 if origin_x is None else origin_x)
|
|
343
|
+
|
|
344
|
+
# one palette color per category, same hues as the bar chart
|
|
345
|
+
pt_cols = make_trans(
|
|
346
|
+
to_hex([fill[i % len(fill)] if isinstance(fill, (list, tuple))
|
|
347
|
+
else fill for i in range(len(vals))]),
|
|
348
|
+
pt_opacity)
|
|
349
|
+
_add_marker_trace(
|
|
350
|
+
fig, cats, [None if not np.isfinite(v) else v for v in vals],
|
|
351
|
+
marker=_marker_list(ms, len(vals), color_override=pt_cols),
|
|
352
|
+
orientation=orientation, hover_tmpl=hover_tmpl)
|
|
353
|
+
|
|
354
|
+
# grid lines run perpendicular to the value axis
|
|
355
|
+
if gridT:
|
|
356
|
+
grid_shapes = (y_grid(gridT) if orientation == "v"
|
|
357
|
+
else x_grid(gridT))
|
|
358
|
+
else:
|
|
359
|
+
grid_shapes = []
|
|
360
|
+
cat_ax = _cat_axis(x_lab if orientation == "v" else y_lab,
|
|
361
|
+
cats_all)
|
|
362
|
+
val_ax = _val_axis(x_lab if orientation == "h" else y_lab,
|
|
363
|
+
gridT, origin_x, val_ticks(orientation))
|
|
364
|
+
|
|
365
|
+
if orientation == "h":
|
|
366
|
+
margin = dict(l=70,
|
|
367
|
+
t=round(title_size * 2.2) if main else 30,
|
|
368
|
+
b=max(40, 60 if x_lab else 0))
|
|
369
|
+
apply_rotate(val_ax, cat_ax)
|
|
370
|
+
fig.update_layout(xaxis=val_ax, yaxis=cat_ax)
|
|
371
|
+
else:
|
|
372
|
+
margin = dict(t=round(title_size * 2.2) if main else 30,
|
|
373
|
+
b=max(12, 60 if x_lab else 0))
|
|
374
|
+
apply_rotate(cat_ax, val_ax)
|
|
375
|
+
fig.update_layout(xaxis=cat_ax, yaxis=val_ax)
|
|
376
|
+
|
|
377
|
+
fig.update_layout(
|
|
378
|
+
shapes=grid_shapes + plot_border(),
|
|
379
|
+
margin=margin,
|
|
380
|
+
title=_dot_title(main),
|
|
381
|
+
template=None,
|
|
382
|
+
plot_bgcolor=to_hex(style_opts["panel_fill"]),
|
|
383
|
+
paper_bgcolor=to_hex(style_opts["window_fill"]),
|
|
384
|
+
)
|
|
385
|
+
return fig
|