lessPython 0.3.0__tar.gz → 0.3.1__tar.gz

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.
Files changed (150) hide show
  1. {lesspython-0.3.0/src/lessPython.egg-info → lesspython-0.3.1}/PKG-INFO +8 -1
  2. {lesspython-0.3.0 → lesspython-0.3.1}/README.md +7 -0
  3. {lesspython-0.3.0 → lesspython-0.3.1}/pyproject.toml +1 -1
  4. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/ANOVA.py +70 -11
  5. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/Chart.py +49 -4
  6. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/Correlation.py +39 -10
  7. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/Flows.py +3 -3
  8. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/Logit.py +14 -10
  9. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/Regression.py +22 -18
  10. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/X.py +3 -2
  11. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/XY.py +528 -91
  12. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/bc_items_plotly.py +2 -2
  13. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/bc_plotly.py +6 -5
  14. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/bubble_plotly.py +16 -15
  15. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/corCFA.py +7 -3
  16. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/corProp.py +7 -3
  17. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/corReflect.py +7 -3
  18. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/corReorder.py +7 -3
  19. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/corScree.py +10 -6
  20. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/dn_plotly.py +9 -9
  21. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/dot_plotly.py +9 -8
  22. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/freq_poly_plotly.py +6 -6
  23. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/hier_plotly.py +5 -5
  24. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/hs_plotly.py +7 -7
  25. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/pie_plotly.py +1 -1
  26. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/pivot.py +39 -9
  27. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/plotly_utils.py +90 -26
  28. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/plt_add.py +2 -7
  29. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/plt_contour.py +6 -6
  30. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/plt_contour_facet.py +3 -3
  31. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/plt_mat_plotly.py +11 -10
  32. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/plt_plotly.py +34 -18
  33. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/plt_smooth.py +4 -4
  34. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/prob_norm.py +5 -1
  35. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/prob_tcut.py +5 -1
  36. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/prob_znorm.py +5 -1
  37. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/profile_plotly.py +5 -4
  38. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/radar_plotly.py +4 -3
  39. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/simCImean.py +5 -1
  40. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/simCLT.py +5 -1
  41. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/simFlips.py +5 -1
  42. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/simMeans.py +5 -1
  43. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/style.py +8 -1
  44. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/sunflower_plotly.py +6 -5
  45. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/ttest.py +13 -9
  46. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/utils.py +103 -0
  47. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/vbs_plotly.py +23 -22
  48. {lesspython-0.3.0 → lesspython-0.3.1/src/lessPython.egg-info}/PKG-INFO +8 -1
  49. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPython.egg-info/SOURCES.txt +3 -0
  50. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_anova.py +10 -1
  51. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_chart.py +10 -0
  52. lesspython-0.3.1/tests/test_file_output.py +77 -0
  53. lesspython-0.3.1/tests/test_lessr_457_oct.py +171 -0
  54. lesspython-0.3.1/tests/test_plot_fixes.py +231 -0
  55. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_xy.py +5 -2
  56. {lesspython-0.3.0 → lesspython-0.3.1}/LICENSE +0 -0
  57. {lesspython-0.3.0 → lesspython-0.3.1}/setup.cfg +0 -0
  58. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/Prop_test.py +0 -0
  59. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/VariableLabels.py +0 -0
  60. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/__init__.py +0 -0
  61. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/_themes.py +0 -0
  62. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/anova_rmd.py +0 -0
  63. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/corEFA.py +0 -0
  64. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/corPrint.py +0 -0
  65. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/corRead.py +0 -0
  66. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/data/Anova_1way.csv +0 -0
  67. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/data/Anova_2way.csv +0 -0
  68. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/data/Anova_rb.csv +0 -0
  69. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/data/Anova_rbf.csv +0 -0
  70. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/data/Anova_sp.csv +0 -0
  71. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/data/BodyMeas.csv +0 -0
  72. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/data/Cars93.csv +0 -0
  73. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/data/Employee.csv +0 -0
  74. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/data/Employee_lbl.csv +0 -0
  75. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/data/FreqTable99.csv +0 -0
  76. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/data/Jackets.csv +0 -0
  77. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/data/Learn.csv +0 -0
  78. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/data/Mach4.csv +0 -0
  79. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/data/Mach4_lbl.csv +0 -0
  80. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/data/Reading.csv +0 -0
  81. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/data/StockPrice.csv +0 -0
  82. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/data/WeightLoss.csv +0 -0
  83. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/datasets.py +0 -0
  84. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/date_infer.py +0 -0
  85. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/details.py +0 -0
  86. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/getColors.py +0 -0
  87. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/logit_rmd.py +0 -0
  88. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/order_by.py +0 -0
  89. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/plt_forecast.py +0 -0
  90. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/plt_time.py +0 -0
  91. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/rcolors.py +0 -0
  92. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/reg_rmd.py +0 -0
  93. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/rename.py +0 -0
  94. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/reshape.py +0 -0
  95. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/showColors.py +0 -0
  96. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/stats_out.py +0 -0
  97. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPy/x_console.py +0 -0
  98. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPython.egg-info/dependency_links.txt +0 -0
  99. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPython.egg-info/requires.txt +0 -0
  100. {lesspython-0.3.0 → lesspython-0.3.1}/src/lessPython.egg-info/top_level.txt +0 -0
  101. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_bc_plotly.py +0 -0
  102. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_bubble_plotly.py +0 -0
  103. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_by_colors.py +0 -0
  104. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_chart_colors.py +0 -0
  105. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_chart_items.py +0 -0
  106. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_chart_ordered_fill.py +0 -0
  107. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_chart_stats.py +0 -0
  108. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_corcfa.py +0 -0
  109. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_corefa.py +0 -0
  110. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_corprint.py +0 -0
  111. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_corprop.py +0 -0
  112. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_corread.py +0 -0
  113. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_correflect.py +0 -0
  114. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_correlation.py +0 -0
  115. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_correorder.py +0 -0
  116. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_corscree.py +0 -0
  117. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_datasets.py +0 -0
  118. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_date_infer.py +0 -0
  119. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_details.py +0 -0
  120. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_dot_plotly.py +0 -0
  121. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_flows.py +0 -0
  122. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_getcolors.py +0 -0
  123. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_getcolors_r_palettes.py +0 -0
  124. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_hier_plotly.py +0 -0
  125. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_logit.py +0 -0
  126. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_order_by.py +0 -0
  127. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_pie_plotly.py +0 -0
  128. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_pivot.py +0 -0
  129. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_prob_norm.py +0 -0
  130. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_prob_tcut.py +0 -0
  131. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_prob_znorm.py +0 -0
  132. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_profile.py +0 -0
  133. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_prop_test.py +0 -0
  134. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_radar_plotly.py +0 -0
  135. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_regression.py +0 -0
  136. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_rename.py +0 -0
  137. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_reshape.py +0 -0
  138. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_showcolors.py +0 -0
  139. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_simcimean.py +0 -0
  140. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_simclt.py +0 -0
  141. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_simflips.py +0 -0
  142. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_simmeans.py +0 -0
  143. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_stats_out.py +0 -0
  144. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_style.py +0 -0
  145. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_ttest.py +0 -0
  146. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_variablelabels.py +0 -0
  147. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_vbs_plotly.py +0 -0
  148. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_x.py +0 -0
  149. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_x_console.py +0 -0
  150. {lesspython-0.3.0 → lesspython-0.3.1}/tests/test_xy_console.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lessPython
3
- Version: 0.3.0
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
  ```
@@ -82,6 +82,13 @@ when the analysis runs, even when the result is assigned, as
82
82
  `r = lp.Regression(...)`. From a script, show one with
83
83
  `r.plots["scatter"].show()`.
84
84
 
85
+ Every function that draws also takes `pdf_file=` and `png_file=`
86
+ (needs the png extra, `pip install "lessPython[png]"`, then
87
+ `plotly_get_chrome -y` once). A name such as `png_file="reg"`
88
+ names the file, or with several figures is the stem of each
89
+ name (`reg_scatter_matrix.png`), as lessR's `pdf_file=`. On Colab
90
+ the files also download to your computer.
91
+
85
92
  ## Development install
86
93
 
87
94
  ```
@@ -12,7 +12,7 @@ build-backend = "setuptools.build_meta"
12
12
 
13
13
  [project]
14
14
  name = "lessPython"
15
- version = "0.3.0"
15
+ version = "0.3.1"
16
16
  description = "Python port of R's lessR: analytic-view visualizations, statistical models, and data utilities, rendered with plotly"
17
17
  authors = [{ name = "David W. Gerbing", email = "gerbing@pdx.edu" }]
18
18
  readme = "README.md"
@@ -22,10 +22,10 @@ import pandas as pd
22
22
  import plotly.graph_objects as go
23
23
 
24
24
  from .plotly_utils import (
25
- BASE_COLORS, axis_cat, axis_format, axis_num, make_trans,
25
+ as_plotly_color, BASE_COLORS, axis_cat, axis_format, axis_num, make_trans,
26
26
  plot_border, plotly_style, to_hex, x_grid)
27
27
  from .Regression import _getdigits, _prntbl
28
- from .utils import fmt, get_column, get_option, pretty, show_plots
28
+ from .utils import fmt, get_column, get_option, pretty, file_output, show_plots
29
29
 
30
30
 
31
31
  class ANOVAResults:
@@ -380,6 +380,8 @@ def _oneway(fit, yv, gv, y_name, x_name, levels, n_keep, d,
380
380
  lines += _prntbl(tukey, d).split("\n")
381
381
 
382
382
  plots = {}
383
+ if graphics and tukey is not None:
384
+ plots["tukey"] = _tukey_plot(tukey, x_name, d)
383
385
  if graphics:
384
386
  plots["means"] = _anova_means_plot(
385
387
  yv, gv, y_name, x_name, levels, jitter_x, d)
@@ -394,6 +396,59 @@ def _oneway(fit, yv, gv, y_name, x_name, levels, n_keep, d,
394
396
  tukey=tukey, plots=plots))
395
397
 
396
398
 
399
+ def _tukey_plot(tukey, x_name, d):
400
+ """Each pairwise difference of means with its 95% family-wise
401
+ interval, the first comparison at the top, and a dashed
402
+ reference line at 0. ~ .ANOVAz1 plot(TukeyHSD())"""
403
+ style = plotly_style()
404
+ k = len(tukey)
405
+ ypos = list(range(k, 0, -1))
406
+ col = as_plotly_color(get_option("pt_color", "#324E5C"))
407
+ fig = go.Figure()
408
+ for yp, (lbl, r) in zip(ypos, tukey.iterrows()):
409
+ fig.add_trace(go.Scatter(
410
+ x=[r["lwr"], r["upr"]], y=[yp, yp], mode="lines",
411
+ line=dict(color=col, width=2), hoverinfo="skip",
412
+ showlegend=False))
413
+ fig.add_trace(go.Scatter(
414
+ x=tukey["diff"], y=ypos, mode="markers",
415
+ marker=dict(symbol="circle", size=9, color=col),
416
+ customdata=np.column_stack([tukey["lwr"], tukey["upr"],
417
+ tukey["p adj"]]),
418
+ text=list(tukey.index),
419
+ hovertemplate=("%{text}<br>diff: %{x:." + str(d) + "f}"
420
+ "<br>95% CI: %{customdata[0]:." + str(d)
421
+ + "f} to %{customdata[1]:." + str(d) + "f}"
422
+ "<br>p adj: %{customdata[2]:.3f}"
423
+ "<extra></extra>"),
424
+ showlegend=False))
425
+ axT1 = pretty(min(0.0, float(tukey["lwr"].min())),
426
+ max(0.0, float(tukey["upr"].max())))
427
+ ax_x = axis_num(f"Differences in mean levels of {x_name}", axT1,
428
+ axis_format(axT1, d))
429
+ ax_x.update(showgrid=True,
430
+ gridcolor=as_plotly_color(style["grid_col"]),
431
+ gridwidth=1, griddash="dot",
432
+ range=[axT1[0], axT1[-1]], zeroline=False)
433
+ ax_y = axis_cat("")
434
+ ax_y.update(tickmode="array", tickvals=ypos,
435
+ ticktext=list(tukey.index), range=[0.5, k + 0.5])
436
+ zero = dict(type="line", xref="x", yref="paper", x0=0, x1=0,
437
+ y0=0, y1=1, layer="below",
438
+ line=dict(color=as_plotly_color("gray50"), width=1,
439
+ dash="dash"))
440
+ fig.update_layout(
441
+ xaxis=ax_x, yaxis=ax_y, shapes=[zero] + list(plot_border()),
442
+ template=None,
443
+ plot_bgcolor=as_plotly_color(style["panel_fill"]),
444
+ paper_bgcolor=as_plotly_color(style["window_fill"]),
445
+ title=dict(text="95% family-wise confidence level",
446
+ x=0.5, xanchor="center",
447
+ font=dict(size=round(
448
+ 16 * get_option("main_size", 1)))))
449
+ return fig
450
+
451
+
397
452
  def _anova_means_plot(yv, gv, y_name, x_name, levels, jitter_x,
398
453
  d):
399
454
  """Scatterplot of the response by factor level, jittered
@@ -410,18 +465,18 @@ def _anova_means_plot(yv, gv, y_name, x_name, levels, jitter_x,
410
465
  x=xn, y=yv, mode="markers",
411
466
  marker=dict(symbol="circle", size=6,
412
467
  color=make_trans(pt, 0.85), opacity=1,
413
- line=dict(color=to_hex(pt), width=1)),
468
+ line=dict(color=as_plotly_color(pt), width=1)),
414
469
  hoverinfo="y", showlegend=False))
415
470
  means = [yv[gv == lv].mean() for lv in levels]
416
471
  for i, mval in enumerate(means):
417
472
  fig.add_shape(type="line", xref="x", yref="y",
418
473
  x0=-0.5, x1=len(levels) - 0.5,
419
474
  y0=mval, y1=mval, layer="below",
420
- line=dict(color=to_hex("gray70"), width=1))
475
+ line=dict(color=as_plotly_color("gray70"), width=1))
421
476
  fig.add_trace(go.Scatter(
422
477
  x=list(range(len(levels))), y=means, mode="markers",
423
478
  marker=dict(symbol="diamond", size=13,
424
- color=to_hex(get_option("fit_color",
479
+ color=as_plotly_color(get_option("fit_color",
425
480
  "#5C4032"))),
426
481
  hoverinfo="x+y", showlegend=False))
427
482
  axT2 = pretty(float(yv.min()), float(yv.max()))
@@ -431,13 +486,13 @@ def _anova_means_plot(yv, gv, y_name, x_name, levels, jitter_x,
431
486
  ticktext=levels, range=[-0.5, len(levels) - 0.5])
432
487
  ax_y = axis_num(y_name, axT2, axis_format(axT2, d))
433
488
  ax_y.update(showgrid=True,
434
- gridcolor=to_hex(style["grid_col"]),
489
+ gridcolor=as_plotly_color(style["grid_col"]),
435
490
  gridwidth=1, griddash="dot")
436
491
  fig.update_layout(
437
492
  xaxis=ax_x, yaxis=ax_y, shapes=fig.layout.shapes
438
493
  + tuple(plot_border()), template=None,
439
- plot_bgcolor=to_hex(style["panel_fill"]),
440
- paper_bgcolor=to_hex(style["window_fill"]),
494
+ plot_bgcolor=as_plotly_color(style["panel_fill"]),
495
+ paper_bgcolor=as_plotly_color(style["window_fill"]),
441
496
  title=dict(text="Scatterplot with Cell Means",
442
497
  x=0.5, xanchor="center",
443
498
  font=dict(size=round(
@@ -644,13 +699,13 @@ def _cat_line_fig(title, x_name, y_name, levels_x, series, d,
644
699
  range=[-0.4, len(levels_x) - 0.6])
645
700
  ax_y = axis_num(y_name, axT2, axis_format(axT2, d))
646
701
  ax_y.update(showgrid=True,
647
- gridcolor=to_hex(style["grid_col"]),
702
+ gridcolor=as_plotly_color(style["grid_col"]),
648
703
  gridwidth=1, griddash="dot")
649
704
  fig.update_layout(
650
705
  xaxis=ax_x, yaxis=ax_y, shapes=plot_border(),
651
706
  template=None, legend=dict(title=dict(text=legend_title)),
652
- plot_bgcolor=to_hex(style["panel_fill"]),
653
- paper_bgcolor=to_hex(style["window_fill"]),
707
+ plot_bgcolor=as_plotly_color(style["panel_fill"]),
708
+ paper_bgcolor=as_plotly_color(style["window_fill"]),
654
709
  title=dict(text=title, x=0.5, xanchor="center",
655
710
  font=dict(size=round(
656
711
  16 * get_option("main_size", 1)))))
@@ -678,3 +733,7 @@ def _blocks_plot(x1v, yvals, x2v, y_name, f1, f2, l1, l2, d,
678
733
  sub = dp[dp["b"] == b].set_index("a")["y"]
679
734
  series.append((b, [sub.get(a, np.nan) for a in l1]))
680
735
  return _cat_line_fig(title, f1, y_name, l1, series, d, f2)
736
+
737
+
738
+ # pdf_file= and png_file= write the figures, as lessR's pdf_file=
739
+ ANOVA = file_output(ANOVA)
@@ -402,7 +402,8 @@ def _chart_per_variable(data, items, cols, fill, xlab, main, quiet):
402
402
  R analog: bc.data.frame()"""
403
403
  from plotly.subplots import make_subplots
404
404
  import plotly.graph_objects as go
405
- from .plotly_utils import (BASE_COLORS, auto_text_color,
405
+ from .plotly_utils import (
406
+ as_plotly_color, BASE_COLORS, auto_text_color,
406
407
  axis_format, plotly_style, to_hex)
407
408
  from .stats_out import _counts_1d
408
409
  show = not resolve_quiet(quiet)
@@ -466,8 +467,8 @@ def _chart_per_variable(data, items, cols, fill, xlab, main, quiet):
466
467
  quiet)
467
468
  fig.update_layout(
468
469
  template=None, height=140 + 260 * n_row,
469
- plot_bgcolor=to_hex(st["panel_fill"]),
470
- paper_bgcolor=to_hex(st["window_fill"]),
470
+ plot_bgcolor=as_plotly_color(st["panel_fill"]),
471
+ paper_bgcolor=as_plotly_color(st["window_fill"]),
471
472
  title=(dict(text=main, x=0.5) if main else None))
472
473
  return fig
473
474
 
@@ -1916,6 +1917,26 @@ def Chart(x, y=None, data=None, filter=None, by=None, facet=None,
1916
1917
  facet_name=facet_ttl, y_name=y_name, stat=stat)
1917
1918
  elif main == "":
1918
1919
  main = None
1920
+ # a palette name in fill names the palette, not a color, as
1921
+ # for the bar chart. A sequential palette shades each slice
1922
+ # by its value off the palette's ramp, larger darker, as
1923
+ # for the treemap. R analog: Chart.R pie palette branch
1924
+ if isinstance(fill, str) and fill in _palette_names():
1925
+ from .hier_plotly import _HIER_SEQ
1926
+ slices = list(tbl.columns if isinstance(tbl, pd.DataFrame)
1927
+ else tbl.index)
1928
+ vals = (tbl.sum(axis=0) if isinstance(tbl, pd.DataFrame)
1929
+ else tbl).to_numpy(dtype=float)
1930
+ if (fill.lower() in _HIER_SEQ and len(slices) > 1
1931
+ and np.ptp(vals) > 0):
1932
+ from .getColors import getColors
1933
+ ramp = [c[:7] for c in getColors(fill.lower(), n=256,
1934
+ quiet=True)]
1935
+ pos = (vals - vals.min()) / np.ptp(vals)
1936
+ fill = [ramp[i] for i in
1937
+ np.round(pos * (len(ramp) - 1)).astype(int)]
1938
+ else:
1939
+ fill = _color_range(fill, len(slices))
1919
1940
  return pie_plotly(
1920
1941
  tbl,
1921
1942
  x_name=x, y_name=y_name, by_name=by, main=main,
@@ -1984,6 +2005,30 @@ def Chart(x, y=None, data=None, filter=None, by=None, facet=None,
1984
2005
  return fig
1985
2006
 
1986
2007
 
2008
+ def _with_height(func):
2009
+ """height= in inches, as lessR's Chart(height=), converted at
2010
+ 96 pixels per inch, so pdf_file= also writes that height.
2011
+ None keeps the figure's own height."""
2012
+ import functools
2013
+
2014
+ @functools.wraps(func)
2015
+ def wrapper(*args, height=None, **kwargs):
2016
+ if height is not None and (isinstance(height, bool) or
2017
+ not isinstance(height, (int, float))
2018
+ or height <= 0):
2019
+ raise ValueError(
2020
+ f"height is in inches, a positive number such as "
2021
+ f"height=4, not {height!r}")
2022
+ fig = func(*args, **kwargs)
2023
+ if height is not None and hasattr(fig, "layout"):
2024
+ fig.update_layout(height=round(96 * height))
2025
+ return fig
2026
+
2027
+ return wrapper
2028
+
2029
+
1987
2030
  # font_size= scales all text of the returned figure
1988
2031
  from .style import with_theme as _with_theme # noqa: E402
1989
- Chart = font_scaled(attach_stats(_with_theme(Chart, own_param=True)))
2032
+ from .utils import file_output # noqa: E402
2033
+ Chart = file_output(_with_height(font_scaled(attach_stats(
2034
+ _with_theme(Chart, own_param=True)))))
@@ -12,7 +12,7 @@
12
12
  import numpy as np
13
13
  import pandas as pd
14
14
 
15
- from .utils import fmt, get_column, get_option, show_plots
15
+ from .utils import fmt, get_column, get_option, file_output, show_plots
16
16
 
17
17
 
18
18
  class CorrelationResults:
@@ -110,6 +110,7 @@ def _two_var(x, y, x_name, y_name, method, brief):
110
110
  r, p = sps.pearsonr(xk, yk)
111
111
  df = n - 2
112
112
  t = r * np.sqrt(df / (1 - r ** 2))
113
+ stat_nm = "t"
113
114
  p = 2 * sps.t.sf(abs(t), df)
114
115
  z = np.arctanh(r)
115
116
  sig = 1 / np.sqrt(n - 3)
@@ -119,17 +120,41 @@ def _two_var(x, y, x_name, y_name, method, brief):
119
120
  sym, sym_pop = "r", "Correlation"
120
121
  cov = float(np.cov(xk, yk)[0, 1])
121
122
  elif method == "spearman":
123
+ # the statistic cor.test() reports: S, the sum of the squared
124
+ # rank differences, with no df. R analog: cr.main.R
125
+ # names(ct$statistic)
122
126
  res = sps.spearmanr(xk, yk)
123
127
  r, p = float(res.statistic), float(res.pvalue)
124
- t, df = res.statistic * np.sqrt(
125
- (n - 2) / (1 - r ** 2)), n - 2
128
+ t, df, stat_nm = (n ** 3 - n) * (1 - r) / 6, None, "S"
126
129
  method_txt = "Spearman's rank correlation rho"
127
130
  sym = sym_pop = "rho"
128
131
  else:
132
+ # cor.test(): T, the count of concordant pairs, with its exact
133
+ # p-value for n < 50 and no ties, else z with the variance
134
+ # corrected for ties
129
135
  res = sps.kendalltau(xk, yk)
130
- r, p = float(res.statistic), float(res.pvalue)
131
- t = res.statistic
136
+ r = float(res.statistic)
137
+ _, xt = np.unique(xk, return_counts=True)
138
+ _, yt = np.unique(yk, return_counts=True)
139
+ ties = (xt > 1).any() or (yt > 1).any()
132
140
  df = None
141
+ if n < 50 and not ties:
142
+ t, stat_nm = (r + 1) * n * (n - 1) / 4, "T"
143
+ p = float(sps.kendalltau(xk, yk, method="exact").pvalue)
144
+ else:
145
+ T0 = n * (n - 1) / 2
146
+ S = r * np.sqrt((T0 - (xt * (xt - 1)).sum() / 2)
147
+ * (T0 - (yt * (yt - 1)).sum() / 2))
148
+ v0 = n * (n - 1) * (2 * n + 5)
149
+ vt = (xt * (xt - 1) * (2 * xt + 5)).sum()
150
+ vu = (yt * (yt - 1) * (2 * yt + 5)).sum()
151
+ v1 = (xt * (xt - 1)).sum() * (yt * (yt - 1)).sum()
152
+ v2 = ((xt * (xt - 1) * (xt - 2)).sum()
153
+ * (yt * (yt - 1) * (yt - 2)).sum())
154
+ var_S = ((v0 - vt - vu) / 18 + v1 / (2 * n * (n - 1))
155
+ + v2 / (9 * n * (n - 1) * (n - 2)))
156
+ t, stat_nm = S / np.sqrt(var_S), "z"
157
+ p = float(2 * sps.norm.sf(abs(t)))
133
158
  method_txt = "Kendall's rank correlation tau"
134
159
  sym = sym_pop = "tau"
135
160
 
@@ -152,9 +177,9 @@ def _two_var(x, y, x_name, y_name, method, brief):
152
177
  else:
153
178
  L.append(f"Sample Correlation: {sym} = {fmt(r, 3)}")
154
179
  L.append("")
155
- dfs = "NA" if df is None else str(df)
156
- L.append(f"Hypothesis Test of 0 {sym_pop}: t = {fmt(t, 3)}, "
157
- f" df = {dfs}, p-value = {fmt(p, 3)}")
180
+ dfs = "" if df is None else f", df = {df}"
181
+ L.append(f"Hypothesis Test of 0 {sym_pop}: {stat_nm} = "
182
+ f"{fmt(t, 3)}{dfs}, p-value = {fmt(p, 3)}")
158
183
  if method == "pearson":
159
184
  L.append("95% Confidence Interval for Correlation: "
160
185
  f"{fmt(lb, 3)} to {fmt(ub, 3)}")
@@ -228,7 +253,7 @@ def _matrix(df, miss, show, method, digits_d, heat_map, main):
228
253
  def _heatmap(df, main):
229
254
  import plotly.graph_objects as go
230
255
 
231
- from .plotly_utils import plotly_style, to_hex
256
+ from .plotly_utils import as_plotly_color, plotly_style, to_hex
232
257
  labels = list(df.columns)
233
258
  Z = df.to_numpy(dtype=float).copy()
234
259
  np.fill_diagonal(Z, np.nan)
@@ -239,9 +264,13 @@ def _heatmap(df, main):
239
264
  colorbar=dict(title="r")))
240
265
  fig.update_yaxes(autorange="reversed")
241
266
  fig.update_layout(
242
- template=None, paper_bgcolor=to_hex(style["window_fill"]),
267
+ template=None, paper_bgcolor=as_plotly_color(style["window_fill"]),
243
268
  title=dict(text=main or "Correlations", x=0.5,
244
269
  xanchor="center",
245
270
  font=dict(size=round(
246
271
  16 * get_option("main_size", 1)))))
247
272
  return fig
273
+
274
+
275
+ # pdf_file= and png_file= write the figures, as lessR's pdf_file=
276
+ Correlation = file_output(Correlation)
@@ -9,7 +9,7 @@
9
9
  import numpy as np
10
10
  import pandas as pd
11
11
 
12
- from .plotly_utils import BASE_COLORS, make_trans, to_hex
12
+ from .plotly_utils import as_plotly_color, BASE_COLORS, make_trans, to_hex
13
13
  from .utils import get_column, get_option
14
14
 
15
15
 
@@ -96,7 +96,7 @@ def Flows(value, stage1, stage2, stage3=None, data=None,
96
96
  node=dict(
97
97
  label=nodes, color=node_color, x=node_x, y=node_y,
98
98
  pad=12, thickness=16,
99
- line=dict(color=to_hex(border_gray), width=1),
99
+ line=dict(color=as_plotly_color(border_gray), width=1),
100
100
  hovertemplate="%{label}<extra></extra>"),
101
101
  link=dict(
102
102
  source=src, target=tgt, value=val, color=lcol,
@@ -116,5 +116,5 @@ def Flows(value, stage1, stage2, stage3=None, data=None,
116
116
  r=30, b=30, l=30),
117
117
  font=dict(size=round(15 * labels_size)),
118
118
  template=None,
119
- paper_bgcolor=to_hex(get_option("window_fill", "white")))
119
+ paper_bgcolor=as_plotly_color(get_option("window_fill", "white")))
120
120
  return fig
@@ -36,13 +36,13 @@ import pandas as pd
36
36
  import plotly.graph_objects as go
37
37
 
38
38
  from .plotly_utils import (
39
- axis_format, axis_num, make_trans, plot_border,
39
+ as_plotly_color, axis_format, axis_num, make_trans, plot_border,
40
40
  plotly_style, to_hex, x_grid)
41
41
  from .plt_mat_plotly import scatter_matrix
42
42
  from .logit_rmd import logit_rmd
43
43
  from .Regression import (
44
44
  _expand_indicators, _parse_formula, _prntbl)
45
- from .utils import fmt, get_column, get_option, pretty, show_plots
45
+ from .utils import fmt, get_column, get_option, pretty, file_output, show_plots
46
46
 
47
47
 
48
48
  class LogitResults:
@@ -564,13 +564,13 @@ def _logit_plot(xv, y01, mu, levels, y_name, x_name, pc,
564
564
  color=make_trans(pt_fill,
565
565
  1 - transparency),
566
566
  opacity=1,
567
- line=dict(color=to_hex(pt_fill),
567
+ line=dict(color=as_plotly_color(pt_fill),
568
568
  width=1)),
569
569
  hoverinfo="x+y", showlegend=False))
570
570
  od = np.argsort(xv, kind="stable")
571
571
  fig.add_trace(go.Scatter(
572
572
  x=xv[od], y=mu[od], mode="lines",
573
- line=dict(color=to_hex(pt_fill), width=2),
573
+ line=dict(color=as_plotly_color(pt_fill), width=2),
574
574
  hoverinfo="skip", showlegend=False))
575
575
  axT1 = pretty(float(xv.min()), float(xv.max()))
576
576
  axT2 = [0, 0.2, 0.4, 0.6, 0.8, 1]
@@ -579,7 +579,7 @@ def _logit_plot(xv, y01, mu, levels, y_name, x_name, pc,
579
579
  ax_y = axis_num(y_lab, axT2,
580
580
  [f"{v:g}" for v in axT2])
581
581
  ax_y.update(range=[-0.10, 1.10], showgrid=True,
582
- gridcolor=to_hex(style_opts["grid_col"]),
582
+ gridcolor=as_plotly_color(style_opts["grid_col"]),
583
583
  gridwidth=1, griddash="dot")
584
584
  shapes = x_grid(axT1) + plot_border()
585
585
  if pc is not None: # threshold crosshairs
@@ -588,13 +588,13 @@ def _logit_plot(xv, y01, mu, levels, y_name, x_name, pc,
588
588
  shapes.append(dict(
589
589
  type="line", xref="paper", yref="y",
590
590
  x0=0, x1=1, y0=pc, y1=pc,
591
- line=dict(color=to_hex("gray35"), width=0.75,
591
+ line=dict(color=as_plotly_color("gray35"), width=0.75,
592
592
  dash="dash")))
593
593
  if float(xv.min()) <= x_cut <= float(xv.max()):
594
594
  shapes.append(dict(
595
595
  type="line", xref="x", yref="paper",
596
596
  x0=x_cut, x1=x_cut, y0=0, y1=1,
597
- line=dict(color=to_hex("gray35"), width=0.75,
597
+ line=dict(color=as_plotly_color("gray35"), width=0.75,
598
598
  dash="dash")))
599
599
  anns = []
600
600
  if levels is not None: # right-axis level labels
@@ -605,11 +605,15 @@ def _logit_plot(xv, y01, mu, levels, y_name, x_name, pc,
605
605
  xanchor="left",
606
606
  font=dict(size=round(
607
607
  14 * get_option("axis_size", 0.9)),
608
- color=to_hex(get_option(
608
+ color=as_plotly_color(get_option(
609
609
  "axis_color", "black")))))
610
610
  fig.update_layout(
611
611
  xaxis=ax_x, yaxis=ax_y, shapes=shapes,
612
612
  annotations=anns, template=None,
613
- plot_bgcolor=to_hex(style_opts["panel_fill"]),
614
- paper_bgcolor=to_hex(style_opts["window_fill"]))
613
+ plot_bgcolor=as_plotly_color(style_opts["panel_fill"]),
614
+ paper_bgcolor=as_plotly_color(style_opts["window_fill"]))
615
615
  return fig
616
+
617
+
618
+ # pdf_file= and png_file= write the figures, as lessR's pdf_file=
619
+ Logit = file_output(Logit)
@@ -51,7 +51,7 @@ from .plotly_utils import (
51
51
  make_trans, plot_border, plotly_style, to_hex, x_grid)
52
52
  from .utils import (
53
53
  category_order, fmt, get_column, get_option, pretty,
54
- show_plots)
54
+ file_output, show_plots)
55
55
  from .X import _breaks_from_args
56
56
 
57
57
 
@@ -1228,13 +1228,13 @@ def _moderation_plot(fit, Xd, y_name, mod_info, d):
1228
1228
  ax_x = axis_num(x_name, axT1, axis_format(axT1, d))
1229
1229
  ax_y = axis_num(y_name, axT2, axis_format(axT2, d))
1230
1230
  ax_y.update(showgrid=True,
1231
- gridcolor=to_hex(style_opts["grid_col"]),
1231
+ gridcolor=as_plotly_color(style_opts["grid_col"]),
1232
1232
  gridwidth=1, griddash="dot")
1233
1233
  fig.update_layout(
1234
1234
  xaxis=ax_x, yaxis=ax_y,
1235
1235
  shapes=x_grid(axT1) + plot_border(), template=None,
1236
- plot_bgcolor=to_hex(style_opts["panel_fill"]),
1237
- paper_bgcolor=to_hex(style_opts["window_fill"]),
1236
+ plot_bgcolor=as_plotly_color(style_opts["panel_fill"]),
1237
+ paper_bgcolor=as_plotly_color(style_opts["window_fill"]),
1238
1238
  legend=dict(title=dict(text=w_name)),
1239
1239
  title=dict(text="Moderator Variable Interaction Plot",
1240
1240
  x=0.5, xanchor="center",
@@ -1331,13 +1331,13 @@ def _reg_ancova_plot(fit, Xd, yv, y_name, info, d):
1331
1331
  ax_x = axis_num(cov, axT1, axis_format(axT1, d))
1332
1332
  ax_y = axis_num(y_name, axT2, axis_format(axT2, d))
1333
1333
  ax_y.update(showgrid=True,
1334
- gridcolor=to_hex(style_opts["grid_col"]),
1334
+ gridcolor=as_plotly_color(style_opts["grid_col"]),
1335
1335
  gridwidth=1, griddash="dot")
1336
1336
  fig.update_layout(
1337
1337
  xaxis=ax_x, yaxis=ax_y,
1338
1338
  shapes=x_grid(axT1) + plot_border(), template=None,
1339
- plot_bgcolor=to_hex(style_opts["panel_fill"]),
1340
- paper_bgcolor=to_hex(style_opts["window_fill"]),
1339
+ plot_bgcolor=as_plotly_color(style_opts["panel_fill"]),
1340
+ paper_bgcolor=as_plotly_color(style_opts["window_fill"]),
1341
1341
  legend=dict(title=dict(text=fac)),
1342
1342
  title=dict(text="Scatterplot and Least-Squares Lines",
1343
1343
  x=0.5, xanchor="center",
@@ -1384,11 +1384,11 @@ def _reg_scatter(xv, yv, x_name, y_name, fit, smapi, digits_d,
1384
1384
  sizemode="diameter",
1385
1385
  color=make_trans(pt_fill, 0.9),
1386
1386
  opacity=1,
1387
- line=dict(color=to_hex(pt_fill), width=1)),
1387
+ line=dict(color=as_plotly_color(pt_fill), width=1)),
1388
1388
  hoverinfo="x+y", showlegend=False))
1389
1389
  fig.add_trace(go.Scatter(
1390
1390
  x=xs, y=sf["mean"].to_numpy(), mode="lines",
1391
- line=dict(color=to_hex(get_option("fit_color",
1391
+ line=dict(color=as_plotly_color(get_option("fit_color",
1392
1392
  "#5C4032")),
1393
1393
  width=get_option("fit_lwd", 2)),
1394
1394
  hoverinfo="skip", showlegend=False))
@@ -1405,7 +1405,7 @@ def _reg_scatter(xv, yv, x_name, y_name, fit, smapi, digits_d,
1405
1405
  ax_y = axis_num(y_name, axT2,
1406
1406
  axis_format(axT2, digits_d))
1407
1407
  ax_y.update(showgrid=True,
1408
- gridcolor=to_hex(style_opts["grid_col"]),
1408
+ gridcolor=as_plotly_color(style_opts["grid_col"]),
1409
1409
  gridwidth=1, griddash="dot")
1410
1410
  title = ("Reg Line, Confidence & Prediction Intervals"
1411
1411
  if with_bands
@@ -1413,8 +1413,8 @@ def _reg_scatter(xv, yv, x_name, y_name, fit, smapi, digits_d,
1413
1413
  fig.update_layout(
1414
1414
  xaxis=ax_x, yaxis=ax_y,
1415
1415
  shapes=x_grid(axT1) + plot_border(), template=None,
1416
- plot_bgcolor=to_hex(style_opts["panel_fill"]),
1417
- paper_bgcolor=to_hex(style_opts["window_fill"]),
1416
+ plot_bgcolor=as_plotly_color(style_opts["panel_fill"]),
1417
+ paper_bgcolor=as_plotly_color(style_opts["window_fill"]),
1418
1418
  title=dict(text=title, x=0.5, xanchor="center",
1419
1419
  font=dict(size=round(
1420
1420
  16 * get_option("main_size", 1)))))
@@ -1444,14 +1444,14 @@ def _reg_resfit(fitted, resid, cooks, cooks_cut, labels,
1444
1444
  sizemode="diameter",
1445
1445
  color=make_trans(pt_fill, 0.9),
1446
1446
  opacity=1,
1447
- line=dict(color=to_hex(pt_fill), width=0.5)),
1447
+ line=dict(color=as_plotly_color(pt_fill), width=0.5)),
1448
1448
  hoverinfo="x+y", showlegend=False))
1449
1449
  flag = np.where(cooks >= cut)[0]
1450
1450
  for i in flag:
1451
1451
  fig.add_annotation(
1452
1452
  x=float(fitted[i]), y=float(resid[i]),
1453
1453
  text=str(labels[i]), showarrow=False, yshift=12,
1454
- font=dict(size=11, color=to_hex("gray30")))
1454
+ font=dict(size=11, color=as_plotly_color("gray30")))
1455
1455
  axT1 = pretty(float(fitted.min()), float(fitted.max()))
1456
1456
  axT2 = pretty(float(resid.min()), float(resid.max()))
1457
1457
  ax_x = axis_num("Fitted Values", axT1,
@@ -1459,18 +1459,18 @@ def _reg_resfit(fitted, resid, cooks, cooks_cut, labels,
1459
1459
  ax_y = axis_num("Residuals", axT2,
1460
1460
  axis_format(axT2, digits_d))
1461
1461
  ax_y.update(showgrid=True,
1462
- gridcolor=to_hex(style_opts["grid_col"]),
1462
+ gridcolor=as_plotly_color(style_opts["grid_col"]),
1463
1463
  gridwidth=1, griddash="dot")
1464
1464
  shapes = x_grid(axT1) + plot_border()
1465
1465
  shapes.append(dict( # zero residual line
1466
1466
  type="line", xref="paper", yref="y",
1467
1467
  x0=0, x1=1, y0=0, y1=0,
1468
- line=dict(color=to_hex("gray50"), width=1,
1468
+ line=dict(color=as_plotly_color("gray50"), width=1,
1469
1469
  dash="dash")))
1470
1470
  fig.update_layout(
1471
1471
  xaxis=ax_x, yaxis=ax_y, shapes=shapes, template=None,
1472
- plot_bgcolor=to_hex(style_opts["panel_fill"]),
1473
- paper_bgcolor=to_hex(style_opts["window_fill"]),
1472
+ plot_bgcolor=as_plotly_color(style_opts["panel_fill"]),
1473
+ paper_bgcolor=as_plotly_color(style_opts["window_fill"]),
1474
1474
  title=dict(
1475
1475
  text=("Residuals vs Fitted Values<br>"
1476
1476
  f"<sup>{sub}</sup>"),
@@ -1491,3 +1491,7 @@ def _reg_dn_residual(resid):
1491
1491
  show_histogram=True, hist_edges=edges,
1492
1492
  main="Distribution of Residuals")
1493
1493
  return fig
1494
+
1495
+
1496
+ # pdf_file= and png_file= write the figures, as lessR's pdf_file=
1497
+ Regression = file_output(Regression)
@@ -659,5 +659,6 @@ def _per_variable(func):
659
659
  # font_size= scales all text of the returned figure
660
660
  from .stats_out import attach_stats as _attach_stats # noqa: E402
661
661
  from .style import with_theme as _with_theme # noqa: E402
662
- X = _per_variable(font_scaled(_attach_stats(_with_theme(X),
663
- capture=True)))
662
+ from .utils import file_output # noqa: E402
663
+ X = file_output(_per_variable(font_scaled(_attach_stats(
664
+ _with_theme(X), capture=True))))