MCPower 0.4.0__tar.gz → 0.4.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.
Files changed (115) hide show
  1. {mcpower-0.4.0 → mcpower-0.4.2}/.github/workflows/release.yml +1 -1
  2. {mcpower-0.4.0 → mcpower-0.4.2}/.github/workflows/test-release.yml +1 -1
  3. {mcpower-0.4.0 → mcpower-0.4.2}/.github/workflows/tests.yml +3 -3
  4. mcpower-0.4.2/CHANGELOG.md +130 -0
  5. {mcpower-0.4.0 → mcpower-0.4.2}/CMakeLists.txt +27 -4
  6. {mcpower-0.4.0 → mcpower-0.4.2}/PKG-INFO +95 -46
  7. {mcpower-0.4.0 → mcpower-0.4.2}/README.md +92 -43
  8. {mcpower-0.4.0 → mcpower-0.4.2}/cpp/src/bindings.cpp +83 -0
  9. mcpower-0.4.2/cpp/src/lme_solver.cpp +540 -0
  10. mcpower-0.4.2/cpp/src/lme_solver.hpp +154 -0
  11. {mcpower-0.4.0 → mcpower-0.4.2}/mcpower/backends/__init__.py +21 -0
  12. {mcpower-0.4.0 → mcpower-0.4.2}/mcpower/backends/jit.py +34 -4
  13. {mcpower-0.4.0 → mcpower-0.4.2}/mcpower/backends/native.py +51 -0
  14. {mcpower-0.4.0 → mcpower-0.4.2}/mcpower/backends/python.py +32 -2
  15. {mcpower-0.4.0 → mcpower-0.4.2}/mcpower/core/simulation.py +155 -22
  16. {mcpower-0.4.0 → mcpower-0.4.2}/mcpower/core/variables.py +235 -60
  17. {mcpower-0.4.0 → mcpower-0.4.2}/mcpower/model.py +593 -97
  18. mcpower-0.4.2/mcpower/stats/__init__.py +6 -0
  19. {mcpower-0.4.0/mcpower/utils → mcpower-0.4.2/mcpower/stats}/data_generation.py +232 -1
  20. mcpower-0.4.2/mcpower/stats/lme_solver.py +1433 -0
  21. {mcpower-0.4.0/mcpower/utils → mcpower-0.4.2/mcpower/stats}/mixed_models.py +401 -11
  22. {mcpower-0.4.0/mcpower/utils → mcpower-0.4.2/mcpower/stats}/ols.py +183 -0
  23. {mcpower-0.4.0 → mcpower-0.4.2}/mcpower/utils/__init__.py +5 -2
  24. mcpower-0.4.2/mcpower/utils/data_generation.py +13 -0
  25. {mcpower-0.4.0 → mcpower-0.4.2}/mcpower/utils/formatters.py +46 -17
  26. mcpower-0.4.2/mcpower/utils/mixed_models.py +7 -0
  27. mcpower-0.4.2/mcpower/utils/ols.py +9 -0
  28. {mcpower-0.4.0 → mcpower-0.4.2}/mcpower/utils/parsers.py +66 -18
  29. {mcpower-0.4.0 → mcpower-0.4.2}/mcpower/utils/updates.py +7 -1
  30. {mcpower-0.4.0 → mcpower-0.4.2}/mcpower/utils/validators.py +26 -21
  31. {mcpower-0.4.0 → mcpower-0.4.2}/mcpower/utils/visualization.py +9 -1
  32. {mcpower-0.4.0 → mcpower-0.4.2}/pyproject.toml +2 -2
  33. {mcpower-0.4.0 → mcpower-0.4.2}/tests/backend/test_backend_parity.py +0 -2
  34. {mcpower-0.4.0 → mcpower-0.4.2}/tests/backend/test_datagen_isolation.py +0 -1
  35. {mcpower-0.4.0 → mcpower-0.4.2}/tests/backend/test_ols_isolation.py +0 -1
  36. mcpower-0.4.2/tests/cars.csv +33 -0
  37. {mcpower-0.4.0 → mcpower-0.4.2}/tests/conftest.py +1 -1
  38. {mcpower-0.4.0 → mcpower-0.4.2}/tests/helpers/power_helpers.py +1 -1
  39. {mcpower-0.4.0/tests → mcpower-0.4.2/tests/integration}/test_edge_cases.py +3 -2
  40. {mcpower-0.4.0/tests → mcpower-0.4.2/tests/integration}/test_model.py +56 -0
  41. {mcpower-0.4.0/tests → mcpower-0.4.2/tests/integration}/test_parallel.py +1 -0
  42. mcpower-0.4.2/tests/integration/test_posthoc_integration.py +576 -0
  43. {mcpower-0.4.0/tests → mcpower-0.4.2/tests/integration}/test_upload_data.py +251 -9
  44. {mcpower-0.4.0 → mcpower-0.4.2}/tests/mixed_models/test_cluster_validators.py +0 -2
  45. mcpower-0.4.2/tests/mixed_models/test_integration_phase2.py +218 -0
  46. mcpower-0.4.2/tests/mixed_models/test_lme_nested.py +236 -0
  47. mcpower-0.4.2/tests/mixed_models/test_lme_slopes.py +218 -0
  48. mcpower-0.4.2/tests/mixed_models/test_lme_solver.py +465 -0
  49. mcpower-0.4.2/tests/mixed_models/test_lme_vs_statsmodels.py +244 -0
  50. {mcpower-0.4.0 → mcpower-0.4.2}/tests/mixed_models/test_mixed_models.py +7 -3
  51. {mcpower-0.4.0 → mcpower-0.4.2}/tests/mixed_models/test_mixed_models_diagnostics.py +1 -2
  52. {mcpower-0.4.0 → mcpower-0.4.2}/tests/mixed_models/test_mixed_models_extended.py +2 -1
  53. {mcpower-0.4.0 → mcpower-0.4.2}/tests/mixed_models/test_mixed_models_validation.py +7 -7
  54. mcpower-0.4.2/tests/specs/__init__.py +0 -0
  55. {mcpower-0.4.0 → mcpower-0.4.2}/tests/specs/test_alpha_levels.py +1 -0
  56. {mcpower-0.4.0 → mcpower-0.4.2}/tests/specs/test_corrections.py +1 -0
  57. {mcpower-0.4.0 → mcpower-0.4.2}/tests/specs/test_monotonicity.py +1 -0
  58. mcpower-0.4.2/tests/unit/__init__.py +0 -0
  59. {mcpower-0.4.0/tests → mcpower-0.4.2/tests/unit}/test_data_generation.py +28 -28
  60. {mcpower-0.4.0/tests → mcpower-0.4.2/tests/unit}/test_formatters.py +117 -0
  61. {mcpower-0.4.0/tests → mcpower-0.4.2/tests/unit}/test_ols.py +10 -10
  62. {mcpower-0.4.0/tests → mcpower-0.4.2/tests/unit}/test_parsers.py +6 -3
  63. mcpower-0.4.2/tests/unit/test_posthoc.py +213 -0
  64. {mcpower-0.4.0/tests → mcpower-0.4.2/tests/unit}/test_updates.py +1 -1
  65. {mcpower-0.4.0/tests → mcpower-0.4.2/tests/unit}/test_validators.py +6 -0
  66. {mcpower-0.4.0/tests → mcpower-0.4.2/tests/unit}/test_variables.py +119 -0
  67. mcpower-0.4.0/CHANGELOG.md +0 -95
  68. mcpower-0.4.0/scripts/test_quick.py +0 -213
  69. mcpower-0.4.0/tests/LME_BENCHMARK_TESTS.md +0 -208
  70. mcpower-0.4.0/tests/cars.csv +0 -33
  71. {mcpower-0.4.0 → mcpower-0.4.2}/.github/FUNDING.yml +0 -0
  72. {mcpower-0.4.0 → mcpower-0.4.2}/.gitignore +0 -0
  73. {mcpower-0.4.0 → mcpower-0.4.2}/.pre-commit-config.yaml +0 -0
  74. {mcpower-0.4.0 → mcpower-0.4.2}/LICENSE +0 -0
  75. {mcpower-0.4.0 → mcpower-0.4.2}/cpp/src/data_generation.cpp +0 -0
  76. {mcpower-0.4.0 → mcpower-0.4.2}/cpp/src/data_generation.hpp +0 -0
  77. {mcpower-0.4.0 → mcpower-0.4.2}/cpp/src/ols.cpp +0 -0
  78. {mcpower-0.4.0 → mcpower-0.4.2}/cpp/src/ols.hpp +0 -0
  79. {mcpower-0.4.0 → mcpower-0.4.2}/examples/01_basic_power.py +0 -0
  80. {mcpower-0.4.0 → mcpower-0.4.2}/examples/02_sample_size.py +0 -0
  81. {mcpower-0.4.0 → mcpower-0.4.2}/examples/03_interactions.py +0 -0
  82. {mcpower-0.4.0 → mcpower-0.4.2}/examples/04_correlations.py +0 -0
  83. {mcpower-0.4.0 → mcpower-0.4.2}/examples/05_multiple_testing.py +0 -0
  84. {mcpower-0.4.0 → mcpower-0.4.2}/examples/06_own_data.py +0 -0
  85. {mcpower-0.4.0 → mcpower-0.4.2}/examples/cars.csv +0 -0
  86. {mcpower-0.4.0 → mcpower-0.4.2}/mcpower/__init__.py +0 -0
  87. {mcpower-0.4.0 → mcpower-0.4.2}/mcpower/core/__init__.py +0 -0
  88. {mcpower-0.4.0 → mcpower-0.4.2}/mcpower/core/results.py +0 -0
  89. {mcpower-0.4.0 → mcpower-0.4.2}/mcpower/core/scenarios.py +0 -0
  90. {mcpower-0.4.0 → mcpower-0.4.2}/mcpower/progress.py +0 -0
  91. {mcpower-0.4.0 → mcpower-0.4.2}/mcpower/tables/__init__.py +0 -0
  92. {mcpower-0.4.0 → mcpower-0.4.2}/mcpower/tables/data/norm_cdf.npz +0 -0
  93. {mcpower-0.4.0 → mcpower-0.4.2}/mcpower/tables/data/t3_ppf.npz +0 -0
  94. {mcpower-0.4.0 → mcpower-0.4.2}/mcpower/tables/lookup.py +0 -0
  95. {mcpower-0.4.0 → mcpower-0.4.2}/mcpower/utils/upload_data_utils.py +0 -0
  96. {mcpower-0.4.0 → mcpower-0.4.2}/scripts/generate_tables.py +0 -0
  97. {mcpower-0.4.0 → mcpower-0.4.2}/tests/backend/__init__.py +0 -0
  98. {mcpower-0.4.0/tests → mcpower-0.4.2/tests/backend}/test_jit_backend.py +0 -0
  99. {mcpower-0.4.0 → mcpower-0.4.2}/tests/config.py +0 -0
  100. {mcpower-0.4.0 → mcpower-0.4.2}/tests/helpers/__init__.py +0 -0
  101. {mcpower-0.4.0 → mcpower-0.4.2}/tests/helpers/analytical.py +0 -0
  102. {mcpower-0.4.0 → mcpower-0.4.2}/tests/helpers/mc_margins.py +0 -0
  103. {mcpower-0.4.0 → mcpower-0.4.2}/tests/integration/__init__.py +0 -0
  104. {mcpower-0.4.0 → mcpower-0.4.2}/tests/integration/test_find_power_api.py +0 -0
  105. {mcpower-0.4.0/tests → mcpower-0.4.2/tests/integration}/test_scenarios.py +0 -0
  106. {mcpower-0.4.0/tests → mcpower-0.4.2/tests/integration}/test_visualization.py +0 -0
  107. {mcpower-0.4.0/tests/specs → mcpower-0.4.2/tests/mixed_models}/__init__.py +0 -0
  108. {mcpower-0.4.0 → mcpower-0.4.2}/tests/specs/test_monotonicity_lme.py +0 -0
  109. {mcpower-0.4.0 → mcpower-0.4.2}/tests/specs/test_power_accuracy.py +0 -0
  110. {mcpower-0.4.0 → mcpower-0.4.2}/tests/specs/test_power_accuracy_lme.py +0 -0
  111. {mcpower-0.4.0 → mcpower-0.4.2}/tests/specs/test_type1_error.py +0 -0
  112. {mcpower-0.4.0 → mcpower-0.4.2}/tests/specs/test_type1_error_lme.py +0 -0
  113. {mcpower-0.4.0/tests → mcpower-0.4.2/tests/unit}/test_lookup.py +0 -0
  114. {mcpower-0.4.0/tests → mcpower-0.4.2/tests/unit}/test_progress.py +0 -0
  115. {mcpower-0.4.0/tests → mcpower-0.4.2/tests/unit}/test_simulation_unit.py +0 -0
@@ -69,7 +69,7 @@ jobs:
69
69
  CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28
70
70
  CIBW_MANYLINUX_AARCH64_IMAGE: manylinux_2_28
71
71
  CIBW_TEST_REQUIRES: "pytest numpy pandas matplotlib scipy statsmodels"
72
- CIBW_TEST_COMMAND: "pytest {project}/tests -v -x --ignore={project}/tests/backend/ -m \"not slow\""
72
+ CIBW_TEST_COMMAND: "pytest {project}/tests -v -x --ignore={project}/tests/backend/ -m \"not slow and not lme\""
73
73
 
74
74
  - name: Verify native extension in wheels
75
75
  shell: bash
@@ -34,7 +34,7 @@ jobs:
34
34
  CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28
35
35
  CIBW_MANYLINUX_AARCH64_IMAGE: manylinux_2_28
36
36
  CIBW_TEST_REQUIRES: "pytest numpy pandas matplotlib scipy statsmodels"
37
- CIBW_TEST_COMMAND: "pytest {project}/tests -v -x --ignore={project}/tests/backend/ -m \"not slow\""
37
+ CIBW_TEST_COMMAND: "pytest {project}/tests -v -x --ignore={project}/tests/backend/ -m \"not slow and not lme\""
38
38
 
39
39
  - name: Verify native extension in wheels
40
40
  shell: bash
@@ -37,12 +37,12 @@ jobs:
37
37
  run: |
38
38
  python -m pip install --upgrade pip
39
39
  pip install pytest pytest-cov
40
- pip install pandas joblib matplotlib statsmodels
40
+ pip install pandas joblib matplotlib
41
41
  pip install numba || echo "Numba not available for Python ${{ matrix.python-version }}, will use Python fallback"
42
42
  pip install -e .
43
43
 
44
44
  - name: Run tests with coverage
45
- run: pytest tests/ -v --tb=short --cov=mcpower --cov-report=xml -m "not slow"
45
+ run: pytest tests/ -v --tb=short --cov=mcpower --cov-report=xml -m "not slow and not lme"
46
46
 
47
47
  - name: Upload coverage to Codecov
48
48
  uses: codecov/codecov-action@v4
@@ -98,7 +98,7 @@ jobs:
98
98
  - name: Test Python fallback mode
99
99
  run: |
100
100
  python -c "import mcpower; print('Python fallback mode working')"
101
- pytest tests/ -v --tb=short -m "not slow"
101
+ pytest tests/ -v --tb=short -m "not slow and not lme"
102
102
 
103
103
  test-performance:
104
104
  runs-on: ubuntu-latest
@@ -0,0 +1,130 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [0.4.2] - 2026-02-17
6
+
7
+ ### Major changes
8
+ - **Custom LME solver**: Replaced statsmodels MixedLM with a custom profiled-deviance REML solver (Python + C++ + JIT backends), eliminating the statsmodels runtime dependency for mixed models
9
+ - **C++ LME backend**: LME analysis exposed via PyBind11/Eigen (`mcpower_native.lme_analysis`) for maximum throughput
10
+ - **Post-hoc pairwise comparisons**: `target_test="group[1] vs group[2]"` syntax for contrasts between factor levels
11
+ - **Tukey HSD correction**: New `correction="tukey"` option for pairwise comparisons using studentized range critical values
12
+
13
+ ### Minor changes
14
+ - `target_test` default changed from `"overall"` to `"all"` (overall F-test + all individual fixed effects)
15
+ - `target_test` now supports keyword expansion (`"all"`, `"all-posthoc"`), exclusion prefixes (`"-overall"`), and comma-separated combinations
16
+ - Renamed optional dependency extra from `[mixed]` to `[lme]`
17
+ - Formatter handles `NaN` power values gracefully (displays `"-"` instead of crashing)
18
+ - README rewritten with clearer examples and MCPower GUI cross-link
19
+
20
+ ### Technical
21
+ - New `mcpower/stats/` package: statistical routines (`ols.py`, `lme_solver.py`, `mixed_models.py`, `data_generation.py`) extracted from `utils/`
22
+ - Reclassified several `@pytest.mark.slow` tests to run in the default (non-slow) suite
23
+ - Removed old test files and `scripts/test_quick.py`; moved `LME_BENCHMARK_TESTS.md` to `docs/`
24
+
25
+ ### Experimental
26
+ - **Random slopes**: Support for `(1 + x|group)` syntax with configurable slope variance and slope-intercept correlation
27
+ - **Nested random effects**: Support for `(1|A/B)` syntax with automatic expansion to parent and child grouping terms
28
+
29
+ ## [0.4.1] - 2026-02-14
30
+
31
+ ### Minor changes
32
+ - README badges now link to their respective pages
33
+ - Added MCPower GUI desktop application section to README
34
+
35
+ ### Technical
36
+ - Reclassified several backend and mixed-model tests from `@pytest.mark.slow` to default suite
37
+ - Moved slow markers to individual test methods for finer-grained control
38
+
39
+ ## [0.4.0] - 2026-02-12
40
+
41
+ Releasing pipeline fixed
42
+
43
+ ## [0.4.0b0] - 2026-02-12
44
+
45
+ The package has been almost entirely rebuilt from the ground up.
46
+
47
+ ### Major changes
48
+ - **C++ native backend**: New PyBind11/Eigen-based compute backend for OLS and data generation (~3x speedup), removed Numba AOT
49
+ - **Multi-backend architecture**: Automatic selection between C++ native, Numba JIT, and pure Python backends with graceful fallback
50
+ - **Mixed-effects models**: Random intercept models via `(1|group)` syntax with statsmodels MixedLM
51
+ - **Build system migration**: Moved from setuptools to scikit-build-core with CMake for C++ compilation
52
+ - **Python 3.14 support**
53
+ - **Lookup tables reduced and reorganized**: removed lookup tables from OLS, precomputed critical values added
54
+
55
+ ### Minor changes
56
+ - `joblib` is now a core dependency; `statsmodels` moved to optional `[lme]` extra
57
+ - Progress callback system for GUI/notebook integration
58
+
59
+ ### Technical
60
+ - Complete code reorganization: monolithic `base.py` split into modular structure (`mcpower/core/`, `mcpower/backends/`, `mcpower/model.py`)
61
+ - New CI/CD pipeline: separate workflows for stable releases (PyPI) and pre-releases (TestPyPI)
62
+ - Comprehensive test suite overhaul (650 tests across unit, integration, spec, and mixed-model categories)
63
+ - Pre-commit hooks with ruff for linting and formatting
64
+ - Removed legacy `setup.py`, `pytest.ini`, and `paper/` directory
65
+
66
+ ## [0.3.3] - 2025-07-27
67
+
68
+ ### Technical
69
+ - Wheels for Python 3.13
70
+
71
+ ## [0.3.2] - 2025-07-27
72
+
73
+ ### Major changes
74
+ - Factors are now supported
75
+ - The workflow requires that `set_variable_types` be set before correlation and effects
76
+
77
+ ## [0.3.1] - 2025-07-17
78
+
79
+ ### Major changes
80
+ - Added weekly update checker
81
+
82
+ ### Minor changes
83
+ - Small changes in documentation
84
+
85
+ ## [0.3.0] - 2025-07-16
86
+
87
+ ### Major changes
88
+ - AOT compiled files added
89
+ - PyPI release
90
+
91
+ ### Minor changes
92
+ - Small changes in documentation, added fallback to compilation
93
+
94
+ ## [0.2.4] - 2025-07-16
95
+ - Test PyPI release
96
+
97
+ ## [0.2.3] - 2025-07-16
98
+ - Test PyPI release
99
+
100
+ ## [0.2.2] - 2025-07-11
101
+
102
+ ### Major changes
103
+ - Moved seed setting from method arguments to `model.set_seed()` for better control
104
+ - Added public method to customize scenario configurations
105
+ - Added support for 3-way and n-way interactions in formulas
106
+
107
+ ### Minor changes
108
+ - Improved lookup table performance with extended range and faster fallbacks
109
+ - Fixed import error when uploading user data
110
+ - Fixed minor cache creation error
111
+ - Adjusted vulnerability thresholds from 30% to 20% power drop
112
+ - Added warning for inflated Type I error risk
113
+
114
+ ### Technical
115
+ - Added comprehensive test suite
116
+ - Improved code documentation with clearer comments and global variables
117
+ - Clarified correlation behavior during variable transformations
118
+
119
+ ## [0.2.1] - 2025-06-26
120
+
121
+ ### Minor changes
122
+ - Fixed import statements to use relative imports instead of absolute imports
123
+ - Added changelog
124
+ - Extended docstrings
125
+
126
+ ## [0.2.0] - 2025-06-26
127
+ - Initial release
128
+
129
+ ## [0.1.0] - 2025-06-07
130
+ - Proof of concept release
@@ -6,11 +6,32 @@ set(CMAKE_CXX_STANDARD 17)
6
6
  set(CMAKE_CXX_STANDARD_REQUIRED ON)
7
7
  set(CMAKE_CXX_EXTENSIONS OFF)
8
8
 
9
- # Find pybind11 — optional so sdist installs without a compiler still succeed
10
- find_package(pybind11 CONFIG)
9
+ # Find pybind11 — try CONFIG first, then query the Python module as fallback
10
+ find_package(pybind11 CONFIG QUIET)
11
11
  if(NOT pybind11_FOUND)
12
- message(WARNING "pybind11 not found — building Python-only package (no C++ backend)")
13
- return()
12
+ # Fallback: ask the pybind11 Python module where its cmake files live
13
+ # (scikit-build-core may not always add site-packages to CMAKE_PREFIX_PATH on Windows)
14
+ find_package(Python COMPONENTS Interpreter QUIET)
15
+ if(Python_FOUND)
16
+ execute_process(
17
+ COMMAND "${Python_EXECUTABLE}" -m pybind11 --cmakedir
18
+ OUTPUT_VARIABLE _pybind11_cmake_dir
19
+ OUTPUT_STRIP_TRAILING_WHITESPACE
20
+ ERROR_QUIET
21
+ RESULT_VARIABLE _pybind11_result
22
+ )
23
+ if(_pybind11_result EQUAL 0 AND _pybind11_cmake_dir)
24
+ find_package(pybind11 CONFIG REQUIRED PATHS "${_pybind11_cmake_dir}" NO_DEFAULT_PATH)
25
+ endif()
26
+ endif()
27
+ endif()
28
+ if(NOT pybind11_FOUND)
29
+ if(DEFINED ENV{CIBUILDWHEEL})
30
+ message(FATAL_ERROR "pybind11 not found in CI — cannot build wheel without native extension")
31
+ else()
32
+ message(WARNING "pybind11 not found — building Python-only package (no C++ backend)")
33
+ return()
34
+ endif()
14
35
  endif()
15
36
 
16
37
  # Try to find Eigen3, make it optional
@@ -47,6 +68,7 @@ endif()
47
68
  set(MCPOWER_SOURCES
48
69
  cpp/src/ols.cpp
49
70
  cpp/src/data_generation.cpp
71
+ cpp/src/lme_solver.cpp
50
72
  cpp/src/bindings.cpp
51
73
  )
52
74
 
@@ -78,6 +100,7 @@ elseif(MSVC)
78
100
  /fp:fast
79
101
  /DNDEBUG
80
102
  )
103
+ target_compile_definitions(mcpower_native PRIVATE _USE_MATH_DEFINES)
81
104
  endif()
82
105
 
83
106
  # OpenMP support (optional)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: MCPower
3
- Version: 0.4.0
3
+ Version: 0.4.2
4
4
  Summary: Monte Carlo Power Analysis for Statistical Models
5
5
  Keywords: power analysis,statistics,monte carlo,linear regression
6
6
  Author-Email: =?utf-8?q?Pawe=C5=82_Lenartowicz?= <pawellenartowicz@europe.com>
@@ -28,8 +28,8 @@ Requires-Dist: scipy>=1.11.0
28
28
  Requires-Dist: joblib>=1.3.0
29
29
  Provides-Extra: jit
30
30
  Requires-Dist: numba>=0.61.0; extra == "jit"
31
- Provides-Extra: mixed
32
- Requires-Dist: statsmodels>=0.14.0; extra == "mixed"
31
+ Provides-Extra: lme
32
+ Requires-Dist: statsmodels>=0.14.0; extra == "lme"
33
33
  Provides-Extra: dev
34
34
  Requires-Dist: pytest>=7.0.0; extra == "dev"
35
35
  Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
@@ -45,11 +45,11 @@ Requires-Dist: pytest>=7.0.0; extra == "all"
45
45
  Requires-Dist: pytest-cov>=4.0.0; extra == "all"
46
46
  Description-Content-Type: text/markdown
47
47
 
48
- ![Tests](https://github.com/pawlenartowicz/MCPower/workflows/Tests/badge.svg)
49
- ![PyPI](https://img.shields.io/pypi/v/MCPower)
50
- ![Python](https://img.shields.io/pypi/pyversions/mcpower)
51
- ![License: GPL v3](https://img.shields.io/badge/License-GPLv3-green.svg)
52
- [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.16502734.svg)](https://doi.org/10.5281/zenodo.16502734)
48
+ [![Tests](https://github.com/pawlenartowicz/MCPower/workflows/Tests/badge.svg)](https://github.com/pawlenartowicz/MCPower/actions)
49
+ [![PyPI](https://img.shields.io/pypi/v/MCPower)](https://pypi.org/project/MCPower/)
50
+ [![Python](https://img.shields.io/pypi/pyversions/mcpower)](https://pypi.org/project/MCPower/)
51
+ [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-green.svg)](https://www.gnu.org/licenses/gpl-3.0)
52
+ [![DOI](https://img.shields.io/badge/DOI-10.5281%2Fzenodo.16502734-blue)](https://doi.org/10.5281/zenodo.16502734)
53
53
 
54
54
  ```
55
55
  ███╗ ███╗ ██████╗ ██████╗
@@ -64,16 +64,23 @@ Description-Content-Type: text/markdown
64
64
 
65
65
  **Simple Monte Carlo power analysis for complex models.** Find the sample size you need or check if your study has enough power - even with complex models that traditional power analysis can't handle.
66
66
 
67
+ ## Desktop Application
68
+
69
+ It it Python package, prefer a graphical interface? **[MCPower GUI](https://github.com/pawlenartowicz/mcpower-gui)** is a standalone desktop app — no Python installation required. Download ready-to-run executables for Windows, Linux, and macOS from the [releases page](https://github.com/pawlenartowicz/mcpower-gui/releases/latest).
70
+
67
71
  ## Why MCPower?
68
72
 
69
- **Traditional power analysis breaks down** with interactions, correlated predictors, categorical variables, or non-normal data. MCPower uses simulation instead of formulas - it generates thousands of datasets exactly like yours, then sees how often your analysis finds real effects.
73
+ **Traditional power analysis breaks down** with interactions, correlated predictors, categorical variables, or non-normal data. MCPower uses simulation instead of formulas — it generates thousands of datasets exactly like yours, then sees how often your analysis finds real effects.
74
+
75
+ **Just write your formula.** Define your model the way you'd write it in R — `outcome = treatment + covariate + treatment*covariate`. MCPower parses the formula, sets up the simulation, and handles interactions, factor coding, and dummy variables automatically. You focus on the research question, not the mechanics. More model types (logistic regression, ANOVA) are on the way.
76
+
77
+ **Test your assumptions with scenarios.** Real studies rarely match textbook conditions — effect sizes may be smaller than expected, distributions may be skewed, or variance may not be constant. Turn on `scenarios=True` and MCPower automatically tests your power under optimistic, realistic, and worst-case conditions. Instead of a single number, you get a range that shows how sensitive your design is to violated assumptions — so you plan for reality, not just the best case.
78
+
79
+ **Use your own data.** Upload a CSV and MCPower auto-detects variable types (continuous, binary, or categorical), preserves real distributions, and handles correlations between predictors. No need to overthink whether your data is normal, skewed, or categorical — just upload it and MCPower samples from the empirical distribution. This is especially useful when you have pilot data or a related dataset and want your power analysis to reflect actual conditions rather than idealized ones.
70
80
 
71
- ✅ **Works with complexity**: Interactions, correlations, factors, any distribution
72
- ✅ **R-style formulas**: `outcome = treatment + covariate + treatment*covariate`
73
- ✅ **Categorical variables**: Multi-level factors automatically handled
74
- ✅ **Two simple commands**: Find sample size or check power
75
- ✅ **Scenario analysis**: Test robustness under realistic conditions
76
- ✅ **Minimal math required**: Just specify your model and effects
81
+ ✅ **Works with complexity**: Interactions, correlations, multi-level factors, any distribution
82
+ ✅ **Two simple commands**: `find_sample_size()` or `find_power()` — that's the entire API
83
+ ✅ **Minimal math required**: Just specify your model formula and expected effect sizes
77
84
 
78
85
  ## Get Started in 2 Minutes
79
86
 
@@ -91,10 +98,10 @@ pip install --upgrade mcpower
91
98
  ```python
92
99
 
93
100
  # 0. Import installed package
94
- import mcpower
101
+ from mcpower import MCPower
95
102
 
96
103
  # 1. Define your model (just like R)
97
- model = mcpower.MCPower("satisfaction = treatment + motivation")
104
+ model = MCPower("satisfaction = treatment + motivation")
98
105
 
99
106
  # 2. Set effect sizes (how big you expect effects to be)
100
107
  model.set_effects("treatment=0.5, motivation=0.3")
@@ -175,10 +182,10 @@ model.set_effects("treatment=0.5, age=0.3, income=0.2")
175
182
 
176
183
  ### Randomized Controlled Trial
177
184
  ```python
178
- import mcpower
185
+ from mcpower import MCPower
179
186
 
180
187
  # RCT with treatment + control variables
181
- model = mcpower.MCPower("outcome = treatment + age + baseline_score")
188
+ model = MCPower("outcome = treatment + age + baseline_score")
182
189
  model.set_effects("treatment=0.6, age=0.2, baseline_score=0.8")
183
190
  model.set_variable_type("treatment=binary") # 0/1 treatment
184
191
 
@@ -189,10 +196,10 @@ model.find_sample_size(target_test="treatment", from_size=100, to_size=500,
189
196
 
190
197
  ### A/B Test with Interaction
191
198
  ```python
192
- import mcpower
199
+ from mcpower import MCPower
193
200
 
194
201
  # Test if treatment effect depends on user type
195
- model = mcpower.MCPower("conversion = treatment + user_type + treatment*user_type")
202
+ model = MCPower("conversion = treatment + user_type + treatment*user_type")
196
203
  model.set_effects("treatment=0.4, user_type=0.3, treatment:user_type=0.5")
197
204
  model.set_variable_type("treatment=binary, user_type=binary")
198
205
 
@@ -202,10 +209,10 @@ model.find_power(sample_size=400, target_test="treatment:user_type", scenarios=T
202
209
 
203
210
  ### Multi-Group Study with Categorical Variables
204
211
  ```python
205
- import mcpower
212
+ from mcpower import MCPower
206
213
 
207
214
  # Study with 3 treatment groups and 4 education levels
208
- model = mcpower.MCPower("wellbeing = treatment + education + age")
215
+ model = MCPower("wellbeing = treatment + education + age")
209
216
  model.set_variable_type("treatment=(factor,3), education=(factor,4)")
210
217
 
211
218
  # Set effects for each factor level (vs. reference level 1)
@@ -217,10 +224,10 @@ model.find_sample_size(target_test="treatment[2], treatment[3]", scenarios=True)
217
224
 
218
225
  ### Survey with Correlated Predictors
219
226
  ```python
220
- import mcpower
227
+ from mcpower import MCPower
221
228
 
222
229
  # Predictors are often correlated in real data
223
- model = mcpower.MCPower("wellbeing = income + education + social_support")
230
+ model = MCPower("wellbeing = income + education + social_support")
224
231
  model.set_effects("income=0.4, education=0.3, social_support=0.6")
225
232
  model.set_correlations("corr(income, education)=0.5, corr(income, social_support)=0.3")
226
233
 
@@ -246,7 +253,7 @@ model.set_variable_type("condition=(factor,0.2,0.5,0.3)")
246
253
  ### Working with Factors (Categorical Variables)
247
254
  ```python
248
255
  # Factors automatically create dummy variables
249
- model = mcpower.MCPower("outcome = treatment + education")
256
+ model = MCPower("outcome = treatment + education")
250
257
  model.set_variable_type("treatment=(factor,3), education=(factor,4)")
251
258
 
252
259
  # Set effects for specific levels (level 1 is always reference)
@@ -264,7 +271,7 @@ import pandas as pd
264
271
  data = pd.read_csv("my_data.csv")
265
272
 
266
273
  # Upload with automatic type detection
267
- model = mcpower.MCPower("mpg = hp + wt + cyl")
274
+ model = MCPower("mpg = hp + wt + cyl")
268
275
  model.upload_data(data[["hp", "wt", "cyl"]])
269
276
  model.set_effects("hp=0.5, wt=0.3, cyl[2]=0.2, cyl[3]=0.4")
270
277
  model.find_power(sample_size=100)
@@ -308,13 +315,28 @@ model.upload_data(
308
315
  ```python
309
316
  # Testing multiple effects? Control false positives
310
317
  model.find_power(
311
- sample_size=200,
318
+ sample_size=200,
312
319
  target_test="treatment,covariate,treatment:covariate",
313
320
  correction="Benjamini-Hochberg",
314
321
  scenarios=True # Test robustness too!
315
322
  )
316
323
  ```
317
324
 
325
+ ### Post-Hoc Pairwise Comparisons (Tukey HSD)
326
+ ```python
327
+ # Compare specific factor levels with Tukey correction
328
+ model = MCPower("outcome = group + covariate")
329
+ model.set_variable_type("group=(factor,3)")
330
+ model.set_effects("group[2]=0.4, group[3]=0.6, covariate=0.3")
331
+
332
+ # Use "vs" syntax for pairwise comparisons + correction="tukey"
333
+ model.find_power(
334
+ sample_size=150,
335
+ target_test="group[0] vs group[1], group[0] vs group[2]",
336
+ correction="tukey"
337
+ )
338
+ ```
339
+
318
340
  ### Test the single violation of assumptions.
319
341
  ```python
320
342
  # Customize how much "messiness" to add in scenarios
@@ -325,27 +347,37 @@ model.set_heteroskedasticity(0.15) # Violation of equal variance assumption
325
347
  model.find_sample_size(target_test="treatment", scenarios=False)
326
348
  ```
327
349
 
328
- ### Mixed-Effects Models (Random Intercept)
350
+ ### Mixed-Effects Models (Experimental)
329
351
  ```python
330
- import mcpower
352
+ from mcpower import MCPower
331
353
 
332
- # Define a model with a random intercept for clustered data
333
- model = mcpower.MCPower("satisfaction ~ treatment + motivation + (1|school)")
354
+ # Random intercept — clustered data
355
+ model = MCPower("satisfaction ~ treatment + motivation + (1|school)")
334
356
  model.set_cluster("school", ICC=0.2, n_clusters=20)
335
357
  model.set_effects("treatment=0.5, motivation=0.3")
336
358
  model.set_variable_type("treatment=binary")
337
-
338
- # Allow some convergence failures (common with mixed models)
339
- model.set_max_failed_simulations(0.10) # Up to 10% failures tolerated
340
-
341
- # Total sample_size is split across clusters: 1000 / 20 = 50 per cluster
359
+ model.set_max_failed_simulations(0.10)
360
+ model.find_power(sample_size=1000) # 1000/20 = 50 per cluster
361
+
362
+ # Random slopes — effect varies across clusters
363
+ model = MCPower("y ~ x1 + (1 + x1|school)")
364
+ model.set_cluster("school", ICC=0.2, n_clusters=20,
365
+ random_slopes=["x1"], slope_variance=0.1,
366
+ slope_intercept_corr=0.3)
367
+ model.set_effects("x1=0.5")
368
+ model.set_max_failed_simulations(0.30)
342
369
  model.find_power(sample_size=1000)
370
+
371
+ # Nested random effects — students in classrooms in schools
372
+ model = MCPower("y ~ treatment + (1|school/classroom)")
373
+ model.set_cluster("school", ICC=0.15, n_clusters=10)
374
+ model.set_cluster("classroom", ICC=0.10, n_per_parent=3) # 3 classrooms per school
375
+ model.set_effects("treatment=0.5")
376
+ model.set_max_failed_simulations(0.30)
377
+ model.find_power(sample_size=1500)
343
378
  ```
344
379
 
345
- **Constraints:**
346
- - Only random intercepts `(1|group)` — random slopes are not yet supported
347
- - At least 25 observations per cluster and 10 observations per model parameter
348
- - ICC must be between 0.1 and 0.9 (or exactly 0)
380
+ See the [Mixed-Effects Models wiki page](https://github.com/pawlenartowicz/MCPower/wiki/Mixed-Effects-Models) for detailed documentation on all model types, parameters, and design recommendations.
349
381
 
350
382
  ### More precision
351
383
  ```python
@@ -392,8 +424,11 @@ model.find_power(sample_size=200, progress_callback=False)
392
424
  | **Factor variables** | **`model.set_variable_type("var=(factor,3)")`** |
393
425
  | **Factor effects** | **`model.set_effects("var[2]=0.5, var[3]=0.7")`** |
394
426
  | Correlated predictors | `model.set_correlations("corr(var1, var2)=0.4")` |
395
- | Multiple testing correction | Add `correction="FDR"`, `"Holm"`, or `"Bonferroni"`|
396
- | Mixed model (random intercept) | `MCPower("y ~ x + (1\|group)")` + `model.set_cluster(...)` |
427
+ | Multiple testing correction | Add `correction="FDR"`, `"Holm"`, `"Bonferroni"`, or `"Tukey"`|
428
+ | Post-hoc pairwise comparison | `target_test="group[0] vs group[1]"` with `correction="tukey"` |
429
+ | Mixed model (random intercept) | `MCPower("y ~ x + (1\|group)")` + `model.set_cluster(...)` (experimental) |
430
+ | Random slopes | `MCPower("y ~ x + (1+x\|group)")` + `set_cluster(..., random_slopes=["x"], slope_variance=0.1)` (experimental) |
431
+ | Nested random effects | `MCPower("y ~ x + (1\|A/B)")` + two `set_cluster()` calls (experimental) |
397
432
  | Reproducible results | `model.set_seed(42)` |
398
433
  | Get results as dict | Add `return_results=True` to either method |
399
434
  | Stricter significance | `model.set_alpha(0.01)` |
@@ -465,7 +500,7 @@ model.set_variable_type("""
465
500
  ### Factor Variables in Detail
466
501
  ```python
467
502
  # Factor variables are categorical with multiple levels
468
- model = mcpower.MCPower("outcome = treatment + education")
503
+ model = MCPower("outcome = treatment + education")
469
504
 
470
505
  # Create factors
471
506
  model.set_variable_type("treatment=(factor,3), education=(factor,4)")
@@ -539,9 +574,23 @@ model.set_correlations("(x1, x2)=0.3, (x1, x3)=-0.2")
539
574
  - Python ≥ 3.10
540
575
  - NumPy, SciPy, matplotlib, Pandas, joblib
541
576
  - C++ compiler (automatically used during install for native backend; falls back to Python if unavailable)
542
- - statsmodels (optional, for mixed-effects models — install with `pip install mcpower[mixed]`)
577
+ - statsmodels (optional, for mixed-effects models — install with `pip install mcpower[lme]`)
543
578
  - Numba (optional, for JIT compilation fallback — install with `pip install mcpower[JIT]`)
544
579
 
580
+
581
+ ## Documentation
582
+
583
+ Full documentation is available on the **[MCPower Wiki](https://github.com/pawlenartowicz/MCPower/wiki)**, including:
584
+
585
+ - [Quick Start](https://github.com/pawlenartowicz/MCPower/wiki/Quick-Start)
586
+ - [Model Specification](https://github.com/pawlenartowicz/MCPower/wiki/Model-Specification)
587
+ - [Variable Types](https://github.com/pawlenartowicz/MCPower/wiki/Variable-Types)
588
+ - [Effect Sizes](https://github.com/pawlenartowicz/MCPower/wiki/Effect-Sizes)
589
+ - [Mixed-Effects Models](https://github.com/pawlenartowicz/MCPower/wiki/Mixed-Effects-Models) (random intercepts, slopes, nested effects)
590
+ - [ANOVA & Post-Hoc Tests](https://github.com/pawlenartowicz/MCPower/wiki/ANOVA-and-Post-Hoc-Tests)
591
+ - [Scenario Analysis](https://github.com/pawlenartowicz/MCPower/wiki/Scenario-Analysis)
592
+ - [API Reference](https://github.com/pawlenartowicz/MCPower/wiki/API-Reference)
593
+
545
594
  ## Need Help?
546
595
 
547
596
  - **Issues**: [GitHub Issues](https://github.com/pawlenartowicz/MCPower/issues)
@@ -552,7 +601,7 @@ model.set_correlations("(x1, x2)=0.3, (x1, x3)=-0.2")
552
601
  - ✅ Scenarios, robustness analysis
553
602
  - ✅ Factor variables (categorical predictors)
554
603
  - ✅ C++ native backend (pybind11 + Eigen, 3x speedup)
555
- - 🚧 Mixed Effects Models (partly implemented — random intercept only)
604
+ - ⚠️ Mixed Effects Models (random intercepts, random slopes, nested effects) — experimental
556
605
  - 🚧 Logistic Regression (coming soon)
557
606
  - 🚧 ANOVA (coming soon)
558
607
  - 🚧 Guide about methods, corrections (coming soon)