SleePyPhases 0.1.6__tar.gz → 0.2.0__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.
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/PKG-INFO +11 -2
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/DataManipulation.py +9 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/Plugin.py +3 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/RecordFeatureExtraction.py +61 -20
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/SignalPreprocessing.py +1 -1
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/SleepMetaData.py +5 -1
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/__init__.py +2 -1
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/phases/BuildDataset.py +1 -1
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/phases/Extract.py +3 -4
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/phases/Setup.py +6 -1
- sleepyphases-0.2.0/SleePyPhases/phases/TestRun.py +147 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases.egg-info/PKG-INFO +11 -2
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases.egg-info/SOURCES.txt +1 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/setup.py +1 -1
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/tests/unit/test_DataManipulation.py +27 -1
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/LICENSE +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/README.md +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/EventScorer.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/FeatureExtraction copy.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/FeatureExtraction.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/MultiScorer.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/PSGEventManager.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/Plot.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/PreManipulation.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/Reporter.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/phases/DataAnalysis.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/phases/DataReport.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/phases/Eval.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/phases/EvalPlotExamples.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/phases/EvalReport.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/phases/ExportMetadataToMeticalDB.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/phases/ExtractEvents.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/phases/ExtractFeatures.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/phases/ExtractRecordFeatures.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/phases/GatherMetadata.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/phases/ThresholdOptimisation.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/phases/Training.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/phases/Validation.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/phases/VisualizeConfig.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/phases/__init__.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/recordloaders/MedicalDB.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/recordloaders/RecordLoaderTSM.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases/recordloaders/__init__.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases.egg-info/dependency_links.txt +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases.egg-info/requires.txt +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/SleePyPhases.egg-info/top_level.txt +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/pyproject.toml +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/setup.cfg +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/tests/__init__.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/tests/conftest.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/tests/unit/__init__.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/tests/unit/test_BuildDataset.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/tests/unit/test_BuildDatasetFolded.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/tests/unit/test_BuildDatasetSegments.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/tests/unit/test_Eval.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/tests/unit/test_EventScorer.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/tests/unit/test_Extract.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/tests/unit/test_PreManipulation.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/tests/unit/test_RecordFeatureExtraction.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/tests/unit/test_RecordProcessor.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/tests/unit/test_Setup.py +0 -0
- {sleepyphases-0.1.6 → sleepyphases-0.2.0}/tests/unit/test_SignalPreprocessing.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: SleePyPhases
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A framwork for creating deep learning pipelines for sleep data
|
|
5
5
|
Home-page: https://gitlab.com/sleep-is-all-you-need/sleepyphases
|
|
6
6
|
Author: Franz Ehrlich
|
|
@@ -14,6 +14,15 @@ License-File: LICENSE
|
|
|
14
14
|
Requires-Dist: SleepHarmonizer
|
|
15
15
|
Requires-Dist: pyPhasesML
|
|
16
16
|
Requires-Dist: phases
|
|
17
|
+
Dynamic: author
|
|
18
|
+
Dynamic: author-email
|
|
19
|
+
Dynamic: classifier
|
|
20
|
+
Dynamic: description
|
|
21
|
+
Dynamic: description-content-type
|
|
22
|
+
Dynamic: home-page
|
|
23
|
+
Dynamic: requires-dist
|
|
24
|
+
Dynamic: requires-python
|
|
25
|
+
Dynamic: summary
|
|
17
26
|
|
|
18
27
|
# Arousal Detector
|
|
19
28
|
|
|
@@ -880,4 +880,13 @@ class DataManipulation(pyPhasesDataManipulation, Swappable):
|
|
|
880
880
|
# Update the channel in Y
|
|
881
881
|
Y[:, :, channel] = y_channel
|
|
882
882
|
|
|
883
|
+
return X, Y
|
|
884
|
+
|
|
885
|
+
|
|
886
|
+
def combineY(self, X, Y, values, channel=0):
|
|
887
|
+
"""" Combine multiple Y values into one. """
|
|
888
|
+
smallest = min(values)
|
|
889
|
+
Y[:, :, channel] = np.where(np.isin(Y[:, :, channel], values), values[-1], Y[:, :, channel])
|
|
890
|
+
Y[:, :, channel][Y[:, :, channel] > smallest] -= len(values) - 1
|
|
891
|
+
|
|
883
892
|
return X, Y
|
|
@@ -11,6 +11,8 @@ from SleePyPhases.phases.Validation import Validation
|
|
|
11
11
|
from SleePyPhases.phases.Eval import Eval
|
|
12
12
|
from SleePyPhases.phases.EvalReport import EvalReport
|
|
13
13
|
from SleePyPhases.phases.ThresholdOptimisation import ThresholdOptimisation
|
|
14
|
+
from SleePyPhases.phases.TestRun import TestRun
|
|
15
|
+
|
|
14
16
|
|
|
15
17
|
from pyPhases.exporter.PickleExporter import PickleExporter
|
|
16
18
|
from pyPhases.exporter.PandasExporter import PandasExporter
|
|
@@ -31,6 +33,7 @@ class Plugin(PluginAdapter):
|
|
|
31
33
|
"ThresholdOptimisation": (ThresholdOptimisation, ["threshold", "validationResult"]),
|
|
32
34
|
"Eval": (Eval, ["evalResults", "eventResults"]),
|
|
33
35
|
"EvalReport": (EvalReport, []),
|
|
36
|
+
"TestRun": (TestRun, [])
|
|
34
37
|
|
|
35
38
|
}
|
|
36
39
|
dataMap = {
|
|
@@ -56,7 +56,7 @@ class RecordFeatureExtraction(pyPhasesFeatureExtraction):
|
|
|
56
56
|
rem_starts = [rem[i].start for i in range(1, len(rem)) if rem[i].start - rem[i-1].end() > 1]
|
|
57
57
|
rem_ends = [rem[i-1].end() for i in range(1, len(rem)) if rem[i].start - rem[i-1].end() > 1]
|
|
58
58
|
|
|
59
|
-
if len(rem_ends) == 0:
|
|
59
|
+
if len(rem_ends) == 0 or len(nrem) == 0:
|
|
60
60
|
return cycles
|
|
61
61
|
|
|
62
62
|
# Set cycle start and stop times
|
|
@@ -123,12 +123,35 @@ class RecordFeatureExtraction(pyPhasesFeatureExtraction):
|
|
|
123
123
|
|
|
124
124
|
return offsetStart, offsetEnd
|
|
125
125
|
|
|
126
|
+
def _updateEvents(self, eventlist, offsetStart, offsetEnd):
|
|
127
|
+
|
|
128
|
+
# update events
|
|
129
|
+
newEvents = []
|
|
130
|
+
for event in eventlist:
|
|
131
|
+
if offsetStart > 0:
|
|
132
|
+
if event.end() <= offsetStart:
|
|
133
|
+
continue
|
|
134
|
+
if event.start < offsetStart:
|
|
135
|
+
event.duration -= offsetStart - event.start
|
|
136
|
+
event.start = 0
|
|
137
|
+
else:
|
|
138
|
+
event.start -= offsetStart
|
|
139
|
+
if offsetEnd is not None:
|
|
140
|
+
if event.start >= offsetEnd - offsetStart:
|
|
141
|
+
continue
|
|
142
|
+
if event.end() > offsetEnd - offsetStart:
|
|
143
|
+
event.duration -= event.end() - offsetEnd + offsetStart
|
|
144
|
+
newEvents.append(event)
|
|
145
|
+
return newEvents
|
|
126
146
|
|
|
127
147
|
# TODO: outsource to PSGEventManager
|
|
128
148
|
def _tailorToSleepScoring(self, segmentSignal: RecordSignal, eventlist):
|
|
129
149
|
|
|
130
150
|
sleepEventNames = ["R", "N1", "N2", "N3", "W"]
|
|
131
151
|
sleepEvents = [e for e in eventlist if e.name in sleepEventNames]
|
|
152
|
+
|
|
153
|
+
if len(sleepEvents) == 0:
|
|
154
|
+
return segmentSignal, []
|
|
132
155
|
|
|
133
156
|
# fix if the last sleep stage has no duration (should be fixed in Recordloader (Alice))
|
|
134
157
|
if sleepEvents[-1].duration == 0:
|
|
@@ -145,23 +168,7 @@ class RecordFeatureExtraction(pyPhasesFeatureExtraction):
|
|
|
145
168
|
|
|
146
169
|
segmentSignal.signalOffset(offsetStart, offsetEnd)
|
|
147
170
|
|
|
148
|
-
|
|
149
|
-
newEvents = []
|
|
150
|
-
for event in eventlist:
|
|
151
|
-
if offsetStart > 0:
|
|
152
|
-
if event.end() <= offsetStart:
|
|
153
|
-
continue
|
|
154
|
-
if event.start < offsetStart:
|
|
155
|
-
event.duration -= offsetStart - event.start
|
|
156
|
-
event.start = 0
|
|
157
|
-
else:
|
|
158
|
-
event.start -= offsetStart
|
|
159
|
-
if offsetEnd is not None:
|
|
160
|
-
if event.start >= offsetEnd - offsetStart:
|
|
161
|
-
continue
|
|
162
|
-
if event.end() > offsetEnd - offsetStart:
|
|
163
|
-
event.duration -= event.end() - offsetEnd + offsetStart
|
|
164
|
-
newEvents.append(event)
|
|
171
|
+
newEvents = self._updateEvents(eventlist, offsetStart, offsetEnd)
|
|
165
172
|
|
|
166
173
|
return segmentSignal, newEvents
|
|
167
174
|
|
|
@@ -173,6 +180,17 @@ class RecordFeatureExtraction(pyPhasesFeatureExtraction):
|
|
|
173
180
|
|
|
174
181
|
signal = segmentSignal.getSignalByName(channel)
|
|
175
182
|
|
|
183
|
+
# check if the scoring is longer than the actual signal
|
|
184
|
+
sleepEventNames = ["R", "N1", "N2", "N3", "W"]
|
|
185
|
+
sleepEvents = [e for e in eventlist if e.name in sleepEventNames]
|
|
186
|
+
scoringLength = sleepEvents[-1].end() - sleepEvents[0].start
|
|
187
|
+
signalLength = signal.signal.shape[0] / signal.frequency
|
|
188
|
+
if scoringLength > signalLength:
|
|
189
|
+
newLength = (len(signal.signal)/signal.frequency//30)*30
|
|
190
|
+
newLength = min(newLength, signalLength)
|
|
191
|
+
eventlist = self._updateEvents(eventlist, 0, newLength)
|
|
192
|
+
signal.signal = signal.signal[:int(newLength*signal.frequency)]
|
|
193
|
+
|
|
176
194
|
|
|
177
195
|
labelSignal = PSGEventManager().getEventSignalFromList(
|
|
178
196
|
eventlist,
|
|
@@ -190,7 +208,6 @@ class RecordFeatureExtraction(pyPhasesFeatureExtraction):
|
|
|
190
208
|
stage_mask[stage_mask == 0] = np.nan
|
|
191
209
|
|
|
192
210
|
power_epoch_mean = self._swa(signal)
|
|
193
|
-
|
|
194
211
|
assert len(power_epoch_mean) == len(labelSignal)
|
|
195
212
|
|
|
196
213
|
signal_masked = stage_mask * power_epoch_mean
|
|
@@ -208,6 +225,8 @@ class RecordFeatureExtraction(pyPhasesFeatureExtraction):
|
|
|
208
225
|
def SWA3(self, segmentSignal: RecordSignal, eventlist: List[Event], channel: str):
|
|
209
226
|
|
|
210
227
|
segmentSignal, eventlist = self._tailorToSleepScoring(segmentSignal, eventlist)
|
|
228
|
+
if len(eventlist) == 0:
|
|
229
|
+
return {}
|
|
211
230
|
|
|
212
231
|
swa_cycles_all, swa_cycles_n2n3, cycles = self._SWAandCycles(segmentSignal, eventlist, channel)
|
|
213
232
|
|
|
@@ -222,4 +241,26 @@ class RecordFeatureExtraction(pyPhasesFeatureExtraction):
|
|
|
222
241
|
"deltapower_0.5_n2n3_first": swa_cycles_n2n3.iloc[0].to_list()[0],
|
|
223
242
|
"deltapower_0.5_n2n3_max": swa_cycles_n2n3.max().to_list()[0],
|
|
224
243
|
"deltapower_0.5_n2n3_last": swa_cycles_n2n3.iloc[-1].to_list()[0],
|
|
225
|
-
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
def REMCycleLength(self, segmentSignal: RecordSignal, eventlist: List[Event], channel: str):
|
|
247
|
+
cycles = self._SleepCycles(segmentSignal, eventlist)
|
|
248
|
+
|
|
249
|
+
lastStop = 0
|
|
250
|
+
remEvents = [e for e in eventlist if e.name == "R"]
|
|
251
|
+
|
|
252
|
+
ret = {
|
|
253
|
+
"cycle-count": len(cycles["start"]),
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
for cycleNumber, (start, stop) in enumerate(zip(cycles["start"], cycles["end"])):
|
|
257
|
+
if lastStop > 0:
|
|
258
|
+
remTime = sum([e.duration for e in remEvents if e.start >= lastStop and e.start < start])
|
|
259
|
+
ret[f"cycle-{cycleNumber}-rem-duration"] = remTime
|
|
260
|
+
|
|
261
|
+
lastStop = stop
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
"cycle-count": len(cycles["start"]),
|
|
265
|
+
}
|
|
266
|
+
|
|
@@ -244,7 +244,7 @@ class SignalPreprocessing(pyPhaseSignalPreprocessing, Swappable):
|
|
|
244
244
|
return np.concatenate(signal)
|
|
245
245
|
|
|
246
246
|
def fir(self, signal: Signal, recordSignal: RecordSignal, nFir, cutoff, pass_zero=False):
|
|
247
|
-
from scipy import signal
|
|
247
|
+
from scipy import signal as scipySignal
|
|
248
248
|
|
|
249
249
|
fs = signal.frequency
|
|
250
250
|
nyq = fs/2
|
|
@@ -68,16 +68,20 @@ class SleepMetaData:
|
|
|
68
68
|
self.fromArousalSignal(signal)
|
|
69
69
|
case 'ApneaBin':
|
|
70
70
|
self.fromApneaSignal(signal, ["None", "respEvent"])
|
|
71
|
+
case 'Apnea4':
|
|
72
|
+
self.fromApneaSignal(signal, ["None", "obstructive/mixed", "central", "hypopnea"])
|
|
71
73
|
case 'LMBin':
|
|
72
74
|
self.fromLegMovementSignal(signal)
|
|
73
75
|
case 'SleepBin':
|
|
74
76
|
self.fromSleepSignalBin(signal, frequency=frequency)
|
|
77
|
+
case 'Sleep4':
|
|
78
|
+
self.fromSleepSignalBin(signal, frequency=frequency)
|
|
75
79
|
|
|
76
80
|
def fromSleepSignalBin(self, signal, frequency = 1):
|
|
77
81
|
# all time values in seconds
|
|
78
82
|
trt = len(signal) / (frequency)
|
|
79
83
|
tst = sum(signal) / (frequency)
|
|
80
|
-
whereSleep = np.where(signal
|
|
84
|
+
whereSleep = np.where(signal > 0)[0]
|
|
81
85
|
if len(whereSleep) == 0:
|
|
82
86
|
sLatency = 0
|
|
83
87
|
else:
|
|
@@ -30,7 +30,7 @@ class BuildDataset(Phase):
|
|
|
30
30
|
|
|
31
31
|
self.project.updateConfig(overwriteConfig)
|
|
32
32
|
memmapOptions = {
|
|
33
|
-
"dtype": self.getConfig("preprocessing.dtype"),
|
|
33
|
+
"dtype": self.getConfig("preprocessing.dtype", "float32"),
|
|
34
34
|
}
|
|
35
35
|
dataExporterSignals = self.project.getData("data-processed", np.memmap, options=memmapOptions)
|
|
36
36
|
dataExporterFeatures = self.project.getData("data-features", np.memmap, options=memmapOptions)
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
from pyPhases import Phase
|
|
3
3
|
from pyPhases.util import BatchProgress
|
|
4
|
-
from
|
|
5
|
-
from pyPhasesRecordloader import AnnotationNotFound, ChannelsNotPresent, RecordLoader, ParseError
|
|
4
|
+
from pyPhasesRecordloader import AnnotationNotFound, ChannelsNotPresent, ParseError, RecordLoader
|
|
6
5
|
|
|
6
|
+
from SleePyPhases.FeatureExtraction import FeatureExtraction
|
|
7
7
|
from SleePyPhases.PreManipulation import PreManipulation
|
|
8
8
|
from SleePyPhases.PSGEventManager import PSGEventManager
|
|
9
9
|
from SleePyPhases.SignalPreprocessing import SignalPreprocessing
|
|
10
|
-
from SleePyPhases.FeatureExtraction import FeatureExtraction
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
class RecordProcessor:
|
|
@@ -207,7 +206,7 @@ class Extract(Phase):
|
|
|
207
206
|
processRecord = RecordProcessor(
|
|
208
207
|
recordLoader=RecordLoader.get(),
|
|
209
208
|
preProcessingConfig=preprocessingConfig,
|
|
210
|
-
signalProcessing=SignalPreprocessing(preprocessingConfig),
|
|
209
|
+
signalProcessing=SignalPreprocessing.getInstance(preprocessingConfig),
|
|
211
210
|
eventManager=PSGEventManager(),
|
|
212
211
|
labelChannels=self.getConfig("labelChannels"),
|
|
213
212
|
project=self.project,
|
|
@@ -97,7 +97,12 @@ class Setup(Phase):
|
|
|
97
97
|
|
|
98
98
|
# add training folding splits
|
|
99
99
|
if "trainval" in splits:
|
|
100
|
-
|
|
100
|
+
foldcount = self.getConfig("dataversion.folds", 0)
|
|
101
|
+
if foldcount > 0:
|
|
102
|
+
dm.addSplitsByFold("training", "validation", splits["trainval"], self.getConfig("dataversion.folds", 0), self.getConfig("fold", 0))
|
|
103
|
+
else:
|
|
104
|
+
dm.addSplitByRemaining("validation", valSplit) # , remainingSplit=trainingSlice
|
|
105
|
+
dm.addSplitByRemaining("training", 1)
|
|
101
106
|
|
|
102
107
|
if "training" in splits or "validation" in splits:
|
|
103
108
|
raise Exception("trainval and training/validation split are mutually exclusive. Please remove one of them from the config. This can be caused by loading multiple configs with different splits.")
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
from SleePyPhases import SignalPreprocessing as SP, PreManipulation as PM, FeatureExtraction as FE, DataManipulation as DM
|
|
5
|
+
from SleePyPhases.phases.Extract import Extract
|
|
6
|
+
from SleePyPhases.phases.BuildDataset import BuildDataset
|
|
7
|
+
from pyPhases import Phase
|
|
8
|
+
from pyPhasesRecordloader import RecordLoader
|
|
9
|
+
import os
|
|
10
|
+
from pyPhasesML import ModelManager, TrainingSetLoader
|
|
11
|
+
|
|
12
|
+
class TestRun(Phase):
|
|
13
|
+
useMultiThreading = False
|
|
14
|
+
def main(self):
|
|
15
|
+
import numpy as np
|
|
16
|
+
import pandas as pd
|
|
17
|
+
|
|
18
|
+
debugConfig = self.getConfig("debugConfig", {})
|
|
19
|
+
print(f"Overwrite Config with debug values in debugConfig: {debugConfig}")
|
|
20
|
+
|
|
21
|
+
with self.project:
|
|
22
|
+
self.project.updateConfig(debugConfig)
|
|
23
|
+
loader = self.getConfig("useLoader")
|
|
24
|
+
rl = RecordLoader.get()
|
|
25
|
+
# rl.debug = True
|
|
26
|
+
|
|
27
|
+
if not self.useMultiThreading:
|
|
28
|
+
self.log("Disable multi threading for better error messages. To test multi threading, set TestRun.useMultiThreading to True")
|
|
29
|
+
Extract.useMultiThreading = False
|
|
30
|
+
BuildDataset.useMultiThreading = False
|
|
31
|
+
|
|
32
|
+
self.log("Testing pyPhases settings")
|
|
33
|
+
dataPath = Path(self.getConfig("data-path"))
|
|
34
|
+
if dataPath.exists():
|
|
35
|
+
self.logSuccess(f"Path {dataPath} exists: {dataPath.exists()}")
|
|
36
|
+
|
|
37
|
+
# Test if directory is writable
|
|
38
|
+
if os.access(dataPath, os.W_OK):
|
|
39
|
+
self.logSuccess(f"Data Path ({dataPath}) is writable")
|
|
40
|
+
else:
|
|
41
|
+
self.logError(f"Data Path ({dataPath}) is not writable. Make sure to set the correct path (Config: data-path) and permissions.")
|
|
42
|
+
exit(1)
|
|
43
|
+
else:
|
|
44
|
+
self.logError(f"Data Path ({dataPath}) does not exist. Please set the correct path (Config: data-path)")
|
|
45
|
+
exit(1)
|
|
46
|
+
|
|
47
|
+
testRunData = dataPath / "testrun"
|
|
48
|
+
self.log(f"Creating a new Data Folder ({testRunData.as_posix()}) for the testrun")
|
|
49
|
+
if not testRunData.exists():
|
|
50
|
+
testRunData.mkdir()
|
|
51
|
+
# else:
|
|
52
|
+
# # remove all files from the testrun folder
|
|
53
|
+
# for f in testRunData.iterdir():
|
|
54
|
+
# f.unlink()
|
|
55
|
+
|
|
56
|
+
self.log("Creating a new Data Folder for the testrun")
|
|
57
|
+
|
|
58
|
+
self.log(f"Test specified RecordLoader: {loader} ({type(rl)})")
|
|
59
|
+
datasetPath = Path(rl.filePath)
|
|
60
|
+
if datasetPath.exists():
|
|
61
|
+
self.logSuccess(f"RL Path ({rl.filePath}) exists: {Path(rl.filePath).exists()}")
|
|
62
|
+
else:
|
|
63
|
+
self.logError(f"RL Path ({rl.filePath}) does not exist. Make sure to download the dataset and set the correct path (Config: {loader}-path)")
|
|
64
|
+
exit(1)
|
|
65
|
+
|
|
66
|
+
self.setConfig("testrun", True)
|
|
67
|
+
|
|
68
|
+
self.log("Test generating dataset metadata, for first records")
|
|
69
|
+
df = self.getData("metadata", pd.DataFrame)
|
|
70
|
+
self.log(f"Metadata: {df.head()} / {df.shape}")
|
|
71
|
+
|
|
72
|
+
if len(df) > 0:
|
|
73
|
+
self.logSuccess("Dataset records loaded")
|
|
74
|
+
else:
|
|
75
|
+
self.logError("Dataset records not loaded")
|
|
76
|
+
# metadata-channels, dataIsFinal
|
|
77
|
+
|
|
78
|
+
self.log("Test data version and split")
|
|
79
|
+
dm = self.getData("dataversionmanager")
|
|
80
|
+
self.log(f"Splits (assuming all records exist): {dm.splits}")
|
|
81
|
+
|
|
82
|
+
self.log("For debugging purpose, we will reduce the split using the same 2 records for all splits")
|
|
83
|
+
dm.splits["training"][0] = slice(0, 2)
|
|
84
|
+
dm.splits["validation"][0] = slice(0, 2)
|
|
85
|
+
dm.splits["test"][0] = slice(0, 2)
|
|
86
|
+
|
|
87
|
+
self.log("Test extracting data with preprocessing defined in (Config: preprocessing)")
|
|
88
|
+
self.log(f"Preprocessing Config: {self.getConfig('preprocessing')}")
|
|
89
|
+
self.log(f"Preprocessing target frequency: {self.getConfig('preprocessing.targetFrequency')}")
|
|
90
|
+
self.log(f"Preprocessing target labelFrequency: {self.getConfig('preprocessing.labelFrequency')}")
|
|
91
|
+
self.log(f"Preprocessing target channels: {self.getConfig('preprocessing.targetChannels')}")
|
|
92
|
+
# check that resample is in each target signal related preprocessing
|
|
93
|
+
|
|
94
|
+
self.log(f"Current SignalPreprocessing Class: {type(SP.getInstance(self.getConfig('preprocessing')))}")
|
|
95
|
+
self.log(f"Current PreManipulation Class: {type(PM.getInstance(self.getConfig('preprocessing')))}")
|
|
96
|
+
# self.log(f"Current FeatureExtraction: {type(FE.getInstance(self.getConfig('preprocessing')))}")
|
|
97
|
+
self.log(f"Current SignalPreprocessing: {self.getConfig('preprocessing.stepsPerType')}")
|
|
98
|
+
self.log(f"Current PreManipulation: {self.getConfig('preprocessing.manipulationSteps')}")
|
|
99
|
+
# self.log(f"Current FeatureExtraction: {type(FE.getInstance(self.getConfig('preprocessing')))}")
|
|
100
|
+
|
|
101
|
+
memmapOptions = {
|
|
102
|
+
"dtype": self.getConfig("preprocessing.dtype", "float32"),
|
|
103
|
+
}
|
|
104
|
+
dataExporterSignals = self.getData("data-processed", np.memmap, options=memmapOptions)
|
|
105
|
+
dataExporterFeatures = self.getData("data-features", np.memmap, options=memmapOptions)
|
|
106
|
+
|
|
107
|
+
self.logSuccess(f"Extract finished: len X: {len(dataExporterSignals)} / len Y: {len(dataExporterFeatures)}")
|
|
108
|
+
|
|
109
|
+
self.log("Test data manipulation")
|
|
110
|
+
manipulationSteps = self.getConfig("segmentManipulation")
|
|
111
|
+
self.log(f"Current segmentManipulation: {manipulationSteps}")
|
|
112
|
+
segmentManipulation = DM.getInstance(manipulationSteps, "training", self.project.config)
|
|
113
|
+
self.log(f"Current DataManipulation Class: {type(segmentManipulation)}")
|
|
114
|
+
# segmentManipulation = DM.getInstance(manipulationSteps, "training", self.project.config, recordMetadata=recordMetaData)
|
|
115
|
+
|
|
116
|
+
X, Y = dataExporterSignals[0], dataExporterFeatures[0]
|
|
117
|
+
X, Y = segmentManipulation((X, Y), None, 0)
|
|
118
|
+
|
|
119
|
+
self.logSuccess(f"Segment Manipulation finished: X: {X.shape} / Y: {Y.shape}")
|
|
120
|
+
|
|
121
|
+
self.log(f"Test Model: {self.getConfig('modelName')}")
|
|
122
|
+
|
|
123
|
+
model = ModelManager.getModel()
|
|
124
|
+
self.log(f"Current Model: {type(model)}")
|
|
125
|
+
self.log(model.summary())
|
|
126
|
+
train = self.project.generateData("dataset-training")
|
|
127
|
+
val = self.project.generateData("dataset-validation")
|
|
128
|
+
trainingsSet = TrainingSetLoader(trainingData=train, validationData=val)
|
|
129
|
+
model.debug = True
|
|
130
|
+
trainedModel = model.train(trainingsSet)
|
|
131
|
+
|
|
132
|
+
self.logSuccess("Debug Model trained")
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
# allDBRecordIds
|
|
138
|
+
# self.getData("allRecordIds")
|
|
139
|
+
# extract data
|
|
140
|
+
# build data
|
|
141
|
+
# data manipulation
|
|
142
|
+
# load model
|
|
143
|
+
# training
|
|
144
|
+
# validation
|
|
145
|
+
# threshold optimization
|
|
146
|
+
# segment evaluation
|
|
147
|
+
# event evaluation
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: SleePyPhases
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A framwork for creating deep learning pipelines for sleep data
|
|
5
5
|
Home-page: https://gitlab.com/sleep-is-all-you-need/sleepyphases
|
|
6
6
|
Author: Franz Ehrlich
|
|
@@ -14,6 +14,15 @@ License-File: LICENSE
|
|
|
14
14
|
Requires-Dist: SleepHarmonizer
|
|
15
15
|
Requires-Dist: pyPhasesML
|
|
16
16
|
Requires-Dist: phases
|
|
17
|
+
Dynamic: author
|
|
18
|
+
Dynamic: author-email
|
|
19
|
+
Dynamic: classifier
|
|
20
|
+
Dynamic: description
|
|
21
|
+
Dynamic: description-content-type
|
|
22
|
+
Dynamic: home-page
|
|
23
|
+
Dynamic: requires-dist
|
|
24
|
+
Dynamic: requires-python
|
|
25
|
+
Dynamic: summary
|
|
17
26
|
|
|
18
27
|
# Arousal Detector
|
|
19
28
|
|
|
@@ -34,6 +34,7 @@ SleePyPhases/phases/ExtractFeatures.py
|
|
|
34
34
|
SleePyPhases/phases/ExtractRecordFeatures.py
|
|
35
35
|
SleePyPhases/phases/GatherMetadata.py
|
|
36
36
|
SleePyPhases/phases/Setup.py
|
|
37
|
+
SleePyPhases/phases/TestRun.py
|
|
37
38
|
SleePyPhases/phases/ThresholdOptimisation.py
|
|
38
39
|
SleePyPhases/phases/Training.py
|
|
39
40
|
SleePyPhases/phases/Validation.py
|
|
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
|
|
|
5
5
|
|
|
6
6
|
setuptools.setup(
|
|
7
7
|
name="SleePyPhases",
|
|
8
|
-
version="v0.
|
|
8
|
+
version="v0.2.0"[1:],
|
|
9
9
|
author="Franz Ehrlich",
|
|
10
10
|
author_email="fehrlichd@gmail.com",
|
|
11
11
|
description="A framwork for creating deep learning pipelines for sleep data",
|
|
@@ -268,4 +268,30 @@ class TestDataManipulation(unittest.TestCase):
|
|
|
268
268
|
|
|
269
269
|
npt.assert_equal(Y_new.shape, expected.shape)
|
|
270
270
|
npt.assert_equal(Y_new[0, :, 0], expected[0, :, 0])
|
|
271
|
-
npt.assert_equal(X_new, X) # X should remain unchanged
|
|
271
|
+
npt.assert_equal(X_new, X) # X should remain unchanged
|
|
272
|
+
|
|
273
|
+
def test_combineY(self):
|
|
274
|
+
da = self.getDA()
|
|
275
|
+
|
|
276
|
+
X = np.zeros((2, 10, 2))
|
|
277
|
+
Y = np.array([
|
|
278
|
+
[[0], [1], [2], [3], [4], [0], [1], [2], [3], [4]],
|
|
279
|
+
[[4], [3], [2], [1], [0], [4], [3], [2], [1], [0]]
|
|
280
|
+
])
|
|
281
|
+
|
|
282
|
+
# Test combining values 2,1 into 0
|
|
283
|
+
_, Y_out = da.combineY(X, Y.copy(), values=[2, 1, 0])
|
|
284
|
+
expected = np.array([
|
|
285
|
+
[[0], [0], [0], [1], [2], [0], [0], [0], [1], [2]],
|
|
286
|
+
[[2], [1], [0], [0], [0], [2], [1], [0], [0], [0]]
|
|
287
|
+
])
|
|
288
|
+
|
|
289
|
+
np.testing.assert_array_equal(Y_out, expected)
|
|
290
|
+
|
|
291
|
+
# Test combining values 4,3,2 into 1
|
|
292
|
+
_, Y_out = da.combineY(X, Y.copy(), values=[1, 2])
|
|
293
|
+
expected = np.array([
|
|
294
|
+
[[0], [1], [1], [2], [3], [0], [1], [1], [2], [3]],
|
|
295
|
+
[[3], [2], [1], [1], [0], [3], [2], [1], [1], [0]]
|
|
296
|
+
])
|
|
297
|
+
np.testing.assert_array_equal(Y_out, expected)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|