dimwit 0.2.7__tar.gz → 0.2.8__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dimwit
3
- Version: 0.2.7
3
+ Version: 0.2.8
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
@@ -13,6 +13,8 @@ Classifier: Programming Language :: Python :: 3.12
13
13
  Requires-Dist: matplotlib (>=3.8.3,<4.0.0)
14
14
  Requires-Dist: pandas (>=2.1.4,<3.0.0)
15
15
  Requires-Dist: requests (>=2.31.0,<3.0.0)
16
+ Requires-Dist: scipy (>=1.12.0,<2.0.0)
17
+ Requires-Dist: seaborn (>=0.13.2,<0.14.0)
16
18
  Project-URL: Repository, https://github.com/danielsoutar/dimwit
17
19
  Description-Content-Type: text/markdown
18
20
 
@@ -1,3 +1,4 @@
1
1
  from dimwit.main import *
2
- import dimwit.legacy as legacy
2
+ import dimwit.airflow
3
+ import dimwit.legacy
3
4
  import dimwit.air_pollution as ap
@@ -0,0 +1,277 @@
1
+ from dimwit.main import get_moving_average_trend, populate_with_events
2
+
3
+ import datetime as dat
4
+ import matplotlib.pyplot as plt
5
+ import numpy as np
6
+ from scipy.stats import norm
7
+ import seaborn as sb
8
+
9
+
10
+ def beginning_of_data():
11
+ uk_with_dst = dat.timezone(dat.timedelta(seconds=3600))
12
+ return dat.datetime(2023, 8, 21, 0, 0, tzinfo=uk_with_dst)
13
+
14
+
15
+ def get_events():
16
+ uk_with_dst = dat.timezone(dat.timedelta(seconds=3600))
17
+ dates = [
18
+ dat.datetime(2023, 9, 12, 13, 40, tzinfo=uk_with_dst),
19
+ dat.datetime(2024, 2, 4, 0, 0, tzinfo=dat.timezone.utc),
20
+ dat.datetime(2024, 2, 8, 0, 0, tzinfo=dat.timezone.utc),
21
+ ]
22
+ descs = [
23
+ "Started using inhaler",
24
+ "Only using inhaler as needed",
25
+ "Using inhaler unless healthy",
26
+ ]
27
+ return [(date, "gray", "--", desc) for date, desc in zip(dates, descs)]
28
+
29
+
30
+ def data_over_period(df, from_date, moving_average_window_size, events, title):
31
+ latest_df = df.loc[from_date:]
32
+ fig, ax = plt.subplots(1, 1, figsize=(12, 6))
33
+
34
+ x = list(latest_df.index)
35
+
36
+ ax.scatter(x, latest_df["Recording 1"], label="Point 1", alpha=0.3)
37
+ ax.scatter(x, latest_df["Recording 2"], label="Point 2", alpha=0.3)
38
+ ax.scatter(x, latest_df["Recording 3"], label="Point 3", alpha=0.3)
39
+
40
+ # Plot a line for the maximum of the points
41
+ ax.plot(x, latest_df["Max Point"], label="Max", color="red")
42
+
43
+ window_size = moving_average_window_size
44
+
45
+ # Calculate the rolling average over the max values array
46
+ # TODO: Figure out whether to use actual data rather than repeating first
47
+ # point (k - 1) / 2 times, where possible.
48
+ rolling_average = get_moving_average_trend(
49
+ np.array(latest_df["Max Point"]), window_size
50
+ )
51
+ ax.plot(
52
+ x,
53
+ rolling_average,
54
+ label=f"Rolling Avg ({window_size})",
55
+ color="green",
56
+ )
57
+
58
+ ax = populate_with_events(ax, events, x[0])
59
+
60
+ plt.legend()
61
+ plt.grid()
62
+ # Add a title to the entire figure
63
+ fig.suptitle(title, fontsize=16)
64
+
65
+ return fig, ax
66
+
67
+
68
+ def differences_over_period(
69
+ df,
70
+ from_date,
71
+ moving_average_window_size,
72
+ events,
73
+ title,
74
+ ):
75
+ latest_df = df.loc[from_date:]
76
+ fig, ax = plt.subplots(1, 1, figsize=(12, 6))
77
+
78
+ max_vals = df[["Recording 1", "Recording 2", "Recording 3"]].max(axis=1)
79
+ min_vals = df[["Recording 1", "Recording 2", "Recording 3"]].min(axis=1)
80
+ df["Delta"] = max_vals - min_vals
81
+
82
+ x = list(latest_df.index)
83
+
84
+ ax.scatter(x, df["Delta"], label="Max-Min Difference", alpha=0.3)
85
+
86
+ window_size = moving_average_window_size
87
+
88
+ # Calculate the rolling average over the max values array
89
+ # TODO: Figure out whether to use actual data rather than repeating first
90
+ # point (k - 1) / 2 times, where possible.
91
+ rolling_average = get_moving_average_trend(
92
+ np.array(df["Delta"]),
93
+ window_size,
94
+ )
95
+ ax.plot(
96
+ x,
97
+ rolling_average,
98
+ label=f"Rolling Avg ({window_size})",
99
+ color="green",
100
+ )
101
+
102
+ ax = populate_with_events(ax, events, x[0])
103
+
104
+ plt.legend()
105
+ plt.grid()
106
+ # Add a title to the entire figure
107
+ fig.suptitle(title, fontsize=16)
108
+
109
+ return fig, ax
110
+
111
+
112
+ def get_pretty_image(
113
+ image_arr,
114
+ title,
115
+ colour_bar_title,
116
+ palette="viridis",
117
+ foreground_colour="white",
118
+ background_colour="black",
119
+ ):
120
+ # 'flare_r' is a neat fire-y palette, as an alternative.
121
+ cmap = sb.color_palette(palette, as_cmap=True)
122
+
123
+ fig, ax = plt.subplots(figsize=(24, 4))
124
+ image = ax.imshow(
125
+ image_arr,
126
+ interpolation="nearest",
127
+ aspect="auto",
128
+ cmap=cmap,
129
+ )
130
+ colour_bar = plt.colorbar(image)
131
+
132
+ # set figure facecolor
133
+ ax.patch.set_facecolor(background_colour)
134
+
135
+ # set tick and ticklabel color
136
+ image.axes.get_xaxis().set_visible(False)
137
+ image.axes.get_yaxis().set_visible(False)
138
+
139
+ # set imshow outline
140
+ for spine in image.axes.spines.values():
141
+ spine.set_edgecolor(background_colour)
142
+
143
+ # set colorbar label plus label color
144
+ colour_bar.set_label(colour_bar_title, color=foreground_colour)
145
+
146
+ # set colorbar tick color
147
+ colour_bar.ax.yaxis.set_tick_params(color=foreground_colour)
148
+
149
+ # set colorbar edgecolor
150
+ colour_bar.outline.set_edgecolor(foreground_colour)
151
+
152
+ # set colorbar ticklabels
153
+ plt.setp(
154
+ plt.getp(colour_bar.ax.axes, "yticklabels"),
155
+ color=foreground_colour,
156
+ )
157
+
158
+ _ = ax.set_title(title, color=foreground_colour)
159
+ fig.patch.set_facecolor(background_colour)
160
+ return fig, ax
161
+
162
+
163
+ def generate_month_year_tuples(start=None, end=None):
164
+ if start is None:
165
+ start = (2023, 8)
166
+
167
+ if end is None:
168
+ end_date = dat.datetime.now()
169
+ end = (end_date.year, end_date.month)
170
+ else:
171
+ assert end[0] >= start[0]
172
+ if end[0] == start[0]:
173
+ assert end[1] > start[1]
174
+
175
+ month_names = {
176
+ 1: "Jan",
177
+ 2: "Feb",
178
+ 3: "Mar",
179
+ 4: "Apr",
180
+ 5: "May",
181
+ 6: "Jun",
182
+ 7: "Jul",
183
+ 8: "Aug",
184
+ 9: "Sep",
185
+ 10: "Oct",
186
+ 11: "Nov",
187
+ 12: "Dec",
188
+ }
189
+
190
+ tuples = []
191
+ current = start
192
+
193
+ while current != end:
194
+ current_year, current_month = current
195
+ tuples.append((current_year, current_month))
196
+ increment_year = current_month == 12
197
+ if increment_year:
198
+ next_year, next_month = current_year + 1, 1
199
+ else:
200
+ next_year, next_month = current_year, current_month + 1
201
+
202
+ current = (next_year, next_month)
203
+
204
+ tuples.append(end)
205
+
206
+ res = list(map(lambda t: (*t, f"{month_names[t[1]]} {str(t[0])}"), tuples))
207
+
208
+ return res
209
+
210
+
211
+ def get_hist_data_for_month(df, year, month, use_maxes=False):
212
+ utc = dat.timezone.utc
213
+
214
+ dt = dat.datetime(year, month, 1, 0, 0, tzinfo=utc)
215
+ if month == 12:
216
+ end_dt = dat.datetime(year + 1, 1, 1, 0, 0, tzinfo=utc)
217
+ else:
218
+ end_dt = dat.datetime(year, month + 1, 1, 0, 0, tzinfo=utc)
219
+
220
+ month_data = df[["Recording 1", "Recording 2", "Recording 3"]][dt:end_dt]
221
+ month_data = np.array(month_data).reshape((-1, 3))
222
+
223
+ if use_maxes:
224
+ month_maxes = np.max(month_data, axis=1).reshape((-1, 1))
225
+ return month_maxes
226
+
227
+ return month_data
228
+
229
+
230
+ def generate_overlaid_monthly_pdfs(df, xmin=500, xmax=800, num_samples=100):
231
+ fig, ax = plt.subplots(1, 1)
232
+
233
+ for year, month, name in generate_month_year_tuples():
234
+ all_samples_for_month = get_hist_data_for_month(df, year, month)
235
+ maxes_for_month = get_hist_data_for_month(
236
+ df,
237
+ year,
238
+ month,
239
+ use_maxes=True,
240
+ )
241
+ mean, std_dev = norm.fit(all_samples_for_month)
242
+ max_mean, _ = norm.fit(maxes_for_month)
243
+
244
+ pdf_x = np.linspace(xmin, xmax, num_samples)
245
+ pdf_y = norm.pdf(pdf_x, mean, std_dev)
246
+
247
+ month_label = f"{name} ({round(mean)}/{round(max_mean)})"
248
+ ax.plot(pdf_x, pdf_y, linewidth=2, label=month_label)
249
+
250
+ ax.legend()
251
+ ax.set_xlabel("Airflow (L/Min)")
252
+ ax.set_ylabel("Count")
253
+ ax.set_title("PDF of data, per month", size=16)
254
+ ax.set_xlim(xmin, xmax)
255
+ return fig, ax
256
+
257
+
258
+ def create_monthly_airflow_histograms(df, month_year_tuples):
259
+ # Create a figure with subplots
260
+ rows = len(month_year_tuples)
261
+ fig, axes = plt.subplots(rows, 1, figsize=(5, 10), sharex=True)
262
+
263
+ # Plot data for each week on separate axes
264
+ for i, ax in enumerate(axes):
265
+ year, month, name = month_year_tuples[i]
266
+ month_data = get_hist_data_for_month(df, year, month)
267
+ flattened_month_data = month_data.ravel()
268
+ name = name + f" ({len(flattened_month_data)} samples)"
269
+ ax.hist(flattened_month_data, label=f"{name}")
270
+ ax.set_title(f"{name}")
271
+ ax.set_ylim(0, 50)
272
+
273
+ # Add a title to the entire figure
274
+ fig.suptitle("Distribution of data, per month", fontsize=16)
275
+ fig.supxlabel("Airflow (L/Min)")
276
+ fig.supylabel("Count")
277
+ return fig, axes
@@ -356,6 +356,9 @@ def get_aggregated_event_counts(ts, events, period):
356
356
  return grouped_df
357
357
 
358
358
 
359
+ # TODO: Add 'get all events where equal to' function. Do not want a dense df.
360
+
361
+
359
362
  def populate_with_events(ax, events, from_date):
360
363
  for event in events:
361
364
  event_date, event_colour, event_style, event_label = event
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "dimwit"
3
- version = "0.2.7"
3
+ version = "0.2.8"
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"
@@ -11,6 +11,8 @@ python = "^3.10"
11
11
  pandas = "^2.1.4"
12
12
  requests = "^2.31.0"
13
13
  matplotlib = "^3.8.3"
14
+ seaborn = "^0.13.2"
15
+ scipy = "^1.12.0"
14
16
 
15
17
 
16
18
  [build-system]
File without changes
File without changes
File without changes