Skip to content

Commit fa09455

Browse files
committed
feat: add options to enable/disable spurious sleep removal
1 parent a208857 commit fa09455

3 files changed

Lines changed: 28 additions & 6 deletions

File tree

src/accelerometer/accProcess.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,15 @@ def main(): # noqa: C901
175175
parser.add_argument('--activityModel', type=str,
176176
default="walmsley",
177177
help="""trained activity model .tar file""")
178+
parser.add_argument('--removeSpuriousSleep',
179+
metavar='True/False', default=True, type=str2bool,
180+
help="""Remove spurious sleep periods from the
181+
activity classification? (default : %(default)s)""")
182+
parser.add_argument('--removeSpuriousSleepTol',
183+
metavar='mins', default=60, type=int,
184+
help="""Sleep tolerance in minutes. If `--removeSpuriousSleep`
185+
and a sleep streak is shorter than this, it will be replaced
186+
with sedentary activity (default : %(default)s)""")
178187

179188
# circadian rhythm options
180189
parser.add_argument('--psd',
@@ -329,6 +338,7 @@ def deleteIntermediateFiles():
329338
endTime=args.endTime, epochPeriod=args.epochPeriod,
330339
stationaryStd=args.stationaryStd, minNonWearDuration=args.minNonWearDuration,
331340
mgCpLPA=args.mgCpLPA, mgCpMPA=args.mgCpMPA, mgCpVPA=args.mgCpVPA,
341+
removeSpuriousSleep=args.removeSpuriousSleep, removeSpuriousSleepTol=args.removeSpuriousSleepTol,
332342
activityModel=args.activityModel,
333343
intensityDistribution=args.intensityDistribution,
334344
psd=args.psd, fourierFrequency=args.fourierFrequency,

src/accelerometer/classification.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ def activityClassification(
2525
activityModel: str = "walmsley",
2626
mgCpLPA: int = 45,
2727
mgCpMPA: int = 100,
28-
mgCpVPA: int = 400
28+
mgCpVPA: int = 400,
29+
spuriousSleepRemoval: bool = True,
30+
spuriousSleepTol: int = 60, # in minutes
2931
):
3032
"""
3133
Perform classification of activity states from epoch feature data. Based on
@@ -72,7 +74,8 @@ def activityClassification(
7274
del enmo
7375
del other
7476

75-
Y = removeSpuriousSleep(Y, activityModel=activityModel)
77+
if spuriousSleepRemoval:
78+
Y = removeSpuriousSleep(Y, activityModel=activityModel, sleepTol=spuriousSleepTol)
7679

7780
# One-hot encoding
7881
epoch.loc[ok, labels] = (Y[ok].to_numpy()[:, None] == labels).astype('float')
@@ -334,13 +337,14 @@ def log(x):
334337
return viterbi_path
335338

336339

337-
def removeSpuriousSleep(Y, activityModel='walmsley', sleepTol='1H'):
340+
def removeSpuriousSleep(Y, activityModel='walmsley', sleepTol=60):
338341
"""
339342
Remove spurious sleep epochs from activity classification.
340343
341344
:param pandas.Series Y: Model output
342345
:param str activityModel: Model identifier
343-
:param str sleepTol: Minimum sleep duration, e.g. '1H'
346+
:param str sleepTol: Sleep tolerance in minutes. If a sleep streak is shorter
347+
than this, it will be replaced with sedentary activity.
344348
345349
:return: Dataframe of revised model output
346350
:rtype: pandas.DataFrame
@@ -359,7 +363,7 @@ def removeSpuriousSleep(Y, activityModel='walmsley', sleepTol='1H'):
359363
.cumsum()
360364
.pipe(lambda x: x.groupby(x).transform('count') * sleep)
361365
)
362-
sleepTol = pd.Timedelta(sleepTol) / Y.index.freq
366+
sleepTol = pd.Timedelta(f"{sleepTol}min") / Y.index.freq
363367
whr = sleep & (sleepStreak < sleepTol)
364368
Y = Y.copy() # no modify original
365369
Y.loc[whr] = newValue

src/accelerometer/summarisation.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ def getActivitySummary( # noqa: C901
1515
startTime=None, endTime=None,
1616
epochPeriod=30, stationaryStd=13, minNonWearDuration=60,
1717
mgCpLPA=45, mgCpMPA=100, mgCpVPA=400,
18+
removeSpuriousSleep=True, removeSpuriousSleepTol=60,
1819
activityModel="walmsley",
1920
intensityDistribution=False, imputation=True,
2021
psd=False, fourierFrequency=False, fourierWithAcc=False, m10l5=False
@@ -41,6 +42,8 @@ def getActivitySummary( # noqa: C901
4142
:param int minNonWearDuration: Minimum duration of nonwear events (minutes)
4243
:param int mgCutPointMVPA: Milli-gravity threshold for moderate intensity activity
4344
:param int mgCutPointVPA: Milli-gravity threshold for vigorous intensity activity
45+
:param bool removeSpuriousSleep: Remove spurious sleep epochs
46+
:param int removeSpuriousSleepTol: Tolerance (in minutes) for spurious sleep removal
4447
:param str activityModel: Input tar model file which contains random forest
4548
pickle model, HMM priors/transitions/emissions npy files, and npy file
4649
of METS for each activity state
@@ -103,7 +106,12 @@ def getActivitySummary( # noqa: C901
103106
# Predict activity from features, and add label column
104107
labels = []
105108
if activityClassification:
106-
data, labels = classification.activityClassification(data, activityModel, mgCpLPA, mgCpMPA, mgCpVPA)
109+
data, labels = classification.activityClassification(
110+
data,
111+
activityModel,
112+
mgCpLPA, mgCpMPA, mgCpVPA,
113+
removeSpuriousSleep, removeSpuriousSleepTol
114+
)
107115

108116
# Calculate empirical cumulative distribution function of vector magnitudes
109117
if intensityDistribution:

0 commit comments

Comments
 (0)