nakas commited on
Commit
2195fe7
·
verified ·
1 Parent(s): 6a8c36c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +18 -252
app.py CHANGED
@@ -140,9 +140,9 @@ def parse_weather_data(data):
140
  def calculate_total_new_snow(df):
141
  """
142
  Calculate total new snow by:
143
- 1. Looking at values before 9 AM each day
144
- 2. Ensuring reasonable hourly change rates (max 3 inches/hour)
145
- 3. Allowing high totals if they accumulate gradually
146
 
147
  Parameters:
148
  df (pandas.DataFrame): DataFrame with datetime and snowfall_3hr columns
@@ -161,60 +161,17 @@ def calculate_total_new_snow(df):
161
  lambda x: x.date() if x.hour >= 9 else (x - pd.Timedelta(days=1)).date()
162
  )
163
 
164
- def validate_sequence(values, times):
165
- """
166
- Validate a sequence of snow readings by checking hourly change rates.
167
- Returns the most reliable final value.
168
- """
169
- if len(values) <= 1:
170
- return values[-1] if len(values) > 0 else 0.0
171
-
172
- # Calculate hour-to-hour changes
173
- changes = np.diff(values)
174
-
175
- # If any change is too large (> 3 inches per hour), we need to find the reliable sequence
176
- if any(change > 3.0 for change in changes):
177
- # Find the longest sequence of reasonable changes
178
- valid_indices = [0] # Start with first reading
179
- current_value = values[0]
180
-
181
- for i in range(1, len(values)):
182
- change = values[i] - values[i-1]
183
- if 0 <= change <= 3.0: # Allow only positive changes up to 3 inches
184
- valid_indices.append(i)
185
- current_value = values[i]
186
- else:
187
- # Check if this might be a reset (value lower than previous)
188
- if values[i] < values[i-1]:
189
- # If it looks like a reset, start new sequence
190
- current_value = values[i]
191
- valid_indices.append(i)
192
- else:
193
- # If it's a spike, ignore it
194
- values[i] = current_value
195
-
196
- # Return the last valid value
197
- return values[valid_indices[-1]]
198
- else:
199
- # If all changes are reasonable, use the last value
200
- return values[-1]
201
-
202
  def process_daily_snow(group):
203
- """Process snow measurements for a single day period"""
204
- # Only look at measurements before 9 AM
205
- morning_data = group[group['datetime'].dt.hour < 9].copy()
206
-
207
- if len(morning_data) == 0:
208
- return 0.0
209
-
210
  # Sort by time to ensure proper sequence
211
- morning_data = morning_data.sort_values('datetime')
212
 
213
- values = morning_data['snowfall_3hr'].values
214
- times = morning_data['datetime'].values
 
215
 
216
- # Validate the sequence and get the final value
217
- daily_total = validate_sequence(values, times)
218
 
219
  return daily_total
220
 
@@ -225,107 +182,7 @@ def calculate_total_new_snow(df):
225
 
226
  def create_daily_snow_plot(df, ax):
227
  """
228
- Create an improved daily snow plot focusing on pre-9 AM totals
229
- with validation of hourly change rates
230
- """
231
- # Create a copy of the dataframe
232
- snow_df = df[['datetime', 'snowfall_3hr']].copy()
233
-
234
- # Create day groups based on 9 AM reset
235
- snow_df['day_group'] = snow_df['datetime'].apply(
236
- lambda x: x.date() if x.hour >= 9 else (x - pd.Timedelta(days=1)).date()
237
- )
238
-
239
- # Calculate daily totals using the improved method
240
- daily_snow = snow_df.groupby('day_group').apply(process_daily_snow).reset_index()
241
- daily_snow.columns = ['date', 'new_snow']
242
-
243
- # Create the bar plot
244
- ax.bar(daily_snow['date'], daily_snow['new_snow'], color='blue')
245
- ax.set_title('Daily New Snow (9 AM Reset)', pad=20)
246
- ax.set_xlabel('Date')
247
- ax.set_ylabel('New Snow (inches)')
248
- ax.tick_params(axis='x', rotation=45)
249
- ax.grid(True, axis='y', linestyle='--', alpha=0.7)
250
-
251
- def create_daily_snow_plot(df, ax):
252
- """
253
- Create an improved daily snow plot that accounts for 9 AM reset
254
- and filters out anomalous readings
255
-
256
- Parameters:
257
- df (pandas.DataFrame): DataFrame with weather data
258
- ax (matplotlib.axes.Axes): Axes object to plot on
259
- """
260
- # Create a copy of the dataframe
261
- snow_df = df[['datetime', 'snowfall_3hr']].copy()
262
-
263
- # Create day groups based on 9 AM reset
264
- snow_df['day_group'] = snow_df['datetime'].apply(
265
- lambda x: x.date() if x.hour >= 9 else (x - pd.Timedelta(days=1)).date()
266
- )
267
-
268
- # Calculate daily totals using the improved method
269
- daily_snow = snow_df.groupby('day_group').apply(process_daily_snow).reset_index()
270
- daily_snow.columns = ['date', 'new_snow']
271
-
272
- # Create the bar plot
273
- ax.bar(daily_snow['date'], daily_snow['new_snow'], color='blue')
274
- ax.set_title('Daily New Snow (9 AM Reset)', pad=20)
275
- ax.set_xlabel('Date')
276
- ax.set_ylabel('New Snow (inches)')
277
- ax.tick_params(axis='x', rotation=45)
278
-
279
- # Add grid for better readability
280
- ax.grid(True, axis='y', linestyle='--', alpha=0.7)
281
-
282
- def process_daily_snow(group):
283
- # Reset the index to work with the time series
284
- group = group.sort_values('datetime').reset_index(drop=True)
285
-
286
- # Calculate hour-to-hour changes
287
- group['snow_change'] = group['snowfall_3hr'].diff()
288
-
289
- # Mark values as anomalous if:
290
- # 1. The hour-to-hour change is greater than 3 inches
291
- # 2. The total value jumps by more than 3 inches from the previous reading
292
- max_hourly_change = 3.0 # Maximum allowed change per hour
293
-
294
- is_anomaly = (
295
- (abs(group['snow_change']) > max_hourly_change) |
296
- (group['snowfall_3hr'] > group['snowfall_3hr'].shift(1) + max_hourly_change)
297
- )
298
-
299
- # Replace anomalous values with NaN
300
- group.loc[is_anomaly, 'snowfall_3hr'] = np.nan
301
-
302
- # Forward fill NaN values with the last valid measurement
303
- group['snowfall_3hr'] = group['snowfall_3hr'].fillna(method='ffill')
304
-
305
- # If no valid measurements exist, use backward fill
306
- group['snowfall_3hr'] = group['snowfall_3hr'].fillna(method='bfill')
307
-
308
- # For each day, take the maximum valid value as the total new snow
309
- if len(group) > 0:
310
- return group['snowfall_3hr'].max()
311
- return 0
312
-
313
- # Calculate daily snow totals
314
- daily_totals = snow_df.groupby('day_group').apply(process_daily_snow)
315
-
316
- # Sum up all daily totals
317
- total_snow = daily_totals.sum()
318
-
319
- return total_snow
320
-
321
- def create_daily_snow_plot(df, ax):
322
- """
323
- Create an improved daily snow plot that accounts for 9 AM reset
324
- and filters out anomalous readings
325
-
326
- Parameters:
327
- df (pandas.DataFrame): DataFrame with weather data
328
- ax (matplotlib.axes.Axes): Axes object to plot on
329
  """
330
  # Create a copy of the dataframe
331
  snow_df = df[['datetime', 'snowfall_3hr']].copy()
@@ -335,116 +192,25 @@ def create_daily_snow_plot(df, ax):
335
  lambda x: x.date() if x.hour >= 9 else (x - pd.Timedelta(days=1)).date()
336
  )
337
 
338
- # Calculate daily totals using the improved method
339
  daily_snow = snow_df.groupby('day_group').apply(process_daily_snow).reset_index()
340
  daily_snow.columns = ['date', 'new_snow']
341
 
342
  # Create the bar plot
343
  ax.bar(daily_snow['date'], daily_snow['new_snow'], color='blue')
344
- ax.set_title('Daily New Snow (9 AM Reset)', pad=20)
345
  ax.set_xlabel('Date')
346
  ax.set_ylabel('New Snow (inches)')
347
  ax.tick_params(axis='x', rotation=45)
348
-
349
- # Add grid for better readability
350
  ax.grid(True, axis='y', linestyle='--', alpha=0.7)
351
 
352
- def process_daily_snow(group):
353
- # Reset the index to work with the time series
354
- group = group.sort_values('datetime').reset_index(drop=True)
355
-
356
- # Calculate hour-to-hour changes
357
- group['snow_change'] = group['snowfall_3hr'].diff()
358
-
359
- # Mark values as anomalous if:
360
- # 1. The hour-to-hour change is greater than 3 inches
361
- # 2. The total value jumps by more than 3 inches from the previous reading
362
- max_hourly_change = 3.0 # Maximum allowed change per hour
363
-
364
- is_anomaly = (
365
- (abs(group['snow_change']) > max_hourly_change) |
366
- (group['snowfall_3hr'] > group['snowfall_3hr'].shift(1) + max_hourly_change)
367
- )
368
-
369
- # Replace anomalous values with NaN
370
- group.loc[is_anomaly, 'snowfall_3hr'] = np.nan
371
-
372
- # Forward fill NaN values with the last valid measurement
373
- group['snowfall_3hr'] = group['snowfall_3hr'].fillna(method='ffill')
374
-
375
- # If no valid measurements exist, use backward fill
376
- group['snowfall_3hr'] = group['snowfall_3hr'].fillna(method='bfill')
377
-
378
- # For each day, take the maximum valid value as the total new snow
379
- if len(group) > 0:
380
- return group['snowfall_3hr'].max()
381
- return 0
382
-
383
- # Calculate daily snow totals
384
- daily_totals = snow_df.groupby('day_group').apply(process_daily_snow)
385
-
386
- # Sum up all daily totals
387
- total_snow = daily_totals.sum()
388
-
389
- return total_snow
390
 
391
- def create_daily_snow_plot(df, ax):
392
- """
393
- Create an improved daily snow plot that accounts for 9 AM reset
394
- and filters out anomalous readings
395
-
396
- Parameters:
397
- df (pandas.DataFrame): DataFrame with weather data
398
- ax (matplotlib.axes.Axes): Axes object to plot on
399
- """
400
- # Create a copy of the dataframe
401
- snow_df = df[['datetime', 'snowfall_3hr']].copy()
402
-
403
- # Create day groups based on 9 AM reset
404
- snow_df['day_group'] = snow_df['datetime'].apply(
405
- lambda x: x.date() if x.hour >= 9 else (x - pd.Timedelta(days=1)).date()
406
- )
407
-
408
- # Calculate daily totals using the improved method
409
- daily_snow = snow_df.groupby('day_group').apply(process_daily_snow).reset_index()
410
- daily_snow.columns = ['date', 'new_snow']
411
-
412
- # Create the bar plot
413
- ax.bar(daily_snow['date'], daily_snow['new_snow'], color='blue')
414
- ax.set_title('Daily New Snow (9 AM Reset)', pad=20)
415
- ax.set_xlabel('Date')
416
- ax.set_ylabel('New Snow (inches)')
417
- ax.tick_params(axis='x', rotation=45)
418
-
419
- # Add grid for better readability
420
- ax.grid(True, axis='y', linestyle='--', alpha=0.7)
421
 
422
- def create_daily_snow_plot(df, ax):
423
- """
424
- Create an improved daily snow plot that accounts for 9 AM reset
425
-
426
- Parameters:
427
- df (pandas.DataFrame): DataFrame with weather data
428
- ax (matplotlib.axes.Axes): Axes object to plot on
429
- """
430
- # Create a copy of the dataframe
431
- snow_df = df[['datetime', 'snowfall_3hr']].copy()
432
-
433
- # Create day groups based on 9 AM reset
434
- snow_df['day_group'] = snow_df['datetime'].apply(
435
- lambda x: x.date() if x.hour >= 9 else (x - pd.Timedelta(days=1)).date()
436
- )
437
-
438
- # Calculate daily totals using the improved method
439
- daily_snow = snow_df.groupby('day_group').apply(process_daily_snow).reset_index()
440
- daily_snow.columns = ['date', 'new_snow']
441
-
442
- # Create the bar plot
443
- ax.bar(daily_snow['date'], daily_snow['new_snow'], color='blue')
444
- ax.set_title('Daily New Snow (9 AM Reset)', pad=20)
445
- ax.set_xlabel('Date')
446
- ax.set_ylabel('New Snow (inches)')
447
- ax.tick_params(axis='x', rotation=45)
448
 
449
  def create_wind_rose(df, ax):
450
  """Create a wind rose plot"""
 
140
  def calculate_total_new_snow(df):
141
  """
142
  Calculate total new snow by:
143
+ 1. Summing 3-hour snowfall amounts within each day period
144
+ 2. Using 9 AM as the daily reset point
145
+ 3. Filtering out obvious anomalies (>9 inches in 3 hours)
146
 
147
  Parameters:
148
  df (pandas.DataFrame): DataFrame with datetime and snowfall_3hr columns
 
161
  lambda x: x.date() if x.hour >= 9 else (x - pd.Timedelta(days=1)).date()
162
  )
163
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  def process_daily_snow(group):
165
+ """Sum up the 3-hour snowfall amounts for each day period"""
 
 
 
 
 
 
166
  # Sort by time to ensure proper sequence
167
+ group = group.sort_values('datetime')
168
 
169
+ # Filter out obvious anomalies (more than 9 inches in 3 hours)
170
+ MAX_THREE_HOUR_SNOW = 9.0 # Maximum reasonable snow in 3 hours
171
+ valid_snow = group['snowfall_3hr'].where(group['snowfall_3hr'] <= MAX_THREE_HOUR_SNOW, 0.0)
172
 
173
+ # Sum up all valid 3-hour amounts
174
+ daily_total = valid_snow.sum()
175
 
176
  return daily_total
177
 
 
182
 
183
  def create_daily_snow_plot(df, ax):
184
  """
185
+ Create a daily snow plot showing summed 3-hour amounts
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  """
187
  # Create a copy of the dataframe
188
  snow_df = df[['datetime', 'snowfall_3hr']].copy()
 
192
  lambda x: x.date() if x.hour >= 9 else (x - pd.Timedelta(days=1)).date()
193
  )
194
 
195
+ # Calculate daily totals by summing 3-hour amounts
196
  daily_snow = snow_df.groupby('day_group').apply(process_daily_snow).reset_index()
197
  daily_snow.columns = ['date', 'new_snow']
198
 
199
  # Create the bar plot
200
  ax.bar(daily_snow['date'], daily_snow['new_snow'], color='blue')
201
+ ax.set_title('Daily New Snow (Sum of 3-hour amounts, 9 AM Reset)', pad=20)
202
  ax.set_xlabel('Date')
203
  ax.set_ylabel('New Snow (inches)')
204
  ax.tick_params(axis='x', rotation=45)
 
 
205
  ax.grid(True, axis='y', linestyle='--', alpha=0.7)
206
 
207
+ # Add value labels on top of each bar
208
+ for i, v in enumerate(daily_snow['new_snow']):
209
+ if v > 0: # Only label bars with snow
210
+ ax.text(i, v, f'{v:.1f}"',
211
+ ha='center', va='bottom')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
 
215
  def create_wind_rose(df, ax):
216
  """Create a wind rose plot"""