HPI/my/jawbone/plots.py

146 lines
4.1 KiB
Python
Executable file

#!/usr/bin/env python3
# TODO this should be in dashboard
from pathlib import Path
# from kython.plotting import *
from csv import DictReader
from itertools import islice
from typing import Dict, Any, NamedTuple, Dict
# sleep = []
# with open('2017.csv', 'r') as fo:
# reader = DictReader(fo)
# for line in islice(reader, 0, 10):
# sleep
# print(line)
import numpy as np # type: ignore
import matplotlib.pyplot as plt # type: ignore
from numpy import genfromtxt # type: ignore
import matplotlib.pylab as pylab # type: ignore
pylab.rcParams['figure.figsize'] = (32.0, 24.0)
pylab.rcParams['font.size'] = 10
jawboneDataFeatures = Path(__file__).parent / 'features.csv' # Data File Path
featureDesc: Dict[str, str] = {}
for x in genfromtxt(jawboneDataFeatures, dtype='unicode', delimiter=','):
featureDesc[x[0]] = x[1]
def _safe_float(s: str):
if len(s) == 0:
return None
return float(s)
def _safe_int(s: str):
if len(s) == 0:
return None
return int(float(s)) # TODO meh
def _safe_mins(s: float):
if s is None:
return None
return s / 60
class SleepData(NamedTuple):
date: str
asleep_time: float
awake_time: float
total: float
awake: float # 'awake for' from app, time awake duing sleep (seconds)
awakenings: int
light: float # 'light sleep' from app (seconds)
deep: float # 'deep sleep' from app (sec)
quality: float # ???
@classmethod
def from_jawbone_dict(cls, d: Dict[str, Any]):
return cls(
date=d['DATE'],
asleep_time=_safe_mins(_safe_float(d['s_asleep_time'])),
awake_time=_safe_mins(_safe_float(d['s_awake_time'])),
total=_safe_mins(_safe_float(d['s_duration'])),
light=_safe_mins(_safe_float(d['s_light'])),
deep =_safe_mins(_safe_float(d['s_deep'])),
awake=_safe_mins(_safe_float(d['s_awake'])),
awakenings=_safe_int(d['s_awakenings']),
quality=_safe_float(d['s_quality']),
)
def is_bad(self):
return self.deep is None and self.light is None
# @property
# def total(self) -> float:
# return self.light + self.deep
def iter_useful(data_file: str):
from csv import DictReader
with open(data_file) as fo:
reader = DictReader(fo)
for d in reader:
dt = SleepData.from_jawbone_dict(d)
if not dt.is_bad():
yield dt
# TODO <<< hmm. these files do contain deep and light sleep??
# also steps stats??
from my.config import jawbone as config
p = config.export_dir / 'old_csv'
# TODO with_my?
files = [
p / "2015.csv",
p / "2016.csv",
p / "2017.csv",
]
from kython import concat, parse_date
useful = concat(*(list(iter_useful(str(f))) for f in files))
# for u in useful:
# print(f"{u.total} {u.asleep_time} {u.awake_time}")
# # pprint(u.total)
# pprint(u)
# pprint("---")
dates = [parse_date(u.date, yearfirst=True, dayfirst=False) for u in useful]
# TODO filter outliers?
# TODO don't need this anymore? it's gonna be in dashboards package
from kython.plotting import plot_timestamped
for attr, lims, mavg, fig in [ # type: ignore
('light', (0, 400), 5, None),
('deep', (0, 600), 5, None),
('total', (200, 600), 5, None),
('awake_time', (0, 1200), None, 1),
('asleep_time', (-100, 1000), None, 1),
# ('awakenings', (0, 5)),
]:
dates_wkd = [d for d in dates if d.weekday() < 5]
dates_wke = [d for d in dates if d.weekday() >= 5]
for dts, dn in [
(dates, 'total'),
(dates_wkd, 'weekday'),
(dates_wke, 'weekend')
]:
mavgs = []
if mavg is not None:
mavgs.append((mavg, 'green'))
fig = plot_timestamped(
dts, # type: ignore
[getattr(u, attr) for u in useful],
marker='.',
ratio=(16, 4),
mavgs=mavgs,
ylimits=lims,
ytick_size=60,
# figure=1,
)
plt.savefig(f'{attr}_{dn}.png')
# TODO use proper names?
# plt.savefig('res.png')
# fig.show()