davis-stats 2.2__tar.gz → 2.2.2__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.
- {davis_stats-2.2 → davis_stats-2.2.2}/PKG-INFO +2 -3
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/__init__.py +2 -1
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/datasets/__init__.py +20 -0
- davis_stats-2.2.2/davis_stats/datasets/sweet_things.xlsx +0 -0
- davis_stats-2.2.2/davis_stats/datasets/sweet_things_simple.xlsx +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/visualization/boxplot.py +2 -1
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/visualization/histogram.py +2 -1
- davis_stats-2.2.2/davis_stats/visualization/residuals.py +74 -0
- davis_stats-2.2.2/davis_stats/visualization/scatter.py +194 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats.egg-info/PKG-INFO +2 -3
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats.egg-info/SOURCES.txt +2 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats.egg-info/requires.txt +0 -1
- {davis_stats-2.2 → davis_stats-2.2.2}/setup.py +2 -3
- davis_stats-2.2/davis_stats/visualization/residuals.py +0 -25
- davis_stats-2.2/davis_stats/visualization/scatter.py +0 -128
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/datasets/ceo_comp.xlsx +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/datasets/fired_ceo.xlsx +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/datasets/just_games.xlsx +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/datasets/m.xlsx +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/datasets/nba.xlsx +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/datasets/netflix_content.xlsx +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/datasets/new_ceo.xlsx +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/datasets/olympic_medals.xlsx +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/datasets/restate.xlsx +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/datasets/restaurants.xlsx +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/datasets/sp1500_2025.xlsx +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/datasets/sp1500_panel.xlsx +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/datasets/student_athletes.xlsx +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/datasets/world_cup_goals.xlsx +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/practice/__init__.py +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/practice/stats_practice.py +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/reg_modeling/__init__.py +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/reg_modeling/marginal_effects.py +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/reg_modeling/reg.py +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/reg_tests/__init__.py +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/reg_tests/bp_test.py +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/reg_tests/dw_test.py +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/reg_tests/hausman_test.py +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/reg_tests/sw_test.py +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/reg_tests/vif_test.py +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/visualization/__init__.py +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats/visualization/trim.py +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats.egg-info/dependency_links.txt +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/davis_stats.egg-info/top_level.txt +0 -0
- {davis_stats-2.2 → davis_stats-2.2.2}/setup.cfg +0 -0
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: davis_stats
|
|
3
|
-
Version: 2.2
|
|
3
|
+
Version: 2.2.2
|
|
4
4
|
Summary: davis_stats is a teaching-focused repository
|
|
5
5
|
Author: Justin G. Davis
|
|
6
6
|
Author-email:
|
|
7
7
|
Keywords: statistics,data science,business analytics
|
|
8
|
-
Requires-Python: >=3.
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
9
|
Requires-Dist: pandas>=3.0.6
|
|
10
10
|
Requires-Dist: openpyxl>=3.0.0
|
|
11
11
|
Requires-Dist: numpy>=1.20.0
|
|
12
12
|
Requires-Dist: matplotlib>=3.11.2
|
|
13
13
|
Requires-Dist: scipy>=1.6.0
|
|
14
|
-
Requires-Dist: seaborn>=0.12.0
|
|
15
14
|
Requires-Dist: statsmodels>=0.14.0
|
|
16
15
|
Requires-Dist: ipympl>=0.8.0
|
|
17
16
|
Requires-Dist: linearmodels>=7.0
|
|
@@ -19,4 +19,5 @@ from .datasets import (
|
|
|
19
19
|
ceo_comp, netflix_content, olympic_medals,
|
|
20
20
|
restaurants, world_cup_goals, just_games,
|
|
21
21
|
nba, fired_ceo, restate, new_ceo, sp1500_2025,
|
|
22
|
-
sp1500_panel, student_athletes, m
|
|
22
|
+
sp1500_panel, student_athletes, m, sweet_things,
|
|
23
|
+
sweet_things_simple)
|
|
@@ -19,6 +19,8 @@ class DataFrames:
|
|
|
19
19
|
_sp1500_panel = None
|
|
20
20
|
_student_athletes = None
|
|
21
21
|
_m = None
|
|
22
|
+
_sweet_things = None
|
|
23
|
+
_sweet_things_simple = None
|
|
22
24
|
|
|
23
25
|
@property
|
|
24
26
|
def ceo_comp(self):
|
|
@@ -104,6 +106,18 @@ class DataFrames:
|
|
|
104
106
|
self._m = pd.read_excel(DATA_DIR / 'm.xlsx')
|
|
105
107
|
return self._m
|
|
106
108
|
|
|
109
|
+
@property
|
|
110
|
+
def sweet_things(self):
|
|
111
|
+
if self._sweet_things is None:
|
|
112
|
+
self._sweet_things = pd.read_excel(DATA_DIR / 'sweet_things.xlsx')
|
|
113
|
+
return self._sweet_things
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def sweet_things_simple(self):
|
|
117
|
+
if self._sweet_things_simple is None:
|
|
118
|
+
self._sweet_things_simple = pd.read_excel(DATA_DIR / 'sweet_things_simple.xlsx')
|
|
119
|
+
return self._sweet_things_simple
|
|
120
|
+
|
|
107
121
|
_datasets = DataFrames()
|
|
108
122
|
|
|
109
123
|
|
|
@@ -148,3 +162,9 @@ def student_athletes():
|
|
|
148
162
|
|
|
149
163
|
def m():
|
|
150
164
|
return _datasets.m
|
|
165
|
+
|
|
166
|
+
def sweet_things():
|
|
167
|
+
return _datasets.sweet_things
|
|
168
|
+
|
|
169
|
+
def sweet_things_simple():
|
|
170
|
+
return _datasets.sweet_things_simple
|
|
Binary file
|
|
Binary file
|
|
@@ -10,6 +10,7 @@ def boxplot(
|
|
|
10
10
|
title=None,
|
|
11
11
|
trim_outliers=100,
|
|
12
12
|
details=True,
|
|
13
|
+
decimals=1,
|
|
13
14
|
dpi=160,
|
|
14
15
|
save_dpi=300,
|
|
15
16
|
figsize=(7, 4.5),
|
|
@@ -79,7 +80,7 @@ def boxplot(
|
|
|
79
80
|
ax.ticklabel_format(style="plain", axis=value_axis)
|
|
80
81
|
ax.tick_params(axis=value_axis, labelsize=11, length=5, width=1.1)
|
|
81
82
|
getattr(ax, f"{value_axis}axis").set_major_formatter(
|
|
82
|
-
FuncFormatter(lambda v, _: f"{v:,.
|
|
83
|
+
FuncFormatter(lambda v, _: f"{v:,.{decimals}f}")
|
|
83
84
|
)
|
|
84
85
|
|
|
85
86
|
if details:
|
|
@@ -12,6 +12,7 @@ def histogram(
|
|
|
12
12
|
bins=30,
|
|
13
13
|
trim_outliers=100,
|
|
14
14
|
details=True,
|
|
15
|
+
decimals=1,
|
|
15
16
|
dpi=200,
|
|
16
17
|
save_dpi=300,
|
|
17
18
|
figsize=(7, 4.5),
|
|
@@ -55,7 +56,7 @@ def histogram(
|
|
|
55
56
|
ax.set_xlabel("")
|
|
56
57
|
ax.tick_params(labelsize=10, length=5, width=1.1)
|
|
57
58
|
ax.ticklabel_format(style="plain", axis="x")
|
|
58
|
-
ax.xaxis.set_major_formatter(FuncFormatter(lambda v, _: f"{v:,.
|
|
59
|
+
ax.xaxis.set_major_formatter(FuncFormatter(lambda v, _: f"{v:,.{decimals}f}"))
|
|
59
60
|
ax.yaxis.set_major_formatter(FuncFormatter(lambda v, _: f"{v:,.0f}"))
|
|
60
61
|
ax.yaxis.set_major_locator(MaxNLocator(nbins=6))
|
|
61
62
|
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import matplotlib.pyplot as plt
|
|
2
|
+
import numpy as np
|
|
3
|
+
from matplotlib.ticker import FuncFormatter, MaxNLocator
|
|
4
|
+
from ..reg_modeling.reg import reg
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def residuals(
|
|
8
|
+
df,
|
|
9
|
+
y,
|
|
10
|
+
x,
|
|
11
|
+
dummies=None,
|
|
12
|
+
logistic=False,
|
|
13
|
+
decimals=1,
|
|
14
|
+
dpi=200,
|
|
15
|
+
save_dpi=300,
|
|
16
|
+
figsize=(7, 4.5),
|
|
17
|
+
save_path=None,
|
|
18
|
+
facecolor="white",
|
|
19
|
+
):
|
|
20
|
+
"""
|
|
21
|
+
Plot residuals vs fitted values to check linearity.
|
|
22
|
+
"""
|
|
23
|
+
results = reg(df, y, x, dummies=dummies, logistic=logistic, silent=True)
|
|
24
|
+
if results is None:
|
|
25
|
+
print("Error: Could not fit regression model")
|
|
26
|
+
return
|
|
27
|
+
|
|
28
|
+
fitted = np.asarray(results.fittedvalues, dtype=float)
|
|
29
|
+
resid = np.asarray(results.resid, dtype=float)
|
|
30
|
+
|
|
31
|
+
fig, ax = plt.subplots(figsize=figsize, dpi=dpi, facecolor=facecolor)
|
|
32
|
+
fig.patch.set_facecolor(facecolor)
|
|
33
|
+
ax.set_facecolor(facecolor)
|
|
34
|
+
|
|
35
|
+
ax.scatter(
|
|
36
|
+
fitted,
|
|
37
|
+
resid,
|
|
38
|
+
s=32,
|
|
39
|
+
c="#7eb6d9",
|
|
40
|
+
alpha=0.78,
|
|
41
|
+
edgecolors="#1f1f1f",
|
|
42
|
+
linewidths=0.45,
|
|
43
|
+
zorder=3,
|
|
44
|
+
)
|
|
45
|
+
ax.axhline(
|
|
46
|
+
0, color="#c0392b", linestyle="--", linewidth=1.8, zorder=4,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
fmt = FuncFormatter(lambda v, _: f"{v:,.{decimals}f}")
|
|
50
|
+
ax.grid(True, linestyle="--", linewidth=0.8, alpha=0.45)
|
|
51
|
+
ax.set_axisbelow(True)
|
|
52
|
+
ax.spines["top"].set_visible(False)
|
|
53
|
+
ax.spines["right"].set_visible(False)
|
|
54
|
+
ax.spines["left"].set_linewidth(1.2)
|
|
55
|
+
ax.spines["bottom"].set_linewidth(1.2)
|
|
56
|
+
ax.tick_params(labelsize=10, length=5, width=1.1)
|
|
57
|
+
ax.ticklabel_format(style="plain")
|
|
58
|
+
ax.xaxis.set_major_formatter(fmt)
|
|
59
|
+
ax.yaxis.set_major_formatter(fmt)
|
|
60
|
+
ax.xaxis.set_major_locator(MaxNLocator(nbins=6))
|
|
61
|
+
ax.yaxis.set_major_locator(MaxNLocator(nbins=6))
|
|
62
|
+
|
|
63
|
+
ax.set_title("Residuals vs Fitted Values", fontsize=13, pad=10)
|
|
64
|
+
ax.set_xlabel("Fitted Values", fontsize=11)
|
|
65
|
+
ax.set_ylabel("Residuals", fontsize=11)
|
|
66
|
+
|
|
67
|
+
fig.tight_layout()
|
|
68
|
+
if save_path is not None:
|
|
69
|
+
fig.savefig(
|
|
70
|
+
save_path, dpi=save_dpi, facecolor=facecolor,
|
|
71
|
+
bbox_inches="tight", pad_inches=0.15,
|
|
72
|
+
)
|
|
73
|
+
plt.show(block=False)
|
|
74
|
+
return ax
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import matplotlib.pyplot as plt
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from matplotlib.ticker import FuncFormatter, MaxNLocator
|
|
5
|
+
from mpl_toolkits.mplot3d import Axes3D # noqa: F401
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def scatter(
|
|
9
|
+
df,
|
|
10
|
+
y,
|
|
11
|
+
x,
|
|
12
|
+
fit_line=False,
|
|
13
|
+
decimals=1,
|
|
14
|
+
dpi=200,
|
|
15
|
+
save_dpi=300,
|
|
16
|
+
figsize=(7, 4.5),
|
|
17
|
+
save_path=None,
|
|
18
|
+
facecolor="white",
|
|
19
|
+
):
|
|
20
|
+
"""
|
|
21
|
+
Create a 2D or 3D scatter plot with optional fit line.
|
|
22
|
+
|
|
23
|
+
fit_line:
|
|
24
|
+
False — points only
|
|
25
|
+
"linear" — OLS of y on x (or a plane in 3D)
|
|
26
|
+
"quadratic" — OLS of y on x and x² (2D only)
|
|
27
|
+
"""
|
|
28
|
+
if isinstance(x, str):
|
|
29
|
+
x = [x]
|
|
30
|
+
|
|
31
|
+
if len(x) == 1:
|
|
32
|
+
ax = _scatter_2d(
|
|
33
|
+
df, y, x[0], fit_line=fit_line, decimals=decimals,
|
|
34
|
+
dpi=dpi, figsize=figsize, facecolor=facecolor,
|
|
35
|
+
)
|
|
36
|
+
else:
|
|
37
|
+
ax = _scatter_3d(
|
|
38
|
+
df, y, x[0], x[1], fit_line=fit_line, decimals=decimals,
|
|
39
|
+
dpi=dpi, figsize=figsize, facecolor=facecolor,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
fig = ax.figure
|
|
43
|
+
fig.tight_layout()
|
|
44
|
+
if save_path is not None:
|
|
45
|
+
fig.savefig(
|
|
46
|
+
save_path, dpi=save_dpi, facecolor=facecolor,
|
|
47
|
+
bbox_inches="tight", pad_inches=0.15,
|
|
48
|
+
)
|
|
49
|
+
plt.show(block=False)
|
|
50
|
+
return ax
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _fit_kind(fit_line):
|
|
54
|
+
if not fit_line:
|
|
55
|
+
return None
|
|
56
|
+
if fit_line == "linear":
|
|
57
|
+
return "linear"
|
|
58
|
+
if fit_line == "quadratic":
|
|
59
|
+
return "quadratic"
|
|
60
|
+
raise ValueError("fit_line must be False, 'linear', or 'quadratic'")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _style_2d(ax, decimals):
|
|
64
|
+
fmt = FuncFormatter(lambda v, _: f"{v:,.{decimals}f}")
|
|
65
|
+
ax.grid(True, linestyle="--", linewidth=0.8, alpha=0.45)
|
|
66
|
+
ax.set_axisbelow(True)
|
|
67
|
+
ax.spines["top"].set_visible(False)
|
|
68
|
+
ax.spines["right"].set_visible(False)
|
|
69
|
+
ax.spines["left"].set_linewidth(1.2)
|
|
70
|
+
ax.spines["bottom"].set_linewidth(1.2)
|
|
71
|
+
ax.tick_params(labelsize=10, length=5, width=1.1)
|
|
72
|
+
ax.ticklabel_format(style="plain")
|
|
73
|
+
ax.xaxis.set_major_formatter(fmt)
|
|
74
|
+
ax.yaxis.set_major_formatter(fmt)
|
|
75
|
+
ax.xaxis.set_major_locator(MaxNLocator(nbins=6))
|
|
76
|
+
ax.yaxis.set_major_locator(MaxNLocator(nbins=6))
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _scatter_2d(df, y, x_var, fit_line, decimals, dpi, figsize, facecolor):
|
|
80
|
+
plot_df = df[[x_var, y]].dropna()
|
|
81
|
+
if plot_df.empty:
|
|
82
|
+
raise ValueError("No data to plot after dropping NaNs.")
|
|
83
|
+
|
|
84
|
+
xv = plot_df[x_var].to_numpy(dtype=float)
|
|
85
|
+
yv = plot_df[y].to_numpy(dtype=float)
|
|
86
|
+
corr = float(pd.Series(xv).corr(pd.Series(yv)))
|
|
87
|
+
|
|
88
|
+
fig, ax = plt.subplots(figsize=figsize, dpi=dpi, facecolor=facecolor)
|
|
89
|
+
fig.patch.set_facecolor(facecolor)
|
|
90
|
+
ax.set_facecolor(facecolor)
|
|
91
|
+
|
|
92
|
+
ax.scatter(
|
|
93
|
+
xv,
|
|
94
|
+
yv,
|
|
95
|
+
s=32,
|
|
96
|
+
c="#7eb6d9",
|
|
97
|
+
alpha=0.78,
|
|
98
|
+
edgecolors="#1f1f1f",
|
|
99
|
+
linewidths=0.45,
|
|
100
|
+
zorder=3,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
kind = _fit_kind(fit_line)
|
|
104
|
+
if kind is not None and len(xv) >= (3 if kind == "quadratic" else 2):
|
|
105
|
+
degree = 2 if kind == "quadratic" else 1
|
|
106
|
+
coefs = np.polyfit(xv, yv, degree)
|
|
107
|
+
x_line = np.linspace(xv.min(), xv.max(), 200)
|
|
108
|
+
y_line = np.polyval(coefs, x_line)
|
|
109
|
+
ax.plot(
|
|
110
|
+
x_line,
|
|
111
|
+
y_line,
|
|
112
|
+
color="#c0392b",
|
|
113
|
+
linewidth=1.8,
|
|
114
|
+
zorder=4,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
_style_2d(ax, decimals)
|
|
118
|
+
ax.set_title(
|
|
119
|
+
f"{y} and {x_var}\nCorrelation: {corr:.2f}",
|
|
120
|
+
fontsize=13, pad=10,
|
|
121
|
+
)
|
|
122
|
+
ax.set_xlabel(x_var, fontsize=11)
|
|
123
|
+
ax.set_ylabel(y, fontsize=11)
|
|
124
|
+
return ax
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _scatter_3d(df, y, x_var, z_var, fit_line, decimals, dpi, figsize, facecolor):
|
|
128
|
+
kind = _fit_kind(fit_line)
|
|
129
|
+
if kind == "quadratic":
|
|
130
|
+
raise ValueError("fit_line='quadratic' is only available for 2D scatter plots.")
|
|
131
|
+
|
|
132
|
+
plot_df = df[[x_var, z_var, y]].dropna()
|
|
133
|
+
if plot_df.empty:
|
|
134
|
+
raise ValueError("No data to plot after dropping NaNs.")
|
|
135
|
+
|
|
136
|
+
fig = plt.figure(figsize=figsize, dpi=dpi, facecolor=facecolor)
|
|
137
|
+
fig.patch.set_facecolor(facecolor)
|
|
138
|
+
ax = fig.add_subplot(111, projection="3d", facecolor=facecolor)
|
|
139
|
+
|
|
140
|
+
ax.scatter(
|
|
141
|
+
plot_df[x_var],
|
|
142
|
+
plot_df[z_var],
|
|
143
|
+
plot_df[y],
|
|
144
|
+
s=32,
|
|
145
|
+
c="#7eb6d9",
|
|
146
|
+
alpha=0.78,
|
|
147
|
+
edgecolors="#1f1f1f",
|
|
148
|
+
linewidths=0.45,
|
|
149
|
+
depthshade=True,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
if kind == "linear":
|
|
153
|
+
X_data = np.column_stack(
|
|
154
|
+
[plot_df[x_var], plot_df[z_var], np.ones(len(plot_df))]
|
|
155
|
+
)
|
|
156
|
+
y_data = plot_df[y].to_numpy(dtype=float)
|
|
157
|
+
coeffs, *_ = np.linalg.lstsq(X_data, y_data, rcond=None)
|
|
158
|
+
a, b, c = coeffs
|
|
159
|
+
|
|
160
|
+
x_surf = np.linspace(plot_df[x_var].min(), plot_df[x_var].max(), 12)
|
|
161
|
+
z_surf = np.linspace(plot_df[z_var].min(), plot_df[z_var].max(), 12)
|
|
162
|
+
X_mesh, Z_mesh = np.meshgrid(x_surf, z_surf)
|
|
163
|
+
Y_mesh = a * X_mesh + b * Z_mesh + c
|
|
164
|
+
ax.plot_surface(
|
|
165
|
+
X_mesh,
|
|
166
|
+
Z_mesh,
|
|
167
|
+
Y_mesh,
|
|
168
|
+
color="#c0392b",
|
|
169
|
+
alpha=0.28,
|
|
170
|
+
edgecolor="none",
|
|
171
|
+
shade=False,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
fmt = FuncFormatter(lambda v, _: f"{v:,.{decimals}f}")
|
|
175
|
+
ax.xaxis.set_major_formatter(fmt)
|
|
176
|
+
ax.yaxis.set_major_formatter(fmt)
|
|
177
|
+
ax.zaxis.set_major_formatter(fmt)
|
|
178
|
+
|
|
179
|
+
ax.set_title(f"{y}, {x_var}, and {z_var}", fontsize=13, pad=10)
|
|
180
|
+
ax.set_xlabel(x_var, fontsize=11, labelpad=8)
|
|
181
|
+
ax.set_ylabel(z_var, fontsize=11, labelpad=8)
|
|
182
|
+
ax.set_zlabel(y, fontsize=11, labelpad=8)
|
|
183
|
+
ax.tick_params(labelsize=9, length=4, width=1.0)
|
|
184
|
+
|
|
185
|
+
ax.zaxis._axinfo["juggled"] = (1, 2, 0)
|
|
186
|
+
ax.invert_xaxis()
|
|
187
|
+
ax.invert_yaxis()
|
|
188
|
+
ax.view_init(elev=25, azim=135)
|
|
189
|
+
|
|
190
|
+
grid = (0.55, 0.55, 0.55, 0.35)
|
|
191
|
+
ax.xaxis._axinfo["grid"]["color"] = grid
|
|
192
|
+
ax.yaxis._axinfo["grid"]["color"] = grid
|
|
193
|
+
ax.zaxis._axinfo["grid"]["color"] = grid
|
|
194
|
+
return ax
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: davis_stats
|
|
3
|
-
Version: 2.2
|
|
3
|
+
Version: 2.2.2
|
|
4
4
|
Summary: davis_stats is a teaching-focused repository
|
|
5
5
|
Author: Justin G. Davis
|
|
6
6
|
Author-email:
|
|
7
7
|
Keywords: statistics,data science,business analytics
|
|
8
|
-
Requires-Python: >=3.
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
9
|
Requires-Dist: pandas>=3.0.6
|
|
10
10
|
Requires-Dist: openpyxl>=3.0.0
|
|
11
11
|
Requires-Dist: numpy>=1.20.0
|
|
12
12
|
Requires-Dist: matplotlib>=3.11.2
|
|
13
13
|
Requires-Dist: scipy>=1.6.0
|
|
14
|
-
Requires-Dist: seaborn>=0.12.0
|
|
15
14
|
Requires-Dist: statsmodels>=0.14.0
|
|
16
15
|
Requires-Dist: ipympl>=0.8.0
|
|
17
16
|
Requires-Dist: linearmodels>=7.0
|
|
@@ -19,6 +19,8 @@ davis_stats/datasets/restaurants.xlsx
|
|
|
19
19
|
davis_stats/datasets/sp1500_2025.xlsx
|
|
20
20
|
davis_stats/datasets/sp1500_panel.xlsx
|
|
21
21
|
davis_stats/datasets/student_athletes.xlsx
|
|
22
|
+
davis_stats/datasets/sweet_things.xlsx
|
|
23
|
+
davis_stats/datasets/sweet_things_simple.xlsx
|
|
22
24
|
davis_stats/datasets/world_cup_goals.xlsx
|
|
23
25
|
davis_stats/practice/__init__.py
|
|
24
26
|
davis_stats/practice/stats_practice.py
|
|
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|
|
2
2
|
|
|
3
3
|
setup(
|
|
4
4
|
name='davis_stats',
|
|
5
|
-
version='2.2',
|
|
5
|
+
version='2.2.2',
|
|
6
6
|
packages=find_packages(),
|
|
7
7
|
package_data={
|
|
8
8
|
'davis_stats': ['datasets/*.xlsx']},
|
|
@@ -12,11 +12,10 @@ setup(
|
|
|
12
12
|
'numpy>=1.20.0',
|
|
13
13
|
'matplotlib>=3.11.2',
|
|
14
14
|
'scipy>=1.6.0',
|
|
15
|
-
'seaborn>=0.12.0',
|
|
16
15
|
'statsmodels>=0.14.0',
|
|
17
16
|
'ipympl>=0.8.0',
|
|
18
17
|
'linearmodels>=7.0'],
|
|
19
|
-
python_requires='>=3.
|
|
18
|
+
python_requires='>=3.11',
|
|
20
19
|
author='Justin G. Davis',
|
|
21
20
|
author_email='',
|
|
22
21
|
description='''
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import matplotlib.pyplot as plt
|
|
2
|
-
from ..reg_modeling.reg import reg
|
|
3
|
-
|
|
4
|
-
def residuals(df, y, x, dummies=None, logistic=False, dpi=150, figsize=(6, 4)):
|
|
5
|
-
"""
|
|
6
|
-
Plot residuals vs fitted values to check linearity.
|
|
7
|
-
"""
|
|
8
|
-
results = reg(df, y, x, dummies=dummies, logistic=logistic, silent=True)
|
|
9
|
-
|
|
10
|
-
if results is None:
|
|
11
|
-
print("Error: Could not fit regression model")
|
|
12
|
-
return
|
|
13
|
-
|
|
14
|
-
residuals = results.resid
|
|
15
|
-
fitted = results.fittedvalues
|
|
16
|
-
|
|
17
|
-
plt.figure(figsize=figsize, dpi=dpi)
|
|
18
|
-
plt.scatter(fitted, residuals, alpha=0.5, edgecolors='k', linewidth=0.5)
|
|
19
|
-
plt.axhline(y=0, color='r', linestyle='--', linewidth=2)
|
|
20
|
-
plt.xlabel('Fitted Values', fontsize=12)
|
|
21
|
-
plt.ylabel('Residuals', fontsize=12)
|
|
22
|
-
plt.title('Residuals vs Fitted Values', fontsize=14, fontweight='bold')
|
|
23
|
-
plt.grid(True, alpha=0.3)
|
|
24
|
-
plt.tight_layout()
|
|
25
|
-
plt.show()
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import seaborn as sns
|
|
2
|
-
import matplotlib.pyplot as plt
|
|
3
|
-
import numpy as np
|
|
4
|
-
from mpl_toolkits.mplot3d import Axes3D
|
|
5
|
-
|
|
6
|
-
def scatter(df, y, x, fit_line=False, dpi=150, figsize=(6, 4)):
|
|
7
|
-
"""
|
|
8
|
-
Create a 2D or 3D scatter plot with optional fit line.
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
# Convert x to list if string
|
|
12
|
-
if isinstance(x, str):
|
|
13
|
-
x = [x]
|
|
14
|
-
|
|
15
|
-
# Determine plot type based on number of x variables
|
|
16
|
-
if len(x) == 1:
|
|
17
|
-
# 2D scatter plot
|
|
18
|
-
x_var = x[0]
|
|
19
|
-
|
|
20
|
-
# Calculate correlation coefficient
|
|
21
|
-
corr = df[x_var].corr(df[y])
|
|
22
|
-
|
|
23
|
-
# Set style
|
|
24
|
-
sns.set_style("whitegrid")
|
|
25
|
-
|
|
26
|
-
# Create figure
|
|
27
|
-
fig, ax = plt.subplots(figsize=figsize, dpi=dpi)
|
|
28
|
-
|
|
29
|
-
# Create scatter plot
|
|
30
|
-
if fit_line:
|
|
31
|
-
sns.regplot(data=df,
|
|
32
|
-
x=x_var,
|
|
33
|
-
y=y,
|
|
34
|
-
scatter_kws={'alpha':0.5},
|
|
35
|
-
line_kws={'color': 'red'},
|
|
36
|
-
ci=None)
|
|
37
|
-
else:
|
|
38
|
-
sns.scatterplot(data=df,
|
|
39
|
-
x=x_var,
|
|
40
|
-
y=y,
|
|
41
|
-
alpha=0.5)
|
|
42
|
-
|
|
43
|
-
# Customize plot
|
|
44
|
-
plt.title(f'{y} and {x_var}\nCorrelation: {corr:.3f}', pad=15)
|
|
45
|
-
plt.xlabel(x_var)
|
|
46
|
-
plt.ylabel(y)
|
|
47
|
-
|
|
48
|
-
# Adjust layout
|
|
49
|
-
plt.tight_layout()
|
|
50
|
-
|
|
51
|
-
else:
|
|
52
|
-
# 3D scatter plot (use first 2 variables from x list)
|
|
53
|
-
x_var = x[0]
|
|
54
|
-
z_var = x[1]
|
|
55
|
-
|
|
56
|
-
# Create 3D figure
|
|
57
|
-
fig = plt.figure(figsize=figsize, dpi=dpi)
|
|
58
|
-
ax = fig.add_subplot(111, projection='3d')
|
|
59
|
-
|
|
60
|
-
# Remove NaN values for plotting
|
|
61
|
-
plot_df = df[[x_var, z_var, y]].dropna()
|
|
62
|
-
|
|
63
|
-
# Create 3D scatter plot
|
|
64
|
-
scatter = ax.scatter(plot_df[x_var], plot_df[z_var], plot_df[y],
|
|
65
|
-
c=plot_df[y], cmap='RdYlBu_r', s=50,
|
|
66
|
-
edgecolor='black', linewidth=0.5,
|
|
67
|
-
alpha=0.8)
|
|
68
|
-
|
|
69
|
-
# Add colorbar
|
|
70
|
-
cbar = plt.colorbar(scatter, ax=ax, pad=0.15, shrink=0.8)
|
|
71
|
-
cbar.set_label(y, rotation=270, labelpad=15)
|
|
72
|
-
|
|
73
|
-
# Add best-fit plane if requested
|
|
74
|
-
if fit_line:
|
|
75
|
-
# Prepare data for plane fitting: y = a*x + b*z + c
|
|
76
|
-
X_data = np.column_stack([plot_df[x_var], plot_df[z_var], np.ones(len(plot_df))])
|
|
77
|
-
y_data = plot_df[y].values
|
|
78
|
-
|
|
79
|
-
# Fit plane using least squares
|
|
80
|
-
coeffs, residuals, rank, s = np.linalg.lstsq(X_data, y_data, rcond=None)
|
|
81
|
-
a, b, c = coeffs
|
|
82
|
-
|
|
83
|
-
# Create mesh grid for the regression plane
|
|
84
|
-
x_surf = np.linspace(plot_df[x_var].min(), plot_df[x_var].max(), 20)
|
|
85
|
-
z_surf = np.linspace(plot_df[z_var].min(), plot_df[z_var].max(), 20)
|
|
86
|
-
X_mesh, Z_mesh = np.meshgrid(x_surf, z_surf)
|
|
87
|
-
|
|
88
|
-
# Calculate y values for the plane
|
|
89
|
-
Y_mesh = a * X_mesh + b * Z_mesh + c
|
|
90
|
-
|
|
91
|
-
# Plot the regression plane
|
|
92
|
-
surf = ax.plot_surface(X_mesh, Z_mesh, Y_mesh,
|
|
93
|
-
alpha=0.4, cmap='coolwarm',
|
|
94
|
-
edgecolor='black', linewidth=0.5,
|
|
95
|
-
rstride=1, cstride=1,
|
|
96
|
-
antialiased=True)
|
|
97
|
-
|
|
98
|
-
# Set title
|
|
99
|
-
title = f'{y}, {x_var}, and {z_var}'
|
|
100
|
-
|
|
101
|
-
# Set axis labels
|
|
102
|
-
ax.set_xlabel(x_var, labelpad=10)
|
|
103
|
-
ax.set_ylabel(z_var, labelpad=10)
|
|
104
|
-
ax.set_zlabel(y, labelpad=10)
|
|
105
|
-
|
|
106
|
-
# Move z-axis to left side
|
|
107
|
-
ax.zaxis._axinfo['juggled'] = (1, 2, 0)
|
|
108
|
-
|
|
109
|
-
# Flip axes
|
|
110
|
-
ax.invert_xaxis()
|
|
111
|
-
ax.invert_yaxis()
|
|
112
|
-
|
|
113
|
-
# Adjust viewing angle
|
|
114
|
-
ax.view_init(elev=25, azim=135)
|
|
115
|
-
|
|
116
|
-
# Grid styling
|
|
117
|
-
ax.xaxis._axinfo["grid"]['color'] = (0.5, 0.5, 0.5, 0.5)
|
|
118
|
-
ax.yaxis._axinfo["grid"]['color'] = (0.5, 0.5, 0.5, 0.5)
|
|
119
|
-
ax.zaxis._axinfo["grid"]['color'] = (0.5, 0.5, 0.5, 0.5)
|
|
120
|
-
|
|
121
|
-
# Set title
|
|
122
|
-
ax.set_title(title, pad=15)
|
|
123
|
-
|
|
124
|
-
# Adjust layout
|
|
125
|
-
plt.tight_layout()
|
|
126
|
-
|
|
127
|
-
# Show plot
|
|
128
|
-
plt.show()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|