I'm building an application for sleep analysis using Apple Healthkit and wish to retrieve nightly sleep statistics (time in REM, deep, light etc). The apple developer video gives the following code to retrieve samples in all sleep stages...
let stagePredicate = HKCategoryValueSleepAnalysis.predicateForSamples(equalTo: .allAsleepValues)
let queryPredicate = HKSamplePredicate.sample(type: HKCategoryType(.sleepAnalysis), predicate: stagePredicate)
let sleepQuery = HKSampleQueryDescriptor(predicates: [queryPredicate], sortDescriptors: [])
// Run the query
let sleepSamples = try async sleepQuery.result(for: healthStore)
but how do I compute the time in each of the stages for the previous night? I'm very new to healthkit so any help would be appreciated.
To compute the time in each stage you'll want to iterate over the samples and check the category value. You can keep a time interval per stage and increment it based on the duration of the sample you're looking at.
The tricky part is determining what "previous night" is. Try defining equally sized buckets where you will count all samples in that bucket as part of the same "sleep session" (e.g. 6pm - 6pm instead of 12am - 12am).
Related
I know that HealthKit can retrieve sleep states based on some samples given start and end time. But I wonder if it can give a current realtime status of the sleep state, meaning just retrieving the current state instead. Or is it using some algorithm and this requires several values and that's why all I can find is about taking samples with some time gap in middle?
let sleepSampleType = HKCategoryType(.sleepAnalysis)
let sleepCategory = HKCategoryValueSleepAnalysis.asleepDeep.rawValue
let deepSleepSample = HKCategorySample(type: sleepSampleType,
value:sleepCategory,
start: startDate,
end: endDate)
Sleep stages data is not available in real-time. You can only read it after the user wakes up and the samples are written.
What I am trying to do:
Consume json messages from PubSub subscription using Apache Beam Streaming pipeline & Dataflow Runner
Unmarshal payload strings into objects.
Assume 'messageId' is the unique Id of incoming message. Ex: msgid1, msgid2, etc
Retrieve child records from a database for each object resulted from #2. Same child can be applicable for multiple messages.
Assume 'childId' as the unique Id of child record. Ex: cid1234, cid1235 etc
Group child records by their unique id as shown in example below
KV.of(cid1234,Map.of(msgid1, msgid2)) and KV.of(cid1235,Map.of(msgid1, msgid2))
Write grouped result at childId level to the database
Questions:
Where should the windowing be introduced? we currently have 30minutes fixed windowing after step#1
How does Beam define start and end time of 30mins window? is it right after we start pipeline or after first message of batch?
What if the steps 2 to 5 take more than 1hour for a window and next window batch is ready. Would both windows batches gets processed in parallel?
How can make the next window messages wait until previous window batch is completed?
If we dont do this, the result at childId level will be overwritten by next batches
Code snippet:
PCollection<PubsubMessage> messages = pipeline.apply("ReadPubSubSubscription",
PubsubIO.readMessagesWithAttributes()
.fromSubscription("projects/project1/subscriptions/subscription1"));
PCollection<PubsubMessage> windowedMessages = messages.apply(Window.into(FixedWindows
.of(Duration.standardMinutes(30))));
PCollectionTuple unmarshalResultTuple = windowedMessages.apply("UnmarshalJsonStrings",
ParDo.of(new JsonUnmarshallFn())
.withOutputTags(JsonUnmarshallFn.mainOutputTag,
TupleTagList.of(JsonUnmarshallFn.deadLetterTag)));
PCollectionTuple childRecordsTuple = unmarshalResultTuple
.get(JsonUnmarshallFn.mainOutputTag)
.apply("FetchChildsFromDBAndProcess",
ParDo.of(new ChildsReadFn() )
.withOutputTags(ChildsReadFn.mainOutputTag,
TupleTagList.of(ChildsReadFn.deadLetterTag)));
// input is KV of (childId, msgids), output is mutations to write to BT
PCollectionTuple postProcessTuple = childRecordsTuple
.get(ChildsReadFn.mainOutputTag)
.apply(GroupByKey.create())
.apply("UpdateChildAssociations",
ParDo.of(new ChildsProcessorFn())
.withOutputTags(ChildsProcessorFn.mutations,
TupleTagList.of(ChildsProcessorFn.deadLetterTag)));
postProcessTuple.get(ChildsProcessorFn.mutations).CloudBigtableIO.write(...);
Addressing each of your questions.
Regarding questions 1 and 2 When you us Windowing within Apache Beam, you need to understand that the "windows existed before the job". What I mean is that the windows start at the UNIX epoch (timestamp = 0). In other words, your data will be allocated within each fixed time range, example with fixed 60 seconds windows:
PCollection<String> items = ...;
PCollection<String> fixedWindowedItems = items.apply(
Window.<String>into(FixedWindows.of(Duration.standardSeconds(60))));
First window: [0s;59s) - Second : [60s;120s)...and so on
Please refer to the documentation 1, 2 and 3
About question 3, the default of Windowing and Triggering in Apache Beam is to ignore late data. Although, it is possible to configure the handling of late data using withAllowedLateness. In order to do so, it is necessary to understand the concept of Watermarks before. Watermark is a metric of how far behind the data is. Example: you can have a 3 second watermark, then if your data is 3 seconds late it will be assigned to the right window. On the other hand, if it is passed the watermark, you define what it will happen with this data, you can reprocess or ignore it using Triggers.
withAllowedLateness
PCollection<String> items = ...;
PCollection<String> fixedWindowedItems = items.apply(
Window.<String>into(FixedWindows.of(Duration.standardMinutes(1)))
.withAllowedLateness(Duration.standardDays(2)));
Pay attention that an amount of time is set for late data to arrive.
Triggering
PCollection<String> pc = ...;
pc.apply(Window.<String>into(FixedWindows.of(1, TimeUnit.MINUTES))
.triggering(AfterProcessingTime.pastFirstElementInPane() .plusDelayOf(Duration.standardMinutes(1)))
.withAllowedLateness(Duration.standardMinutes(30));
Notice that the window is re-processed and re-computed event time there is late data. This trigger gives you the opportunity to react to the late data.
Finally, about question 4, which is partially explained with the concepts described above. The computations will occur within each fixed window and recomputed/processed every time a trigger is fired. This logic will guarantee your data it is in the right window.
I am trying to display the sleep from the HealthKit. I am using AppCore
to display other HKQuantity. I use the following for HKQuantities like Steps, etc.
[[APCScoring alloc] initWithHealthKitQuantityType:HKQuantityType
unit:[HKUnit countUnit]
numberOfDays:-kNumberOfDaysToDisplay];
My issue is that sleep data is not a HealthKitQuantityType and I can't use HKStatisticsCollectionQuery.
I am looking to display HKCategoryValueSleepAnalysisAsleep.
As you probably figured out, Healthkit sleep analysis is not quantitative.
As describe in the Apple documentation you have only 3 states: inBed, aSleep or awake.
I've got same question and to work around, I count minutes aSleep versus minutes inBed and minutes awake (depending of what's relevant to you) based on startDate and endDate. Then I display the result in an histogram chart or similar.
If you're looking for a way to fetch or save sleep analysis data with Healthkit, I've written a post a year ago here that can eventually help you.
I'm need to query HealthKit for HKCategoryTypeIdentifierSleepAnalysis data, but can't find the compatible HKUnit for quantity value. Apple documentation is silent on units for Sleep Analysis. Am hoping someone already knows the answer.
BTW, the iOS Health app shows Hrs & Minutes on the Sleep chart, but the HKUnit reference doesn't include options for such composite units.
In Apples documentation I found this:
By comparing the start and end times of these samples, apps can calculate a number of secondary statistics: the amount of time it took for the user to fall asleep, the percentage of time in bed that the user actually spent sleeping, the number of times the user woke while in bed, and the total amount of time spent both in bed and asleep.
This means that you have to use the startDate and endDate property of your sample to calculate sleep durations.
Sleep samples are instances of HKCategorySample, which is unit-less. You should perform calculations for sleep samples using the startDate and endDate properties on the sample.
I want to do a sleep analysis for the user in my app.And I think the CoreMotion Framewrok should help.
func queryActivityStartingFromDate(start: NSDate!, toDate end: NSDate!, toQueue queue: NSOperationQueue!, withHandler handler: CMMotionActivityQueryHandler!)
So I use the API above to get the user motion data in the last 7 days.And Now I get a list of CMMotionActivity Object.
And My question is how to calculate the user sleep status from these thousands of CMMotionActivity Object.Is there any algorithms? Or any other way to achieve sleep analysis.
Many Thanks!
CMMotionActivity includes a stationary property which might be useful in your case.
Look for contiguous periods of inactivity, paired with location, timezone & CMDeviceMotion data you should begin to detect patterns in the dataset you have. use statistical variation to define thresholds and fine-tune your results.
Caveat, you will make assumptions which might not be true. Some people sleep in moving vehicles for instance.
I found this pdf useful ftp://ftp.tik.ee.ethz.ch/pub/students/2010-HS/SA-2010-26.pdf