accusleepy 0.8.1__py3-none-any.whl → 0.9.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.
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  import os
2
3
  import warnings
3
4
 
@@ -16,6 +17,7 @@ from accusleepy.constants import (
16
17
  EMG_COPIES,
17
18
  FILENAME_COL,
18
19
  LABEL_COL,
20
+ MIN_EPOCHS_PER_STATE,
19
21
  MIN_WINDOW_LEN,
20
22
  UPPER_FREQ,
21
23
  SPECTROGRAM_UPPER_FREQ,
@@ -25,10 +27,15 @@ from accusleepy.multitaper import spectrogram
25
27
 
26
28
  # note: scipy is lazily imported
27
29
 
30
+ logger = logging.getLogger(__name__)
31
+
28
32
 
29
33
  def resample(
30
- eeg: np.array, emg: np.array, sampling_rate: int | float, epoch_length: int | float
31
- ) -> (np.array, np.array, float):
34
+ eeg: np.ndarray,
35
+ emg: np.ndarray,
36
+ sampling_rate: int | float,
37
+ epoch_length: int | float,
38
+ ) -> tuple[np.ndarray, np.ndarray, float]:
32
39
  """Resample recording so that epochs contain equal numbers of samples
33
40
 
34
41
  If the number of samples per epoch is not an integer, epoch-level calculations
@@ -62,8 +69,11 @@ def resample(
62
69
 
63
70
 
64
71
  def standardize_signal_length(
65
- eeg: np.array, emg: np.array, sampling_rate: int | float, epoch_length: int | float
66
- ) -> (np.array, np.array):
72
+ eeg: np.ndarray,
73
+ emg: np.ndarray,
74
+ sampling_rate: int | float,
75
+ epoch_length: int | float,
76
+ ) -> tuple[np.ndarray, np.ndarray]:
67
77
  """Truncate or pad EEG/EMG signals to have an integer number of epochs
68
78
 
69
79
  :param eeg: EEG signal
@@ -93,8 +103,11 @@ def standardize_signal_length(
93
103
 
94
104
 
95
105
  def resample_and_standardize(
96
- eeg: np.array, emg: np.array, sampling_rate: int | float, epoch_length: int | float
97
- ) -> (np.array, np.array, float):
106
+ eeg: np.ndarray,
107
+ emg: np.ndarray,
108
+ sampling_rate: int | float,
109
+ epoch_length: int | float,
110
+ ) -> tuple[np.ndarray, np.ndarray, float]:
98
111
  """Preprocess EEG and EMG signals
99
112
 
100
113
  Adjust the length and sampling rate of the EEG and EMG signals so that
@@ -117,12 +130,12 @@ def resample_and_standardize(
117
130
 
118
131
 
119
132
  def create_spectrogram(
120
- eeg: np.array,
133
+ eeg: np.ndarray,
121
134
  sampling_rate: int | float,
122
135
  epoch_length: int | float,
123
- time_bandwidth=2,
124
- n_tapers=3,
125
- ) -> (np.array, np.array):
136
+ time_bandwidth: int = 2,
137
+ n_tapers: int = 3,
138
+ ) -> tuple[np.ndarray, np.ndarray]:
126
139
  """Create an EEG spectrogram image
127
140
 
128
141
  :param eeg: EEG signal
@@ -168,11 +181,11 @@ def create_spectrogram(
168
181
 
169
182
 
170
183
  def get_emg_power(
171
- emg: np.array,
184
+ emg: np.ndarray,
172
185
  sampling_rate: int | float,
173
186
  epoch_length: int | float,
174
187
  emg_filter: EMGFilter,
175
- ) -> np.array:
188
+ ) -> np.ndarray:
176
189
  """Calculate EMG power for each epoch
177
190
 
178
191
  This applies a 20-50 Hz bandpass filter to the EMG, calculates the RMS
@@ -202,17 +215,18 @@ def get_emg_power(
202
215
  [round(len(emg) / samples_per_epoch), samples_per_epoch],
203
216
  )
204
217
  rms = np.sqrt(np.mean(np.power(reshaped, 2), axis=1))
205
-
206
- return np.log(rms)
218
+ log_rms = np.log(rms)
219
+ log_rms[np.isinf(log_rms)] = 0
220
+ return log_rms
207
221
 
208
222
 
209
223
  def create_eeg_emg_image(
210
- eeg: np.array,
211
- emg: np.array,
224
+ eeg: np.ndarray,
225
+ emg: np.ndarray,
212
226
  sampling_rate: int | float,
213
227
  epoch_length: int | float,
214
228
  emg_filter: EMGFilter,
215
- ) -> np.array:
229
+ ) -> np.ndarray:
216
230
  """Stack EEG spectrogram and EMG power into an image
217
231
 
218
232
  This assumes that each epoch contains an integer number of samples and
@@ -247,8 +261,8 @@ def create_eeg_emg_image(
247
261
 
248
262
 
249
263
  def get_mixture_values(
250
- img: np.array, labels: np.array, brain_state_set: BrainStateSet
251
- ) -> (np.array, np.array):
264
+ img: np.ndarray, labels: np.ndarray, brain_state_set: BrainStateSet
265
+ ) -> tuple[np.ndarray, np.ndarray]:
252
266
  """Compute weighted feature means and SDs for mixture z-scoring
253
267
 
254
268
  The outputs of this function can be used to standardize features
@@ -289,12 +303,12 @@ def get_mixture_values(
289
303
 
290
304
 
291
305
  def mixture_z_score_img(
292
- img: np.array,
306
+ img: np.ndarray,
293
307
  brain_state_set: BrainStateSet,
294
- labels: np.array = None,
295
- mixture_means: np.array = None,
296
- mixture_sds: np.array = None,
297
- ) -> np.array:
308
+ labels: np.ndarray | None = None,
309
+ mixture_means: np.ndarray | None = None,
310
+ mixture_sds: np.ndarray | None = None,
311
+ ) -> tuple[np.ndarray, bool]:
298
312
  """Perform mixture z-scoring on a combined EEG+EMG image
299
313
 
300
314
  If brain state labels are provided, they will be used to calculate
@@ -307,10 +321,10 @@ def mixture_z_score_img(
307
321
  :param labels: labels, in "class" format
308
322
  :param mixture_means: mixture means
309
323
  :param mixture_sds: mixture standard deviations
310
- :return:
324
+ :return: tuple of (z-scored image, whether zero-variance features were detected)
311
325
  """
312
326
  if labels is None and (mixture_means is None or mixture_sds is None):
313
- raise Exception("must provide either labels or mixture means+SDs")
327
+ raise ValueError("must provide either labels or mixture means+SDs")
314
328
  if labels is not None and ((mixture_means is not None) ^ (mixture_sds is not None)):
315
329
  warnings.warn("labels were given, mixture means / SDs will be ignored")
316
330
 
@@ -319,14 +333,27 @@ def mixture_z_score_img(
319
333
  img=img, labels=labels, brain_state_set=brain_state_set
320
334
  )
321
335
 
336
+ # replace zero SDs with epsilon to avoid division by zero
337
+ # This can occur when a feature has no variance (e.g., no EMG signal)
338
+ zero_sd_mask = mixture_sds == 0
339
+ had_zero_variance = np.any(zero_sd_mask)
340
+ if had_zero_variance:
341
+ n_zero = np.sum(zero_sd_mask)
342
+ logger.warning(
343
+ "%s feature(s) have zero variance and will be mapped to neutral values",
344
+ n_zero,
345
+ )
346
+ mixture_sds = mixture_sds.copy()
347
+ mixture_sds[zero_sd_mask] = 1e-10
348
+
322
349
  img = ((img.T - mixture_means) / mixture_sds).T
323
350
  img = (img + ABS_MAX_Z_SCORE) / (2 * ABS_MAX_Z_SCORE)
324
351
  img = np.clip(img, 0, 1)
325
352
 
326
- return img
353
+ return img, had_zero_variance
327
354
 
328
355
 
329
- def format_img(img: np.array, epochs_per_img: int, add_padding: bool) -> np.array:
356
+ def format_img(img: np.ndarray, epochs_per_img: int, add_padding: bool) -> np.ndarray:
330
357
  """Adjust the format of an EEG+EMG image
331
358
 
332
359
  This function converts the values in a combined EEG+EMG image to uint8.
@@ -369,9 +396,18 @@ def create_training_images(
369
396
  model_type: str,
370
397
  calibration_fraction: float,
371
398
  emg_filter: EMGFilter,
372
- ) -> list[int]:
373
- """Create training dataset
374
-
399
+ ) -> tuple[list[int], np.ndarray, bool]:
400
+ """Create training dataset and calculate class balance
401
+
402
+ This function creates images that can be used to train the
403
+ SSANN model, as well as files that describe the training data
404
+ (optionally split into training and calibration sets).
405
+ It returns a list of recordings that could not be processed,
406
+ the class balance of the usable training data, and a flag if any
407
+ recordings had features with 0 variance.
408
+
409
+ For each epoch, the model expects an image containing the
410
+ EEG spectrogram and EMG power for several surrounding epochs.
375
411
  By default, the current epoch is located in the central column
376
412
  of pixels in each image. For real-time scoring applications,
377
413
  the current epoch is at the right edge of each image.
@@ -384,8 +420,7 @@ def create_training_images(
384
420
  :param model_type: default or real-time
385
421
  :param calibration_fraction: fraction of training data to use for calibration
386
422
  :param emg_filter: EMG filter parameters
387
- :return: list of the names of any recordings that could not
388
- be used to create training images.
423
+ :return: tuple of (failed recording names, training class balance, had zero-variance)
389
424
  """
390
425
  # recordings that had to be skipped
391
426
  failed_recordings = list()
@@ -393,9 +428,36 @@ def create_training_images(
393
428
  filenames = list()
394
429
  # all valid labels from all valid recordings
395
430
  all_labels = list()
431
+ # track if any recording had zero-variance features
432
+ any_zero_variance = False
396
433
  # try to load each recording and create training images
397
434
  for i in trange(len(recordings)):
398
435
  recording = recordings[i]
436
+ try:
437
+ labels, _ = load_labels(recording.label_file)
438
+ except Exception:
439
+ logger.exception("Could not load labels for recording %s", recording.name)
440
+ failed_recordings.append(recording.name)
441
+ continue
442
+
443
+ # Check that each scored brain state has sufficient observations
444
+ # Ideally, we could use mixture means/SDs from another recording...
445
+ insufficient_labels = False
446
+ for brain_state in brain_state_set.brain_states:
447
+ if brain_state.is_scored:
448
+ count = np.sum(labels == brain_state.digit)
449
+ if count < MIN_EPOCHS_PER_STATE:
450
+ logger.warning(
451
+ "Recording %s can't be used: insufficient labels for class '%s'",
452
+ recording.name,
453
+ brain_state.name,
454
+ )
455
+ failed_recordings.append(recording.name)
456
+ insufficient_labels = True
457
+ break
458
+ if insufficient_labels:
459
+ continue
460
+
399
461
  try:
400
462
  eeg, emg = load_recording(recording.recording_file)
401
463
  sampling_rate = recording.sampling_rate
@@ -406,14 +468,15 @@ def create_training_images(
406
468
  epoch_length=epoch_length,
407
469
  )
408
470
 
409
- labels, _ = load_labels(recording.label_file)
410
471
  labels = brain_state_set.convert_digit_to_class(labels)
411
472
  img = create_eeg_emg_image(
412
473
  eeg, emg, sampling_rate, epoch_length, emg_filter
413
474
  )
414
- img = mixture_z_score_img(
475
+ img, had_zero_variance = mixture_z_score_img(
415
476
  img=img, brain_state_set=brain_state_set, labels=labels
416
477
  )
478
+ if had_zero_variance:
479
+ any_zero_variance = True
417
480
  img = format_img(img=img, epochs_per_img=epochs_per_img, add_padding=True)
418
481
 
419
482
  # the model type determines which epochs are used in each image
@@ -442,8 +505,10 @@ def create_training_images(
442
505
  all_labels.append(labels[j])
443
506
  Image.fromarray(im).save(os.path.join(output_path, filename))
444
507
 
445
- except Exception as e:
446
- print(e)
508
+ except Exception:
509
+ logger.exception(
510
+ "Failed to create training images for recording %s", recording.name
511
+ )
447
512
  failed_recordings.append(recording.name)
448
513
 
449
514
  annotations = pd.DataFrame({FILENAME_COL: filenames, LABEL_COL: all_labels})
@@ -460,11 +525,18 @@ def create_training_images(
460
525
  os.path.join(output_path, CALIBRATION_ANNOTATION_FILENAME),
461
526
  index=False,
462
527
  )
528
+ training_labels = training_set[LABEL_COL].values
463
529
  else:
464
530
  # annotation file contains info on all training images
465
531
  annotations.to_csv(
466
532
  os.path.join(output_path, ANNOTATIONS_FILENAME),
467
533
  index=False,
468
534
  )
535
+ training_labels = np.array(all_labels)
536
+
537
+ # compute class balance from training set
538
+ class_counts = np.bincount(training_labels, minlength=brain_state_set.n_classes)
539
+ training_class_balance = class_counts / class_counts.sum()
540
+ logger.info("Training set class balance: %s", training_class_balance)
469
541
 
470
- return failed_recordings
542
+ return failed_recordings, training_class_balance, any_zero_variance
@@ -1,8 +1,12 @@
1
+ import logging
2
+
1
3
  import numpy as np
2
4
  import torch
3
5
  from torch import nn, optim
4
6
  from torch.nn import functional as F
5
7
 
8
+ logger = logging.getLogger(__name__)
9
+
6
10
 
7
11
  class ModelWithTemperature(nn.Module):
8
12
  """
@@ -72,9 +76,10 @@ class ModelWithTemperature(nn.Module):
72
76
  # Calculate NLL and ECE before temperature scaling
73
77
  before_temperature_nll = nll_criterion(logits, labels).item()
74
78
  before_temperature_ece = ece_criterion(logits, labels).item()
75
- print(
76
- "Before temperature - NLL: %.3f, ECE: %.3f"
77
- % (before_temperature_nll, before_temperature_ece)
79
+ logger.info(
80
+ "Before temperature - NLL: %.3f, ECE: %.3f",
81
+ before_temperature_nll,
82
+ before_temperature_ece,
78
83
  )
79
84
 
80
85
  # Next: optimize the temperature w.r.t. NLL
@@ -96,16 +101,17 @@ class ModelWithTemperature(nn.Module):
96
101
  after_temperature_ece = ece_criterion(
97
102
  self.temperature_scale(logits), labels
98
103
  ).item()
99
- print("Optimal temperature: %.3f" % self.temperature.item())
100
- print(
101
- "After temperature - NLL: %.3f, ECE: %.3f"
102
- % (after_temperature_nll, after_temperature_ece)
104
+ logger.info("Optimal temperature: %.3f", self.temperature.item())
105
+ logger.info(
106
+ "After temperature - NLL: %.3f, ECE: %.3f",
107
+ after_temperature_nll,
108
+ after_temperature_ece,
103
109
  )
104
110
 
105
111
  val_acc = round(
106
112
  100 * np.mean(labels.cpu().numpy() == predictions.cpu().numpy()), 2
107
113
  )
108
- print(f"Validation accuracy: {val_acc}%")
114
+ logger.info("Validation accuracy: %s%%", val_acc)
109
115
 
110
116
  return self
111
117
 
accusleepy/validation.py CHANGED
@@ -7,8 +7,8 @@ LABEL_LENGTH_ERROR = "label file length does not match recording length"
7
7
 
8
8
 
9
9
  def check_label_validity(
10
- labels: np.array,
11
- confidence_scores: np.array,
10
+ labels: np.ndarray,
11
+ confidence_scores: np.ndarray | None,
12
12
  samples_in_recording: int,
13
13
  sampling_rate: int | float,
14
14
  epoch_length: int | float,
@@ -46,6 +46,71 @@ def check_label_validity(
46
46
  return None
47
47
 
48
48
 
49
+ def validate_and_correct_labels(
50
+ labels: np.ndarray,
51
+ confidence_scores: np.ndarray | None,
52
+ samples_in_recording: int,
53
+ sampling_rate: int | float,
54
+ epoch_length: int | float,
55
+ brain_state_set: BrainStateSet,
56
+ ) -> tuple[np.ndarray | None, np.ndarray | None, str | None]:
57
+ """Validate labels and attempt to correct minor length mismatches.
58
+
59
+ If the label array is off by exactly one epoch, it will be padded or
60
+ truncated and a warning will be returned.
61
+
62
+ :param labels: brain state labels
63
+ :param confidence_scores: optional confidence scores
64
+ :param samples_in_recording: number of samples in the recording
65
+ :param sampling_rate: sampling rate in Hz
66
+ :param epoch_length: epoch length in seconds
67
+ :param brain_state_set: set of brain state options
68
+ :return: (labels, confidence_scores, message) - if labels is None,
69
+ message is an error. Otherwise, any message is a warning
70
+ """
71
+ label_error = check_label_validity(
72
+ labels=labels,
73
+ confidence_scores=confidence_scores,
74
+ samples_in_recording=samples_in_recording,
75
+ sampling_rate=sampling_rate,
76
+ epoch_length=epoch_length,
77
+ brain_state_set=brain_state_set,
78
+ )
79
+
80
+ if not label_error:
81
+ return labels, confidence_scores, None
82
+
83
+ # If length is off by one, try to correct it
84
+ if label_error == LABEL_LENGTH_ERROR:
85
+ samples_per_epoch = round(sampling_rate * epoch_length)
86
+ epochs_in_recording = round(samples_in_recording / samples_per_epoch)
87
+
88
+ if epochs_in_recording - labels.size == 1:
89
+ # Pad with one undefined label
90
+ labels = np.concatenate((labels, np.array([UNDEFINED_LABEL])))
91
+ if confidence_scores is not None:
92
+ confidence_scores = np.concatenate((confidence_scores, np.array([0])))
93
+ return (
94
+ labels,
95
+ confidence_scores,
96
+ "An undefined epoch was added to the label file to correct its length.",
97
+ )
98
+
99
+ if labels.size - epochs_in_recording == 1:
100
+ # Truncate by one label
101
+ labels = labels[:-1]
102
+ if confidence_scores is not None:
103
+ confidence_scores = confidence_scores[:-1]
104
+ return (
105
+ labels,
106
+ confidence_scores,
107
+ "The last epoch was removed from the label file to correct its length.",
108
+ )
109
+
110
+ # error could not be fixed
111
+ return None, None, label_error
112
+
113
+
49
114
  def check_config_consistency(
50
115
  current_brain_states: dict,
51
116
  model_brain_states: dict,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: accusleepy
3
- Version: 0.8.1
3
+ Version: 0.9.2
4
4
  Summary: Python implementation of AccuSleep
5
5
  License: GPL-3.0-only
6
6
  Author: Zeke Barger
@@ -80,7 +80,7 @@ please consult the [developer guide](accusleepy/gui/text/dev_guide.md).
80
80
 
81
81
  ## Changelog
82
82
 
83
- - 0.8.1: Increased test coverage
83
+ - 0.8.1-0.9.2: Improved error handling and code quality
84
84
  - 0.8.0: More configurable settings, visual improvements
85
85
  - 0.7.1-0.7.3: Bugfixes, code cleanup
86
86
  - 0.7.0: More settings can be configured in the UI
@@ -1,12 +1,13 @@
1
1
  accusleepy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  accusleepy/__main__.py,sha256=dKzl2N2Hg9lD264CWYNxThRyDKzWwyMwHRXmJxOmMis,104
3
- accusleepy/bouts.py,sha256=biJEzCjMQR-RTlQcfeomkp6NxcA5FXkhE8Kp2XyL4P4,6039
4
- accusleepy/brain_state_set.py,sha256=fRkrArHLIbEKimub804yt_mUXoyfsjJEfiJnTjeCMkY,3233
5
- accusleepy/classification.py,sha256=mF35xMrD9QXGldSnl3vkdHbm7CAptPUNjHxUA_agOTA,9778
3
+ accusleepy/bouts.py,sha256=GVYOSdsQjHzYirRRSFlie8w5iJq74umyGNMRVsQlpK0,6045
4
+ accusleepy/brain_state_set.py,sha256=i9RUwSMKG21AjromjOJYpsHkip3elTFhHYntMguBF6Q,3273
5
+ accusleepy/classification.py,sha256=UJ48QamTvqwqBwu-r4weSno-9TQyPFCk62nbUc5_iKA,8672
6
6
  accusleepy/config.json,sha256=VmUFsiGD1ymEyjdzqeM5nTp8jWvDI-DIxLy1_92nueo,875
7
- accusleepy/constants.py,sha256=62mmsr1NKzF-psS-9esuAE65kAcPL6o8v9UXQEvb5yc,2983
8
- accusleepy/fileio.py,sha256=-aEgp2oonjQKRizVI1uI5CGknnBOxE6HG0DzxDYbSkM,8512
7
+ accusleepy/constants.py,sha256=y3tvYxJy3M1zycOPVfXLpvhLVdyd7s0dSlokgKG7mUE,3127
8
+ accusleepy/fileio.py,sha256=A-ttN-N3wsEHrU89NvUYb5osuMK1HMmPecCDgtPTiqo,9076
9
9
  accusleepy/gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ accusleepy/gui/dialogs.py,sha256=DqkarJKgRovTuDDJhqPaw_o8ZfopuR7R6M8C49QWZu0,1230
10
11
  accusleepy/gui/icons/brightness_down.png,sha256=PLT1fb83RHIhSRuU7MMMx0G7oJAY7o9wUcnqM8veZfM,12432
11
12
  accusleepy/gui/icons/brightness_up.png,sha256=64GnUqgPvN5xZ6Um3wOzwqvUmdAWYZT6eFmWpBsHyks,12989
12
13
  accusleepy/gui/icons/double_down_arrow.png,sha256=fGiPIP7_RJ3UAonNhORFVX0emXEmtzRlHI3Tfjai064,4964
@@ -18,26 +19,29 @@ accusleepy/gui/icons/save.png,sha256=J3EA8iU1BqLYRSsrq_OdoZlqrv2yfL7oV54DklTy_DI
18
19
  accusleepy/gui/icons/up_arrow.png,sha256=V9yF9t1WgjPaUu-mF1YGe_DfaRHg2dUpR_sUVVcvVvY,3329
19
20
  accusleepy/gui/icons/zoom_in.png,sha256=MFWnKZp7Rvh4bLPq4Cqo4sB_jQYedUUtT8-ZO8tNYyc,13589
20
21
  accusleepy/gui/icons/zoom_out.png,sha256=IB8Jecb3i0U4qjWRR46ridjLpvLCSe7PozBaLqQqYSw,13055
21
- accusleepy/gui/images/primary_window.png,sha256=yRGUdIcByF8DtPtDWqjmnQ8J3IHKtmijBsT7N9e1Ij0,595764
22
+ accusleepy/gui/images/primary_window.png,sha256=hO28hOOoQmBljED1UDQES1ZxaAq8431scOzHemKUpCA,168073
22
23
  accusleepy/gui/images/viewer_window.png,sha256=b_B7m9WSLMAOzNjctq76SyekO1WfC6qYZVNnYfhjPe8,977197
23
24
  accusleepy/gui/images/viewer_window_annotated.png,sha256=uMNUmsZIdzDlQpyoiS3lJGoWlg_T325Oj5hDZhM3Y14,146817
24
- accusleepy/gui/main.py,sha256=cZQH0qJ_mykeQP7eadc1yzFHYeDwbRngrR4_H1wMxWY,58438
25
- accusleepy/gui/manual_scoring.py,sha256=QCFF-CIU6nh26NB8Z9ktohCk8p7ola2hgjazD-OrK_o,40697
25
+ accusleepy/gui/main.py,sha256=jtbeF1IXwCBDzpLTc_fFamkfDuNDc9ElotSLYPedWew,25094
26
+ accusleepy/gui/manual_scoring.py,sha256=9Lom83nPqSr_nbudgPQ92rEOUBt-s9dh2ExcL9-dHRo,40698
26
27
  accusleepy/gui/mplwidget.py,sha256=rJSTtWmLjHn8r3c9Kb23Rc4XzXl3i9B-JrjNjjlNnmQ,13492
27
- accusleepy/gui/primary_window.py,sha256=qqV-GAC3BgOus1lJMOg2U4AGQbhy35zLMyN8Q7ouTd8,141479
28
- accusleepy/gui/primary_window.ui,sha256=2mD0G6b8bsyyUvMCUosQ0K-nAzMA2iiP3yMpeh9INcQ,208209
28
+ accusleepy/gui/primary_window.py,sha256=-CbQ_dn3svE7J1VCGjTFDBCC4Q1DcV7Q7kz3UX3Kwhw,141613
29
+ accusleepy/gui/primary_window.ui,sha256=WmeY7L9VyuiNRx3kSM56kPv5t6RDBVXlte8kT5wH-2s,208461
30
+ accusleepy/gui/recording_manager.py,sha256=0lYrGu-7gX0xpLxBBcn4-TcuSy8CsmiPBJ3l_fZa158,3916
29
31
  accusleepy/gui/resources.qrc,sha256=wqPendnTLAuKfVI6v2lKHiRqAWM0oaz2ZuF5cucJdS4,803
30
32
  accusleepy/gui/resources_rc.py,sha256=Z2e34h30U4snJjnYdZVV9B6yjATKxxfvgTRt5uXtQdo,329727
33
+ accusleepy/gui/settings_widget.py,sha256=OG9DojTnbHVqJjE0Nk-OVU5c4UlHv7vA94H_dFJB-Jc,16824
31
34
  accusleepy/gui/text/dev_guide.md,sha256=PgOXfGvN17fCtnsfGvPhrhK4FUWFGP_TsHA6t9skP3U,2060
32
- accusleepy/gui/text/main_guide.md,sha256=iZDRp5OWyQX9LV7CMeUFIYv2ryKlIcGALRLXjxR8HpI,8288
35
+ accusleepy/gui/text/main_guide.md,sha256=QO0isKO-fqI-m062NY7k56fmBMNAV_ZFFNvE6ugCMi8,8289
33
36
  accusleepy/gui/text/manual_scoring_guide.md,sha256=ow_RMSjFy05NupEDSCuJtu-V65-BPnIkrZqtssFoZCQ,999
34
37
  accusleepy/gui/viewer_window.py,sha256=jysFw7C_Tr7mtK1XNWhIpHblBvatwduE3RF2GP4lrro,24479
35
38
  accusleepy/gui/viewer_window.ui,sha256=a89iVLk1sJg9N6ZvWAV6YNPStb2Tm4-rs-W7TmIDkb4,31658
36
- accusleepy/models.py,sha256=kqkcQJoKi7gpnM8gZ7nZbWGvpv7ruNnFLaB7ED1X6Dc,3493
39
+ accusleepy/models.py,sha256=MHMsC9sYfR3fTOQLh4COyjCZo9q2vaX1FmpTHaaVtM4,3516
37
40
  accusleepy/multitaper.py,sha256=D5-iglwkFBRciL5tKSNcunMtcq0rM3zHwRHUVPgem1U,25679
38
- accusleepy/signal_processing.py,sha256=47fEAx8Aqqkiqix1ai2YEK9Fhq6UHoQcwAcOi-a8ewo,16834
39
- accusleepy/temperature_scaling.py,sha256=glvPcvxHpBdFjwjGfZdNku9L_BozycEmdqZhKKUCCNg,5749
40
- accusleepy/validation.py,sha256=VpLWK-wD5tCU6lTBG3KYgTi3PWGuYh6NitMgMoMH8JM,4434
41
- accusleepy-0.8.1.dist-info/METADATA,sha256=29kPvzKr2ykosWwidIt6FKrjvIY-m88Ui9GQPoQPN-g,4710
42
- accusleepy-0.8.1.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
43
- accusleepy-0.8.1.dist-info/RECORD,,
41
+ accusleepy/services.py,sha256=4xl9uWDaJXIpJTYH08mSgipkkEYM9fDLIcMj7-k9qf0,20228
42
+ accusleepy/signal_processing.py,sha256=k3a2IKchok3-38P7T0XHNpGTiWKBqWtQm7SHwFEkTNA,19973
43
+ accusleepy/temperature_scaling.py,sha256=RXsbDLT5JDrjPbz5ZdFXKj6NxpQCHpq5BzcLBzsUJKc,5848
44
+ accusleepy/validation.py,sha256=h3iHVW71AaUC4lp1KMQXo5j81bS-IuYbTZWRSIkmHWE,6933
45
+ accusleepy-0.9.2.dist-info/METADATA,sha256=50NVD9h3oIMy-R9WLVypyNO-KiGxHF8eeUzjvgWtUJ0,4733
46
+ accusleepy-0.9.2.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
47
+ accusleepy-0.9.2.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.2.1
2
+ Generator: poetry-core 2.3.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any