accusleepy 0.6.0__py3-none-any.whl → 0.7.1__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.
@@ -29,7 +29,8 @@ At this point, you can score the recordings manually.
29
29
  5. Score all recordings automatically using the classifier
30
30
 
31
31
  By default, there are three brain state options: REM, wake, and NREM.
32
- If you want to change this configuration, click the "Settings" tab.
32
+ If you want to change this configuration, click the "Settings" tab and
33
+ choose "Brain states" from the drop-down menu.
33
34
  Note that if you change the configuration, you might be unable to load
34
35
  existing labels and calibration data, and you may need to train a new
35
36
  classification model.
accusleepy/models.py CHANGED
@@ -1,24 +1,13 @@
1
- import numpy as np
2
1
  from torch import device, flatten, nn
3
2
  from torch import load as torch_load
4
3
  from torch import save as torch_save
5
4
 
6
5
  from accusleepy.brain_state_set import BRAIN_STATES_KEY, BrainStateSet
7
6
  from accusleepy.constants import (
8
- DOWNSAMPLING_START_FREQ,
9
- EMG_COPIES,
10
- MIN_WINDOW_LEN,
11
- UPPER_FREQ,
7
+ IMAGE_HEIGHT,
12
8
  )
13
9
  from accusleepy.temperature_scaling import ModelWithTemperature
14
10
 
15
- # height in pixels of each training image
16
- IMAGE_HEIGHT = (
17
- len(np.arange(0, DOWNSAMPLING_START_FREQ, 1 / MIN_WINDOW_LEN))
18
- + len(np.arange(DOWNSAMPLING_START_FREQ, UPPER_FREQ, 2 / MIN_WINDOW_LEN))
19
- + EMG_COPIES
20
- )
21
-
22
11
 
23
12
  class SSANN(nn.Module):
24
13
  """Small CNN for classifying images"""
@@ -8,6 +8,7 @@ from tqdm import trange
8
8
 
9
9
  from accusleepy.brain_state_set import BrainStateSet
10
10
  from accusleepy.constants import (
11
+ ABS_MAX_Z_SCORE,
11
12
  ANNOTATIONS_FILENAME,
12
13
  CALIBRATION_ANNOTATION_FILENAME,
13
14
  DEFAULT_MODEL_TYPE,
@@ -17,18 +18,13 @@ from accusleepy.constants import (
17
18
  LABEL_COL,
18
19
  MIN_WINDOW_LEN,
19
20
  UPPER_FREQ,
21
+ SPECTROGRAM_UPPER_FREQ,
20
22
  )
21
- from accusleepy.fileio import Recording, load_labels, load_recording
23
+ from accusleepy.fileio import Recording, load_labels, load_recording, EMGFilter
22
24
  from accusleepy.multitaper import spectrogram
23
25
 
24
26
  # note: scipy is lazily imported
25
27
 
26
- # clip mixture z-scores above and below this level
27
- # in the matlab implementation, I used 4.5
28
- ABS_MAX_Z_SCORE = 3.5
29
- # upper frequency limit when generating EEG spectrograms
30
- SPECTROGRAM_UPPER_FREQ = 64
31
-
32
28
 
33
29
  def resample(
34
30
  eeg: np.array, emg: np.array, sampling_rate: int | float, epoch_length: int | float
@@ -172,7 +168,10 @@ def create_spectrogram(
172
168
 
173
169
 
174
170
  def get_emg_power(
175
- emg: np.array, sampling_rate: int | float, epoch_length: int | float
171
+ emg: np.array,
172
+ sampling_rate: int | float,
173
+ epoch_length: int | float,
174
+ emg_filter: EMGFilter,
176
175
  ) -> np.array:
177
176
  """Calculate EMG power for each epoch
178
177
 
@@ -182,18 +181,14 @@ def get_emg_power(
182
181
  :param emg: EMG signal
183
182
  :param sampling_rate: sampling rate, in Hz
184
183
  :param epoch_length: epoch length, in seconds
184
+ :param emg_filter: EMG filter parameters
185
185
  :return: EMG "power" for each epoch
186
186
  """
187
187
  from scipy.signal import butter, filtfilt
188
188
 
189
- # filter parameters
190
- order = 8
191
- bp_lower = 20
192
- bp_upper = 50
193
-
194
189
  b, a = butter(
195
- N=order,
196
- Wn=[bp_lower, bp_upper],
190
+ N=emg_filter.order,
191
+ Wn=[emg_filter.bp_lower, emg_filter.bp_upper],
197
192
  btype="bandpass",
198
193
  output="ba",
199
194
  fs=sampling_rate,
@@ -216,6 +211,7 @@ def create_eeg_emg_image(
216
211
  emg: np.array,
217
212
  sampling_rate: int | float,
218
213
  epoch_length: int | float,
214
+ emg_filter: EMGFilter,
219
215
  ) -> np.array:
220
216
  """Stack EEG spectrogram and EMG power into an image
221
217
 
@@ -227,6 +223,7 @@ def create_eeg_emg_image(
227
223
  :param emg: EMG signal
228
224
  :param sampling_rate: sampling rate, in Hz
229
225
  :param epoch_length: epoch length, in seconds
226
+ :param emg_filter: EMG filter parameters
230
227
  :return: combined EEG + EMG image for a recording
231
228
  """
232
229
  spec, f = create_spectrogram(eeg, sampling_rate, epoch_length)
@@ -242,7 +239,7 @@ def create_eeg_emg_image(
242
239
  ]
243
240
  )
244
241
 
245
- emg_log_rms = get_emg_power(emg, sampling_rate, epoch_length)
242
+ emg_log_rms = get_emg_power(emg, sampling_rate, epoch_length, emg_filter)
246
243
  output = np.concatenate(
247
244
  [modified_spectrogram, np.tile(emg_log_rms, (EMG_COPIES, 1))]
248
245
  )
@@ -371,6 +368,7 @@ def create_training_images(
371
368
  brain_state_set: BrainStateSet,
372
369
  model_type: str,
373
370
  calibration_fraction: float,
371
+ emg_filter: EMGFilter,
374
372
  ) -> list[int]:
375
373
  """Create training dataset
376
374
 
@@ -385,6 +383,7 @@ def create_training_images(
385
383
  :param brain_state_set: set of brain state options
386
384
  :param model_type: default or real-time
387
385
  :param calibration_fraction: fraction of training data to use for calibration
386
+ :param emg_filter: EMG filter parameters
388
387
  :return: list of the names of any recordings that could not
389
388
  be used to create training images.
390
389
  """
@@ -409,7 +408,9 @@ def create_training_images(
409
408
 
410
409
  labels, _ = load_labels(recording.label_file)
411
410
  labels = brain_state_set.convert_digit_to_class(labels)
412
- img = create_eeg_emg_image(eeg, emg, sampling_rate, epoch_length)
411
+ img = create_eeg_emg_image(
412
+ eeg, emg, sampling_rate, epoch_length, emg_filter
413
+ )
413
414
  img = mixture_z_score_img(
414
415
  img=img, brain_state_set=brain_state_set, labels=labels
415
416
  )
@@ -0,0 +1,128 @@
1
+ import numpy as np
2
+
3
+ from accusleepy.brain_state_set import BrainStateSet
4
+ from accusleepy.constants import UNDEFINED_LABEL
5
+
6
+ LABEL_LENGTH_ERROR = "label file length does not match recording length"
7
+
8
+
9
+ def check_label_validity(
10
+ labels: np.array,
11
+ confidence_scores: np.array,
12
+ samples_in_recording: int,
13
+ sampling_rate: int | float,
14
+ epoch_length: int | float,
15
+ brain_state_set: BrainStateSet,
16
+ ) -> str | None:
17
+ """Check whether a set of brain state labels is valid
18
+
19
+ This returns an error message if a problem is found with the
20
+ brain state labels.
21
+
22
+ :param labels: brain state labels
23
+ :param confidence_scores: confidence scores
24
+ :param samples_in_recording: number of samples in the recording
25
+ :param sampling_rate: sampling rate, in Hz
26
+ :param epoch_length: epoch length, in seconds
27
+ :param brain_state_set: BrainStateMapper object
28
+ :return: error message
29
+ """
30
+ # check that number of labels is correct
31
+ samples_per_epoch = round(sampling_rate * epoch_length)
32
+ epochs_in_recording = round(samples_in_recording / samples_per_epoch)
33
+ if epochs_in_recording != labels.size:
34
+ return LABEL_LENGTH_ERROR
35
+
36
+ # check that entries are valid
37
+ if not set(labels.tolist()).issubset(
38
+ set([b.digit for b in brain_state_set.brain_states] + [UNDEFINED_LABEL])
39
+ ):
40
+ return "label file contains invalid entries"
41
+
42
+ if confidence_scores is not None:
43
+ if np.min(confidence_scores) < 0 or np.max(confidence_scores) > 1:
44
+ return "label file contains invalid confidence scores"
45
+
46
+ return None
47
+
48
+
49
+ def check_config_consistency(
50
+ current_brain_states: dict,
51
+ model_brain_states: dict,
52
+ current_epoch_length: int | float,
53
+ model_epoch_length: int | float,
54
+ ) -> list[str]:
55
+ """Compare current brain state config to the model's config
56
+
57
+ This only displays warnings - the user should decide whether to proceed
58
+
59
+ :param current_brain_states: current brain state config
60
+ :param model_brain_states: brain state config when the model was created
61
+ :param current_epoch_length: current epoch length setting
62
+ :param model_epoch_length: epoch length used when the model was created
63
+ """
64
+ output = list()
65
+
66
+ # make lists of names and digits for scored brain states
67
+ current_scored_states = {
68
+ f: [b[f] for b in current_brain_states if b["is_scored"]]
69
+ for f in ["name", "digit"]
70
+ }
71
+ model_scored_states = {
72
+ f: [b[f] for b in model_brain_states if b["is_scored"]]
73
+ for f in ["name", "digit"]
74
+ }
75
+
76
+ # generate message comparing the brain state configs
77
+ config_comparisons = list()
78
+ for config, config_name in zip(
79
+ [current_scored_states, model_scored_states], ["current", "model's"]
80
+ ):
81
+ config_comparisons.append(
82
+ f"Scored brain states in {config_name} configuration: "
83
+ f"""{
84
+ ", ".join(
85
+ [
86
+ f"{x}: {y}"
87
+ for x, y in zip(
88
+ config["digit"],
89
+ config["name"],
90
+ )
91
+ ]
92
+ )
93
+ }"""
94
+ )
95
+
96
+ # check if the number of scored states is different
97
+ len_diff = len(current_scored_states["name"]) - len(model_scored_states["name"])
98
+ if len_diff != 0:
99
+ output.append(
100
+ (
101
+ "WARNING: current brain state configuration has "
102
+ f"{'fewer' if len_diff < 0 else 'more'} "
103
+ "scored brain states than the model's configuration."
104
+ )
105
+ )
106
+ output = output + config_comparisons
107
+ else:
108
+ # the length is the same, but names might be different
109
+ if current_scored_states["name"] != model_scored_states["name"]:
110
+ output.append(
111
+ (
112
+ "WARNING: current brain state configuration appears "
113
+ "to contain different brain states than "
114
+ "the model's configuration."
115
+ )
116
+ )
117
+ output = output + config_comparisons
118
+
119
+ if current_epoch_length != model_epoch_length:
120
+ output.append(
121
+ (
122
+ "Warning: the epoch length used when training this model "
123
+ f"({model_epoch_length} seconds) "
124
+ "does not match the current epoch length setting."
125
+ )
126
+ )
127
+
128
+ return output
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: accusleepy
3
- Version: 0.6.0
3
+ Version: 0.7.1
4
4
  Summary: Python implementation of AccuSleep
5
5
  License: GPL-3.0-only
6
6
  Author: Zeke Barger
@@ -39,6 +39,7 @@ It offers the following improvements over the MATLAB version (AccuSleep):
39
39
  - Model files contain useful metadata (brain state configuration,
40
40
  epoch length, number of epochs)
41
41
  - Models optimized for real-time scoring can be trained
42
+ - Confidence scores can be saved and visualized
42
43
  - Lists of recordings can be imported and exported for repeatable batch processing
43
44
  - Undo/redo functionality in the manual scoring interface
44
45
 
@@ -75,6 +76,8 @@ to the [config file](accusleepy/config.json).
75
76
 
76
77
  ## Changelog
77
78
 
79
+ - 0.7.1: Bugfixes, code cleanup
80
+ - 0.7.0: More settings can be configured in the UI
78
81
  - 0.6.0: Confidence scores can now be displayed and saved. Retraining your models is recommended
79
82
  since the new calibration feature will make the confidence scores more accurate.
80
83
  - 0.5.0: Performance improvements
@@ -2,10 +2,10 @@ accusleepy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  accusleepy/__main__.py,sha256=dKzl2N2Hg9lD264CWYNxThRyDKzWwyMwHRXmJxOmMis,104
3
3
  accusleepy/bouts.py,sha256=F_y6DxnpKFfImYb7vCZluZ2eD5I_33gZXmRM8mvebsg,5679
4
4
  accusleepy/brain_state_set.py,sha256=fRkrArHLIbEKimub804yt_mUXoyfsjJEfiJnTjeCMkY,3233
5
- accusleepy/classification.py,sha256=wg0EoUrXAYqdUWr6SyJDl_5Jzx7lck9ta3-dsGzD208,9180
6
- accusleepy/config.json,sha256=n-CTGn7hljHQ7MKRMvGvwCAzcjWmUuqzAWrnXnk2pGU,501
7
- accusleepy/constants.py,sha256=0gIzZY544Y1tL-n0HxFsCMzBdprOKNYVYlNxF45o3kw,1652
8
- accusleepy/fileio.py,sha256=z6Cuhue__-RL7s3jjY23IjMl8nhow3P1mDx7j_rRLo4,5441
5
+ accusleepy/classification.py,sha256=mF35xMrD9QXGldSnl3vkdHbm7CAptPUNjHxUA_agOTA,9778
6
+ accusleepy/config.json,sha256=Ip0qTMAn2LZfof9GVA_azOvpXP0WKnqLCZeSaya1sss,819
7
+ accusleepy/constants.py,sha256=r53FWeMMefuKA-mR_b2E1KBeJbq2Ck9c5VHoJ1WGZjg,2815
8
+ accusleepy/fileio.py,sha256=woIF0zgJt6Lx6T9KBXAQ-AlbQAwOK1_RUmVF710nltI,7383
9
9
  accusleepy/gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  accusleepy/gui/icons/brightness_down.png,sha256=PLT1fb83RHIhSRuU7MMMx0G7oJAY7o9wUcnqM8veZfM,12432
11
11
  accusleepy/gui/icons/brightness_up.png,sha256=64GnUqgPvN5xZ6Um3wOzwqvUmdAWYZT6eFmWpBsHyks,12989
@@ -18,25 +18,25 @@ accusleepy/gui/icons/save.png,sha256=J3EA8iU1BqLYRSsrq_OdoZlqrv2yfL7oV54DklTy_DI
18
18
  accusleepy/gui/icons/up_arrow.png,sha256=V9yF9t1WgjPaUu-mF1YGe_DfaRHg2dUpR_sUVVcvVvY,3329
19
19
  accusleepy/gui/icons/zoom_in.png,sha256=MFWnKZp7Rvh4bLPq4Cqo4sB_jQYedUUtT8-ZO8tNYyc,13589
20
20
  accusleepy/gui/icons/zoom_out.png,sha256=IB8Jecb3i0U4qjWRR46ridjLpvLCSe7PozBaLqQqYSw,13055
21
- accusleepy/gui/images/primary_window.png,sha256=rlEflF0w3IdwQKDo3tejqSmfNp8omoxgI8sVDqF3hto,600458
21
+ accusleepy/gui/images/primary_window.png,sha256=VVCjwolHsi4HdYehe3ZG_QpZ0XK39Qrz4Q6SqBAc5Cg,597045
22
22
  accusleepy/gui/images/viewer_window.png,sha256=b_B7m9WSLMAOzNjctq76SyekO1WfC6qYZVNnYfhjPe8,977197
23
23
  accusleepy/gui/images/viewer_window_annotated.png,sha256=uMNUmsZIdzDlQpyoiS3lJGoWlg_T325Oj5hDZhM3Y14,146817
24
- accusleepy/gui/main.py,sha256=npQbJ11fV3lpKaoChdjRI-KsgneM-IQRrWZkSDHBkWI,57737
25
- accusleepy/gui/manual_scoring.py,sha256=QD3T-0vEH5E08Vw6YAzp7eBETsNTRU4BGuyzm-3oMfI,40635
24
+ accusleepy/gui/main.py,sha256=sLHe1imVU6xz3f6sJ9u7sxrTny9X213ev29ldoxXpX0,57740
25
+ accusleepy/gui/manual_scoring.py,sha256=7jKdw-D9HdpYC2G_2RHt5UALLMjrIsP-hvLFkNM25Ek,40553
26
26
  accusleepy/gui/mplwidget.py,sha256=rJSTtWmLjHn8r3c9Kb23Rc4XzXl3i9B-JrjNjjlNnmQ,13492
27
- accusleepy/gui/primary_window.py,sha256=N-hEI39lQdGSEGq1quxSPLIMuNqhTkGWKM89BL3KgVM,108377
28
- accusleepy/gui/primary_window.ui,sha256=5bSa6c5idYCpnwWZ5Vt3ecrJsC8hiD0JTPLSf_20H2g,153744
27
+ accusleepy/gui/primary_window.py,sha256=Qc3WWiwJHbgmGDzGzNI3fBycBKtlpT5YdskwnHr6OWs,136070
28
+ accusleepy/gui/primary_window.ui,sha256=u7HQdeNKObKsUSOlk91gEMat-RrXITEsnBedZPd7y6Y,201950
29
29
  accusleepy/gui/resources.qrc,sha256=wqPendnTLAuKfVI6v2lKHiRqAWM0oaz2ZuF5cucJdS4,803
30
30
  accusleepy/gui/resources_rc.py,sha256=Z2e34h30U4snJjnYdZVV9B6yjATKxxfvgTRt5uXtQdo,329727
31
- accusleepy/gui/text/config_guide.txt,sha256=xYnVTojDXKph13QBj7mpfjQYa-xmpLkhF19G_bz3xbA,1027
32
- accusleepy/gui/text/main_guide.md,sha256=bl5RDSh-kD44Ch3bVo_Hl7Cn3n8dfh9xHjKKH4ORgS8,8238
31
+ accusleepy/gui/text/main_guide.md,sha256=iZDRp5OWyQX9LV7CMeUFIYv2ryKlIcGALRLXjxR8HpI,8288
33
32
  accusleepy/gui/text/manual_scoring_guide.md,sha256=ow_RMSjFy05NupEDSCuJtu-V65-BPnIkrZqtssFoZCQ,999
34
33
  accusleepy/gui/viewer_window.py,sha256=O4ceqLMYdahxQ9s6DYhloUnNESim-cqIZxFeXEiRjog,24444
35
34
  accusleepy/gui/viewer_window.ui,sha256=jsjydsSSyN49AwJw4nVS2mEJ2JBIUTXesAJsij1JNV0,31530
36
- accusleepy/models.py,sha256=15VjtFoWaYXblyGPbtYgp0yJdyUfGu7t3zCShdtr_7c,3799
35
+ accusleepy/models.py,sha256=kqkcQJoKi7gpnM8gZ7nZbWGvpv7ruNnFLaB7ED1X6Dc,3493
37
36
  accusleepy/multitaper.py,sha256=D5-iglwkFBRciL5tKSNcunMtcq0rM3zHwRHUVPgem1U,25679
38
- accusleepy/signal_processing.py,sha256=sEol01SlwOVwBmKHR-saVhneAA0jr98pcZZ-02uUy2Q,16739
37
+ accusleepy/signal_processing.py,sha256=47fEAx8Aqqkiqix1ai2YEK9Fhq6UHoQcwAcOi-a8ewo,16834
39
38
  accusleepy/temperature_scaling.py,sha256=glvPcvxHpBdFjwjGfZdNku9L_BozycEmdqZhKKUCCNg,5749
40
- accusleepy-0.6.0.dist-info/METADATA,sha256=D2BHcAVQmF4M7ZkCwEiBjIQ6q6aMgcEQghNNtZFb2c8,4437
41
- accusleepy-0.6.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
42
- accusleepy-0.6.0.dist-info/RECORD,,
39
+ accusleepy/validation.py,sha256=VpLWK-wD5tCU6lTBG3KYgTi3PWGuYh6NitMgMoMH8JM,4434
40
+ accusleepy-0.7.1.dist-info/METADATA,sha256=UXIUq2dyYIn7ehR0V0aq3NctxurAodZGGCnFrcNXj6c,4568
41
+ accusleepy-0.7.1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
42
+ accusleepy-0.7.1.dist-info/RECORD,,
@@ -1,27 +0,0 @@
1
- This is the current brain state configuration.
2
- If you make changes, click 'Save' to store them.
3
-
4
- Each brain state has several attributes:
5
-
6
- - Digit: how the brain state is represented in label files,
7
- and the key on the keyboard that, during manual scoring,
8
- sets an epoch to this brain state.
9
-
10
- - Enabled: whether a brain state for this digit exists.
11
-
12
- - Name: unique name of the brain state (e.g., REM).
13
-
14
- - Scored: whether a classification model should output this
15
- brain state. If you have a state that corresponds to
16
- missing or corrupted data, for example, you would
17
- probably want to uncheck this box.
18
-
19
- - Frequency: approximate relative frequency of this brain
20
- state. Does not need to be very accurate, but it can
21
- influence classification accuracy slightly. The values
22
- for all scored brain states must sum to 1.
23
-
24
- Important notes:
25
- - Changing these settings can invalidate existing label files,
26
- calibration files, and trained models!
27
- - Reinstalling AccuSleePy will overwrite this configuration.