confinement-measures-combined.ipynb
Use both Oura Ring and RescueTime data to understand how the confinement has impacted productivity and physical activity/sleep.
The Oura Ring gives you daily scores for activity
, readiness
and sleep
. Let's see how those values change over time, comparing before, during and the confinement. Similarly this notebook compares your productivity as measured by Rescuetime. You can easily run this notebook on your data. You just need to connect your Oura ring to Open Humans and also connect your RescueTime account!
To remove some of the noise inherent to day-to-day measurements we average all of our values by taking the weekly mean values. This makes individual data points more comparable, as we are always guaranteed to have the same amount of weekend days included etc. We start off by plotting the RescueTime data. RescueTime gives us some details on which categories the time is spent in, but we'll define some extra categories, which are meetings (via Zoom, Google Meet, Jitsi etc. as well as in-person meetings) and messages, which would be Slack, Mail, Rocket.Chat etc.
The plot divides the time in before and after the lockdown, with everything in between the two red bars being the lockdown period. The dashed lines are smoothed, while the continuous lines give the individual weekly data points.
Let's also check out the sleep and physical activity for the lockdown, here taken from the Oura ring.
We plot both the raw data for the average nightly sleep time and the average number of steps for a week and plot those, as well as some processed "scores" that Oura assigns, which range between 0-100, with 100 being "the best".
The Oura Ring gives you daily scores for activity
, readiness
and sleep
. Let's see how those values change over time, comparing before, during and the confinement. Similarly this notebook compares your productivity as measured by Rescuetime. You can easily run this notebook on your data. You just need to connect your Oura ring to Open Humans and also connect your RescueTime account!
from ohapi import api
import os
import requests
import tempfile
import json
import pandas as pd
from datetime import datetime
user_details = api.exchange_oauth2_member(os.environ.get('OH_ACCESS_TOKEN'))
for i in user_details['data']:
if i['source'] == 'direct-sharing-184' and i['basename'] == 'oura-data.json':
oura = json.loads(requests.get(i['download_url']).content)
if i['source'] == "direct-sharing-149":
rescuetime_data = json.loads(requests.get(i['download_url']).content)
def read_oura(oura):
dates = []
values = []
value_type = []
for sdate in oura['sleep']:
dates.append(sdate['summary_date'])
values.append(sdate['score'])
value_type.append('sleep')
dates.append(sdate['summary_date'])
values.append(sdate['total'])
value_type.append('sleep_sum')
for sdate in oura['activity']:
dates.append(sdate['summary_date'])
values.append(sdate['score'])
value_type.append('activity')
dates.append(sdate['summary_date'])
values.append(sdate['steps'])
value_type.append('steps')
for sdate in oura['readiness']:
dates.append(sdate['summary_date'])
values.append(sdate['score'])
value_type.append('readiness')
dataframe = pd.DataFrame(
data = {
'date': dates,
'value': values,
'type': value_type
}
)
return dataframe
def read_rescuetime(rescuetime_data):
date = []
time_spent_seconds = []
activity = []
category = []
productivity = []
for element in rescuetime_data['rows']:
date.append(element[0])
time_spent_seconds.append(element[1])
activity.append(element[3])
category.append(element[4])
productivity.append(element[5])
date = [datetime.strptime(dt,"%Y-%m-%dT%H:%M:%S") for dt in date]
rt_df = pd.DataFrame(data={
'date': date,
'time_spent_seconds': time_spent_seconds,
'activity': activity,
'category': category,
'productivity': productivity
})
return rt_df
dataframe_oura = read_oura(oura)
rt_df = read_rescuetime(rescuetime_data)
To remove some of the noise inherent to day-to-day measurements we average all of our values by taking the weekly mean values. This makes individual data points more comparable, as we are always guaranteed to have the same amount of weekend days included etc. We start off by plotting the RescueTime data. RescueTime gives us some details on which categories the time is spent in, but we'll define some extra categories, which are meetings (via Zoom, Google Meet, Jitsi etc. as well as in-person meetings) and messages, which would be Slack, Mail, Rocket.Chat etc.
%load_ext rpy2.ipython
%%R -i dataframe_oura,rt_df -w 10 -h 10 --units in
library(lubridate)
library(ggplot2)
dataframe_oura$date <- as.Date(dataframe_oura$date)
dataframe_oura$week <- floor_date(dataframe_oura$date,unit='week')
df_oura_agg <- aggregate(value~week+type,data=dataframe_oura,FUN=mean)
rt_df$hour <-hour(rt_df$date)
rt_df <- subset(rt_df, rt_df$productivity >= 1)
rt_df$date <- as.Date(rt_df$date)
rt_df$week <- floor_date(rt_df$date,unit='week')
rt_df <- subset(rt_df, rt_df$week < as.Date('2020-06-12'))
rt_df <- subset(rt_df, rt_df$week > as.Date('2019-11-01'))
rt_df_agg <- aggregate(time_spent_seconds~week+activity,data=rt_df,FUN=sum)
rt_df_agg_all <- aggregate(time_spent_seconds~week,data=rt_df,FUN=sum)
meeting_activities = c('meet.google.com', 'google-chrome','meet.learning-planet.org', 'Zoom', 'Meeting (offline)')
meeting_subset_df <- subset(rt_df, rt_df$activity %in% meeting_activities)
rt_df_agg_meetings <- aggregate(time_spent_seconds~week,data=meeting_subset_df, FUN=sum)
message_activities = c('Slack', 'Mail','rocket.chat')
message_subset_df <- subset(rt_df, rt_df$activity %in% message_activities)
rt_df_agg_message <- aggregate(time_spent_seconds~week,data=message_subset_df, FUN=sum)
ggplot(rt_df_agg_all,aes(x=week,y=time_spent_seconds/60/60)) +
geom_vline(xintercept=as.Date('2020-03-01'), color='red') +
geom_vline(xintercept=as.Date('2020-05-11'), color='red') +
geom_line() +
geom_smooth(se=FALSE,color='black',linetype = "dashed",size=0.2) +
geom_line(data=rt_df_agg_meetings,color='#b2df8a') +
geom_smooth(data=rt_df_agg_meetings,se=FALSE,color='#b2df8a',linetype = "dashed",size=0.2) +
geom_line(data=rt_df_agg_message,color='#1f78b4') +
geom_smooth(data=rt_df_agg_message,se=FALSE,color='#1f78b4',linetype = "dashed",size=0.2) +
theme_minimal() +
scale_x_date("date") +
scale_y_continuous("per week",labels = function(x) paste0(x, "h")) +
labs(
title = "Lockdown effects as measured by RescueTime",
subtitle = "Red vertical bar highlight start/end of confinement in Paris",
caption = 'Black line: Total weekly work hours.\nGreen line: Virtual meetings (Google Meet, Zoom, ...)\nBlue line: Messaging (Slack, eMail, ...)\n'
) + theme(text = element_text(size=16)) +
theme(plot.caption= element_text(size=14))
The plot divides the time in before and after the lockdown, with everything in between the two red bars being the lockdown period. The dashed lines are smoothed, while the continuous lines give the individual weekly data points.
Let's also check out the sleep and physical activity for the lockdown, here taken from the Oura ring.
%%R -w 6 -h 15 --units in
install.packages('cowplot',repos = "http://cran.us.r-project.org")
library(cowplot)
step_plot <- ggplot(subset(df_oura_agg, df_oura_agg$week > as.Date('2019-11-01') & df_oura_agg$week < as.Date('2020-06-10') & as.character(df_oura_agg$type) %in% c('steps')), aes(x=week,y=value/1000)) +
geom_vline(xintercept=as.Date('2020-03-01'), color='red') +
geom_vline(xintercept=as.Date('2020-05-11'), color='red') +
geom_line() + theme_minimal() +
geom_smooth(se = FALSE,color='grey') +
scale_y_continuous(" ",labels = function(x) paste0(x, "k")) +
facet_grid(type ~ .) + labs(
) + theme(text = element_text(size=15)) +
theme(plot.caption= element_text(size=9))
sleep_plot <- ggplot(subset(df_oura_agg, df_oura_agg$week > as.Date('2019-11-01')& df_oura_agg$week < as.Date('2020-06-10') & as.character(df_oura_agg$type) %in% c('sleep_sum')), aes(x=week,y=value/60/60)) +
geom_vline(xintercept=as.Date('2020-03-01'), color='red') +
geom_vline(xintercept=as.Date('2020-05-11'), color='red') +
geom_line() + theme_minimal() +
geom_smooth(se = FALSE,color='grey') +
scale_y_continuous(" ",labels = function(x) paste0(x, "h")) +
facet_grid(type ~ .) + labs(
) + theme(text = element_text(size=15)) +
theme(plot.caption= element_text(size=9))
score_plot <- ggplot(subset(df_oura_agg, df_oura_agg$week > as.Date('2019-11-01')& df_oura_agg$week < as.Date('2020-06-10') & as.character(df_oura_agg$type) %in% c('sleep','activity','readiness')), aes(x=week,y=value)) +
geom_vline(xintercept=as.Date('2020-03-01'), color='red') +
geom_vline(xintercept=as.Date('2020-05-11'), color='red') +
geom_line() + theme_minimal() +
geom_smooth(se = FALSE,color='grey') +
scale_y_continuous('score') +
facet_grid(type ~ .) + theme(text = element_text(size=15)) +
theme(plot.caption= element_text(size=9))
title <- ggdraw() +
draw_label(
"Lockdown effects as measured by Oura Ring.",
fontface = 'bold',
x = 0,
hjust = 0
) +
draw_label(
"Red bars highlight start/end of confinement in Paris",
x = 0,
y = 0.3,
hjust = 0
)+
draw_label(
"black lines: weekly averages, grey lines: loess fit",
x = 0,
y = 0.1,
hjust = 0
)
plot_grid(title,step_plot,sleep_plot,score_plot, ncol=1, rel_heights = c(0.3, 1,1,1))
We plot both the raw data for the average nightly sleep time and the average number of steps for a week and plot those, as well as some processed "scores" that Oura assigns, which range between 0-100, with 100 being "the best".