lessPython 0.3.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 +70 -11
- lessPy/Chart.py +49 -4
- lessPy/Correlation.py +39 -10
- lessPy/Flows.py +3 -3
- lessPy/Logit.py +14 -10
- lessPy/Regression.py +22 -18
- lessPy/X.py +3 -2
- lessPy/XY.py +528 -91
- lessPy/bc_items_plotly.py +2 -2
- lessPy/bc_plotly.py +6 -5
- lessPy/bubble_plotly.py +16 -15
- lessPy/corCFA.py +7 -3
- lessPy/corProp.py +7 -3
- lessPy/corReflect.py +7 -3
- lessPy/corReorder.py +7 -3
- lessPy/corScree.py +10 -6
- lessPy/dn_plotly.py +9 -9
- lessPy/dot_plotly.py +9 -8
- lessPy/freq_poly_plotly.py +6 -6
- lessPy/hier_plotly.py +5 -5
- lessPy/hs_plotly.py +7 -7
- lessPy/pie_plotly.py +1 -1
- lessPy/pivot.py +39 -9
- lessPy/plotly_utils.py +90 -26
- lessPy/plt_add.py +2 -7
- lessPy/plt_contour.py +6 -6
- lessPy/plt_contour_facet.py +3 -3
- lessPy/plt_mat_plotly.py +11 -10
- lessPy/plt_plotly.py +34 -18
- lessPy/plt_smooth.py +4 -4
- lessPy/prob_norm.py +5 -1
- lessPy/prob_tcut.py +5 -1
- lessPy/prob_znorm.py +5 -1
- lessPy/profile_plotly.py +5 -4
- lessPy/radar_plotly.py +4 -3
- lessPy/simCImean.py +5 -1
- lessPy/simCLT.py +5 -1
- lessPy/simFlips.py +5 -1
- lessPy/simMeans.py +5 -1
- lessPy/style.py +8 -1
- lessPy/sunflower_plotly.py +6 -5
- lessPy/ttest.py +13 -9
- lessPy/utils.py +103 -0
- lessPy/vbs_plotly.py +23 -22
- {lesspython-0.3.0.dist-info → lesspython-0.3.1.dist-info}/METADATA +8 -1
- lesspython-0.3.1.dist-info/RECORD +89 -0
- {lesspython-0.3.0.dist-info → lesspython-0.3.1.dist-info}/WHEEL +1 -1
- lesspython-0.3.0.dist-info/RECORD +0 -89
- {lesspython-0.3.0.dist-info → lesspython-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {lesspython-0.3.0.dist-info → lesspython-0.3.1.dist-info}/top_level.txt +0 -0
lessPy/profile_plotly.py
CHANGED
|
@@ -21,6 +21,7 @@ import plotly.graph_objects as go
|
|
|
21
21
|
|
|
22
22
|
from .utils import get_option
|
|
23
23
|
from .plotly_utils import (
|
|
24
|
+
as_plotly_color,
|
|
24
25
|
axis_num, facet_layout, make_trans, plot_border, plotly_style,
|
|
25
26
|
to_hex, y_grid,
|
|
26
27
|
)
|
|
@@ -75,9 +76,9 @@ def profile_facet_plotly(cells, x_levels, series, y_tick, y_lab_tick,
|
|
|
75
76
|
marker=dict(symbol="circle", size=px,
|
|
76
77
|
sizemode="diameter",
|
|
77
78
|
color=make_trans(fills[j], 1),
|
|
78
|
-
line=dict(color=
|
|
79
|
+
line=dict(color=as_plotly_color(fills[j]),
|
|
79
80
|
width=1)),
|
|
80
|
-
line=dict(color=
|
|
81
|
+
line=dict(color=as_plotly_color(fills[j]), width=ln_width),
|
|
81
82
|
hovertemplate=hover,
|
|
82
83
|
showlegend=grouped and i == 0,
|
|
83
84
|
))
|
|
@@ -114,8 +115,8 @@ def profile_facet_plotly(cells, x_levels, series, y_tick, y_lab_tick,
|
|
|
114
115
|
yanchor="top", font=dict(size=title_size))
|
|
115
116
|
if main else None),
|
|
116
117
|
template=None,
|
|
117
|
-
plot_bgcolor=
|
|
118
|
-
paper_bgcolor=
|
|
118
|
+
plot_bgcolor=as_plotly_color(style_opts["panel_fill"]),
|
|
119
|
+
paper_bgcolor=as_plotly_color(style_opts["window_fill"]),
|
|
119
120
|
**axes,
|
|
120
121
|
)
|
|
121
122
|
return fig
|
lessPy/radar_plotly.py
CHANGED
|
@@ -20,6 +20,7 @@ import plotly.graph_objects as go
|
|
|
20
20
|
|
|
21
21
|
from .utils import get_option
|
|
22
22
|
from .plotly_utils import (
|
|
23
|
+
as_plotly_color,
|
|
23
24
|
BASE_COLORS, facet_layout, make_trans, plotly_style, to_hex,
|
|
24
25
|
)
|
|
25
26
|
|
|
@@ -182,7 +183,7 @@ def radar_plotly(x=None, x_name=None, by_name=None, main=None,
|
|
|
182
183
|
text=main, x=0.5, xanchor="center",
|
|
183
184
|
y=0.97, yanchor="top",
|
|
184
185
|
font=dict(size=title_size,
|
|
185
|
-
color=
|
|
186
|
+
color=as_plotly_color(get_option("lab_color",
|
|
186
187
|
"black")))),
|
|
187
188
|
margin=dict(t=round(title_size * 2.2), b=10,
|
|
188
189
|
l=20 if n_fac == 1 else r_margin,
|
|
@@ -191,11 +192,11 @@ def radar_plotly(x=None, x_name=None, by_name=None, main=None,
|
|
|
191
192
|
annotations=anns,
|
|
192
193
|
legend=dict(
|
|
193
194
|
x=1.02, xanchor="left", y=1.0, yanchor="top",
|
|
194
|
-
bordercolor=
|
|
195
|
+
bordercolor=as_plotly_color(style_opts["panel_border"]),
|
|
195
196
|
borderwidth=1,
|
|
196
197
|
font=dict(size=round(18 * ax_cex)),
|
|
197
198
|
),
|
|
198
199
|
template=None,
|
|
199
|
-
paper_bgcolor=
|
|
200
|
+
paper_bgcolor=as_plotly_color(style_opts["window_fill"]),
|
|
200
201
|
)
|
|
201
202
|
return fig
|
lessPy/simCImean.py
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
import numpy as np
|
|
17
17
|
|
|
18
|
-
from .utils import fmt, show_plots
|
|
18
|
+
from .utils import fmt, file_output, show_plots
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class SimCImeanResults:
|
|
@@ -163,3 +163,7 @@ def _report(ns, n, ymean, ysd, se, E, lb, ub, hit, mu, sigma, cl,
|
|
|
163
163
|
print(f" Mean: {fmt(ymean.mean(), d2):>{w}}")
|
|
164
164
|
print(f"Std Dev: {fmt(ymean.std(ddof=1), d2):>{w}}")
|
|
165
165
|
print()
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
# pdf_file= and png_file= write the figures, as lessR's pdf_file=
|
|
169
|
+
simCImean = file_output(simCImean)
|
lessPy/simCLT.py
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
import numpy as np
|
|
23
23
|
|
|
24
|
-
from .utils import fmt, show_plots
|
|
24
|
+
from .utils import fmt, file_output, show_plots
|
|
25
25
|
|
|
26
26
|
_GHOST = "#F8F8FF"
|
|
27
27
|
_STEEL = "#A2B5CD" # lightsteelblue3
|
|
@@ -263,3 +263,7 @@ def _report(dist, mu, sigma, skew, med, ns, n, mx, sx, ymean,
|
|
|
263
263
|
print(f" Mean: {fmt(ymean[i], digits_d)}")
|
|
264
264
|
print(f" Rounded Data Values: {vals}")
|
|
265
265
|
print()
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
# pdf_file= and png_file= write the figures, as lessR's pdf_file=
|
|
269
|
+
simCLT = file_output(simCLT)
|
lessPy/simFlips.py
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import numpy as np
|
|
14
14
|
|
|
15
|
-
from .utils import fmt, show_plots
|
|
15
|
+
from .utils import fmt, file_output, show_plots
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class SimFlipsResults:
|
|
@@ -102,3 +102,7 @@ def _plot(n, flips, ybar, prob, grid, show_flips, show_title,
|
|
|
102
102
|
title=dict(text=title, x=0.5, xanchor="center",
|
|
103
103
|
font=dict(size=12)))
|
|
104
104
|
return fig
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
# pdf_file= and png_file= write the figures, as lessR's pdf_file=
|
|
108
|
+
simFlips = file_output(simFlips)
|
lessPy/simMeans.py
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import numpy as np
|
|
18
18
|
|
|
19
|
-
from .utils import fmt, show_plots
|
|
19
|
+
from .utils import fmt, file_output, show_plots
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class SimMeansResults:
|
|
@@ -144,3 +144,7 @@ def _report(ns, n, o, ymean, ysd, data, mu, sigma, max_data,
|
|
|
144
144
|
" Direct estimate of the standard error of the "
|
|
145
145
|
"sample mean")
|
|
146
146
|
print()
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
# pdf_file= and png_file= write the figures, as lessR's pdf_file=
|
|
150
|
+
simMeans = file_output(simMeans)
|
lessPy/style.py
CHANGED
|
@@ -116,10 +116,17 @@ def style(theme=None, sub_theme=None, set=None, get=False, reset=True,
|
|
|
116
116
|
|
|
117
117
|
for k, v in settings.items():
|
|
118
118
|
utils._OPTIONS[k] = v
|
|
119
|
+
# the line that connects the points of a single series takes
|
|
120
|
+
# segment_color only when it is given, otherwise color. A
|
|
121
|
+
# theme, applied above, starts from neither. R analog:
|
|
122
|
+
# style() option segment_color_set
|
|
123
|
+
if "segment_color" in settings:
|
|
124
|
+
utils._OPTIONS["segment_color_set"] = True
|
|
119
125
|
|
|
120
126
|
if get:
|
|
121
127
|
return {k: copy.deepcopy(utils._OPTIONS.get(k))
|
|
122
|
-
for k in sorted(keys | {"theme", "sub_theme"
|
|
128
|
+
for k in sorted(keys | {"theme", "sub_theme",
|
|
129
|
+
"segment_color_set"})}
|
|
123
130
|
return None
|
|
124
131
|
|
|
125
132
|
|
lessPy/sunflower_plotly.py
CHANGED
|
@@ -16,6 +16,7 @@ import numpy as np
|
|
|
16
16
|
import plotly.graph_objects as go
|
|
17
17
|
|
|
18
18
|
from .plotly_utils import (
|
|
19
|
+
as_plotly_color,
|
|
19
20
|
axis_format, axis_num, facet_fig, facet_panels, finish_facet,
|
|
20
21
|
make_trans, plot_border, plotly_style, to_hex, x_grid,
|
|
21
22
|
)
|
|
@@ -57,10 +58,10 @@ def _panel_traces(x, y, xr, yr, w_px, h_px, fill, color, pt_size,
|
|
|
57
58
|
px = max(3.0, 5.5 * float(pt_size))
|
|
58
59
|
return [
|
|
59
60
|
go.Scatter(x=sx, y=sy, mode="lines",
|
|
60
|
-
line=dict(color=
|
|
61
|
+
line=dict(color=as_plotly_color(color), width=1),
|
|
61
62
|
hoverinfo="skip", showlegend=False),
|
|
62
63
|
go.Scatter(x=xx, y=yy, mode="markers",
|
|
63
|
-
marker=dict(size=px, color=
|
|
64
|
+
marker=dict(size=px, color=as_plotly_color(fill),
|
|
64
65
|
line=dict(width=0)),
|
|
65
66
|
customdata=cnt, hovertemplate=hover,
|
|
66
67
|
showlegend=False)]
|
|
@@ -104,13 +105,13 @@ def sunflower_plotly(x, y, x_lab, y_lab, main=None, facet=None,
|
|
|
104
105
|
axis_fmt, axis_y_pre))
|
|
105
106
|
ax_x.update(range=list(xr))
|
|
106
107
|
ax_y.update(range=list(yr), showgrid=True,
|
|
107
|
-
gridcolor=
|
|
108
|
+
gridcolor=as_plotly_color(style_opts["grid_col"]),
|
|
108
109
|
gridwidth=1, griddash="dot")
|
|
109
110
|
fig.update_layout(
|
|
110
111
|
xaxis=ax_x, yaxis=ax_y, template=None,
|
|
111
112
|
shapes=x_grid(axT1) + plot_border(),
|
|
112
|
-
plot_bgcolor=
|
|
113
|
-
paper_bgcolor=
|
|
113
|
+
plot_bgcolor=as_plotly_color(style_opts["panel_fill"]),
|
|
114
|
+
paper_bgcolor=as_plotly_color(style_opts["window_fill"]))
|
|
114
115
|
else:
|
|
115
116
|
labels, pos, sel, n_row_g, n_col = facet_panels(
|
|
116
117
|
facet, facet_order, facet2, facet2_order, facet_name,
|
lessPy/ttest.py
CHANGED
|
@@ -24,7 +24,7 @@ import numpy as np
|
|
|
24
24
|
import pandas as pd
|
|
25
25
|
|
|
26
26
|
from .Regression import _getdigits
|
|
27
|
-
from .utils import fmt, get_column, get_option, show_plots
|
|
27
|
+
from .utils import fmt, get_column, get_option, file_output, show_plots
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
class ttestResults:
|
|
@@ -572,7 +572,7 @@ def _kde(v):
|
|
|
572
572
|
def _two_group_plot(YA, YB, Ynm, X1nm, X2nm, m1, m2, d):
|
|
573
573
|
import plotly.graph_objects as go
|
|
574
574
|
from .plotly_utils import (
|
|
575
|
-
BASE_COLORS, axis_format, axis_num, make_trans,
|
|
575
|
+
as_plotly_color, BASE_COLORS, axis_format, axis_num, make_trans,
|
|
576
576
|
plot_border, plotly_style, to_hex, x_grid)
|
|
577
577
|
from .utils import pretty
|
|
578
578
|
style = plotly_style()
|
|
@@ -595,8 +595,8 @@ def _two_group_plot(YA, YB, Ynm, X1nm, X2nm, m1, m2, d):
|
|
|
595
595
|
fig.update_layout(
|
|
596
596
|
xaxis=ax_x, yaxis=ax_y,
|
|
597
597
|
shapes=x_grid(axT1) + plot_border(), template=None,
|
|
598
|
-
plot_bgcolor=
|
|
599
|
-
paper_bgcolor=
|
|
598
|
+
plot_bgcolor=as_plotly_color(style["panel_fill"]),
|
|
599
|
+
paper_bgcolor=as_plotly_color(style["window_fill"]),
|
|
600
600
|
legend=dict(title=dict(text=str(X1nm) + " / " + str(X2nm))),
|
|
601
601
|
title=dict(text="Two-Group Density Plot", x=0.5,
|
|
602
602
|
xanchor="center",
|
|
@@ -608,7 +608,7 @@ def _two_group_plot(YA, YB, Ynm, X1nm, X2nm, m1, m2, d):
|
|
|
608
608
|
def _one_group_plot(Y, Ynm, m, lb, ub, d, paired):
|
|
609
609
|
import plotly.graph_objects as go
|
|
610
610
|
from .plotly_utils import (
|
|
611
|
-
BASE_COLORS, axis_format, axis_num, make_trans,
|
|
611
|
+
as_plotly_color, BASE_COLORS, axis_format, axis_num, make_trans,
|
|
612
612
|
plot_border, plotly_style, to_hex, x_grid)
|
|
613
613
|
from .utils import pretty
|
|
614
614
|
style = plotly_style()
|
|
@@ -616,13 +616,13 @@ def _one_group_plot(Y, Ynm, m, lb, ub, d, paired):
|
|
|
616
616
|
c = BASE_COLORS[0]
|
|
617
617
|
fig = go.Figure()
|
|
618
618
|
fig.add_trace(go.Scatter(
|
|
619
|
-
x=xs, y=ys, mode="lines", line=dict(color=
|
|
619
|
+
x=xs, y=ys, mode="lines", line=dict(color=as_plotly_color(c),
|
|
620
620
|
width=2),
|
|
621
621
|
fill="tozeroy", fillcolor=make_trans(c, 0.85),
|
|
622
622
|
hoverinfo="x", showlegend=False))
|
|
623
623
|
ym = float(np.interp(m, xs, ys))
|
|
624
624
|
fig.add_shape(type="line", x0=m, x1=m, y0=0, y1=ym,
|
|
625
|
-
line=dict(color=
|
|
625
|
+
line=dict(color=as_plotly_color("gray50"), width=1))
|
|
626
626
|
axT1 = pretty(float(xs.min()), float(xs.max()))
|
|
627
627
|
xlab = ("Differences of Matched Pairs" if paired else Ynm)
|
|
628
628
|
ax_x = axis_num(xlab, axT1, axis_format(axT1, d))
|
|
@@ -631,11 +631,15 @@ def _one_group_plot(Y, Ynm, m, lb, ub, d, paired):
|
|
|
631
631
|
fig.update_layout(
|
|
632
632
|
xaxis=ax_x, yaxis=ax_y,
|
|
633
633
|
shapes=x_grid(axT1) + plot_border(), template=None,
|
|
634
|
-
plot_bgcolor=
|
|
635
|
-
paper_bgcolor=
|
|
634
|
+
plot_bgcolor=as_plotly_color(style["panel_fill"]),
|
|
635
|
+
paper_bgcolor=as_plotly_color(style["window_fill"]),
|
|
636
636
|
title=dict(
|
|
637
637
|
text="One-Group Density Plot", x=0.5,
|
|
638
638
|
xanchor="center",
|
|
639
639
|
font=dict(size=round(16 * get_option("main_size",
|
|
640
640
|
1)))))
|
|
641
641
|
return fig
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
# pdf_file= and png_file= write the figures, as lessR's pdf_file=
|
|
645
|
+
ttest = file_output(ttest)
|
lessPy/utils.py
CHANGED
|
@@ -310,3 +310,106 @@ def show_plots(result):
|
|
|
310
310
|
warnings.warn(f"figures not displayed ({e}); they are "
|
|
311
311
|
"in .plots, show one with .show()")
|
|
312
312
|
return result
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def _figures_of(out, names=None):
|
|
316
|
+
"""The figures a function returned, keyed by the file-name
|
|
317
|
+
suffix: an analysis result's .plots (or .attrs["plots"]) by plot
|
|
318
|
+
name, a list of Figures by variable name (X() of a list), and a
|
|
319
|
+
single Figure by no suffix."""
|
|
320
|
+
if hasattr(out, "write_image"):
|
|
321
|
+
return {"": out}
|
|
322
|
+
if isinstance(out, (list, tuple)):
|
|
323
|
+
keys = ([str(v) for v in names] if names
|
|
324
|
+
and len(names) == len(out)
|
|
325
|
+
else [str(i + 1) for i in range(len(out))])
|
|
326
|
+
return {k: f for k, f in zip(keys, out)
|
|
327
|
+
if hasattr(f, "write_image")}
|
|
328
|
+
attrs = getattr(out, "attrs", None)
|
|
329
|
+
plots = (attrs.get("plots") if isinstance(attrs, dict)
|
|
330
|
+
else getattr(out, "plots", None))
|
|
331
|
+
return {k: f for k, f in (plots or {}).items() if f is not None}
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def write_figures(out, stem, ext, names=None):
|
|
335
|
+
"""Write the returned figures to ext ("pdf", "png") files named
|
|
336
|
+
from stem, as lessR's pdf_file=: one figure is stem.ext, several
|
|
337
|
+
are stem_<name>.ext. Reports the files as lessR's .showfile();
|
|
338
|
+
on Colab also downloads them to the user's computer. A write
|
|
339
|
+
failure warns and leaves the analysis result intact."""
|
|
340
|
+
import os
|
|
341
|
+
import sys
|
|
342
|
+
import warnings
|
|
343
|
+
stem = str(stem)
|
|
344
|
+
if stem.lower().endswith("." + ext):
|
|
345
|
+
stem = stem[:-len(ext) - 1]
|
|
346
|
+
figs = _figures_of(out, names)
|
|
347
|
+
if not figs:
|
|
348
|
+
return
|
|
349
|
+
if len(figs) == 1:
|
|
350
|
+
figs = {"": next(iter(figs.values()))}
|
|
351
|
+
dir_nm = os.path.dirname(stem)
|
|
352
|
+
if dir_nm and not os.path.isdir(dir_nm):
|
|
353
|
+
warnings.warn(f"{ext} file not written: no folder {dir_nm}",
|
|
354
|
+
stacklevel=3)
|
|
355
|
+
return
|
|
356
|
+
files = []
|
|
357
|
+
try:
|
|
358
|
+
for key, fig in figs.items():
|
|
359
|
+
fname = f"{stem}_{key}.{ext}" if key else f"{stem}.{ext}"
|
|
360
|
+
fig.write_image(fname, scale=2 if ext == "png" else 1)
|
|
361
|
+
files.append(fname)
|
|
362
|
+
except Exception as e:
|
|
363
|
+
warnings.warn(
|
|
364
|
+
f"{ext} file not written ({e})\nWriting {ext} files needs "
|
|
365
|
+
"the png extra: pip install \"lessPython[png]\", then "
|
|
366
|
+
"plotly_get_chrome -y once (on Colab: !plotly_get_chrome "
|
|
367
|
+
"-y).", stacklevel=3)
|
|
368
|
+
if not files:
|
|
369
|
+
return
|
|
370
|
+
where = ("written at the current working directory"
|
|
371
|
+
if not dir_nm else "written")
|
|
372
|
+
workdir = os.getcwd() if not dir_nm else dir_nm
|
|
373
|
+
word = "file" if len(files) == 1 else "files"
|
|
374
|
+
print(f"\nThe {ext} {word} {where}")
|
|
375
|
+
for f in files:
|
|
376
|
+
print(f" {os.path.basename(f)}")
|
|
377
|
+
print(f" in: {workdir}\n")
|
|
378
|
+
if "google.colab" in sys.modules:
|
|
379
|
+
try:
|
|
380
|
+
from google.colab import files as _colab_files
|
|
381
|
+
for f in files:
|
|
382
|
+
_colab_files.download(f)
|
|
383
|
+
except Exception:
|
|
384
|
+
pass
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def file_output(func):
|
|
388
|
+
"""Wrap a function that returns figures so pdf_file= and
|
|
389
|
+
png_file= write them to files, as lessR's pdf_file=: the file
|
|
390
|
+
name, or with several figures the stem of the names,
|
|
391
|
+
stem_<plot>.ext. For a list of x variables (X()), each file is
|
|
392
|
+
named by its variable. As in lessR, True is refused: the name
|
|
393
|
+
is always given, so a second analysis cannot overwrite the
|
|
394
|
+
files of the first under a fixed name."""
|
|
395
|
+
import functools
|
|
396
|
+
|
|
397
|
+
@functools.wraps(func)
|
|
398
|
+
def wrapper(*args, pdf_file=None, png_file=None, **kwargs):
|
|
399
|
+
for given, prm in ((pdf_file, "pdf_file"), (png_file, "png_file")):
|
|
400
|
+
if isinstance(given, bool) and given:
|
|
401
|
+
raise ValueError(
|
|
402
|
+
f"{prm} takes the stem of the file names, such as "
|
|
403
|
+
f"{prm}=\"myReg\", which for Regression() writes "
|
|
404
|
+
"one file per chart, such as "
|
|
405
|
+
"myReg_scatter_matrix." + prm[:3] + ".")
|
|
406
|
+
out = func(*args, **kwargs)
|
|
407
|
+
if pdf_file or png_file:
|
|
408
|
+
xs = kwargs.get("x", args[0] if args else None)
|
|
409
|
+
names = xs if isinstance(xs, (list, tuple)) else None
|
|
410
|
+
for given, ext in ((pdf_file, "pdf"), (png_file, "png")):
|
|
411
|
+
if given:
|
|
412
|
+
write_figures(out, given, ext, names)
|
|
413
|
+
return out
|
|
414
|
+
|
|
415
|
+
return wrapper
|
lessPy/vbs_plotly.py
CHANGED
|
@@ -42,7 +42,7 @@ import plotly.graph_objects as go
|
|
|
42
42
|
from .plotly_utils import (
|
|
43
43
|
as_plotly_color, axis_format, axis_num, get_tick_fmt,
|
|
44
44
|
make_trans,
|
|
45
|
-
plot_border, plotly_style,
|
|
45
|
+
plot_border, plotly_style, marks, to_hex, x_grid,
|
|
46
46
|
)
|
|
47
47
|
from .utils import band_width, fmt, get_option, kde, pretty
|
|
48
48
|
|
|
@@ -259,7 +259,7 @@ def vbs_plotly(x, x_name=None, vbs_plot="vbs",
|
|
|
259
259
|
box_fill = get_option("box_fill", "#419BD2")
|
|
260
260
|
if box_color is None:
|
|
261
261
|
box_color = get_option("box_color", "gray15")
|
|
262
|
-
box_color =
|
|
262
|
+
box_color = as_plotly_color(box_color)
|
|
263
263
|
|
|
264
264
|
fig = go.Figure()
|
|
265
265
|
val_fmt = get_tick_fmt(x, digits_d)
|
|
@@ -347,7 +347,7 @@ def vbs_plotly(x, x_name=None, vbs_plot="vbs",
|
|
|
347
347
|
y=np.concatenate([yc + half,
|
|
348
348
|
yc - half[::-1]]),
|
|
349
349
|
mode="lines",
|
|
350
|
-
line=dict(color=
|
|
350
|
+
line=dict(color=as_plotly_color(violin_color), width=1),
|
|
351
351
|
fill="toself",
|
|
352
352
|
fillcolor=as_plotly_color(violin_fill),
|
|
353
353
|
hoverinfo="skip", showlegend=False,
|
|
@@ -408,21 +408,22 @@ def vbs_plotly(x, x_name=None, vbs_plot="vbs",
|
|
|
408
408
|
len(pts))
|
|
409
409
|
# by groups differ by color only; shape= (scalar or
|
|
410
410
|
# per-group vector) overrides for every group
|
|
411
|
-
sym = "circle" if shape is None else sym_at(shape, i)
|
|
412
411
|
hover = f"{x_lab}: {vpart}"
|
|
413
412
|
if nm is not None:
|
|
414
413
|
hover += f"<br>{by_name}: {nm}"
|
|
415
414
|
hover += lvl_part
|
|
416
415
|
fig.add_trace(go.Scatter(
|
|
417
|
-
x=pts, y=yj,
|
|
416
|
+
x=pts, y=yj,
|
|
418
417
|
name=None if nm is None else str(nm),
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
418
|
+
**marks("circle" if shape is None else shape, i,
|
|
419
|
+
"markers", dict(
|
|
420
|
+
size=px,
|
|
421
|
+
color=make_trans(_col_for(pt_fill, i),
|
|
422
|
+
1 - pt_trans),
|
|
423
|
+
line=dict(
|
|
424
|
+
color=as_plotly_color(
|
|
425
|
+
_col_for(pt_color, i)),
|
|
426
|
+
width=0.5))),
|
|
426
427
|
hovertemplate=hover + "<extra></extra>",
|
|
427
428
|
showlegend=nm is not None and ui == 0,
|
|
428
429
|
))
|
|
@@ -455,8 +456,8 @@ def vbs_plotly(x, x_name=None, vbs_plot="vbs",
|
|
|
455
456
|
mode="markers",
|
|
456
457
|
marker=dict(
|
|
457
458
|
symbol=out_shape, size=px_out,
|
|
458
|
-
color=
|
|
459
|
-
line=dict(color=
|
|
459
|
+
color=as_plotly_color(o_fill),
|
|
460
|
+
line=dict(color=as_plotly_color(o_color),
|
|
460
461
|
width=1)),
|
|
461
462
|
hovertemplate=hover + "<extra></extra>",
|
|
462
463
|
showlegend=False,
|
|
@@ -476,7 +477,7 @@ def vbs_plotly(x, x_name=None, vbs_plot="vbs",
|
|
|
476
477
|
keep_i = cand[np.argsort(-np.abs(xu[cand]),
|
|
477
478
|
kind="stable")][:out_cut]
|
|
478
479
|
id_font = dict(
|
|
479
|
-
color=
|
|
480
|
+
color=as_plotly_color(get_option("ID_color",
|
|
480
481
|
"gray50")),
|
|
481
482
|
size=max(8, round(float(ID_size) * 14)))
|
|
482
483
|
for ii in keep_i:
|
|
@@ -497,8 +498,8 @@ def vbs_plotly(x, x_name=None, vbs_plot="vbs",
|
|
|
497
498
|
annotations = []
|
|
498
499
|
if n_fac > 1: # shaded strip label per band
|
|
499
500
|
s_fill = as_plotly_color(get_option("strip_fill"))
|
|
500
|
-
s_line =
|
|
501
|
-
s_text =
|
|
501
|
+
s_line = as_plotly_color(get_option("strip_color"))
|
|
502
|
+
s_text = as_plotly_color(get_option("strip_text_color"))
|
|
502
503
|
s_font = round(14 * get_option("axis_size", 0.9))
|
|
503
504
|
strip_items = [(u["yc"] + _STRIP[0],
|
|
504
505
|
u["yc"] + _STRIP[1], u["strip"])
|
|
@@ -530,22 +531,22 @@ def vbs_plotly(x, x_name=None, vbs_plot="vbs",
|
|
|
530
531
|
shapes=shapes,
|
|
531
532
|
annotations=annotations,
|
|
532
533
|
template=None,
|
|
533
|
-
plot_bgcolor=
|
|
534
|
-
paper_bgcolor=
|
|
534
|
+
plot_bgcolor=as_plotly_color(style_opts["panel_fill"]),
|
|
535
|
+
paper_bgcolor=as_plotly_color(style_opts["window_fill"]),
|
|
535
536
|
height=(450 if n_fac == 1
|
|
536
537
|
else 120 + 190 * n_fac
|
|
537
538
|
+ 40 * len(sec_strips)),
|
|
538
539
|
)
|
|
539
540
|
if n_grp > 1: # lattice key: top, one row
|
|
540
|
-
leg_color =
|
|
541
|
+
leg_color = as_plotly_color(style_opts["lab_color"])
|
|
541
542
|
fig.update_layout(legend=dict(
|
|
542
543
|
orientation="h",
|
|
543
544
|
x=0.5, xanchor="center", y=1.02, yanchor="bottom",
|
|
544
545
|
font=dict(size=round(14 * get_option("axis_size",
|
|
545
546
|
0.9)),
|
|
546
547
|
family="Arial", color=leg_color),
|
|
547
|
-
bgcolor=
|
|
548
|
-
bordercolor=
|
|
548
|
+
bgcolor=as_plotly_color(style_opts["window_fill"]),
|
|
549
|
+
bordercolor=as_plotly_color("gray80"), borderwidth=1,
|
|
549
550
|
itemsizing="constant",
|
|
550
551
|
))
|
|
551
552
|
if main:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lessPython
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Python port of R's lessR: analytic-view visualizations, statistical models, and data utilities, rendered with plotly
|
|
5
5
|
Author-email: "David W. Gerbing" <gerbing@pdx.edu>
|
|
6
6
|
License-Expression: GPL-2.0-or-later
|
|
@@ -119,6 +119,13 @@ when the analysis runs, even when the result is assigned, as
|
|
|
119
119
|
`r = lp.Regression(...)`. From a script, show one with
|
|
120
120
|
`r.plots["scatter"].show()`.
|
|
121
121
|
|
|
122
|
+
Every function that draws also takes `pdf_file=` and `png_file=`
|
|
123
|
+
(needs the png extra, `pip install "lessPython[png]"`, then
|
|
124
|
+
`plotly_get_chrome -y` once). A name such as `png_file="reg"`
|
|
125
|
+
names the file, or with several figures is the stem of each
|
|
126
|
+
name (`reg_scatter_matrix.png`), as lessR's `pdf_file=`. On Colab
|
|
127
|
+
the files also download to your computer.
|
|
128
|
+
|
|
122
129
|
## Development install
|
|
123
130
|
|
|
124
131
|
```
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
lessPy/ANOVA.py,sha256=p0IUM004vGTKEHl_VAgyf-f_1NnklR8KWx_YdsNOerU,30437
|
|
2
|
+
lessPy/Chart.py,sha256=MYM4Ji5DHstRg2bp9sMjDvCDWLf-cHW9PLjzG6zel2U,90920
|
|
3
|
+
lessPy/Correlation.py,sha256=QdeSiVmaDjE_iNPvAsoZx5a1xBJ1g1t-ZtZrW2zDimU,10331
|
|
4
|
+
lessPy/Flows.py,sha256=5wfIZQGlOsALFinZDpioIR79uJfqn3YrNBLHFb8JGNk,4898
|
|
5
|
+
lessPy/Logit.py,sha256=zuDxYgBjSSt6rM5EIhBA5Ug5p26o4MforABOiE4MkJU,25426
|
|
6
|
+
lessPy/Prop_test.py,sha256=mSYiQdTN7gpF5oMBAjFgicH9-YarJojgAEBnxQg17Og,10452
|
|
7
|
+
lessPy/Regression.py,sha256=WEyf2JhfkKYc73buJVREVaivq0CkXfxuRtRIU2qO3us,61638
|
|
8
|
+
lessPy/VariableLabels.py,sha256=eoRVwDLFQZvvLD-UBePD3BJG1yLVgBJqtmbzfAWOkzI,4336
|
|
9
|
+
lessPy/X.py,sha256=68KnSUtS3icBXeurJlRyRZ3VePhq6Q-h-qAbUidoB68,29369
|
|
10
|
+
lessPy/XY.py,sha256=NnY2vXSJln1dMONpVjtvOrINVYj0qs61sRj6aQBY7yg,136311
|
|
11
|
+
lessPy/__init__.py,sha256=QYPMfVx3LsO4ua59W0h7DxOogsAAgG_aIihu47nmn6U,2773
|
|
12
|
+
lessPy/_themes.py,sha256=ILPfwoqSeFQZyewXX_g_1-s4F3DXO0a1mCPaHulX1Y8,228371
|
|
13
|
+
lessPy/anova_rmd.py,sha256=kc7KtBZU4FF0Q3T1UJvEqq22imtqwJ8ppu9yC_8iTAk,8436
|
|
14
|
+
lessPy/bc_items_plotly.py,sha256=Dz45CfA-s1mHJWXH_5pbFviTV_1E75cFMQVcB0Eeal4,8862
|
|
15
|
+
lessPy/bc_plotly.py,sha256=bZMZDovUKJ2-Ou7zBZsx1SKY50cJSqRk5TbkcH94EQQ,26583
|
|
16
|
+
lessPy/bubble_plotly.py,sha256=nKy5s2VDdBjMOz1wlcp232nSyhhds9mlhFSOIs_OSXM,19107
|
|
17
|
+
lessPy/corCFA.py,sha256=PQsq44xvK9kaL2wFETAUfFpZ5Nabj5Rgarjjr9ArL_Q,11783
|
|
18
|
+
lessPy/corEFA.py,sha256=-umpIEYeT6efN8AkZMlCqOyL8V-e2TqaadHRpuoi7yA,7606
|
|
19
|
+
lessPy/corPrint.py,sha256=kV6ZOu03Cm9j16uCA-An6XvLXbNa3_rRUPhE7zT6zqk,1575
|
|
20
|
+
lessPy/corProp.py,sha256=ObDN6zFuLmc1JZFpdzaW_EiDJGr4Q9NkWl4KJWfgAhE,2934
|
|
21
|
+
lessPy/corRead.py,sha256=VsvdQIYYk8MRHvUCeVx659yGKaeWk8sYeDy7ulu3VUo,2096
|
|
22
|
+
lessPy/corReflect.py,sha256=RhCIt9wybBr-Q5P-UJxIVJBhGCyeciKZ3gKg7vop-pw,2912
|
|
23
|
+
lessPy/corReorder.py,sha256=hYk_fxysyINdt6fSVoKKvu5yz3TeGYODL4AXv9jVr6M,6180
|
|
24
|
+
lessPy/corScree.py,sha256=U3ud1tTwKZGmVROGZlorrlFvLIbMZx9HVMver1k5iCE,3355
|
|
25
|
+
lessPy/datasets.py,sha256=8YiYdcRZJa-UiB4xs3ciZmOfd4ARy3rIJFddY-qdKm4,1690
|
|
26
|
+
lessPy/date_infer.py,sha256=OgTMy5eeu1hhW314FTv034Pl9kthmM2H0kYAeWQObx0,4302
|
|
27
|
+
lessPy/details.py,sha256=kz33hIl0_im0M2G-xF-Hb-e9z87kKOkfC5piJ9Xzd7s,11435
|
|
28
|
+
lessPy/dn_plotly.py,sha256=LdHV0e8opWqkXycXLzNfPCJZLqYvyZc1qVaS230PQ_s,20782
|
|
29
|
+
lessPy/dot_plotly.py,sha256=_yTrkxPhpeLJVFXQ42OoXL52xL8hCRHupzCxMgBnj4E,18660
|
|
30
|
+
lessPy/freq_poly_plotly.py,sha256=3U7yaGU4BGkKbK6qyQIlxtDB81FAoaw3KB8HClFZxpk,12816
|
|
31
|
+
lessPy/getColors.py,sha256=fKKJHmBw8S5BM1636Iaxt1ge1zDFbKweNApf_lE2qqE,22528
|
|
32
|
+
lessPy/hier_plotly.py,sha256=ZH5FKI7SWjRjohgk5LrbPsoVFC4I75kNYTpfFIaN0_o,16376
|
|
33
|
+
lessPy/hs_plotly.py,sha256=Q29ifoZSOVrw2REiID0W-Ta5mo2b5OW4fR5aX_NHpb4,15560
|
|
34
|
+
lessPy/logit_rmd.py,sha256=Wf5f5vRTfxUG467Lad2irMYPSFqvsE5dpvQSG8Aeyzo,15646
|
|
35
|
+
lessPy/order_by.py,sha256=LpqV95REi33CPXxEyFSsM52LqxqkdRFuHIx3D37Bchg,3150
|
|
36
|
+
lessPy/pie_plotly.py,sha256=mLaFTMxemLws--v06-1emJaAfg2VA9eLsCMLmyBX07M,11054
|
|
37
|
+
lessPy/pivot.py,sha256=p6Nq-YUvy7qH9agFMD2cWQ-7AeC7Sjwg-Ax7KubuexE,7705
|
|
38
|
+
lessPy/plotly_utils.py,sha256=YFnPBVU4vOmQDeuugsyPFX5gvxFmcbItKpyVSa8PkMs,37976
|
|
39
|
+
lessPy/plt_add.py,sha256=RjVZoZS4Y2m1ip6-oauwabc2QM_TjJVIPjOXhN6xuQs,5086
|
|
40
|
+
lessPy/plt_contour.py,sha256=1PfxF47ogk5LkDuAdrNZY50OcusKDpFhyuqk05DFpa4,7317
|
|
41
|
+
lessPy/plt_contour_facet.py,sha256=_rRPIiNlW2It-JA8IyqM9qav7sVV6zwDSJoEPL0e6a8,8183
|
|
42
|
+
lessPy/plt_forecast.py,sha256=rIlHbvKL9psCjc2ZebnfHomCzqaMn9PAZxeWNzQOQus,26095
|
|
43
|
+
lessPy/plt_mat_plotly.py,sha256=zcd1kfrAKGZSSgd4Nh4ocnaP-Ztos_29PEK-qF_RsPI,11888
|
|
44
|
+
lessPy/plt_plotly.py,sha256=2EfgZ9Q_yrV6eupxXZsMyRFiVsg7cIjYUwZPl8ZFFCI,9591
|
|
45
|
+
lessPy/plt_smooth.py,sha256=FuGPqrAf4sxL1AIIjoMkf6bRXH8ICL4JAA6s9g9mZxo,6835
|
|
46
|
+
lessPy/plt_time.py,sha256=kaloqSiiDC-10c3xH_qyZ8r9ztTkoJcn3NIzmQFHDBU,6003
|
|
47
|
+
lessPy/prob_norm.py,sha256=QcfRAiFtPlDEtCxSZimONgz_LddgxjkH58dF8MXMg0M,4257
|
|
48
|
+
lessPy/prob_tcut.py,sha256=aM8whjdcC3x2olcNbzIVBer7VDRK7w77I7ohiiRwSb4,5139
|
|
49
|
+
lessPy/prob_znorm.py,sha256=WPE9CWEHntue0ywmwj_8uK9OqurM60fVHPBDg7PihUg,4583
|
|
50
|
+
lessPy/profile_plotly.py,sha256=3fygow-7isg_sHGMMO3rhhWtFknwv9Ng8gVrVyYvXoQ,5224
|
|
51
|
+
lessPy/radar_plotly.py,sha256=xr6WQH8Q-3eBwcepF1gm8B5oJnR7OVjh0Ed9S73kNYI,7367
|
|
52
|
+
lessPy/rcolors.py,sha256=kr5GmImQkYyYnX4GBcDIxmL4pge47aktnwgJkWwGrnM,13129
|
|
53
|
+
lessPy/reg_rmd.py,sha256=B3bGyNL4t9GDtv2aNW5x7ki41uGD_lHJOCe9ZbO-if4,28958
|
|
54
|
+
lessPy/rename.py,sha256=83iwwCidpfi1hm7r4XgsZYeAs2MPHGfueo_UVyVqygc,1244
|
|
55
|
+
lessPy/reshape.py,sha256=KnaFgcRAervP8FSZvYMYw9PXEeP7ecNCLHx1wO91xBU,4035
|
|
56
|
+
lessPy/showColors.py,sha256=y1Qh4kYWbe0YRUyKwSXWWf3ctz8ub1ctnltrmy940IU,5948
|
|
57
|
+
lessPy/simCImean.py,sha256=vfO6xs8u1l7jWkHZ6btxEWRBEAEI-mIB3utDv9r0XzE,6470
|
|
58
|
+
lessPy/simCLT.py,sha256=3rWeMP_VPNIN41bEfOpaamB9kfpkphFblqJFXT-akqQ,10113
|
|
59
|
+
lessPy/simFlips.py,sha256=7O4EoKeNSV8mknsYXawYqCAa8oHyuqFRAhp18s8ZAMc,4161
|
|
60
|
+
lessPy/simMeans.py,sha256=_j7jTPf5ZaHdjGm5OLvb20lCHWoJlA_BBdfHaRiVLEM,5564
|
|
61
|
+
lessPy/stats_out.py,sha256=cbpe8a_OPPOy3WL8s4ZLke6SUYffSK0BR0KExHMN8ok,30564
|
|
62
|
+
lessPy/style.py,sha256=i9W_bVxId7AV3RMzIWebVe3aZYIFfdG6u_ZJGadC0aM,6375
|
|
63
|
+
lessPy/sunflower_plotly.py,sha256=BQfFekQxD-kexOfBiU5cAD_wJyIITXZFxMWSxseYZ6Q,6008
|
|
64
|
+
lessPy/ttest.py,sha256=fP47ziTnoR2S-tOL9CCZONM6kKkpXQxDdlLqJhqgG9w,25033
|
|
65
|
+
lessPy/utils.py,sha256=-CKALbfFEvPYYvsuZ9m8tLf-yZajr2b684eGBMrx09s,15261
|
|
66
|
+
lessPy/vbs_plotly.py,sha256=N68bh7ii9k7qxVYWZNqqEfUVP3abyae8Ywom4tP8tvU,24059
|
|
67
|
+
lessPy/x_console.py,sha256=rj5k2uISIYM2tQgd48E5yJ0-OlAeCYTJ7sUdfy94Sw4,28879
|
|
68
|
+
lessPy/data/Anova_1way.csv,sha256=lA1ZXx7d471QDVXs9E0Chd7FEyCYH1S2FrJVtnp50r8,294
|
|
69
|
+
lessPy/data/Anova_2way.csv,sha256=XJwu_Y12-JZhOMTAOXbIRA0rfNjwkSGPmgJWlMyF8lY,923
|
|
70
|
+
lessPy/data/Anova_rb.csv,sha256=lU5V_TK7F6PigUZa4GLlBEpJsesncb-mswGi9PmxZJg,128
|
|
71
|
+
lessPy/data/Anova_rbf.csv,sha256=WshzhSatbAnR40NSXEWp97JaXwx8CpgDiLVoFXB3mho,1315
|
|
72
|
+
lessPy/data/Anova_sp.csv,sha256=DSuabe7xndzOoRBYotTIE8LlxPULpiZ7MhicriDfB_8,1128
|
|
73
|
+
lessPy/data/BodyMeas.csv,sha256=Z6HR9Xf9igbuwaIglWyqU2Stv9pYMHHDIoGncLtdsnA,8861
|
|
74
|
+
lessPy/data/Cars93.csv,sha256=A3X2C6A9_8USE9uMTfMpBGWfVbi8u-jM_JyJlVWSv5c,10149
|
|
75
|
+
lessPy/data/Employee.csv,sha256=DYUXu_b6uDZNHcXq-5RoGx9IzdnIx__L9D8-qbHnLT8,2054
|
|
76
|
+
lessPy/data/Employee_lbl.csv,sha256=Yzq998mPzRvrufcnpDt0RYK_kLvDJur0IrCmQpj-GMc,331
|
|
77
|
+
lessPy/data/FreqTable99.csv,sha256=mkroqRikxvLzVDiuaD_qJbATWyA-OCPjj-DmayjHH00,72
|
|
78
|
+
lessPy/data/Jackets.csv,sha256=prNRSZpbg928w6AcbzZBiXxeFcpQhsutcNWtu9GTWeU,14524
|
|
79
|
+
lessPy/data/Learn.csv,sha256=fUSWxUEVgxNeCKgvzR9tQN2AyUJarW4UMweXDCmd9Ew,346
|
|
80
|
+
lessPy/data/Mach4.csv,sha256=fRzeipGfzSpzZHWjqHnYRAxD8Jb7R73H1zSGyfdP3Us,14871
|
|
81
|
+
lessPy/data/Mach4_lbl.csv,sha256=okp93EBnSXi4GsXg09M1evwwRiZ77MmCr6VmBgSJbpI,1582
|
|
82
|
+
lessPy/data/Reading.csv,sha256=4RZ9colCorvGSB7qjZCjtpT6t8wCPOUz2siVsdnjpGA,1162
|
|
83
|
+
lessPy/data/StockPrice.csv,sha256=EV9I1PK3ee3hFSLoqZi-4UFESLGD9zAS2zkg3j65iKI,66774
|
|
84
|
+
lessPy/data/WeightLoss.csv,sha256=Iw3P0jHBVXZgESpvfrOzC_Vgg0TVHiPo0ktesmP55qA,234
|
|
85
|
+
lesspython-0.3.1.dist-info/licenses/LICENSE,sha256=7a72Msu2Q-TnoiFxemxEGkwafJGObk1W3rw9hzmyM_Y,17984
|
|
86
|
+
lesspython-0.3.1.dist-info/METADATA,sha256=B24tLpagrqm3Ki4L7SF3odBKAokxUTk3wOaCrK1soqM,5142
|
|
87
|
+
lesspython-0.3.1.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
88
|
+
lesspython-0.3.1.dist-info/top_level.txt,sha256=zwwOr1PSYgRRXTloRB9jFgrQW9_l-aahPmcEtTpCkgo,7
|
|
89
|
+
lesspython-0.3.1.dist-info/RECORD,,
|