Skip to content

Instantly share code, notes, and snippets.

@julian-klode
Last active March 26, 2023 08:32
Show Gist options
  • Save julian-klode/0bd2af3ce21ea0a6bb54a5fd0f2a2035 to your computer and use it in GitHub Desktop.
Save julian-klode/0bd2af3ce21ea0a6bb54a5fd0f2a2035 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
#!/usr/bin/env python
# coding: utf-8
# In[7]:
#!/usr/bin/python3
import json
import requests
import datetime
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import sys
from matplotlib.ticker import FormatStrFormatter
VIEW = int(sys.argv[1]) if len(sys.argv) > 1 else 60
SCORE = True
today = datetime.date.today()
start = (today - datetime.timedelta(days=VIEW + 80))
first = start + datetime.timedelta(days=1)
url = "https://api.ouraring.com/v2/usercollection/sleep"
params = {
"start_date": start.isoformat(),
"end_date": today.isoformat(),
}
headers = {
"Authorization": "Bearer " + open("/home/jak/Private/oura-token").read().strip()
}
try:
with open("/home/jak/.cache/oura.json") as cached:
data = json.load(cached)
if datetime.date.fromisoformat(data["data"][-1]["day"]) < today:
raise ValueError("Out of date")
if datetime.date.fromisoformat(data["data"][0]["day"]) > first:
raise ValueError("Out of date")
except (ValueError, FileNotFoundError) as e:
print("Fetching due to error:", e)
response = requests.request("GET", url, headers=headers, params=params)
data = response.json()
with open("/home/jak/.cache/oura.json", "w") as cached:
json.dump(data, cached)
dates = []
hrs = []
hrvs = []
for sleep in data["data"]:
if sleep["type"] != "long_sleep":
continue
dates.append(datetime.date.fromisoformat(sleep["day"]))
# hrs.append(sleep["average_heart_rate"])
hrs.append(np.round(np.mean([x for x in sleep["heart_rate"]["items"] if x])))
if SCORE:
if sleep["average_hrv"]:
#hrvs.append(np.round(np.log(sleep["average_hrv"]), 1))
#hrvs.append(np.round(1.7 * np.log(sleep["average_hrv"]) + 1, 1))
#hrvs.append(np.round(1.75202 * np.log(sleep["average_hrv"] * 1.57691), 1))
hrvs.append(np.round(1.73677755 * np.log(sleep["average_hrv"] * 1.63747424), 1))
#1.73677755, 1.63747424
else:
hrvs.append(None)
else:
hrvs.append(sleep["average_hrv"])
def normal_range(values):
rolling = values.dropna().rolling(window=60)
mean = rolling.mean()
std = rolling.std()
return (mean - std), mean, (mean + std)
def plot_hrvs(hrvs, label):
# some sample data
ts = pd.Series(hrvs, index=dates)
digits = 0
if label == "HRV" and SCORE:
digits = 1
if "coeff" in label:
digits = 2
rolling = ts.dropna().rolling(window=7)
mean = rolling.mean()
min_range, mean_range, max_range = (r.round(digits) for r in normal_range(ts))
coeff = rolling.std() / rolling.mean() * 100
min_coeff_range, mean_coeff_range, max_coeff_range = normal_range(coeff)
table = (
ts.to_frame(name="HRV")
.join(min_range.round(digits).to_frame(name="MIN"))
.join(max_range.round(digits).to_frame(name="MAX"))
.join(mean.to_frame(name="MEAN"))
)
table_coeff = (
coeff.round(1).to_frame(name="Coeff")
.join(min_coeff_range.round(1).to_frame(name="MIN"))
.join(max_coeff_range.round(1).to_frame(name="MAX"))
)
if "coeff" in label:
coeff.plot(label=label)
max_coeff_range.plot(label="MAX", color="green")
min_coeff_range.plot(label="MIN", color="red")
else:
ts.plot(style="o--", color="#" + ("c" * 6), zorder=1, label=label)
rolling.mean().plot(style="-", label=f"{label} 7 day")
#mean_range.plot(label=f"{label} 60 day", color="#333333", style="--", zorder=2)
max_range.plot(label="MAX", color="green")
min_range.plot(label="MIN", color="red")
if "coeff" in label:
return
for index, entry in table.iterrows():
if index == today or True:
print(
label + ": ",
index,
entry["HRV"],
entry["MIN"],
entry["MAX"],
"" if entry["MIN"] < entry["HRV"] < entry["MAX"] else "(!)",
sep="\t",
)
for index, entry in table_coeff.iterrows():
if index == today:
print(
label + " coeff:",
index,
entry["Coeff"],
entry["MIN"],
entry["MAX"],
"" if entry["MIN"] < entry["Coeff"] < entry["MAX"] else "(!)",
sep="\t",
)
# In[9]:
#fig=plt.figure(figsize=(40, 3*25))
fig=plt.figure(figsize=(8.27*2,11.69*2)) # for landscape
fig.add_subplot(311, title="HRV")
plot_hrvs(hrvs, "HRV")
plt.xlim([today - datetime.timedelta(days=VIEW), today + datetime.timedelta(days=0)])
plt.gca().legend()
from matplotlib.ticker import AutoLocator, ScalarFormatter
if not SCORE:
plt.gca().set_yscale('log')
plt.gca().yaxis.set_major_locator(AutoLocator())
plt.gca().yaxis.set_major_formatter(ScalarFormatter())
plt.gca().minorticks_off()
plt.grid()
# plt.show()
# In[4]:
fig.add_subplot(312, title="HRV coeff")
plot_hrvs(hrvs, "HRV coeff")
plt.xlim([today - datetime.timedelta(days=VIEW), today + datetime.timedelta(days=0)])
plt.gca().yaxis.set_major_formatter(FormatStrFormatter('%d%%'))
plt.gca().legend()
plt.grid(axis="x")
#plt.figure(figsize=(38, 24))
fig.add_subplot(313, title="HR")
plot_hrvs(hrs, "HR")
plt.gca().legend()
plt.xlim([today - datetime.timedelta(days=VIEW), today + datetime.timedelta(days=0)])
plt.grid()
if VIEW >= 210:
from matplotlib.dates import MO, MonthLocator
for ax in plt.gcf().get_axes():
ax.xaxis.set_major_locator(MonthLocator())
else:
from matplotlib.dates import MO, WeekdayLocator
for ax in plt.gcf().get_axes():
ax.xaxis.set_major_locator(WeekdayLocator(byweekday=MO, interval=max(VIEW//70, 1)) )
fig.autofmt_xdate()
plt.text(0.05,0.95, f"{VIEW} day HRV analysis", transform=fig.transFigure, size=24)
plt.savefig("Figure.png", orientation = 'portrait', format = 'png', dpi=300)
plt.savefig("Figure.pdf", orientation = 'portrait', format = 'pdf', dpi=300)
plt.savefig("Figure.svg", orientation = 'portrait', format = 'svg', dpi=300)
#plt.show()
# In[5]:
data["data"][-1]["average_heart_rate"]
# In[6]:
np.mean([x for x in data["data"][-1]["heart_rate"]["items"] if x])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment