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/plotly_utils.py
CHANGED
|
@@ -17,6 +17,43 @@ BASE_COLORS = [
|
|
|
17
17
|
"#00A3BA", "#D26FAF", "#00A76F", "#BD76CB",
|
|
18
18
|
]
|
|
19
19
|
|
|
20
|
+
|
|
21
|
+
def theme_palette(theme=None):
|
|
22
|
+
"""The palette a color theme gives to groups. R analog: .get_fill()
|
|
23
|
+
(not divergent): the qualitative hues for the default theme, else a
|
|
24
|
+
sequential palette in the theme's hue."""
|
|
25
|
+
from .utils import get_option
|
|
26
|
+
if theme is None:
|
|
27
|
+
theme = get_option("theme", "colors")
|
|
28
|
+
if theme == "colors":
|
|
29
|
+
return "hues"
|
|
30
|
+
for pal, members in (
|
|
31
|
+
("grays", ("gray", "white")),
|
|
32
|
+
("blues", ("lightbronze", "dodgerblue", "blue")),
|
|
33
|
+
("browns", ("gold", "brown", "sienna")),
|
|
34
|
+
("rusts", ("orange",)),
|
|
35
|
+
("reds", ("darkred", "red", "rose", "slatered")),
|
|
36
|
+
("greens", ("darkgreen", "green")),
|
|
37
|
+
("violets", ("purple",))):
|
|
38
|
+
if theme in members:
|
|
39
|
+
return pal
|
|
40
|
+
return "blues"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def by_colors(n, theme=None):
|
|
44
|
+
"""One color per group of a by variable under the current theme,
|
|
45
|
+
as X() and XY() draw them. R analog: .color_range(.get_fill(theme),
|
|
46
|
+
n), two grays set apart as gray60 and gray30."""
|
|
47
|
+
pal = theme_palette(theme)
|
|
48
|
+
if pal == "hues":
|
|
49
|
+
return [BASE_COLORS[i % len(BASE_COLORS)] for i in range(n)]
|
|
50
|
+
if pal == "grays" and n == 2:
|
|
51
|
+
return ["#999999", "#4D4D4D"]
|
|
52
|
+
from .getColors import getColors
|
|
53
|
+
cols = getColors(pal, n=max(1, n), quiet=True)
|
|
54
|
+
return [c[:7] if len(c) == 9 else c for c in cols]
|
|
55
|
+
|
|
56
|
+
|
|
20
57
|
# minimal named-color table; R's col2rgb() knows all R names, but
|
|
21
58
|
# plotly accepts CSS names directly so unknowns pass through as-is
|
|
22
59
|
_NAMED = {
|
|
@@ -80,6 +117,10 @@ def to_hex(col):
|
|
|
80
117
|
gray = _gray_level(lower)
|
|
81
118
|
if gray is not None:
|
|
82
119
|
return gray
|
|
120
|
+
# any of R's named colors, as col2rgb() resolves them
|
|
121
|
+
from .rcolors import R_COLORS
|
|
122
|
+
if lower in R_COLORS:
|
|
123
|
+
return R_COLORS[lower]
|
|
83
124
|
return c1 # CSS name unknown to us; plotly handles it
|
|
84
125
|
|
|
85
126
|
|
|
@@ -222,11 +263,15 @@ def legend_style(by_name, style_opts, title=None, position=None,
|
|
|
222
263
|
leg = dict(
|
|
223
264
|
title=dict(text=abbrev(text, abbrev_n) or "",
|
|
224
265
|
font=dict(size=round(
|
|
225
|
-
15 * get_option("lab_size", 1) * mult)
|
|
266
|
+
15 * get_option("lab_size", 1) * mult),
|
|
267
|
+
color=as_plotly_color(style_opts["lab_color"]))),
|
|
268
|
+
# the legend reads in the theme's label color, light on the
|
|
269
|
+
# black sub_theme as the axis labels are
|
|
226
270
|
font=dict(size=round(
|
|
227
|
-
16 * get_option("axis_size", 0.9) * mult)
|
|
228
|
-
|
|
229
|
-
|
|
271
|
+
16 * get_option("axis_size", 0.9) * mult),
|
|
272
|
+
color=as_plotly_color(style_opts["lab_color"])),
|
|
273
|
+
bgcolor=as_plotly_color(style_opts["window_fill"]),
|
|
274
|
+
bordercolor=as_plotly_color(style_opts["legend_border"]),
|
|
230
275
|
borderwidth=1,
|
|
231
276
|
)
|
|
232
277
|
# placement stays plotly's own unless asked for, so the callers
|
|
@@ -315,12 +360,27 @@ def axis_format(vals, digits_d=2, axis_fmt="K", prefix=""):
|
|
|
315
360
|
return base
|
|
316
361
|
|
|
317
362
|
|
|
363
|
+
_R_DASH = {"dashed": "dash", "dotted": "dot",
|
|
364
|
+
"dotdash": "dashdot", "longdash": "longdash",
|
|
365
|
+
"twodash": "longdashdot"}
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def r_dash(lty):
|
|
369
|
+
"""An R line type name as plotly's dash, such as "dotted" to
|
|
370
|
+
"dot"; plotly's own names and None pass through. R analog: lty"""
|
|
371
|
+
return _R_DASH.get(lty, lty) if isinstance(lty, str) else lty
|
|
372
|
+
|
|
373
|
+
|
|
318
374
|
def grid_style():
|
|
319
|
-
"""R analog: .grid_style()
|
|
375
|
+
"""R analog: .grid_style(). A sub_theme sets the grid through
|
|
376
|
+
grid_x_color / grid_y_color (white on the gray panel of "default"),
|
|
377
|
+
which take precedence over the grid color itself"""
|
|
378
|
+
clr = (get_option("grid_y_color") or get_option("grid_x_color")
|
|
379
|
+
or get_option("grid_col", "gray85"))
|
|
320
380
|
return {
|
|
321
|
-
"color":
|
|
381
|
+
"color": as_plotly_color(clr),
|
|
322
382
|
"width": get_option("grid_lwd", 0.5),
|
|
323
|
-
"dash": get_option("grid_lty", None),
|
|
383
|
+
"dash": r_dash(get_option("grid_lty", None)),
|
|
324
384
|
}
|
|
325
385
|
|
|
326
386
|
|
|
@@ -355,27 +415,36 @@ def y_grid(y_vals):
|
|
|
355
415
|
|
|
356
416
|
|
|
357
417
|
def axis_base():
|
|
358
|
-
"""R analog: axis_base()
|
|
418
|
+
"""R analog: axis_base(). A sub_theme sets the axes through their
|
|
419
|
+
own settings, so on sub_theme="black" the axis line and its labels
|
|
420
|
+
are light where axis_color stays dark; the labels are text, so they
|
|
421
|
+
take axis_text_color, as lessR's base R axes do"""
|
|
422
|
+
ax_line = (get_option("axis_x_color")
|
|
423
|
+
or get_option("axis_color", "black"))
|
|
424
|
+
ax_text = (get_option("axis_x_text_color")
|
|
425
|
+
or get_option("axis_text_color")
|
|
426
|
+
or get_option("axis_color", "black"))
|
|
359
427
|
return {
|
|
360
428
|
"zeroline": False,
|
|
361
429
|
"showline": True,
|
|
362
|
-
"linecolor":
|
|
430
|
+
"linecolor": as_plotly_color(ax_line),
|
|
363
431
|
"linewidth": get_option("axis_lwd", 1),
|
|
364
432
|
"ticks": "outside",
|
|
365
433
|
"ticklen": 4,
|
|
434
|
+
"tickcolor": as_plotly_color(ax_line),
|
|
366
435
|
"automargin": True,
|
|
367
436
|
"tickfont": {
|
|
368
|
-
"color":
|
|
437
|
+
"color": as_plotly_color(ax_text),
|
|
369
438
|
"size": 16 * get_option("axis_size", 0.9),
|
|
370
439
|
},
|
|
371
440
|
"title": {
|
|
372
441
|
"font": {
|
|
373
|
-
"color":
|
|
442
|
+
"color": as_plotly_color(get_option("lab_color", "black")),
|
|
374
443
|
"size": 15 * get_option("lab_size", 1),
|
|
375
444
|
},
|
|
376
445
|
"standoff": 10,
|
|
377
446
|
},
|
|
378
|
-
"gridcolor":
|
|
447
|
+
"gridcolor": as_plotly_color(get_option("grid_color", "gray90")),
|
|
379
448
|
"gridwidth": get_option("grid_lwd", 1),
|
|
380
449
|
}
|
|
381
450
|
|
|
@@ -445,7 +514,7 @@ def add_subtitle(fig, sub):
|
|
|
445
514
|
title_size = round(16 * get_option("main_size", 1))
|
|
446
515
|
size = round(title_size * get_option("sub_size", 0.75) / 0.75
|
|
447
516
|
* 0.75)
|
|
448
|
-
color =
|
|
517
|
+
color = as_plotly_color(get_option("sub_color",
|
|
449
518
|
get_option("main_color",
|
|
450
519
|
get_option("lab_color",
|
|
451
520
|
"black"))))
|
|
@@ -486,6 +555,10 @@ def font_scaled(func):
|
|
|
486
555
|
fig = func(*args, **kwargs)
|
|
487
556
|
if sub and hasattr(fig, "layout"):
|
|
488
557
|
add_subtitle(fig, sub)
|
|
558
|
+
# style(font_family=) sets the family of all plotted text
|
|
559
|
+
family = get_option("font_family")
|
|
560
|
+
if family and hasattr(fig, "layout"):
|
|
561
|
+
fig.update_layout(font=dict(family=family))
|
|
489
562
|
try:
|
|
490
563
|
if font_size and font_size != 1 and hasattr(fig, "layout"):
|
|
491
564
|
apply_font_size(fig, font_size)
|
|
@@ -496,13 +569,66 @@ def font_scaled(func):
|
|
|
496
569
|
return wrapper
|
|
497
570
|
|
|
498
571
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
572
|
+
# R's pch codes 0-25 as plotly symbols; 21-25 are lessR's named
|
|
573
|
+
# shapes. R analog: .plt.shapes(), and points() for the open codes
|
|
574
|
+
_R_PCH = {
|
|
575
|
+
0: "square-open", 1: "circle-open", 2: "triangle-up-open",
|
|
576
|
+
3: "cross-thin-open", 4: "x-thin-open", 5: "diamond-open",
|
|
577
|
+
6: "triangle-down-open", 7: "square-x-open",
|
|
578
|
+
8: "asterisk-open", 9: "diamond-cross-open",
|
|
579
|
+
10: "circle-cross-open", 11: "hexagram-open",
|
|
580
|
+
12: "square-cross-open", 13: "circle-x-open",
|
|
581
|
+
14: "square-open", 15: "square", 16: "circle",
|
|
582
|
+
17: "triangle-up", 18: "diamond", 19: "circle", 20: "circle",
|
|
583
|
+
21: "circle", 22: "square", 23: "diamond", 24: "triangle-up",
|
|
584
|
+
25: "triangle-down"}
|
|
585
|
+
_LESSR_SHAPES = {"triup": "triangle-up", "tridown": "triangle-down"}
|
|
586
|
+
# pt_shape="vary": one shape per by group, in lessR's order
|
|
587
|
+
_VARY = (["triup", "tridown", "circle", "square", "diamond",
|
|
588
|
+
8, 7, 9, 10, 12, 13, 14, 11]
|
|
589
|
+
+ list("abcdefghijklmnopqrstuvwxyz")
|
|
590
|
+
+ list("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
|
591
|
+
+ list("+*#%!=-&$?|><@./;:"))
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
def shape_at(shape, i):
|
|
595
|
+
"""Per-group shape as (plotly symbol, None), or (None, char) for
|
|
596
|
+
a single character, drawn as text, as R's pch="$". A scalar
|
|
597
|
+
shape is used for every group (by= differentiates by color
|
|
598
|
+
only, the default); a list gives one shape per group, recycled;
|
|
599
|
+
"vary" a different shape per group. Takes plotly's names,
|
|
600
|
+
lessR's triup/tridown, and R's pch codes 0-25.
|
|
601
|
+
R analog: .plt.shapes()"""
|
|
602
|
+
if isinstance(shape, str) and shape == "vary":
|
|
603
|
+
shape = _VARY
|
|
503
604
|
if isinstance(shape, (list, tuple)):
|
|
504
|
-
|
|
505
|
-
|
|
605
|
+
shape = shape[i % len(shape)]
|
|
606
|
+
if isinstance(shape, (int, np.integer)) and not isinstance(
|
|
607
|
+
shape, bool):
|
|
608
|
+
if int(shape) not in _R_PCH:
|
|
609
|
+
raise ValueError(
|
|
610
|
+
f"pt_shape={shape}: R's numbered shapes are 0 to 25")
|
|
611
|
+
return _R_PCH[int(shape)], None
|
|
612
|
+
s = str(shape)
|
|
613
|
+
if len(s) == 1:
|
|
614
|
+
return None, s
|
|
615
|
+
return _LESSR_SHAPES.get(s, s), None
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
def marks(shape, i, mode, marker):
|
|
619
|
+
"""go.Scatter keywords that draw the points of group i: the
|
|
620
|
+
marker with its symbol, or for a character shape the
|
|
621
|
+
character as text in the marker's border color, sized to the
|
|
622
|
+
marker. mode is "markers" or "lines+markers"."""
|
|
623
|
+
sym, ch = shape_at(shape, i)
|
|
624
|
+
if ch is None:
|
|
625
|
+
return dict(mode=mode, marker=dict(marker, symbol=sym))
|
|
626
|
+
color = (marker.get("line") or {}).get("color", marker.get("color"))
|
|
627
|
+
return dict(mode=mode.replace("markers", "text"), text=ch,
|
|
628
|
+
textposition="middle center",
|
|
629
|
+
textfont=dict(color=color,
|
|
630
|
+
size=max(8, round(float(np.median(
|
|
631
|
+
marker.get("size", 8))) * 1.4))))
|
|
506
632
|
|
|
507
633
|
|
|
508
634
|
def square_layout(main=False, side=460):
|
|
@@ -603,7 +729,7 @@ def facet_panels(facet, facet_order, facet2=None,
|
|
|
603
729
|
|
|
604
730
|
def finish_facet(fig, levels, ax, x_lab, y_lab, gridT1,
|
|
605
731
|
style_opts=None, y_cat=None, n_col=1, pos=None,
|
|
606
|
-
height=None, width=None):
|
|
732
|
+
height=None, width=None, x_cat=None):
|
|
607
733
|
"""Facet styling shared by the faceted renderers: per-panel y
|
|
608
734
|
axes on a common scale (y label on the middle panel of the
|
|
609
735
|
first column), x ticks on the bottom row only, vertical grid
|
|
@@ -631,8 +757,8 @@ def finish_facet(fig, levels, ax, x_lab, y_lab, gridT1,
|
|
|
631
757
|
anns = list(fig.layout.annotations or [])
|
|
632
758
|
s_fill = as_plotly_color(get_option("strip_fill",
|
|
633
759
|
"#7F7F7F37"))
|
|
634
|
-
s_line =
|
|
635
|
-
s_text =
|
|
760
|
+
s_line = as_plotly_color(get_option("strip_color", "gray40"))
|
|
761
|
+
s_text = as_plotly_color(get_option("strip_text_color", "gray15"))
|
|
636
762
|
strip_h = max(0.10, min(0.30, 0.05 * n_row))
|
|
637
763
|
# strip font: shrink so the longest label fits its panel
|
|
638
764
|
# (the two-facet Var = "level" cell labels run long)
|
|
@@ -654,7 +780,7 @@ def finish_facet(fig, levels, ax, x_lab, y_lab, gridT1,
|
|
|
654
780
|
if y_cat is None:
|
|
655
781
|
ay = axis_num(show_ylab, ax["axT2"], ax["axL2"])
|
|
656
782
|
ay.update(showgrid=True,
|
|
657
|
-
gridcolor=
|
|
783
|
+
gridcolor=as_plotly_color(style_opts["grid_col"]),
|
|
658
784
|
gridwidth=1, griddash="dot",
|
|
659
785
|
showticklabels=c == 1,
|
|
660
786
|
range=[0, float(ax["axT2"][-1]) * 1.04])
|
|
@@ -670,7 +796,14 @@ def finish_facet(fig, levels, ax, x_lab, y_lab, gridT1,
|
|
|
670
796
|
showticklabels=c == 1)
|
|
671
797
|
fig.update_yaxes(row=r, col=c, **ay)
|
|
672
798
|
lowest = r == n_row or (r + 1, c) not in occupied
|
|
673
|
-
if lowest:
|
|
799
|
+
if lowest and x_cat is not None:
|
|
800
|
+
# categories along x, the vertical bars of a panel
|
|
801
|
+
ax_x = axis_cat(x_lab if c == mid_c else "")
|
|
802
|
+
ax_x.update(categoryorder="array",
|
|
803
|
+
categoryarray=list(x_cat), tickmode="array",
|
|
804
|
+
tickvals=list(x_cat), ticktext=list(x_cat))
|
|
805
|
+
fig.update_xaxes(row=r, col=c, showticklabels=True, **ax_x)
|
|
806
|
+
elif lowest:
|
|
674
807
|
# a shared x axis hides the labels above the bottom row,
|
|
675
808
|
# so a ragged grid's lowest panel turns its own back on
|
|
676
809
|
fig.update_xaxes(
|
|
@@ -680,7 +813,7 @@ def finish_facet(fig, levels, ax, x_lab, y_lab, gridT1,
|
|
|
680
813
|
else:
|
|
681
814
|
fig.update_xaxes(
|
|
682
815
|
row=r, col=c, showline=True, ticks="",
|
|
683
|
-
linecolor=
|
|
816
|
+
linecolor=as_plotly_color(get_option("axis_color",
|
|
684
817
|
"black")))
|
|
685
818
|
for v in (gridT1 or []): # vertical panel grid
|
|
686
819
|
shapes.append({
|
|
@@ -693,7 +826,7 @@ def finish_facet(fig, levels, ax, x_lab, y_lab, gridT1,
|
|
|
693
826
|
"type": "rect",
|
|
694
827
|
"xref": f"x{suf} domain", "yref": f"y{suf} domain",
|
|
695
828
|
"x0": 0, "x1": 1, "y0": 0, "y1": 1,
|
|
696
|
-
"line": {"color":
|
|
829
|
+
"line": {"color": as_plotly_color("gray75"), "width": 1},
|
|
697
830
|
"fillcolor": "rgba(0,0,0,0)"})
|
|
698
831
|
shapes.append({ # strip label band
|
|
699
832
|
"type": "rect",
|
|
@@ -718,8 +851,8 @@ def finish_facet(fig, levels, ax, x_lab, y_lab, gridT1,
|
|
|
718
851
|
|
|
719
852
|
layout_kw = dict(
|
|
720
853
|
shapes=shapes, annotations=anns, template=None,
|
|
721
|
-
plot_bgcolor=
|
|
722
|
-
paper_bgcolor=
|
|
854
|
+
plot_bgcolor=as_plotly_color(style_opts["panel_fill"]),
|
|
855
|
+
paper_bgcolor=as_plotly_color(style_opts["window_fill"]),
|
|
723
856
|
height=height if height is not None else 140 + 160 * n_row,
|
|
724
857
|
)
|
|
725
858
|
if width is not None:
|
|
@@ -814,7 +947,7 @@ def plot_border(top=True, right=True, color=None, width=None):
|
|
|
814
947
|
if isinstance(color, str) and re.fullmatch(r"gray\d+", color):
|
|
815
948
|
color = "gray75" # make a gray a little lighter for plotly
|
|
816
949
|
|
|
817
|
-
line = {"color":
|
|
950
|
+
line = {"color": as_plotly_color(color), "width": width}
|
|
818
951
|
out = []
|
|
819
952
|
if top:
|
|
820
953
|
out.append({"type": "line", "xref": "paper", "yref": "paper",
|
lessPy/plt_add.py
CHANGED
|
@@ -13,14 +13,9 @@
|
|
|
13
13
|
import numpy as np
|
|
14
14
|
import plotly.graph_objects as go
|
|
15
15
|
|
|
16
|
-
from .plotly_utils import as_plotly_color, make_trans, to_hex
|
|
16
|
+
from .plotly_utils import as_plotly_color, make_trans, r_dash, to_hex
|
|
17
17
|
from .utils import get_option
|
|
18
18
|
|
|
19
|
-
_DASH = {"dashed": "dash", "dotted": "dot",
|
|
20
|
-
"dotdash": "dashdot", "longdash": "longdash",
|
|
21
|
-
"twodash": "longdashdot"}
|
|
22
|
-
|
|
23
|
-
|
|
24
19
|
def _as_list(v):
|
|
25
20
|
if v is None:
|
|
26
21
|
return []
|
|
@@ -86,7 +81,7 @@ def plt_add(fig, add, x1=None, x2=None, y1=None, y2=None):
|
|
|
86
81
|
else as_plotly_color(fll))
|
|
87
82
|
line = dict(color=col, width=max(0.5, lwd * 2))
|
|
88
83
|
if lty != "solid":
|
|
89
|
-
line["dash"] =
|
|
84
|
+
line["dash"] = r_dash(lty)
|
|
90
85
|
|
|
91
86
|
for xx1, yy1, xx2, yy2 in locs(obj):
|
|
92
87
|
if obj == "v_line":
|
lessPy/plt_contour.py
CHANGED
|
@@ -14,7 +14,7 @@ import plotly.graph_objects as go
|
|
|
14
14
|
from scipy import stats as sps
|
|
15
15
|
|
|
16
16
|
from .plotly_utils import (
|
|
17
|
-
axis_format, axis_num, get_tick_fmt, plot_border,
|
|
17
|
+
as_plotly_color, axis_format, axis_num, get_tick_fmt, plot_border,
|
|
18
18
|
plotly_style, to_hex)
|
|
19
19
|
from .utils import get_option, pretty
|
|
20
20
|
|
|
@@ -134,7 +134,7 @@ def plt_contour(xv, yv, contour_n, contour_nbins, contour_points,
|
|
|
134
134
|
showscale=bool(legend),
|
|
135
135
|
colorbar=dict(
|
|
136
136
|
tickfont=dict(
|
|
137
|
-
color=
|
|
137
|
+
color=as_plotly_color(get_option("axis_color", "black")),
|
|
138
138
|
size=16 * get_option("axis_size", 0.9)),
|
|
139
139
|
outlinewidth=0),
|
|
140
140
|
hovertemplate=(f"{x_lab}: {xpart}<br>{y_lab}: {ypart}"
|
|
@@ -158,7 +158,7 @@ def plt_contour(xv, yv, contour_n, contour_nbins, contour_points,
|
|
|
158
158
|
for ex_l, ey_l in ellipses or []:
|
|
159
159
|
fig.add_trace(go.Scatter(
|
|
160
160
|
x=ex_l, y=ey_l, mode="lines",
|
|
161
|
-
line=dict(color=
|
|
161
|
+
line=dict(color=as_plotly_color(ellipse_color),
|
|
162
162
|
width=ellipse_lwd),
|
|
163
163
|
hoverinfo="skip", showlegend=False))
|
|
164
164
|
|
|
@@ -167,7 +167,7 @@ def plt_contour(xv, yv, contour_n, contour_nbins, contour_points,
|
|
|
167
167
|
continue
|
|
168
168
|
fig.add_trace(go.Scatter(
|
|
169
169
|
x=fl["x"], y=fl["y"], mode="lines",
|
|
170
|
-
line=dict(color=
|
|
170
|
+
line=dict(color=as_plotly_color(fit_color), width=fit_lwd),
|
|
171
171
|
hoverinfo="skip", showlegend=False))
|
|
172
172
|
|
|
173
173
|
ax_x = axis_num(x_lab, axT1, axL1)
|
|
@@ -178,8 +178,8 @@ def plt_contour(xv, yv, contour_n, contour_nbins, contour_points,
|
|
|
178
178
|
xaxis=ax_x, yaxis=ax_y,
|
|
179
179
|
shapes=plot_border(),
|
|
180
180
|
template=None,
|
|
181
|
-
plot_bgcolor=
|
|
182
|
-
paper_bgcolor=
|
|
181
|
+
plot_bgcolor=as_plotly_color(style_opts["panel_fill"]),
|
|
182
|
+
paper_bgcolor=as_plotly_color(style_opts["window_fill"]),
|
|
183
183
|
)
|
|
184
184
|
|
|
185
185
|
if main:
|
lessPy/plt_contour_facet.py
CHANGED
|
@@ -17,7 +17,7 @@ import numpy as np
|
|
|
17
17
|
import plotly.graph_objects as go
|
|
18
18
|
|
|
19
19
|
from .plotly_utils import (
|
|
20
|
-
axis_format, facet_fig, facet_panels, finish_facet,
|
|
20
|
+
as_plotly_color, axis_format, facet_fig, facet_panels, finish_facet,
|
|
21
21
|
plotly_style, to_hex)
|
|
22
22
|
from .plt_contour import _COLORSCALE, _bw_nrd, _kde2d
|
|
23
23
|
from .plt_smooth import _RAMP as _SMOOTH_RAMP
|
|
@@ -159,7 +159,7 @@ def plt_contour_facet(xv, yv, facet_arr, facet_order, facet_name,
|
|
|
159
159
|
ex, ey = ellipse_fn(xi, yi, float(lv))
|
|
160
160
|
fig.add_trace(go.Scatter(
|
|
161
161
|
x=ex, y=ey, mode="lines",
|
|
162
|
-
line=dict(color=
|
|
162
|
+
line=dict(color=as_plotly_color(ellipse_color),
|
|
163
163
|
width=ellipse_lwd),
|
|
164
164
|
hoverinfo="skip", showlegend=False,
|
|
165
165
|
), row=row, col=col)
|
|
@@ -169,7 +169,7 @@ def plt_contour_facet(xv, yv, facet_arr, facet_order, facet_name,
|
|
|
169
169
|
if okf.sum() >= 2:
|
|
170
170
|
fig.add_trace(go.Scatter(
|
|
171
171
|
x=xs[okf], y=f[okf], mode="lines",
|
|
172
|
-
line=dict(color=
|
|
172
|
+
line=dict(color=as_plotly_color(fit_color),
|
|
173
173
|
width=fit_lwd),
|
|
174
174
|
hoverinfo="skip", showlegend=False,
|
|
175
175
|
), row=row, col=col)
|
lessPy/plt_mat_plotly.py
CHANGED
|
@@ -29,14 +29,28 @@ def _axis_ref(k, kind):
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
def scatter_matrix(df, fit="lm", digits_d=2, cor_coef=True,
|
|
32
|
-
main=None, band=True
|
|
32
|
+
main=None, band=True, form="scatter",
|
|
33
|
+
contour_n=None, contour_nbins=50,
|
|
34
|
+
contour_points=False, smooth_bins=128,
|
|
35
|
+
smooth_power=0.25):
|
|
33
36
|
"""Scatterplot matrix of the columns of df, in column order.
|
|
34
37
|
fit: "off" (no line, XY default), "lm" (Regression) or
|
|
35
38
|
"loess". cor_coef=True puts the correlation in the upper
|
|
36
39
|
triangle and the scatter below (~ .plt.mat, Regression);
|
|
37
40
|
cor_coef=False draws the scatter in every off-diagonal cell,
|
|
38
41
|
the symmetric matrix R's Logit uses (logit.4Pred). band adds
|
|
39
|
-
the fit's 95% confidence band.
|
|
42
|
+
the fit's 95% confidence band. form="contour" or "smooth"
|
|
43
|
+
draws each cell as its own kernel density over the cell's
|
|
44
|
+
range, 8 contour bands unless contour_n is given (a cell cannot
|
|
45
|
+
resolve the 20 of a single plot), any fit drawn without its
|
|
46
|
+
band. R analog: .plt.mat() panel.dens. Returns a plotly
|
|
47
|
+
Figure."""
|
|
48
|
+
if form not in ("scatter", "contour", "smooth"):
|
|
49
|
+
raise ValueError(
|
|
50
|
+
'A matrix renders its cells as form="scatter", "contour", '
|
|
51
|
+
f'or "smooth", so form="{form}" is not\navailable for a '
|
|
52
|
+
"matrix.")
|
|
53
|
+
contour_n = 8 if contour_n is None else int(contour_n)
|
|
40
54
|
cols = list(df.columns)
|
|
41
55
|
n = len(cols)
|
|
42
56
|
if n < 2:
|
|
@@ -54,12 +68,12 @@ def scatter_matrix(df, fit="lm", digits_d=2, cor_coef=True,
|
|
|
54
68
|
bg = window_fill if panel_fill == "transparent" else panel_fill
|
|
55
69
|
diag_fill = get_option("se_fill", "#1A1A1A19")
|
|
56
70
|
se_fill = get_option("se_fill", "#1A1A1A19")
|
|
57
|
-
border =
|
|
71
|
+
border = as_plotly_color(style["panel_border"])
|
|
58
72
|
pt_fill = get_option("pt_fill", get_option("pt_color",
|
|
59
73
|
"#324E5C"))
|
|
60
74
|
pt_color = get_option("pt_color", "#324E5C")
|
|
61
|
-
lab_color =
|
|
62
|
-
fit_color =
|
|
75
|
+
lab_color = as_plotly_color(style["lab_color"])
|
|
76
|
+
fit_color = as_plotly_color(get_option("fit_color", "#5C4032"))
|
|
63
77
|
fit_lwd = get_option("fit_lwd", 2)
|
|
64
78
|
|
|
65
79
|
# sizes shrink with the variable count, as R's cex.adj
|
|
@@ -78,6 +92,17 @@ def scatter_matrix(df, fit="lm", digits_d=2, cor_coef=True,
|
|
|
78
92
|
|
|
79
93
|
# scatter in the lower triangle, and also in the
|
|
80
94
|
# upper triangle when no correlations are requested
|
|
95
|
+
if r != c and (r > c or not cor_coef) \
|
|
96
|
+
and form != "scatter":
|
|
97
|
+
_cell_bg(fig, xr, yr, bg, border)
|
|
98
|
+
_density_cell(fig, r, c, vals[cols[c - 1]],
|
|
99
|
+
vals[cols[r - 1]], rng[cols[c - 1]],
|
|
100
|
+
rng[cols[r - 1]], form, contour_n,
|
|
101
|
+
contour_nbins, contour_points,
|
|
102
|
+
smooth_bins, smooth_power,
|
|
103
|
+
fit if do_fit else "off", fit_color,
|
|
104
|
+
fit_lwd)
|
|
105
|
+
continue
|
|
81
106
|
if r != c and (r > c or not cor_coef):
|
|
82
107
|
_cell_bg(fig, xr, yr, bg, border)
|
|
83
108
|
_scatter_cell(fig, r, c, vals[cols[c - 1]],
|
|
@@ -135,8 +160,8 @@ def scatter_matrix(df, fit="lm", digits_d=2, cor_coef=True,
|
|
|
135
160
|
|
|
136
161
|
fig.update_layout(
|
|
137
162
|
template=None, showlegend=False,
|
|
138
|
-
plot_bgcolor=
|
|
139
|
-
paper_bgcolor=
|
|
163
|
+
plot_bgcolor=as_plotly_color(bg),
|
|
164
|
+
paper_bgcolor=as_plotly_color(window_fill))
|
|
140
165
|
if main:
|
|
141
166
|
fig.update_layout(title=dict(
|
|
142
167
|
text=main, x=0.5, xanchor="center",
|
|
@@ -182,16 +207,17 @@ def _scatter_cell(fig, r, c, xv, yv, fit, do_fit, px,
|
|
|
182
207
|
(f - tq * se_f)[::-1]]))
|
|
183
208
|
else:
|
|
184
209
|
poly = _se_band(xs, ys_s, f, 0.95)
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
210
|
+
if poly is not None:
|
|
211
|
+
fig.add_trace(go.Scatter(
|
|
212
|
+
x=poly[0], y=poly[1], mode="none", fill="toself",
|
|
213
|
+
fillcolor=as_plotly_color(se_fill),
|
|
214
|
+
hoverinfo="skip", showlegend=False), row=r, col=c)
|
|
189
215
|
|
|
190
216
|
fig.add_trace(go.Scatter(
|
|
191
217
|
x=xv, y=yv, mode="markers",
|
|
192
218
|
marker=dict(symbol="circle", size=px, sizemode="diameter",
|
|
193
219
|
color=make_trans(pt_fill, 0.9), opacity=1,
|
|
194
|
-
line=dict(color=
|
|
220
|
+
line=dict(color=as_plotly_color(pt_color), width=0.5)),
|
|
195
221
|
hoverinfo="x+y", showlegend=False), row=r, col=c)
|
|
196
222
|
|
|
197
223
|
if do_fit and len(xv) >= 2:
|
|
@@ -199,3 +225,50 @@ def _scatter_cell(fig, r, c, xv, yv, fit, do_fit, px,
|
|
|
199
225
|
x=xs, y=f, mode="lines",
|
|
200
226
|
line=dict(color=fit_color, width=fit_lwd),
|
|
201
227
|
hoverinfo="skip", showlegend=False), row=r, col=c)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def _density_cell(fig, r, c, xv, yv, xr, yr, form, contour_n,
|
|
231
|
+
contour_nbins, contour_points, smooth_bins,
|
|
232
|
+
smooth_power, fit, fit_color, fit_lwd):
|
|
233
|
+
"""One cell as a kernel density over the cell's range: filled
|
|
234
|
+
contour bands or the smooth shading, then the fit, unbanded.
|
|
235
|
+
R analog: panel.dens of .plt.mat()"""
|
|
236
|
+
from .plt_contour import _COLORSCALE, _bw_nrd, _kde2d
|
|
237
|
+
from .plt_smooth import _RAMP
|
|
238
|
+
from .XY import _loess, _plt_fit
|
|
239
|
+
|
|
240
|
+
ok = np.isfinite(xv) & np.isfinite(yv)
|
|
241
|
+
xo, yo = xv[ok], yv[ok]
|
|
242
|
+
if len(xo) < 3 or _bw_nrd(xo) <= 0 or _bw_nrd(yo) <= 0:
|
|
243
|
+
return
|
|
244
|
+
lims = (float(xr[0]), float(xr[-1]), float(yr[0]), float(yr[-1]))
|
|
245
|
+
n_grid = smooth_bins if form == "smooth" else contour_nbins
|
|
246
|
+
gx, gy, z = _kde2d(xo, yo, n_grid, lims)
|
|
247
|
+
if form == "smooth":
|
|
248
|
+
fig.add_trace(go.Heatmap(
|
|
249
|
+
x=gx, y=gy, z=(z ** smooth_power).T, colorscale=_RAMP,
|
|
250
|
+
zsmooth="best", showscale=False, hoverinfo="skip"),
|
|
251
|
+
row=r, col=c)
|
|
252
|
+
else:
|
|
253
|
+
lv0, lv1 = float(z.min()), float(z.max())
|
|
254
|
+
step = (lv1 - lv0) / contour_n
|
|
255
|
+
fig.add_trace(go.Contour(
|
|
256
|
+
x=gx, y=gy, z=z.T, colorscale=_COLORSCALE,
|
|
257
|
+
contours=dict(start=lv0 + step, end=lv1, size=step),
|
|
258
|
+
line=dict(width=0), showscale=False, hoverinfo="skip"),
|
|
259
|
+
row=r, col=c)
|
|
260
|
+
if contour_points:
|
|
261
|
+
fig.add_trace(go.Scatter(
|
|
262
|
+
x=xo, y=yo, mode="markers",
|
|
263
|
+
marker=dict(size=3, color="rgba(0,0,0,0.27)",
|
|
264
|
+
line=dict(color="white", width=0.2)),
|
|
265
|
+
hoverinfo="skip", showlegend=False), row=r, col=c)
|
|
266
|
+
if fit in ("lm", "loess"):
|
|
267
|
+
if fit == "loess":
|
|
268
|
+
xs, _, f, _ = _loess(xo, yo, 2 / 3)
|
|
269
|
+
else:
|
|
270
|
+
xs, _, f = _plt_fit(xo, yo, "lm", 1)
|
|
271
|
+
fig.add_trace(go.Scatter(
|
|
272
|
+
x=xs, y=f, mode="lines",
|
|
273
|
+
line=dict(color=fit_color, width=fit_lwd),
|
|
274
|
+
hoverinfo="skip", showlegend=False), row=r, col=c)
|