dimwit 0.2.26__tar.gz → 0.2.28__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dimwit-0.2.26 → dimwit-0.2.28}/PKG-INFO +1 -1
- {dimwit-0.2.26 → dimwit-0.2.28}/dimwit/main.py +128 -91
- {dimwit-0.2.26 → dimwit-0.2.28}/dimwit/pomodoro.py +0 -12
- {dimwit-0.2.26 → dimwit-0.2.28}/dimwit/sleep.py +4 -3
- {dimwit-0.2.26 → dimwit-0.2.28}/pyproject.toml +1 -1
- {dimwit-0.2.26 → dimwit-0.2.28}/README.md +0 -0
- {dimwit-0.2.26 → dimwit-0.2.28}/dimwit/__init__.py +0 -0
- {dimwit-0.2.26 → dimwit-0.2.28}/dimwit/air_pollution.py +0 -0
- {dimwit-0.2.26 → dimwit-0.2.28}/dimwit/airflow.py +0 -0
- {dimwit-0.2.26 → dimwit-0.2.28}/dimwit/legacy.py +0 -0
- {dimwit-0.2.26 → dimwit-0.2.28}/dimwit/weight.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dimwit
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.28
|
|
4
4
|
Summary: A package containing various functions and classes for fetching, transforming, and visualising data for personal metrics.
|
|
5
5
|
Home-page: https://github.com/danielsoutar/dimwit
|
|
6
6
|
Author: Daniel Soutar
|
|
@@ -225,112 +225,149 @@ def get_moving_average_trend(data, k, padding="copy-on"):
|
|
|
225
225
|
return rolling_average
|
|
226
226
|
|
|
227
227
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
228
|
+
# TODO: Figure out how to append to KDE plots so that the lines all start and
|
|
229
|
+
# end at the same place.
|
|
230
|
+
# TODO: Figure out how to 'project'/'predict' for recent data. Look into
|
|
231
|
+
# conditional density estimation.
|
|
232
|
+
def kde_over_all_categories(ts, events, bw_vals, exclude):
|
|
233
|
+
# Share the same axes across the various plots.
|
|
234
|
+
_, ax = plt.subplots(1)
|
|
235
|
+
for category, bw in zip(set(events), bw_vals):
|
|
236
|
+
if category in exclude:
|
|
237
|
+
continue
|
|
238
|
+
# Fetch all timestamps where the event matches the category.
|
|
239
|
+
cat_ts = list(
|
|
240
|
+
map(
|
|
241
|
+
lambda tup: tup[1],
|
|
242
|
+
filter(lambda tup: tup[0] == category, zip(events, ts)),
|
|
243
|
+
)
|
|
244
|
+
)
|
|
245
|
+
cat_timeframe = cat_ts[-1] - cat_ts[0]
|
|
246
|
+
total_timeframe = ts[-1] - ts[0]
|
|
247
|
+
# Exclude short-lived KDEs - these tend to skew the graphs.
|
|
248
|
+
if cat_timeframe < (0.3 * total_timeframe):
|
|
249
|
+
continue
|
|
250
|
+
df = pd.DataFrame(cat_ts, columns=["dates"])
|
|
251
|
+
df["ordinal"] = [x.toordinal() for x in df.dates]
|
|
252
|
+
ax = df["ordinal"].plot(
|
|
253
|
+
kind="kde",
|
|
254
|
+
bw_method=bw,
|
|
255
|
+
ax=ax,
|
|
256
|
+
label=category,
|
|
242
257
|
)
|
|
243
|
-
data = {
|
|
244
|
-
"start_date": starts,
|
|
245
|
-
"end_date": ends,
|
|
246
|
-
"durations": durations,
|
|
247
|
-
"target_sleep_start": [target_sleep_start] * N,
|
|
248
|
-
"target_sleep_end": [target_sleep_end] * N,
|
|
249
|
-
}
|
|
250
|
-
df = pd.DataFrame(data)
|
|
251
|
-
return df
|
|
252
258
|
|
|
259
|
+
ax.set_xlabel("Time")
|
|
260
|
+
ax.set_ylabel("Density")
|
|
261
|
+
ax.set_xlim(ts[0].toordinal(), ts[-1].toordinal())
|
|
262
|
+
|
|
263
|
+
# Take every other tick to reduce clutter.
|
|
264
|
+
x_ticks = ax.get_xticks()[::2]
|
|
265
|
+
ax.set_xticks(x_ticks)
|
|
266
|
+
xlabels = list(
|
|
267
|
+
map(
|
|
268
|
+
lambda x: dat.datetime.fromordinal(int(x)).strftime("%Y-%m-%d"),
|
|
269
|
+
x_ticks,
|
|
270
|
+
)
|
|
271
|
+
)
|
|
272
|
+
ax.set_xticklabels(xlabels)
|
|
273
|
+
ax.legend()
|
|
274
|
+
return ax
|
|
253
275
|
|
|
254
|
-
def compute_rounded_matrix(ts, events, rounding_unit, rounding_size):
|
|
255
|
-
"""
|
|
256
|
-
Compute a matrix where each (sorted, ascending) row corresponds to a
|
|
257
|
-
timestamp, and the columns the types of events, rounding timestamps
|
|
258
|
-
according to `rounding_unit` and `rounding_size`.
|
|
259
276
|
|
|
260
|
-
|
|
261
|
-
|
|
277
|
+
def kde_over_combined_categories(ts, bw):
|
|
278
|
+
df = pd.DataFrame(set(ts), columns=["dates"])
|
|
279
|
+
df["ordinal"] = [x.toordinal() for x in df.dates]
|
|
280
|
+
ax = df["ordinal"].plot(kind="kde", bw_method=bw, label="Combined")
|
|
262
281
|
|
|
263
|
-
|
|
264
|
-
""
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
#
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
282
|
+
ax.set_xlabel("Time")
|
|
283
|
+
ax.set_ylabel("Density")
|
|
284
|
+
ax.set_xlim(ts[0].toordinal(), ts[-1].toordinal())
|
|
285
|
+
|
|
286
|
+
# Take every other tick to reduce clutter.
|
|
287
|
+
x_ticks = ax.get_xticks()[::2]
|
|
288
|
+
ax.set_xticks(x_ticks)
|
|
289
|
+
xlabels = list(
|
|
290
|
+
map(
|
|
291
|
+
lambda x: dat.datetime.fromordinal(int(x)).strftime("%Y-%m-%d"),
|
|
292
|
+
x_ticks,
|
|
293
|
+
)
|
|
274
294
|
)
|
|
275
|
-
|
|
295
|
+
ax.set_xticklabels(xlabels)
|
|
296
|
+
ax.legend()
|
|
297
|
+
return ax
|
|
276
298
|
|
|
277
299
|
|
|
278
|
-
def
|
|
279
|
-
|
|
300
|
+
def day_kde_over_all_categories(ts, events, bw_vals, exclude):
|
|
301
|
+
# Share the same axes across the various plots.
|
|
302
|
+
_, ax = plt.subplots(1, figsize=(8, 6))
|
|
303
|
+
for category, bw in zip(set(events), bw_vals):
|
|
304
|
+
if category in exclude:
|
|
305
|
+
continue
|
|
306
|
+
# Fetch all timestamps where the event matches the category.
|
|
307
|
+
cat_ts = list(
|
|
308
|
+
map(
|
|
309
|
+
lambda tup: tup[1],
|
|
310
|
+
filter(lambda tup: tup[0] == category, zip(events, ts)),
|
|
311
|
+
)
|
|
312
|
+
)
|
|
313
|
+
cat_timeframe = cat_ts[-1] - cat_ts[0]
|
|
314
|
+
total_timeframe = ts[-1] - ts[0]
|
|
315
|
+
# Exclude short-lived KDEs - these tend to skew the graphs.
|
|
316
|
+
if cat_timeframe < (0.3 * total_timeframe):
|
|
317
|
+
continue
|
|
280
318
|
|
|
319
|
+
df = pd.DataFrame(cat_ts, columns=["dates"])
|
|
320
|
+
df["time"] = pd.to_datetime(df["dates"], utc=True).dt.time
|
|
321
|
+
df["time_delta"] = pd.to_timedelta([str(t) for t in df["time"]])
|
|
322
|
+
df["time_delta_float"] = df["time_delta"] // pd.Timedelta(minutes=1)
|
|
323
|
+
# Default size of the graph makes the labels too large, might as well
|
|
324
|
+
# have a bigger plot.
|
|
325
|
+
ax = df["time_delta_float"].plot(
|
|
326
|
+
kind="kde",
|
|
327
|
+
bw_method=bw,
|
|
328
|
+
label=category,
|
|
329
|
+
ax=ax,
|
|
330
|
+
)
|
|
281
331
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
# same- (or zero-) padding.
|
|
285
|
-
pad_t_days = pd.Timedelta(pad_t, unit=unit)
|
|
286
|
-
pad_before_start = df.index[0] - pad_t_days
|
|
287
|
-
pad_after_start = df.index[-1] + pd.Timedelta(1, unit=unit)
|
|
332
|
+
ax.set_xlabel("Time (Clock)")
|
|
333
|
+
ax.set_ylabel("Density")
|
|
288
334
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
335
|
+
ax.set_xlim(0, pd.Timedelta(days=1) // pd.Timedelta(minutes=1))
|
|
336
|
+
hours = [i for i in range(0, 25, 2)]
|
|
337
|
+
ax.set_xticks(
|
|
338
|
+
[i * 60 for i in hours], [str(i).rjust(2, "0") + ":00" for i in hours]
|
|
339
|
+
)
|
|
340
|
+
ax.set_yticks([])
|
|
341
|
+
ax.legend()
|
|
342
|
+
return ax
|
|
293
343
|
|
|
294
|
-
num_rows, num_cols = pad_t, len(df.columns)
|
|
295
|
-
if pad_before:
|
|
296
|
-
df = pd.concat([zero_padded_df(num_rows, num_cols, index_before), df])
|
|
297
|
-
if pad_after:
|
|
298
|
-
df = pd.concat([zero_padded_df(num_rows, num_cols, index_after), df])
|
|
299
344
|
|
|
300
|
-
|
|
345
|
+
def day_kde_over_combined_categories(ts, bw):
|
|
346
|
+
df = pd.DataFrame(set(ts), columns=["dates"])
|
|
347
|
+
df["time"] = pd.to_datetime(df["dates"], utc=True).dt.time
|
|
348
|
+
df["time_delta"] = pd.to_timedelta([str(t) for t in df["time"]])
|
|
349
|
+
df["time_delta_float"] = df["time_delta"] // pd.Timedelta(minutes=1)
|
|
350
|
+
# Default size of the graph makes the labels too large, might as well have
|
|
351
|
+
# a bigger plot.
|
|
352
|
+
_, ax = plt.subplots(1, figsize=(8, 6))
|
|
353
|
+
ax = df["time_delta_float"].plot(
|
|
354
|
+
kind="kde",
|
|
355
|
+
bw_method=bw,
|
|
356
|
+
label="Combined",
|
|
357
|
+
ax=ax,
|
|
358
|
+
)
|
|
301
359
|
|
|
360
|
+
ax.set_xlabel("Time (Clock)")
|
|
361
|
+
ax.set_ylabel("Density")
|
|
302
362
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
)
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
df_needs_padding = window_size > 1
|
|
313
|
-
if df_needs_padding:
|
|
314
|
-
pad_t = (window_size - 1) // 2
|
|
315
|
-
df = pad_out_table(df, pad_t, rounding_unit)
|
|
316
|
-
|
|
317
|
-
window_string = str(rounding_size) + rounding_unit
|
|
318
|
-
dense_series = []
|
|
319
|
-
for column in categories:
|
|
320
|
-
# TODO: Fix annoying ImportError involving 'freq_to_period_freqstr'.
|
|
321
|
-
sparse_column: pd.Series = df[column]
|
|
322
|
-
dense_column = sparse_column.asfreq(freq=window_string)
|
|
323
|
-
dense_column_no_nans = dense_column.fillna(0)
|
|
324
|
-
dense_series.append(dense_column_no_nans)
|
|
325
|
-
|
|
326
|
-
dense_df = pd.DataFrame(dense_series).T
|
|
327
|
-
|
|
328
|
-
if not df_needs_padding:
|
|
329
|
-
return dense_df
|
|
330
|
-
|
|
331
|
-
averaged_df = dense_df.rolling(window_size, center=True).mean()
|
|
332
|
-
# Skip the padding rows since they don't make much sense in the charts.
|
|
333
|
-
return averaged_df.iloc[pad_t:-pad_t]
|
|
363
|
+
ax.set_xlim(0, pd.Timedelta(days=1) // pd.Timedelta(minutes=1))
|
|
364
|
+
hours = [i for i in range(0, 25, 2)]
|
|
365
|
+
ax.set_xticks(
|
|
366
|
+
[i * 60 for i in hours], [str(i).rjust(2, "0") + ":00" for i in hours]
|
|
367
|
+
)
|
|
368
|
+
ax.set_yticks([])
|
|
369
|
+
ax.legend()
|
|
370
|
+
return ax
|
|
334
371
|
|
|
335
372
|
|
|
336
373
|
def get_aggregated_event_counts(ts, events, period):
|
|
@@ -118,18 +118,6 @@ def generate_cumulative_df_for_kth_latest_week(df, k):
|
|
|
118
118
|
)
|
|
119
119
|
start, end = pomodoro_date_range[0], pomodoro_date_range[-1]
|
|
120
120
|
|
|
121
|
-
# If end later than latest available entry in df (i.e. start of the week),
|
|
122
|
-
# then pad out df to generate zero entries.
|
|
123
|
-
# if end > df.index[-1]:
|
|
124
|
-
# num_entries = 0
|
|
125
|
-
# latest_val = df.index[-1]
|
|
126
|
-
# while end > latest_val:
|
|
127
|
-
# latest_val += df.index.freq
|
|
128
|
-
# num_entries += 1
|
|
129
|
-
# df = dimwit.pad_out_table(
|
|
130
|
-
# df, num_entries, df.index.freq, pad_before=False
|
|
131
|
-
# )
|
|
132
|
-
|
|
133
121
|
ref_policy = ReferencePolicy(45.0, 15.0, "(45+15)")
|
|
134
122
|
cum_df = generate_cumulative_df(df, ref_policy, start, end)
|
|
135
123
|
|
|
@@ -101,9 +101,10 @@ def fill_sleep_entries(
|
|
|
101
101
|
def create_sleep_df(ts, durations):
|
|
102
102
|
starts, ends = zip(*ts)
|
|
103
103
|
|
|
104
|
-
#
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
# Replace the timezone with UTC, ensuring that UTC offsets are removed.
|
|
105
|
+
# TODO: For some reason this breaks older data until around mid-Nov 2023.
|
|
106
|
+
starts = [start.replace(tzinfo=dat.timezone.utc) for start in starts]
|
|
107
|
+
ends = [end.replace(tzinfo=dat.timezone.utc) for end in ends]
|
|
107
108
|
|
|
108
109
|
start_dates = get_start_date(starts, ends, first_after_midnight=False)
|
|
109
110
|
end_dates = start_dates.shift(1, freq="D")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "dimwit"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.28"
|
|
4
4
|
description = "A package containing various functions and classes for fetching, transforming, and visualising data for personal metrics."
|
|
5
5
|
authors = ["Daniel Soutar <danielsoutar144@gmail.com>"]
|
|
6
6
|
readme = "README.md"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|