pystatsv1 0.22.2__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.
pystatsv1/__init__.py ADDED
@@ -0,0 +1,36 @@
1
+ """
2
+ Top-level package for the PyStatsV1 project.
3
+
4
+ Convenience re-exports:
5
+
6
+ - DATA_DIR, SYNTHETIC_DATA_DIR
7
+ - OUTPUTS_DIR, TRACK_C_OUTPUT_DIR
8
+ - PROJECT_ROOT
9
+ """
10
+
11
+ from importlib.metadata import PackageNotFoundError, version
12
+ from .docs_helper import get_local_docs_path, open_local_docs
13
+
14
+ from .paths import ( # noqa: F401
15
+ DATA_DIR,
16
+ SYNTHETIC_DATA_DIR,
17
+ OUTPUTS_DIR,
18
+ TRACK_C_OUTPUT_DIR,
19
+ PROJECT_ROOT,
20
+ )
21
+
22
+ __all__ = [
23
+ "DATA_DIR",
24
+ "SYNTHETIC_DATA_DIR",
25
+ "OUTPUTS_DIR",
26
+ "TRACK_C_OUTPUT_DIR",
27
+ "PROJECT_ROOT",
28
+ "__version__",
29
+ "get_local_docs_path",
30
+ "open_local_docs",
31
+ ]
32
+
33
+ try:
34
+ __version__ = version("pystatsv1")
35
+ except PackageNotFoundError: # pragma: no cover
36
+ __version__ = "0.0.0"
pystatsv1/__main__.py ADDED
@@ -0,0 +1,7 @@
1
+ from __future__ import annotations
2
+
3
+ from .cli import main
4
+
5
+
6
+ if __name__ == "__main__":
7
+ raise SystemExit(main())
@@ -0,0 +1 @@
1
+ """Packaged assets used by CLI helpers (workbook starter kit, etc.)."""
Binary file
Binary file
Binary file
pystatsv1/cli.py ADDED
@@ -0,0 +1,501 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import os
5
+ import platform
6
+ import re
7
+ import subprocess
8
+ import sys
9
+ import textwrap
10
+ import zipfile
11
+ from importlib import metadata, resources
12
+ from pathlib import Path
13
+ from typing import Final
14
+
15
+
16
+ PKG: Final[str] = "pystatsv1"
17
+
18
+
19
+ def _in_venv() -> bool:
20
+ # Works for venv and virtualenv.
21
+ return getattr(sys, "base_prefix", sys.prefix) != sys.prefix
22
+
23
+
24
+ def _open_file(path: Path) -> None:
25
+ # Cross-platform "open in default app"
26
+ if sys.platform.startswith("win"):
27
+ os.startfile(str(path)) # type: ignore[attr-defined]
28
+ return
29
+ if sys.platform == "darwin":
30
+ os.execvp("open", ["open", str(path)])
31
+ else:
32
+ os.execvp("xdg-open", ["xdg-open", str(path)])
33
+
34
+
35
+ def _get_packaged_pdf() -> Path | None:
36
+ # Packaged as pystatsv1/docs/pystatsv1.pdf
37
+ try:
38
+ candidate = resources.files(PKG) / "docs" / "pystatsv1.pdf"
39
+ with resources.as_file(candidate) as p:
40
+ return Path(p)
41
+ except Exception:
42
+ return None
43
+
44
+
45
+ def cmd_docs(_: argparse.Namespace) -> int:
46
+ pdf = _get_packaged_pdf()
47
+ if pdf is None or not pdf.exists():
48
+ print("Local docs PDF not found in this installation.")
49
+ return 1
50
+
51
+ print(f"Opening: {pdf}")
52
+ _open_file(pdf)
53
+ return 0
54
+
55
+
56
+ def _normalize_track(track: str | None) -> str:
57
+ t = (track or "c").strip().lower()
58
+ if t in {"c", "track_c"}:
59
+ return "c"
60
+ if t in {"d", "track_d"}:
61
+ return "d"
62
+ raise SystemExit(
63
+ "Unknown track. Use one of: c, track_c, d, track_d.\n"
64
+ "Example: pystatsv1 workbook init --track d"
65
+ )
66
+
67
+
68
+ def _workbook_asset_for_track(track: str) -> str:
69
+ t = _normalize_track(track)
70
+ return {
71
+ "c": "workbook_starter.zip",
72
+ "d": "workbook_track_d.zip",
73
+ }[t]
74
+
75
+
76
+ def _extract_workbook_template(dest: Path, force: bool, track: str = "c") -> None:
77
+ dest = dest.expanduser().resolve()
78
+
79
+ if dest.exists():
80
+ if any(dest.iterdir()) and not force:
81
+ raise SystemExit(
82
+ f"Refusing to write into non-empty directory: {dest}\n"
83
+ "Use --force to overwrite into an existing directory."
84
+ )
85
+ else:
86
+ dest.mkdir(parents=True, exist_ok=True)
87
+
88
+ asset_name = _workbook_asset_for_track(track)
89
+ asset = resources.files(f"{PKG}.assets") / asset_name
90
+ with resources.as_file(asset) as asset_path:
91
+ with zipfile.ZipFile(asset_path) as zf:
92
+ zf.extractall(dest)
93
+
94
+
95
+ def _extract_asset_zip(asset_name: str, dest: Path) -> None:
96
+ asset = resources.files(f"{PKG}.assets") / asset_name
97
+ with resources.as_file(asset) as asset_path:
98
+ with zipfile.ZipFile(asset_path) as zf:
99
+ zf.extractall(dest)
100
+
101
+
102
+ def _extract_track_d_datasets(dest: Path) -> None:
103
+ # Extract canonical Track D datasets (seed=123) into the workbook folder.
104
+ ds_root = dest / "data" / "synthetic"
105
+ ds_root.mkdir(parents=True, exist_ok=True)
106
+
107
+ for asset_name in (
108
+ "ledgerlab_ch01_seed123.zip",
109
+ "nso_v1_seed123.zip",
110
+ ):
111
+ try:
112
+ _extract_asset_zip(asset_name, ds_root)
113
+ except Exception as e:
114
+ raise SystemExit(
115
+ "Failed to extract Track D canonical datasets. "
116
+ "Try upgrading PyStatsV1 (pip install -U pystatsv1) and re-run workbook init. "
117
+ f"Missing or unreadable asset: {asset_name}"
118
+ ) from e
119
+
120
+
121
+ def cmd_workbook_init(args: argparse.Namespace) -> int:
122
+ track = _normalize_track(getattr(args, "track", "c"))
123
+ _extract_workbook_template(Path(args.dest), force=args.force, track=track)
124
+
125
+ dest = Path(args.dest).expanduser().resolve()
126
+ if track == "d":
127
+ _extract_track_d_datasets(dest)
128
+ next_steps = textwrap.dedent(
129
+ f"""\
130
+ ✅ Track D workbook starter created at:
131
+
132
+ {dest}
133
+
134
+ Next steps:
135
+ 1) cd {dest}
136
+ 2) pystatsv1 workbook run d00_peek_data
137
+
138
+ (Datasets are pre-installed under data/synthetic/, seed=123.)
139
+
140
+ Tip: If you're new to Python, always work inside a virtual environment.
141
+ """
142
+ ).rstrip()
143
+ print(next_steps)
144
+ return 0
145
+
146
+ print(
147
+ textwrap.dedent(
148
+ f"""\
149
+ ✅ Workbook starter created at:
150
+
151
+ {dest}
152
+
153
+ Next steps:
154
+ 1) cd {dest}
155
+ 2) pystatsv1 workbook run ch10
156
+ 3) pystatsv1 workbook check ch10
157
+
158
+ Tip: If you're new to Python, always work inside a virtual environment.
159
+ """
160
+ ).rstrip()
161
+ )
162
+ return 0
163
+
164
+
165
+ def cmd_workbook_list(args: argparse.Namespace) -> int:
166
+ track = _normalize_track(getattr(args, "track", "c"))
167
+
168
+ if track == "d":
169
+ chapters = [
170
+ "D00 Setup/reset Track D datasets (run: d00_setup_data)",
171
+ "D00 Peek the Track D datasets (LedgerLab + NSO) (run: d00_peek_data)",
172
+ "D01 Ch01 Accounting as a measurement system (run: d01)",
173
+ "D02 Ch02 Double-entry and the general ledger as a database (run: d02)",
174
+ "D03 Ch03 Financial statements as summaries (run: d03)",
175
+ "D04 Ch04 Assets: inventory + fixed assets (run: d04)",
176
+ "D05 Ch05 Liabilities, payroll, taxes, and equity (run: d05)",
177
+ "D06 Ch06 Reconciliations and quality control (run: d06)",
178
+ "D07 Ch07 Preparing accounting data for analysis (run: d07)",
179
+ "D08 Ch08 Descriptive statistics for financial performance (run: d08)",
180
+ "D09 Ch09 Reporting style contract (run: d09)",
181
+ "D10 Ch10 Probability and risk (run: d10)",
182
+ "D11 Ch11 Sampling, estimation, and audit controls (run: d11)",
183
+ "D12 Ch12 Hypothesis testing for decisions (run: d12)",
184
+ "D13 Ch13 Correlation, causation, and controlled comparisons (run: d13)",
185
+ "D14 Ch14 Regression and driver analysis (run: d14)",
186
+ "D15 Ch15 Forecasting foundations (run: d15)",
187
+ "D16 Ch16 Seasonality and baselines (run: d16)",
188
+ "D17 Ch17 Revenue forecasting: segmentation + drivers (run: d17)",
189
+ "D18 Ch18 Expense forecasting: fixed/variable/step + payroll (run: d18)",
190
+ "D19 Ch19 Cash flow forecasting: direct method (13-week) (run: d19)",
191
+ "D20 Ch20 Integrated forecasting: three statements (run: d20)",
192
+ "D21 Ch21 Scenario planning: sensitivity + stress (run: d21)",
193
+ "D22 Ch22 Financial statement analysis toolkit (run: d22)",
194
+ "D23 Ch23 Communicating results and governance (run: d23)",
195
+ ]
196
+ print("\n".join(chapters))
197
+ return 0
198
+
199
+ # Track C (default): bundled in the starter zip.
200
+ chapters = [
201
+ "Ch10 One-way ANOVA",
202
+ "Ch11 Repeated measures / mixed designs (problem set)",
203
+ "Ch12 Factorial designs",
204
+ "Ch14 Nonparametric tests",
205
+ "Ch15 Correlation & simple regression",
206
+ "Ch16 Multiple regression",
207
+ "Ch17 Logistic regression",
208
+ "Ch18 ANCOVA",
209
+ "Ch19 Mediation / moderation (intro)",
210
+ "Ch20 Power / simulation (intro)",
211
+ ]
212
+ print("\n".join(chapters))
213
+ return 0
214
+
215
+
216
+ def _dist_version(dist_name: str) -> str:
217
+ try:
218
+ return metadata.version(dist_name)
219
+ except metadata.PackageNotFoundError:
220
+ return "not installed"
221
+ except Exception:
222
+ return "unknown"
223
+
224
+
225
+
226
+ def _workdir(args: argparse.Namespace) -> Path:
227
+ return Path(getattr(args, "workdir", ".")).expanduser().resolve()
228
+
229
+
230
+ _CHAPTER_RE: Final[re.Pattern[str]] = re.compile(r"^ch(?P<num>\d+)$", re.IGNORECASE)
231
+
232
+
233
+ def _chapter_num(key: str) -> int | None:
234
+ m = _CHAPTER_RE.match(key.strip())
235
+ if not m:
236
+ return None
237
+ return int(m.group("num"))
238
+
239
+
240
+ def _resolve_script_path(workdir: Path, target: str) -> Path:
241
+ target = target.strip()
242
+
243
+ # Explicit path?
244
+ if target.endswith(".py") or "/" in target or "\\" in target:
245
+ p = Path(target)
246
+ if not p.is_absolute():
247
+ p = workdir / p
248
+ return p
249
+
250
+ ch = _chapter_num(target)
251
+ if ch is not None:
252
+ return workdir / "scripts" / f"psych_ch{ch:02d}_problem_set.py"
253
+
254
+ # Default: treat as a script key under scripts/
255
+ return workdir / "scripts" / f"{target}.py"
256
+
257
+
258
+ def _resolve_test_path(workdir: Path, target: str) -> Path:
259
+ target = target.strip()
260
+
261
+ ch = _chapter_num(target)
262
+ if ch is not None:
263
+ return workdir / "tests" / f"test_psych_ch{ch:02d}_problem_set.py"
264
+
265
+ # Try a few conventions for non-chapter keys.
266
+ candidates = [
267
+ workdir / "tests" / f"test_{target}.py",
268
+ workdir / "tests" / f"test_{target}_case_study.py",
269
+ workdir / "tests" / f"test_{target}_problem_set.py",
270
+ ]
271
+ for c in candidates:
272
+ if c.exists():
273
+ return c
274
+ # If none exist, still return the first candidate for a helpful error path.
275
+ return candidates[0]
276
+
277
+
278
+ def cmd_workbook_run(args: argparse.Namespace) -> int:
279
+ workdir = _workdir(args)
280
+ script = _resolve_script_path(workdir, args.target)
281
+
282
+ if not script.exists():
283
+ print(
284
+ "❌ Could not find the script to run.\n"
285
+ f" Looking for: {script}\n\n"
286
+ "Tip: run this inside your workbook folder (created by `pystatsv1 workbook init`).\n"
287
+ " Or pass --workdir to point at it."
288
+ )
289
+ return 2
290
+
291
+ # Ensure workbook-root imports like `import scripts.foo` work reliably.
292
+ # Many workbook scripts use `from scripts...` imports; adding the workbook
293
+ # directory to PYTHONPATH makes this consistent across platforms.
294
+ env = os.environ.copy()
295
+ existing = env.get("PYTHONPATH", "")
296
+ prefix = str(workdir)
297
+ env["PYTHONPATH"] = (
298
+ f"{prefix}{os.pathsep}{existing}" if existing else prefix
299
+ )
300
+
301
+ cmd = [sys.executable, str(script)]
302
+ try:
303
+ subprocess.run(cmd, cwd=str(workdir), env=env, check=True)
304
+ except subprocess.CalledProcessError as e:
305
+ return int(e.returncode or 1)
306
+ return 0
307
+
308
+
309
+ def cmd_workbook_check(args: argparse.Namespace) -> int:
310
+ workdir = _workdir(args)
311
+ test_file = _resolve_test_path(workdir, args.target)
312
+
313
+ if not test_file.exists():
314
+ print(
315
+ "❌ Could not find the test file to run.\n"
316
+ f" Looking for: {test_file}\n\n"
317
+ "Tip: run this inside your workbook folder (created by `pystatsv1 workbook init`).\n"
318
+ " Or pass --workdir to point at it."
319
+ )
320
+ return 2
321
+
322
+ # Match `workbook run`: keep workbook-root import resolution consistent.
323
+ env = os.environ.copy()
324
+ existing = env.get("PYTHONPATH", "")
325
+ prefix = str(workdir)
326
+ env["PYTHONPATH"] = f"{prefix}{os.pathsep}{existing}" if existing else prefix
327
+
328
+ cmd = [sys.executable, "-m", "pytest", "-q", str(test_file)]
329
+ try:
330
+ subprocess.run(cmd, cwd=str(workdir), env=env, check=True)
331
+ except subprocess.CalledProcessError as e:
332
+ return int(e.returncode or 1)
333
+ return 0
334
+
335
+ _REQUIRED_IMPORTS: Final[list[tuple[str, str]]] = [
336
+ ("numpy", "numpy"),
337
+ ("pandas", "pandas"),
338
+ ("scipy", "scipy"),
339
+ ("statsmodels", "statsmodels"),
340
+ ("matplotlib", "matplotlib"),
341
+ ("pingouin", "pingouin"),
342
+ ("sklearn", "scikit-learn"),
343
+ ]
344
+
345
+
346
+ def cmd_doctor(args: argparse.Namespace) -> int:
347
+ ok = True
348
+
349
+ in_venv = _in_venv()
350
+ if not in_venv:
351
+ print(
352
+ "⚠️ You are NOT in a virtual environment. This is OK, but not recommended.\n"
353
+ "Create one and activate it first:\n"
354
+ " python -m venv .venv\n"
355
+ " source .venv/Scripts/activate # Windows Git Bash\n"
356
+ " source .venv/bin/activate # macOS/Linux\n"
357
+ )
358
+
359
+ if getattr(args, "verbose", False):
360
+ print("Environment")
361
+ print(f" python: {sys.version.splitlines()[0]}")
362
+ print(f" executable: {sys.executable}")
363
+ print(f" platform: {platform.platform()}")
364
+ print(f" venv: {'yes' if in_venv else 'no'}")
365
+ print("\nPackages")
366
+
367
+ missing: list[str] = []
368
+
369
+ # Lightweight import checks (avoid crashing if optional deps are missing)
370
+ for mod, dist in _REQUIRED_IMPORTS:
371
+ try:
372
+ __import__(mod)
373
+ except Exception:
374
+ missing.append(mod)
375
+
376
+ if getattr(args, "verbose", False):
377
+ print(f" - {mod}: {_dist_version(dist)}")
378
+
379
+ if missing:
380
+ ok = False
381
+ print(
382
+ "\n❌ Missing packages in this environment:\n - "
383
+ + "\n - ".join(missing)
384
+ + "\n\nInstall the student bundle:\n"
385
+ " python -m pip install -U pip\n"
386
+ " python -m pip install \"pystatsv1[workbook]\"\n"
387
+ )
388
+
389
+ if ok:
390
+ if in_venv:
391
+ print("✅ Environment looks good.")
392
+ else:
393
+ print("✅ Packages look good (consider using a venv).")
394
+ return 0
395
+
396
+ return 1
397
+
398
+
399
+ def build_parser() -> argparse.ArgumentParser:
400
+ p = argparse.ArgumentParser(
401
+ prog="pystatsv1",
402
+ description="PyStatsV1: treat statistical analysis like production software.",
403
+ )
404
+ p.add_argument(
405
+ "--version",
406
+ action="version",
407
+ version=_version_string(),
408
+ )
409
+
410
+ sub = p.add_subparsers(dest="cmd", required=True)
411
+
412
+ p_docs = sub.add_parser("docs", help="Open packaged PDF docs (if available).")
413
+ p_docs.set_defaults(func=cmd_docs)
414
+
415
+ p_doctor = sub.add_parser("doctor", help="Sanity-check your environment (venv + deps).")
416
+ p_doctor.add_argument(
417
+ "--verbose",
418
+ action="store_true",
419
+ help="Print Python/platform info and package versions.",
420
+ )
421
+ p_doctor.set_defaults(func=cmd_doctor)
422
+
423
+ p_wb = sub.add_parser("workbook", help="Workbook helpers (student labs).")
424
+ wb_sub = p_wb.add_subparsers(dest="workbook_cmd", required=True)
425
+
426
+ p_init = wb_sub.add_parser("init", help="Create a local workbook starter folder.")
427
+ p_init.add_argument(
428
+ "--track",
429
+ default="c",
430
+ help="Which workbook to create: c (intro/psych) or d (business case). Default: c.",
431
+ )
432
+ p_init.add_argument(
433
+ "--dest",
434
+ default="pystatsv1_workbook",
435
+ help="Destination directory to create (default: pystatsv1_workbook).",
436
+ )
437
+ p_init.add_argument(
438
+ "--force",
439
+ action="store_true",
440
+ help="Allow writing into an existing non-empty directory.",
441
+ )
442
+ p_init.set_defaults(func=cmd_workbook_init)
443
+
444
+ p_list = wb_sub.add_parser("list", help="List chapters included in a starter kit.")
445
+ p_list.add_argument(
446
+ "--track",
447
+ default="c",
448
+ help="Which chapter list to show: c (intro/psych) or d (business case). Default: c.",
449
+ )
450
+ p_list.set_defaults(func=cmd_workbook_list)
451
+
452
+ p_run = wb_sub.add_parser("run", help="Run a workbook script (no make required).")
453
+ p_run.add_argument(
454
+ "target",
455
+ help="Target to run: chapter key like 'ch10', a script key like 'study_habits_01_explore', or a .py path.",
456
+ )
457
+ p_run.add_argument(
458
+ "--workdir",
459
+ default=".",
460
+ help="Workbook directory (default: current directory).",
461
+ )
462
+ p_run.set_defaults(func=cmd_workbook_run)
463
+
464
+ p_check = wb_sub.add_parser("check", help="Run workbook checks/tests via pytest (no make required).")
465
+ p_check.add_argument(
466
+ "target",
467
+ help="Target to check: chapter key like 'ch10' or a key that resolves to tests/test_<key>*.py.",
468
+ )
469
+ p_check.add_argument(
470
+ "--workdir",
471
+ default=".",
472
+ help="Workbook directory (default: current directory).",
473
+ )
474
+ p_check.set_defaults(func=cmd_workbook_check)
475
+
476
+
477
+ return p
478
+
479
+
480
+ def _version_string() -> str:
481
+ try:
482
+ from importlib.metadata import version
483
+
484
+ return version(PKG)
485
+ except Exception:
486
+ return "unknown"
487
+
488
+
489
+ def main(argv: list[str] | None = None) -> int:
490
+ parser = build_parser()
491
+ args = parser.parse_args(argv)
492
+ return int(args.func(args))
493
+
494
+
495
+ # Back-compat: allow a dedicated console script for docs if desired.
496
+ def docs_main(argv: list[str] | None = None) -> int:
497
+ return main(["docs"] + ([] if argv is None else argv))
498
+
499
+
500
+ if __name__ == "__main__":
501
+ raise SystemExit(main())
Binary file
@@ -0,0 +1,29 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import sys
5
+ from pathlib import Path
6
+
7
+
8
+ def get_local_docs_path() -> Path:
9
+ """Return the absolute path to the packaged PDF (pystatsv1/docs/pystatsv1.pdf)."""
10
+ # Resolve relative to this module, which is stable in editable installs and wheels.
11
+ pkg_dir = Path(__file__).resolve().parent
12
+ pdf = pkg_dir / "docs" / "pystatsv1.pdf"
13
+ return pdf
14
+
15
+
16
+ def open_local_docs() -> Path:
17
+ """Open the packaged PDF in the system default viewer and return the path."""
18
+ pdf = get_local_docs_path()
19
+ if not pdf.exists():
20
+ raise FileNotFoundError(f"Local docs PDF not found: {pdf}")
21
+
22
+ if sys.platform.startswith("win"):
23
+ os.startfile(str(pdf)) # type: ignore[attr-defined]
24
+ elif sys.platform == "darwin":
25
+ os.system(f'open "{pdf}"')
26
+ else:
27
+ os.system(f'xdg-open "{pdf}"')
28
+
29
+ return pdf
pystatsv1/paths.py ADDED
@@ -0,0 +1,44 @@
1
+ """
2
+ Centralized filesystem paths for the PyStatsV1 project.
3
+
4
+ Repo layout:
5
+
6
+ PyStatsV1/
7
+ data/
8
+ synthetic/
9
+ outputs/
10
+ track_c/
11
+ docs/
12
+ scripts/
13
+ src/pystatsv1/...
14
+
15
+ This module exposes a small set of reusable Path objects so scripts and
16
+ tests don't have to duplicate logic for finding the project root.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from pathlib import Path
22
+
23
+ # src/pystatsv1/paths.py -> .../src/pystatsv1 -> .../src -> .../PyStatsV1
24
+ PACKAGE_ROOT = Path(__file__).resolve().parent
25
+ PROJECT_ROOT = PACKAGE_ROOT.parents[1]
26
+
27
+ # Data directories
28
+ DATA_DIR = PROJECT_ROOT / "data"
29
+ SYNTHETIC_DATA_DIR = DATA_DIR / "synthetic"
30
+
31
+ # Outputs (top-level and Track C specific)
32
+ OUTPUTS_DIR = PROJECT_ROOT / "outputs"
33
+ TRACK_C_OUTPUT_DIR = OUTPUTS_DIR / "track_c"
34
+
35
+
36
+ def ensure_dir(path: Path) -> Path:
37
+ """Ensure that ``path`` exists (mkdir -p) and return it."""
38
+ path.mkdir(parents=True, exist_ok=True)
39
+ return path
40
+
41
+
42
+ def ensure_output_subdir(name: str) -> Path:
43
+ """Ensure that a subdirectory of OUTPUTS_DIR exists and return it."""
44
+ return ensure_dir(OUTPUTS_DIR / name)
@@ -0,0 +1,329 @@
1
+ Metadata-Version: 2.4
2
+ Name: pystatsv1
3
+ Version: 0.22.2
4
+ Summary: PyStatsV1: applied statistics labs in Python.
5
+ Author: PyStatsV1 contributors
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/pystatsv1/PyStatsV1
8
+ Project-URL: Documentation, https://pystatsv1.readthedocs.io
9
+ Project-URL: Source, https://github.com/pystatsv1/PyStatsV1
10
+ Project-URL: Issues, https://github.com/pystatsv1/PyStatsV1/issues
11
+ Project-URL: PyPI, https://pypi.org/project/pystatsv1/
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: numpy>=1.24
16
+ Requires-Dist: pandas>=2.0
17
+ Requires-Dist: scipy>=1.10
18
+ Requires-Dist: statsmodels>=0.14
19
+ Requires-Dist: matplotlib>=3.8
20
+ Requires-Dist: pingouin>=0.5
21
+ Requires-Dist: scikit-learn>=1.3
22
+ Provides-Extra: workbook
23
+ Requires-Dist: pytest>=8.2; extra == "workbook"
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=8.2; extra == "dev"
26
+ Requires-Dist: ruff>=0.6; extra == "dev"
27
+ Requires-Dist: black>=24.0; extra == "dev"
28
+ Provides-Extra: docs
29
+ Requires-Dist: sphinx>=8.0; extra == "docs"
30
+ Requires-Dist: sphinx-rtd-theme>=2.0; extra == "docs"
31
+ Requires-Dist: myst-parser>=3.0; extra == "docs"
32
+ Dynamic: license-file
33
+
34
+ # PyStatsV1 — Applied Statistics (R ↔ Python)
35
+
36
+ [![CI](https://img.shields.io/github/actions/workflow/status/pystatsv1/PyStatsV1/ci.yml?branch=main&label=ci&color=brightgreen)](https://github.com/pystatsv1/PyStatsV1/actions/workflows/ci.yml)
37
+ [![GitHub release](https://img.shields.io/github/v/tag/pystatsv1/PyStatsV1?label=release&color=brightgreen)](https://github.com/pystatsv1/PyStatsV1/tags)
38
+ [![Docs](https://readthedocs.org/projects/pystatsv1/badge/?version=latest)](https://pystatsv1.readthedocs.io/en/latest/?badge=latest)
39
+ [![PyPI - Version](https://img.shields.io/pypi/v/pystatsv1.svg?label=pypi&color=brightgreen)](https://pypi.org/project/pystatsv1/)
40
+ [![Python version](https://img.shields.io/badge/python-3.10%2B-brightgreen)](https://pypi.org/project/pystatsv1/)
41
+
42
+ PyStatsV1 provides **plain, transparent Python scripts** that mirror classical **R textbook analyses**, making it easy for students, tutors, and practitioners to:
43
+
44
+ - run statistical analyses from the command line,
45
+ - generate synthetic data for teaching,
46
+ - produce figures and JSON summaries,
47
+ - and compare outputs across R/Python.
48
+
49
+ ## ⭐ Fastest start (PyPI + Workbook)
50
+
51
+ Install from PyPI (recommended: include the Workbook bundle so you can run `pytest` checks):
52
+
53
+ ```bash
54
+ python -m pip install -U pip
55
+ python -m pip install "pystatsv1[workbook]"
56
+ ```
57
+
58
+ Sanity-check your environment:
59
+
60
+ ```bash
61
+ pystatsv1 doctor
62
+ ```
63
+
64
+ Create a local Workbook starter and run Chapter 10:
65
+
66
+ ```bash
67
+ pystatsv1 workbook init
68
+ cd pystatsv1_workbook
69
+
70
+ python scripts/psych_ch10_problem_set.py
71
+ pytest -q
72
+ ```
73
+
74
+ Open the bundled local PDF docs (works offline):
75
+
76
+ ```bash
77
+ pystatsv1 docs
78
+ # optional convenience script:
79
+ pystatsv1-docs
80
+ ```
81
+
82
+ Tip: the online docs are always available via the ReadTheDocs badge at the top of this README.
83
+
84
+ ## Full repository (scripts, Makefile targets, tests, docs)
85
+
86
+
87
+ If you want the full chapter-by-chapter repo (simulators, analyzers, Makefile targets, tests, and the docs source), clone from GitHub and install in editable mode:
88
+
89
+ ```bash
90
+ git clone https://github.com/pystatsv1/PyStatsV1.git
91
+ cd PyStatsV1
92
+ pip install -e .
93
+ pip install -r requirements-dev.txt
94
+ ```
95
+
96
+ ## Project Structure
97
+
98
+ The project follows a **chapter-based structure** — each chapter includes a simulator, an analyzer, Makefile targets, and CI smoke tests.
99
+
100
+ ### Who is this for?
101
+
102
+ PyStatsV1 is designed for:
103
+
104
+ - **Students** who want to run textbook-style analyses in real Python code.
105
+ - **Instructors / TAs** who need reproducible demos and synthetic data for lectures, labs, or assignments.
106
+ - **Practitioners** who prefer plain scripts and command-line tools over large frameworks.
107
+ - **R users** who want a clear, line-by-line bridge from R examples into Python.
108
+
109
+ ---
110
+
111
+ ## 🚀 Using a Virtual Environment
112
+
113
+ ### Option A — Student mode (PyPI + Workbook)
114
+
115
+ **macOS / Linux**
116
+
117
+ ```bash
118
+ python -m venv pystatsv1-env
119
+ source pystatsv1-env/bin/activate
120
+ python -m pip install -U pip
121
+ python -m pip install "pystatsv1[workbook]"
122
+ pystatsv1 doctor
123
+ pystatsv1 workbook init
124
+ ```
125
+
126
+ **Windows (Git Bash)**
127
+
128
+ ```bash
129
+ python -m venv pystatsv1-env
130
+ source pystatsv1-env/Scripts/activate
131
+ python -m pip install -U pip
132
+ python -m pip install "pystatsv1[workbook]"
133
+ pystatsv1 doctor
134
+ pystatsv1 workbook init
135
+ ```
136
+
137
+ ### Option B — Repo dev install (contributors)
138
+
139
+ ```bash
140
+ python -m venv .venv
141
+ # Git Bash first; PowerShell as fallback
142
+ source .venv/Scripts/activate 2>/dev/null || .venv\\Scripts\\Activate.ps1
143
+ python -m pip install -U pip
144
+ pip install -e .
145
+ pip install -r requirements-dev.txt
146
+ ```
147
+
148
+
149
+ ---
150
+
151
+ ## 📊 Chapter Scripts
152
+
153
+ ### Chapter 1 — Introduction
154
+
155
+ ```bash
156
+ python -m scripts.ch01_introduction
157
+ ```
158
+
159
+ ### Chapter 13 — Within-subjects & Mixed Models
160
+
161
+ ```bash
162
+ make ch13-ci # tiny CI smoke
163
+ make ch13 # full demo
164
+ ```
165
+
166
+ ### Chapter 14 — Tutoring A/B Test (two-sample t-test)
167
+
168
+ ```bash
169
+ make ch14-ci
170
+ make ch14
171
+ ```
172
+
173
+ ### Chapter 15 — Reliability (Cronbach’s α, ICC, Bland–Altman)
174
+
175
+ ```bash
176
+ make ch15-ci
177
+ make ch15
178
+ ```
179
+
180
+ For an overview of what each chapter contains:
181
+
182
+ - **[CHAPTERS.md](CHAPTERS.md)** — coverage, commands, and outputs
183
+ - **[ROADMAP.md](ROADMAP.md)** — planned chapters (e.g., Ch16 Epidemiology RR)
184
+
185
+ ---
186
+
187
+ ## 📚 Project Docs & Policies
188
+
189
+ PyStatsV1 is structured with a core set of documentation:
190
+
191
+ - **[CONTRIBUTING.md](CONTRIBUTING.md)** — environment setup, development workflow, Makefile usage, PR process.
192
+ - **[CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)** — community expectations & enforcement.
193
+ - **[CHAPTERS.md](CHAPTERS.md)** — high-level description of all implemented chapters.
194
+ - **[ROADMAP.md](ROADMAP.md)** — the future of the project: upcoming chapters & milestones.
195
+ - **[SECURITY.md](SECURITY.md)** — how to privately report vulnerabilities.
196
+ - **[SUPPORT.md](SUPPORT.md)** — how to get help or ask questions.
197
+ - **Case Study Template:** [`docs/case_study_template.md`](docs/case_study_template.md) — structure for building new chapter teaching documentation.
198
+
199
+ If you want to contribute, start with **[CONTRIBUTING.md](CONTRIBUTING.md)** and check issues labeled
200
+ `good first issue` or `help wanted`.
201
+
202
+ ---
203
+
204
+ ## 🤝 Contribute in 5 minutes
205
+
206
+ Want to help but not sure where to start?
207
+
208
+ 1. **Browse issues** labeled `good first issue` or `help wanted`.
209
+ 2. **Pick one small thing** (typo, doc improvement, tiny refactor, or a missing test).
210
+ 3. **Fork & clone** the repo.
211
+ 4. **Create and activate a virtual environment**, then:
212
+
213
+ ```bash
214
+ pip install -r requirements.txt
215
+ make lint
216
+ make test
217
+ ```
218
+
219
+ 5. Make your change, and ensure `make lint` and `make test` both pass.
220
+ 6. Open a Pull Request and briefly describe:
221
+ - what you changed,
222
+ - how you tested it,
223
+ - which chapter(s) it touches, if any.
224
+
225
+ Maintainer promise: we’ll give constructive feedback and help first-time contributors land their PRs.
226
+
227
+ ---
228
+
229
+ ## 🗺️ Roadmap snapshot
230
+
231
+ High-level upcoming work (see `ROADMAP.md` for details):
232
+
233
+ - ✅ v0.17.0 — Onboarding and issue templates
234
+ - ⏳ Next steps:
235
+ - Additional regression chapters (logistic, Poisson, etc.)
236
+ - Power and sample size simulations
237
+ - Epidemiology-focused examples (risk ratios, odds ratios)
238
+ - More teaching case studies using `docs/case_study_template.md`
239
+
240
+ If you’d like to champion a specific chapter or topic, open an issue and we can design it together.
241
+
242
+ ---
243
+
244
+ ## 🧪 Development Workflow
245
+
246
+ From the project root:
247
+
248
+ ```bash
249
+ make lint # ruff check
250
+ make test # pytest
251
+ ```
252
+
253
+ To run chapter smoke tests:
254
+
255
+ ```bash
256
+ make ch13-ci
257
+ make ch14-ci
258
+ make ch15-ci
259
+ ```
260
+
261
+ All synthetic data is written to:
262
+
263
+ - `data/synthetic/`
264
+ - `outputs/<chapter>/`
265
+
266
+ …and ignored by Git.
267
+
268
+ ---
269
+
270
+ ## 🔀 Pull Requests
271
+
272
+ Every pull request should:
273
+
274
+ - pass `make lint` and `make test`,
275
+ - avoid committing generated outputs,
276
+ - follow the structure described in **[CONTRIBUTING.md](CONTRIBUTING.md)**.
277
+
278
+ GitHub provides:
279
+
280
+ - 🐛 Bug report template
281
+ - 💡 Feature request template
282
+ - 📘 Good first issue template
283
+ - 🔀 Pull request template
284
+
285
+ ---
286
+
287
+ ## 🔒 Security
288
+
289
+ If you believe you’ve found a security issue, **do not** open a public GitHub issue.
290
+ Follow the private disclosure process described in **[SECURITY.md](SECURITY.md)**.
291
+
292
+ ---
293
+
294
+ ## 💬 Community & support
295
+
296
+ - **Questions?**
297
+ Open a GitHub issue with the `question` label.
298
+
299
+ - **Using PyStatsV1 in a course?**
300
+ We’d love to hear about it — open an issue titled `Course report: <institution>` or mention it in your PR description.
301
+
302
+ - **Feature ideas / chapter requests?**
303
+ Open an issue with the `enhancement` or `chapter-idea` label.
304
+
305
+ As the project grows, we plan to enable GitHub Discussions and possibly a lightweight chat space for instructors and contributors.
306
+
307
+ ---
308
+
309
+
310
+ ```bash
311
+ python -m pip install --upgrade pip
312
+ python -m pip install "pystatsv1[workbook]"
313
+ ```
314
+
315
+ ```bash
316
+ pystatsv1 workbook init --dest pystatsv1_workbook
317
+ pystatsv1 workbook run ch10 --workdir pystatsv1_workbook
318
+ pystatsv1 workbook check ch10 --workdir pystatsv1_workbook
319
+ ```
320
+
321
+ Notes:
322
+
323
+ - **No `make` required.** The workbook commands work on Linux, macOS, and Windows.
324
+ - ``workbook check`` runs `pytest` (installed via the ``[workbook]`` extra).
325
+ - If you prefer, you can also run the chapter scripts directly under ``pystatsv1_workbook/scripts/``.
326
+
327
+ ## 📜 License
328
+
329
+ MIT © 2025 Nicholas Elliott Karlson
@@ -0,0 +1,17 @@
1
+ pystatsv1/__init__.py,sha256=2nPxK5aAN43oTCO3iToQpuuxwvy8pcuMBfHsnSZYcNw,729
2
+ pystatsv1/__main__.py,sha256=nbdP6zFlOqcCirgxoIQ6HiWVhdh7URm5GOWr5_EwmBk,116
3
+ pystatsv1/cli.py,sha256=M4dKCUSOKfpGylyaQC4SQZkIBNsxDXgSzfGxak7eoKE,16163
4
+ pystatsv1/docs_helper.py,sha256=4IAMAofXhl3B2MsODVp9G8RX67PBDZUMx7UwJ7uGMsE,876
5
+ pystatsv1/paths.py,sha256=_N5OxNsHewnpSOgoGMXoW7y0eXwOcQ_OJMyRef9OswI,1120
6
+ pystatsv1/assets/__init__.py,sha256=hlrAbVFNOYExBWItzCFI5hLJzmP8jJ9xiCXWNyXz2Ss,72
7
+ pystatsv1/assets/ledgerlab_ch01_seed123.zip,sha256=2Cr1UF1sR-aCVbuS8tk5x9_NmSS41zz_lqzMdX9-3rk,4442
8
+ pystatsv1/assets/nso_v1_seed123.zip,sha256=V2DshRrJLyB_fuk8i7aGHmj4KSWViZlHmFigI7HysMA,80958
9
+ pystatsv1/assets/workbook_starter.zip,sha256=XYw3HAMPGuNB9sVcXcvOkpRYX5szQFX1Orak__q3KvY,58136
10
+ pystatsv1/assets/workbook_track_d.zip,sha256=qEvS2UN8Ey7519jUASlIXkBiMsw1OH-txQJs-Et2qw8,167899
11
+ pystatsv1/docs/pystatsv1.pdf,sha256=lAWTa9_pfJKpsAqgWlQa2qXI6flCHmKfRwOsQSiVges,1180298
12
+ pystatsv1-0.22.2.dist-info/licenses/LICENSE,sha256=y1zqwmZEm_3E_rohXFs5EwIIB6uvAUB_KcVPFuK4j3U,1120
13
+ pystatsv1-0.22.2.dist-info/METADATA,sha256=P2pCTXpAAOSbzlhDn8D9-wuZCzfRwhDNnXLiavct5uk,9390
14
+ pystatsv1-0.22.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
+ pystatsv1-0.22.2.dist-info/entry_points.txt,sha256=9OWdAl4LypAo03or1aO6ZOJXZkLhpRIaXsL0o9yWxvw,90
16
+ pystatsv1-0.22.2.dist-info/top_level.txt,sha256=WlU1GU7q1JZyOlQv8NS3ciIhsZUL3A55MZkZS7WAnG8,10
17
+ pystatsv1-0.22.2.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ pystatsv1 = pystatsv1.cli:main
3
+ pystatsv1-docs = pystatsv1.cli:docs_main
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Nicholas Elliott Karlson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the “Software”), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice (including the next
13
+ paragraph) shall be included in all copies or substantial portions of the
14
+ Software.
15
+
16
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
@@ -0,0 +1 @@
1
+ pystatsv1