Go to https://www.fitbit.com/export/user/data and export sleep data CSV's month by month. Place it in a folder called data/
. Run the python script.
Last active
May 17, 2021 22:39
-
-
Save rameshvarun/ee1803ea367833738ffcabe630844683 to your computer and use it in GitHub Desktop.
Plot FitBit sleep schedule using Matplotlib.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import glob | |
import csv | |
import itertools | |
from datetime import datetime, timedelta, time | |
from collections import namedtuple | |
import numpy as np | |
import matplotlib.pyplot as plt | |
import matplotlib.patches as mpatches | |
def consume(iterator, n=None): | |
"Advance the iterator n-steps ahead. If n is None, consume entirely." | |
if n is None: | |
collections.deque(iterator, maxlen=0) | |
else: | |
next(itertools.islice(iterator, n, n), None) | |
SleepEntry = namedtuple("SleepEntry", ["start", "end"]) | |
DATE_FORMAT = "%Y-%m-%d %I:%M%p" | |
BLOCKS_PER_DAY = 24 * 2 | |
SPLIT_HOUR = 12 + 4 | |
if __name__ == "__main__": | |
sleep_entries = [] | |
for file in glob.iglob("./data/*.csv"): | |
with open(file, "r") as f: | |
reader = csv.reader(f) | |
consume(reader, 2) | |
for row in reader: | |
if len(row) > 2: | |
sleep_entries.append( | |
SleepEntry( | |
start=datetime.strptime(row[0], DATE_FORMAT), | |
end=datetime.strptime(row[1], DATE_FORMAT), | |
) | |
) | |
earliest_time = min([e.start for e in sleep_entries]) | |
latest_time = max([e.end for e in sleep_entries]) | |
print("Earliest Time: %s, Latest Time: %s" % (earliest_time, latest_time)) | |
print(len(sleep_entries), "entries.") | |
start_date = datetime.combine(earliest_time.date(), time(SPLIT_HOUR, 0)) | |
end_date = datetime.combine(latest_time.date(), time(SPLIT_HOUR, 0)) | |
days = (end_date - start_date).days | |
print(days, "days.") | |
sleep_matrix = np.zeros((BLOCKS_PER_DAY, days)) | |
for i, j in itertools.product(range(BLOCKS_PER_DAY), range(days)): | |
dt = start_date + timedelta(days=j, hours=i // 2, minutes=30 * (i % 2)) | |
if any([(dt >= entry.start and dt < entry.end) for entry in sleep_entries]): | |
sleep_matrix[i, j] = 1.0 | |
fig, ax = plt.subplots() | |
im = ax.imshow(sleep_matrix) | |
ax.set_yticks(range(BLOCKS_PER_DAY)) | |
ax.set_yticklabels( | |
[ | |
time((SPLIT_HOUR + i // 2) % 24, 30 * (i % 2)).strftime("%-I:%M%p") | |
for i in range(BLOCKS_PER_DAY) | |
] | |
) | |
ax.set_xticks(range(days)) | |
ax.set_xticklabels( | |
[(start_date + timedelta(days=j)).strftime("%b-%-d") for j in range(days)], | |
rotation="vertical", | |
) | |
patches = [ | |
mpatches.Patch(color=im.cmap(im.norm(0)), label="Awake"), | |
mpatches.Patch(color=im.cmap(im.norm(1)), label="Sleep"), | |
] | |
ax.legend(handles=patches) | |
for label in ax.yaxis.get_ticklabels()[1::2]: | |
label.set_visible(False) | |
for label in ax.xaxis.get_ticklabels(): | |
label.set_visible(False) | |
for label in ax.xaxis.get_ticklabels()[::5]: | |
label.set_visible(True) | |
plt.show() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment