misleep 0.3.0__tar.gz → 0.3.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.

Potentially problematic release.


This version of misleep might be problematic. Click here for more details.

Files changed (133) hide show
  1. {misleep-0.3.0 → misleep-0.3.2}/CHANGELOG.md +68 -0
  2. {misleep-0.3.0/src/misleep.egg-info → misleep-0.3.2}/PKG-INFO +10 -12
  3. {misleep-0.3.0 → misleep-0.3.2}/README.md +4 -6
  4. {misleep-0.3.0 → misleep-0.3.2}/docs/api_reference.md +6 -3
  5. {misleep-0.3.0 → misleep-0.3.2}/docs/config_file.md +4 -4
  6. {misleep-0.3.0 → misleep-0.3.2}/docs/getting_started.md +8 -9
  7. misleep-0.3.2/docs/imgs/gui/main_light.png +0 -0
  8. {misleep-0.3.0 → misleep-0.3.2}/docs/index.md +4 -0
  9. {misleep-0.3.0 → misleep-0.3.2}/pyproject.toml +14 -7
  10. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/__init__.py +1 -1
  11. misleep-0.3.2/src/misleep/_compat.py +36 -0
  12. misleep-0.3.2/src/misleep/analysis/auto_stage.py +176 -0
  13. misleep-0.3.2/src/misleep/analysis/autostage/__init__.py +24 -0
  14. misleep-0.3.2/src/misleep/analysis/autostage/benchmark.py +186 -0
  15. misleep-0.3.2/src/misleep/analysis/autostage/features.py +368 -0
  16. misleep-0.3.2/src/misleep/analysis/autostage/hmm.py +110 -0
  17. misleep-0.3.2/src/misleep/analysis/autostage/model.py +27 -0
  18. misleep-0.3.2/src/misleep/analysis/autostage/postprocess.py +52 -0
  19. misleep-0.3.2/src/misleep/analysis/models/benchmark_models.pkl +0 -0
  20. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/analysis/transformer/__init__.py +3 -3
  21. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/analysis/transformer/inference.py +3 -3
  22. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/config/__init__.py +4 -3
  23. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/config/default_config.ini +2 -2
  24. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/data/midata.py +15 -5
  25. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/app.py +26 -19
  26. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/dialogs.py +73 -15
  27. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/main_window.py +2834 -2699
  28. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/style.py +20 -7
  29. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/uis/about.ui +3 -3
  30. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/uis/about_ui.py +2 -2
  31. misleep-0.3.2/src/misleep/gui/uis/auto_stage_lightGBM_dialog.ui +209 -0
  32. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/uis/auto_stage_lightGBM_dialog_ui.py +104 -46
  33. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/io/annotation.py +95 -18
  34. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/io/base.py +2 -0
  35. {misleep-0.3.0 → misleep-0.3.2/src/misleep.egg-info}/PKG-INFO +10 -12
  36. {misleep-0.3.0 → misleep-0.3.2}/src/misleep.egg-info/SOURCES.txt +8 -7
  37. {misleep-0.3.0 → misleep-0.3.2}/src/misleep.egg-info/entry_points.txt +3 -0
  38. {misleep-0.3.0 → misleep-0.3.2}/src/misleep.egg-info/requires.txt +6 -2
  39. {misleep-0.3.0 → misleep-0.3.2}/tests/test_analysis.py +1 -1
  40. {misleep-0.3.0 → misleep-0.3.2}/tests/test_data.py +53 -0
  41. {misleep-0.3.0 → misleep-0.3.2}/tests/test_gui.py +1 -1
  42. {misleep-0.3.0 → misleep-0.3.2}/tests/test_io.py +30 -0
  43. misleep-0.3.0/docs/imgs/gui/main_dark.png +0 -0
  44. misleep-0.3.0/docs/imgs/gui/main_light.png +0 -0
  45. misleep-0.3.0/src/misleep/analysis/auto_stage.py +0 -158
  46. misleep-0.3.0/src/misleep/analysis/models/P30_EEG_F_lightgbm.pkl +0 -0
  47. misleep-0.3.0/src/misleep/analysis/models/P30_EEG_P_lightgbm.pkl +0 -0
  48. misleep-0.3.0/src/misleep/analysis/models/ado_EEG_F_lightgbm.pkl +0 -0
  49. misleep-0.3.0/src/misleep/analysis/models/ado_EEG_P_lightgbm.pkl +0 -0
  50. misleep-0.3.0/src/misleep/analysis/models/adult_EEG_F_lightgbm.pkl +0 -0
  51. misleep-0.3.0/src/misleep/analysis/models/adult_EEG_P_lightgbm.pkl +0 -0
  52. misleep-0.3.0/src/misleep/gui/uis/auto_stage_lightGBM_dialog.ui +0 -124
  53. {misleep-0.3.0 → misleep-0.3.2}/LICENSE +0 -0
  54. {misleep-0.3.0 → misleep-0.3.2}/MANIFEST.in +0 -0
  55. {misleep-0.3.0 → misleep-0.3.2}/docs/data_formats.md +0 -0
  56. {misleep-0.3.0 → misleep-0.3.2}/docs/developer_guide.md +0 -0
  57. {misleep-0.3.0 → misleep-0.3.2}/docs/imgs/entire_logo.png +0 -0
  58. {misleep-0.3.0 → misleep-0.3.2}/docs/imgs/logo.png +0 -0
  59. {misleep-0.3.0 → misleep-0.3.2}/docs/user_guide.md +0 -0
  60. {misleep-0.3.0 → misleep-0.3.2}/examples/auto_stage_example.py +0 -0
  61. {misleep-0.3.0 → misleep-0.3.2}/examples/basic_usage.py +0 -0
  62. {misleep-0.3.0 → misleep-0.3.2}/examples/extend_io_example.py +0 -0
  63. {misleep-0.3.0 → misleep-0.3.2}/setup.cfg +0 -0
  64. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/__main__.py +0 -0
  65. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/analysis/__init__.py +0 -0
  66. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/analysis/detection.py +0 -0
  67. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/analysis/features.py +0 -0
  68. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/analysis/models/__init__.py +0 -0
  69. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/analysis/transformer/checkpoints/CausalTransformer_best.pt +0 -0
  70. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/analysis/transformer/checkpoints/__init__.py +0 -0
  71. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/analysis/transformer/configs.py +0 -0
  72. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/analysis/transformer/data.py +0 -0
  73. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/analysis/transformer/models.py +0 -0
  74. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/analysis/transformer/preprocessing.py +0 -0
  75. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/data/__init__.py +0 -0
  76. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/data/annotation.py +0 -0
  77. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/__init__.py +0 -0
  78. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/config_dialog.py +0 -0
  79. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/event_filters.py +0 -0
  80. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/qt_utils.py +0 -0
  81. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/resources/__init__.py +0 -0
  82. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/resources/entire_logo.png +0 -0
  83. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/resources/logo.png +0 -0
  84. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/resources/misleep.ico +0 -0
  85. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/resources/misleep.qrc +0 -0
  86. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/resources/misleep_rc.py +0 -0
  87. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/spec_window.py +0 -0
  88. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/uis/SWA_detect_dialog.ui +0 -0
  89. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/uis/SWA_detect_dialog_ui.py +0 -0
  90. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/uis/__init__.py +0 -0
  91. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/uis/auto_stage_causalTransformer_dialog.ui +0 -0
  92. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/uis/auto_stage_causalTransformer_dialog_ui.py +0 -0
  93. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/uis/horizontal_line_dialog.ui +0 -0
  94. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/uis/horizontal_line_dialog_ui.py +0 -0
  95. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/uis/label_dialog.ui +0 -0
  96. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/uis/label_dialog_ui.py +0 -0
  97. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/uis/main_window.ui +0 -0
  98. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/uis/main_window_ui.py +0 -0
  99. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/uis/save_data_dialog.ui +0 -0
  100. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/uis/save_data_dialog_ui.py +0 -0
  101. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/uis/spec_window.ui +0 -0
  102. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/uis/spec_window_ui.py +0 -0
  103. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/uis/spindle_detect_dialog.ui +0 -0
  104. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/uis/spindle_detect_dialog_ui.py +0 -0
  105. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/uis/state_spectral_dialog.ui +0 -0
  106. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/uis/state_spectral_dialog_ui.py +0 -0
  107. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/uis/transfer_result_dialog.ui +0 -0
  108. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/uis/transfer_result_dialog_ui.py +0 -0
  109. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/gui/workers.py +0 -0
  110. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/io/__init__.py +0 -0
  111. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/io/array.py +0 -0
  112. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/io/edf.py +0 -0
  113. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/io/mat.py +0 -0
  114. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/logger.py +0 -0
  115. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/preprocessing/__init__.py +0 -0
  116. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/preprocessing/artifacts.py +0 -0
  117. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/preprocessing/filtering.py +0 -0
  118. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/preprocessing/segment.py +0 -0
  119. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/preprocessing/spectral.py +0 -0
  120. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/utils/__init__.py +0 -0
  121. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/utils/annotation.py +0 -0
  122. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/utils/entropy.py +0 -0
  123. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/utils/misc.py +0 -0
  124. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/utils/time_utils.py +0 -0
  125. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/viz/__init__.py +0 -0
  126. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/viz/hypnogram.py +0 -0
  127. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/viz/signals.py +0 -0
  128. {misleep-0.3.0 → misleep-0.3.2}/src/misleep/viz/spectral.py +0 -0
  129. {misleep-0.3.0 → misleep-0.3.2}/src/misleep.egg-info/dependency_links.txt +0 -0
  130. {misleep-0.3.0 → misleep-0.3.2}/src/misleep.egg-info/top_level.txt +0 -0
  131. {misleep-0.3.0 → misleep-0.3.2}/tests/test_preprocessing.py +0 -0
  132. {misleep-0.3.0 → misleep-0.3.2}/tests/test_segment.py +0 -0
  133. {misleep-0.3.0 → misleep-0.3.2}/tests/test_viz_config.py +0 -0
@@ -5,6 +5,74 @@ All notable changes to MiSleep are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.3.2] — 2026-08-24
9
+
10
+ ### Fixed
11
+
12
+ - **Filtering broke on fractional sampling frequencies**: `MiData.add()` (used
13
+ by `MiData.filter()`) recomputed the duration with
14
+ `math.floor(len(signal) / sf)` from a signal that had already been
15
+ truncated to `int(duration * sf)` samples. When `sf` is a decimal (e.g.
16
+ `99.9927` Hz), that came out one second short, so filtering either raised a
17
+ spurious "new signal is shorter" error or produced a channel whose length /
18
+ duration did not match the original signal, which then failed the signal /
19
+ label consistency checks in the GUI. The check now compares sample counts
20
+ against `int(duration * sf)` directly, so filtering preserves the sample
21
+ count and duration for any positive `sf` while still rejecting genuinely
22
+ short signals.
23
+
24
+ ## [0.3.1] — 2026-08-18
25
+
26
+ ### Added
27
+
28
+ - **Epoch / BIDS annotation import**: the CSV/TSV annotation loader now
29
+ understands `onset` / `duration` / `stage` (BIDS `events.tsv`),
30
+ headerless `[epoch index, epoch second, epoch label]` and
31
+ `[epoch second, epoch label]` tables (with or without a header row), on
32
+ top of the existing `start`/`end`/`state` and per-second layouts.
33
+
34
+ ### Changed
35
+
36
+ - **Faster startup**: the one-time warm-up (scipy imports, matplotlib font
37
+ cache) now runs in a background thread so the window appears instantly,
38
+ and the redundant `SetProcessDpiAwareness` calls were removed (Qt6
39
+ manages its own per-monitor DPI context - the old call produced an
40
+ "Access is denied" warning).
41
+ - **Hypnogram layout**: the initial hypnogram strip is shorter (drag the
42
+ splitter to resize), the figure now fills the whole strip even without
43
+ the confidence chart, and the axis margins were widened so the time
44
+ tick labels are always visible (no overflow below the panel).
45
+ - **Auto-staging defaults**: the low-confidence rate now defaults to
46
+ **0.8** and the HMM temperature to **0.1**. The HMM decoding is
47
+ confidence-based: the LightGBM probabilities drive the emissions and
48
+ the learned transition matrix only constrains state transitions.
49
+
50
+ - **LightGBM auto-staging rewritten on the benchmark models**
51
+ (`benchmark_models.pkl`, 6 channel-combo models). Every mouse age and
52
+ EEG site uses the same model; the dialog lets the user pick the EEG
53
+ channel, an optional EMG channel and an optional ACC channel (ACC
54
+ requires EMG), the EEG site, the age group, the HMM temperature and the
55
+ **low-confidence rate**. Predictions are decoded with a learned 3-state
56
+ HMM and cover the **whole recording** (the first window and the tail
57
+ take the nearest epoch). The per-epoch **confidence** is attached to the
58
+ annotation and drawn as a separate line chart below the hypnogram with a
59
+ dashed threshold line - static, so page flips stay fast and label edits
60
+ never recompute it. The *cover current* checkbox only fills the INIT
61
+ states, keeping already-scored labels.
62
+
63
+ - **Python support**: the package now supports **Python 3.8 through 3.14**
64
+ (`requires-python >= 3.8`); annotations and resource loading were made
65
+ 3.8-compatible, and the GUI extra pins the last 3.8-compatible PySide6
66
+ line on older Pythons.
67
+ - **Dependencies**: the packaging metadata now reflects the complete set
68
+ of runtime prerequisites, and **PySide6 moved into the base
69
+ dependencies** - a plain `pip install misleep` installs the GUI and all
70
+ core packages automatically (no separate PySide6 install needed).
71
+ LightGBM / torch remain optional extras.
72
+ - **Documentation**: bright theme with a light header (readable
73
+ black-on-transparent logo), full-width MiSleep banner on the home page,
74
+ single GUI preview screenshot.
75
+
8
76
  ## [0.3.0] — 2026-08-18
9
77
 
10
78
  ### Fixed
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.1
2
2
  Name: misleep
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: MiSleep: Mice sleep EEG/EMG visualization, scoring, and analysis
5
5
  Author-email: Xueqiang Wang <swang9194@gmail.com>
6
6
  Maintainer-email: Xueqiang Wang <swang9194@gmail.com>
@@ -43,6 +43,7 @@ Classifier: Intended Audience :: Science/Research
43
43
  Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
44
44
  Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
45
45
  Classifier: Programming Language :: Python :: 3
46
+ Classifier: Programming Language :: Python :: 3.8
46
47
  Classifier: Programming Language :: Python :: 3.9
47
48
  Classifier: Programming Language :: Python :: 3.10
48
49
  Classifier: Programming Language :: Python :: 3.11
@@ -53,7 +54,7 @@ Classifier: License :: OSI Approved :: BSD License
53
54
  Classifier: Operating System :: Microsoft :: Windows
54
55
  Classifier: Operating System :: POSIX :: Linux
55
56
  Classifier: Operating System :: MacOS
56
- Requires-Python: >=3.9
57
+ Requires-Python: >=3.8
57
58
  Description-Content-Type: text/markdown
58
59
  License-File: LICENSE
59
60
  Requires-Dist: numpy>=1.20
@@ -65,19 +66,18 @@ Requires-Dist: mat73>=0.60
65
66
  Requires-Dist: openpyxl>=3.0
66
67
  Requires-Dist: joblib>=1.1
67
68
  Requires-Dist: scikit-learn>=1.0
69
+ Requires-Dist: PySide6>=6.6; python_version >= "3.9"
70
+ Requires-Dist: PySide6<6.6,>=6.4; python_version < "3.9"
68
71
  Provides-Extra: gui
69
- Requires-Dist: PySide6>=6.6; extra == "gui"
70
72
  Provides-Extra: analysis
71
73
  Requires-Dist: lightgbm>=3.3; extra == "analysis"
72
74
  Provides-Extra: transformer
73
75
  Requires-Dist: torch>=2.0; extra == "transformer"
74
76
  Provides-Extra: full
75
- Requires-Dist: PySide6>=6.6; extra == "full"
76
77
  Requires-Dist: lightgbm>=3.3; extra == "full"
77
78
  Provides-Extra: dev
78
79
  Requires-Dist: pytest>=7.0; extra == "dev"
79
80
  Requires-Dist: pytest-cov; extra == "dev"
80
- Dynamic: license-file
81
81
 
82
82
  # MiSleep
83
83
 
@@ -122,19 +122,17 @@ The name *MiSleep* comes from "**Mi**ce **Sleep**" and sounds like
122
122
  ## Installation
123
123
 
124
124
  ```bash
125
- # Core package (data, I/O, preprocessing, analysis, visualization)
125
+ # Base install - includes everything (data, I/O, preprocessing, analysis,
126
+ # visualization AND the PySide6 GUI)
126
127
  pip install misleep
127
128
 
128
- # With the PySide6 GUI (recommended for most users)
129
- pip install "misleep[gui]"
130
-
131
- # With everything, including the LightGBM auto-staging model
129
+ # With the LightGBM auto-staging model as well
132
130
  pip install "misleep[full]"
133
131
 
134
132
  # For development / contributing
135
133
  git clone https://github.com/BryanWang0702/MiSleep.git
136
134
  cd misleepv3
137
- pip install -e ".[gui,analysis,dev]"
135
+ pip install -e ".[analysis,dev]"
138
136
  ```
139
137
 
140
138
  > **Note on PyTorch:** the causal-transformer auto-staging model requires
@@ -41,19 +41,17 @@ The name *MiSleep* comes from "**Mi**ce **Sleep**" and sounds like
41
41
  ## Installation
42
42
 
43
43
  ```bash
44
- # Core package (data, I/O, preprocessing, analysis, visualization)
44
+ # Base install - includes everything (data, I/O, preprocessing, analysis,
45
+ # visualization AND the PySide6 GUI)
45
46
  pip install misleep
46
47
 
47
- # With the PySide6 GUI (recommended for most users)
48
- pip install "misleep[gui]"
49
-
50
- # With everything, including the LightGBM auto-staging model
48
+ # With the LightGBM auto-staging model as well
51
49
  pip install "misleep[full]"
52
50
 
53
51
  # For development / contributing
54
52
  git clone https://github.com/BryanWang0702/MiSleep.git
55
53
  cd misleepv3
56
- pip install -e ".[gui,analysis,dev]"
54
+ pip install -e ".[analysis,dev]"
57
55
  ```
58
56
 
59
57
  > **Note on PyTorch:** the causal-transformer auto-staging model requires
@@ -119,12 +119,15 @@ Default state map: `{1: 'NREM', 2: 'REM', 3: 'Wake', 4: 'Init'}`.
119
119
 
120
120
  ### Automatic staging
121
121
 
122
- * `auto_stage_gbm(EEG, EMG, label, sf, EEG_channel='F', mouse_age='adult')`
123
- → list of per-second states — LightGBM auto staging.
122
+ * `auto_stage_gbm(EEG, EMG=None, label, sf, EEG_channel='F', mouse_age='adult',
123
+ ACC=None, return_probs=False)` → list of per-second states (plus per-epoch
124
+ confidence when `return_probs=True`) — LightGBM auto staging with the
125
+ benchmark models (all ages use the same model; EMG and ACC are optional,
126
+ ACC requires EMG).
124
127
  * `result_constraints(pred_prob)` → list — smooth/constrain raw model
125
128
  probabilities into state labels.
126
129
  * `model_path(mouse_age='adult', EEG_channel='F')` → Path — packaged
127
- LightGBM model path.
130
+ benchmark model path.
128
131
  * `misleep.analysis.transformer.auto_stage_llm(EEG, EMG, label=None,
129
132
  config=None)` → list — transformer auto staging (requires torch).
130
133
  * `misleep.analysis.transformer.AutoStageConfig` — dataclass of
@@ -23,8 +23,8 @@ wipe your personal settings. Open the user file from the GUI via
23
23
 
24
24
  | key | default | meaning |
25
25
  |-----|---------|---------|
26
- | `version` | `v0.3.0` | displayed in the About dialog |
27
- | `updatetime` | `2025/01/01` | displayed in the About dialog |
26
+ | `version` | `v0.3.2` | displayed in the About dialog |
27
+ | `updatetime` | `2026/08/24` | displayed in the About dialog |
28
28
  | `marker` | `['half_signal', 'first REM', ...]` | marker label list (label picker) |
29
29
  | `startend` | `['high_theta', 'REM', ...]` | start-end label list (label picker) |
30
30
  | `statemap` | `{"1": "NREM", "2": "REM", "3": "Wake", "4": "INIT", ...}` | state code -> name mapping (JSON) |
@@ -48,8 +48,8 @@ wipe your personal settings. Open the user file from the GUI via
48
48
 
49
49
  ```ini
50
50
  [gui]
51
- version = v0.3.0
52
- updatetime = 2025/01/01
51
+ version = v0.3.2
52
+ updatetime = 2026/08/24
53
53
  marker = ['half_signal', 'first REM', 'WindEEG', 'W-R', 'maker']
54
54
  startend = ['high_theta', 'REM', 'Wake', 'Spindle', 'SWA', 'start end label', 'start end label']
55
55
  statemap = {"1": "NREM", "2": "REM", "3": "Wake", "4": "INIT", "5": "IS", "6": "MicroArousal"}
@@ -14,19 +14,20 @@
14
14
  ## Installation
15
15
 
16
16
  ```bash
17
- # From PyPI
17
+ # From PyPI - the base install already includes the PySide6 GUI and all
18
+ # core dependencies
18
19
  pip install misleep
19
20
 
20
- # With the GUI
21
- pip install "misleep[gui]"
22
-
23
- # Everything for a full experience (GUI + LightGBM auto staging)
21
+ # Everything for a full experience (adds LightGBM auto staging)
24
22
  pip install "misleep[full]"
25
23
 
24
+ # Causal-transformer auto staging (PyTorch, not on all platforms)
25
+ pip install "misleep[transformer]"
26
+
26
27
  # Development install from the repository
27
28
  git clone https://github.com/BryanWang0702/MiSleep.git
28
29
  cd misleepv3
29
- pip install -e ".[gui,analysis,dev]"
30
+ pip install -e ".[analysis,dev]"
30
31
  ```
31
32
 
32
33
  > **Note for Apple Silicon (macOS):** `torch` and `lightgbm` ship official
@@ -56,9 +57,7 @@ on the right. A light and a dark theme are available
56
57
  (`Ctrl+Shift+T` to toggle, or Settings &rarr; General &rarr; Theme /
57
58
  Color scheme).
58
59
 
59
- ![MiSleep main window (light theme)](imgs/gui/main_light.png)
60
-
61
- ![MiSleep main window (dark theme)](imgs/gui/main_dark.png)
60
+ ![MiSleep main window](imgs/gui/main_light.png)
62
61
 
63
62
  ### Opening files from the command line
64
63
 
@@ -1,5 +1,9 @@
1
1
  # MiSleep documentation
2
2
 
3
+ <p align="center">
4
+ <img src="imgs/entire_logo.png" alt="MiSleep" width="80%"/>
5
+ </p>
6
+
3
7
  MiSleep is an open-source Python toolbox for **mice sleep EEG/EMG**
4
8
  visualization, scoring, and analysis. This documentation covers both the
5
9
  desktop GUI and the Python library.
@@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "misleep"
7
- version = "0.3.0"
7
+ version = "0.3.2"
8
8
  description = "MiSleep: Mice sleep EEG/EMG visualization, scoring, and analysis"
9
9
  readme = "README.md"
10
- requires-python = ">=3.9"
10
+ requires-python = ">=3.8"
11
11
  license = { file = "LICENSE" }
12
12
  authors = [
13
13
  { name = "Xueqiang Wang", email = "swang9194@gmail.com" },
@@ -25,6 +25,7 @@ classifiers = [
25
25
  "Topic :: Scientific/Engineering :: Bio-Informatics",
26
26
  "Topic :: Scientific/Engineering :: Medical Science Apps.",
27
27
  "Programming Language :: Python :: 3",
28
+ "Programming Language :: Python :: 3.8",
28
29
  "Programming Language :: Python :: 3.9",
29
30
  "Programming Language :: Python :: 3.10",
30
31
  "Programming Language :: Python :: 3.11",
@@ -46,13 +47,16 @@ dependencies = [
46
47
  "openpyxl>=3.0",
47
48
  "joblib>=1.1",
48
49
  "scikit-learn>=1.0",
50
+ # GUI toolkit - part of the base install so `pip install misleep` just
51
+ # works (PySide6 >= 6.6 needs Python 3.9+; 3.8 gets the 6.5 line)
52
+ "PySide6>=6.6; python_version >= '3.9'",
53
+ "PySide6>=6.4,<6.6; python_version < '3.9'",
49
54
  ]
50
55
 
51
56
  [project.optional-dependencies]
52
- # PySide6-based graphical user interface
53
- gui = [
54
- "PySide6>=6.6",
55
- ]
57
+ # The GUI (PySide6) is now part of the base dependencies; this empty extra
58
+ # is kept so existing `misleep[gui]` install commands keep working.
59
+ gui = []
56
60
  # Automatic sleep staging with the LightGBM model
57
61
  analysis = [
58
62
  "lightgbm>=3.3",
@@ -63,7 +67,6 @@ transformer = [
63
67
  ]
64
68
  # Everything needed for a full MiSleep experience
65
69
  full = [
66
- "PySide6>=6.6",
67
70
  "lightgbm>=3.3",
68
71
  ]
69
72
  # Tooling for developers / contributors
@@ -81,6 +84,10 @@ Documentation = "https://bryanwang.cn/MiSleep/"
81
84
  [project.scripts]
82
85
  misleep = "misleep.gui.app:main"
83
86
 
87
+ # Windowless launcher (no console window) - creates misleepw.exe on Windows
88
+ [project.gui-scripts]
89
+ misleepw = "misleep.gui.app:main"
90
+
84
91
  [project.entry-points."misleep.signal_readers"]
85
92
  mat = "misleep.io.mat:load_mat"
86
93
  edf = "misleep.io.edf:load_edf"
@@ -22,7 +22,7 @@ works without PySide6; import ``misleep.gui`` explicitly when needed.
22
22
  from importlib import import_module
23
23
 
24
24
  __author__ = "Xueqiang Wang <swang9194@gmail.com>"
25
- __version__ = "0.3.0"
25
+ __version__ = "0.3.2"
26
26
 
27
27
  # Importing ``misleep`` used to import scipy, pandas, sklearn and matplotlib
28
28
  # immediately. The GUI necessarily imports some of those later, but making
@@ -0,0 +1,36 @@
1
+ # -*- coding: UTF-8 -*-
2
+ """Compatibility helpers so MiSleep runs on Python 3.8 through 3.14.
3
+
4
+ Only packaging/resource helpers live here; there is no application logic.
5
+ """
6
+
7
+ from pathlib import Path
8
+
9
+
10
+ def resource_dir(package: str) -> Path:
11
+ """Return the filesystem directory of a package (3.8-compatible)."""
12
+ try:
13
+ from importlib.resources import files
14
+
15
+ return Path(str(files(package)))
16
+ except (ImportError, AttributeError): # pragma: no cover - Python 3.8
17
+ import importlib
18
+
19
+ return Path(str(importlib.import_module(package).__path__[0]))
20
+
21
+
22
+ def resource_path(package: str, name: str) -> Path:
23
+ """Return the filesystem path of a package resource file.
24
+
25
+ ``importlib.resources.files`` (Python 3.9+) is preferred; on Python
26
+ 3.8 the older ``importlib.resources.path`` is used instead.
27
+ """
28
+ try:
29
+ from importlib.resources import files
30
+
31
+ return Path(str(files(package).joinpath(name)))
32
+ except (ImportError, AttributeError): # pragma: no cover - Python 3.8
33
+ import importlib.resources
34
+
35
+ with importlib.resources.path(package, name) as path:
36
+ return Path(str(path))
@@ -0,0 +1,176 @@
1
+ # -*- coding: UTF-8 -*-
2
+ """Automatic sleep staging with the benchmark LightGBM models.
3
+
4
+ The packaged ``benchmark_models.pkl`` contains six LightGBM classifiers
5
+ (one per channel combo: EEG site x optional EMG x optional ACC), each
6
+ combined with a per-recording z-score normalisation, a learned 3-state HMM
7
+ and class priors. **Every mouse age and EEG site use the same model file**;
8
+ the age / site arguments are kept only for API compatibility.
9
+
10
+ The pipeline (adapted from the rodent-autostage benchmark):
11
+
12
+ 1. feature extraction on 5 s epochs (STFT band powers + time-domain
13
+ features + EEG-EMG coherence, W = 10 s window at 1 s stride),
14
+ 2. per-recording z-score normalisation,
15
+ 3. LightGBM class probabilities,
16
+ 4. HMM / Viterbi decoding with a softmax temperature,
17
+ 5. physiologically constrained smoothing (Init -> Wake, REM never after
18
+ Wake, single-epoch flip removal),
19
+ 6. per-second expansion of the per-epoch states.
20
+
21
+ Besides the per-second labels, the per-epoch probability of the predicted
22
+ state is returned (the *confidence* used by the GUI to mark low-confidence
23
+ regions in the hypnogram).
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ import copy
29
+ from pathlib import Path
30
+
31
+ import numpy as np
32
+
33
+ from misleep._compat import resource_dir
34
+ from misleep.logger import logger
35
+
36
+
37
+ def _model_dir() -> Path:
38
+ """Return the directory containing the packaged LightGBM models."""
39
+ return resource_dir("misleep.analysis.models")
40
+
41
+
42
+ def model_path(mouse_age="adult", EEG_channel="F") -> Path:
43
+ """Return the path of the packaged benchmark models.
44
+
45
+ A single model file serves every mouse age and EEG site, so
46
+ ``mouse_age`` and ``EEG_channel`` are accepted for API compatibility
47
+ only.
48
+
49
+ Parameters
50
+ ----------
51
+ mouse_age : str
52
+ Accepted for API compatibility (ignored).
53
+ EEG_channel : str
54
+ Accepted for API compatibility (ignored).
55
+
56
+ Returns
57
+ -------
58
+ Path
59
+ """
60
+ return _model_dir() / "benchmark_models.pkl"
61
+
62
+
63
+ def result_constraints(pred_prob):
64
+ """Post-process raw model probabilities into smooth state labels.
65
+
66
+ Applies the following constraints:
67
+
68
+ 1. A REM probability below 0.15 is overridden.
69
+ 2. REM directly after Wake is set to NREM.
70
+ 3. State 4 (Init) is set to Wake.
71
+ 4. A single epoch between two identical states takes that state.
72
+
73
+ Parameters
74
+ ----------
75
+ pred_prob : ndarray
76
+ Model output probabilities, shape ``(n_windows, n_classes)``.
77
+
78
+ Returns
79
+ -------
80
+ list of int
81
+ Per-window predicted state codes (1-indexed).
82
+ """
83
+ pred_prob = copy.deepcopy(pred_prob)
84
+ pred_label = [each + 1 for each in np.argmax(pred_prob, axis=1)]
85
+ pred_label = [2 if each[1] > 0.15 else pred_label[idx]
86
+ for idx, each in enumerate(pred_prob)]
87
+
88
+ for idx in range(1, len(pred_label) - 1):
89
+ label_ = pred_label[idx]
90
+
91
+ if label_ == 4:
92
+ pred_label[idx] = 3
93
+ if label_ == 3 and pred_label[idx + 1] == 2: # REM after Wake
94
+ pred_label[idx + 1] = 1
95
+ if pred_label[idx - 1] == pred_label[idx + 1] and pred_label[idx] != 3:
96
+ pred_label[idx] = pred_label[idx - 1]
97
+
98
+ return pred_label
99
+
100
+
101
+ def auto_stage_gbm(EEG, EMG, label, sf, EEG_channel="F", mouse_age="adult",
102
+ ACC=None, return_probs=False, temperature=0.1):
103
+ """Auto-stage a recording with the benchmark LightGBM models.
104
+
105
+ All mouse ages use the same model; ``mouse_age`` is kept for API
106
+ compatibility. The channel combo (EEG site x EMG x ACC) selects which
107
+ of the six benchmark models is used - ACC requires EMG.
108
+
109
+ Parameters
110
+ ----------
111
+ EEG : ndarray
112
+ EEG signal.
113
+ EMG : ndarray or None
114
+ EMG signal (optional; pass ``None`` to run EEG-only).
115
+ label : list
116
+ Reference labels (kept for API compatibility; not used by the model).
117
+ sf : float
118
+ Sampling frequency of the signals.
119
+ EEG_channel : {'F', 'P'}
120
+ EEG electrode site (frontal or parietal). Default is ``'F'``.
121
+ mouse_age : str
122
+ Accepted for API compatibility (ignored - all ages use the same
123
+ model).
124
+ ACC : ndarray or None
125
+ ACC signal (optional; requires ``EMG``).
126
+ return_probs : bool
127
+ Also return the per-epoch confidence (probability of the predicted
128
+ state) as a numpy array.
129
+ temperature : float
130
+ HMM softmax temperature (lower sharpens the transition structure).
131
+
132
+ Returns
133
+ -------
134
+ list of int, or (list of int, ndarray) when ``return_probs`` is True.
135
+ Per-second predicted state codes (1 = NREM, 2 = REM, 3 = Wake),
136
+ plus the per-epoch confidence when requested. The prediction may
137
+ be a few seconds shorter than the recording (window drop).
138
+ """
139
+ from misleep.analysis.autostage import benchmark as _bm
140
+
141
+ if EEG is None or len(np.asarray(EEG)) / sf < _bm.W + _bm.EPOCH_S:
142
+ raise ValueError(
143
+ "Signal too short for auto staging (need at least "
144
+ f"{int(_bm.W + _bm.EPOCH_S)} s).")
145
+ if ACC is not None and EMG is None:
146
+ raise ValueError("ACC auto-staging requires an EMG channel as well.")
147
+
148
+ use_emg = EMG is not None
149
+ use_acc = ACC is not None
150
+ combo = _bm.model_combo(EEG_channel, use_emg, use_acc)
151
+
152
+ models = _bm.load_models()
153
+ if combo not in models:
154
+ # ACC without EMG is not part of the model set - fall back to the
155
+ # EEG+EMG (or EEG-only) model.
156
+ if use_acc and use_emg:
157
+ raise ValueError(
158
+ f"No model for combo '{combo}' in the packaged models.")
159
+ combo = _bm.model_combo(EEG_channel, use_emg, False)
160
+
161
+ sig_map = {"eeg": EEG, "emg": EMG, "acc": ACC}
162
+ res = _bm.predict_model(models[combo], sig_map, sf,
163
+ site=EEG_channel, temperature=temperature)
164
+
165
+ # Full-length per-second labels (every second gets a label; the first
166
+ # W seconds and the tail take the nearest epoch) + per-epoch confidence
167
+ # (one value per 5 s epoch - small data).
168
+ pred_second = [int(x) for x in res["label_sec"]]
169
+ conf_epoch = np.asarray(res["prob"], dtype=float)
170
+ logger.info(
171
+ "Auto staging finished (combo=%s, %d epochs, %d seconds)",
172
+ combo, len(res["label"]), len(pred_second))
173
+
174
+ if return_probs:
175
+ return pred_second, conf_epoch
176
+ return pred_second
@@ -0,0 +1,24 @@
1
+ # -*- coding: UTF-8 -*-
2
+ """Benchmark auto-staging pipeline for MiSleep (LightGBM + HMM).
3
+
4
+ Vendored and adapted from the rodent-autostage benchmark models; see
5
+ :mod:`misleep.analysis.autostage.benchmark` for the public API used by the
6
+ LightGBM auto-staging dialog.
7
+ """
8
+
9
+ from misleep.analysis.autostage.benchmark import ( # noqa: F401
10
+ EPOCH_S,
11
+ STRIDE,
12
+ W,
13
+ extract_recording,
14
+ load_models,
15
+ model_combo,
16
+ models_path,
17
+ predict_model,
18
+ )
19
+
20
+ __all__ = [
21
+ "EPOCH_S", "STRIDE", "W",
22
+ "extract_recording", "load_models", "model_combo", "models_path",
23
+ "predict_model",
24
+ ]