hydroanomaly 0.4.0__py3-none-any.whl → 0.6.0__py3-none-any.whl
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.
- hydroanomaly/__init__.py +37 -137
- hydroanomaly/sentinel_bands.py +157 -0
- hydroanomaly/usgs_turbidity.py +150 -0
- hydroanomaly/visualize.py +226 -0
- {hydroanomaly-0.4.0.dist-info → hydroanomaly-0.6.0.dist-info}/METADATA +2 -2
- hydroanomaly-0.6.0.dist-info/RECORD +9 -0
- hydroanomaly/hello.py +0 -29
- hydroanomaly/math_utils.py +0 -50
- hydroanomaly/plotting.py +0 -389
- hydroanomaly/sentinel_data.py +0 -516
- hydroanomaly/usgs_data.py +0 -311
- hydroanomaly-0.4.0.dist-info/RECORD +0 -11
- {hydroanomaly-0.4.0.dist-info → hydroanomaly-0.6.0.dist-info}/WHEEL +0 -0
- {hydroanomaly-0.4.0.dist-info → hydroanomaly-0.6.0.dist-info}/licenses/LICENSE +0 -0
- {hydroanomaly-0.4.0.dist-info → hydroanomaly-0.6.0.dist-info}/top_level.txt +0 -0
hydroanomaly/plotting.py
DELETED
@@ -1,389 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Plotting Module for HydroAnomaly
|
3
|
-
|
4
|
-
This module provides easy-to-use plotting functions for USGS water data time series.
|
5
|
-
Creates professional-looking plots with minimal code.
|
6
|
-
"""
|
7
|
-
|
8
|
-
import matplotlib.pyplot as plt
|
9
|
-
import matplotlib.dates as mdates
|
10
|
-
import seaborn as sns
|
11
|
-
import pandas as pd
|
12
|
-
import numpy as np
|
13
|
-
from datetime import datetime
|
14
|
-
from typing import Optional, Tuple, List, Dict, Any
|
15
|
-
import warnings
|
16
|
-
|
17
|
-
# Set style
|
18
|
-
plt.style.use('default')
|
19
|
-
sns.set_palette("husl")
|
20
|
-
|
21
|
-
|
22
|
-
class WaterDataPlotter:
|
23
|
-
"""
|
24
|
-
A class for creating professional time series plots of water data.
|
25
|
-
|
26
|
-
This class provides methods to create various types of plots including
|
27
|
-
basic time series, multi-parameter plots, and statistical visualizations.
|
28
|
-
"""
|
29
|
-
|
30
|
-
def __init__(self, style: str = 'seaborn-v0_8', figsize: Tuple[int, int] = (12, 6)):
|
31
|
-
"""
|
32
|
-
Initialize the plotter with default settings.
|
33
|
-
|
34
|
-
Args:
|
35
|
-
style (str): Matplotlib style to use
|
36
|
-
figsize (tuple): Default figure size (width, height)
|
37
|
-
"""
|
38
|
-
self.default_figsize = figsize
|
39
|
-
self.colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd',
|
40
|
-
'#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf']
|
41
|
-
|
42
|
-
# Set plotting style
|
43
|
-
try:
|
44
|
-
plt.style.use(style)
|
45
|
-
except:
|
46
|
-
plt.style.use('default')
|
47
|
-
warnings.warn(f"Style '{style}' not available, using default")
|
48
|
-
|
49
|
-
def plot_timeseries(
|
50
|
-
self,
|
51
|
-
data: pd.DataFrame,
|
52
|
-
parameter_name: str = "Value",
|
53
|
-
title: Optional[str] = None,
|
54
|
-
ylabel: Optional[str] = None,
|
55
|
-
color: str = '#1f77b4',
|
56
|
-
save_path: Optional[str] = None,
|
57
|
-
show_stats: bool = True,
|
58
|
-
figsize: Optional[Tuple[int, int]] = None
|
59
|
-
) -> plt.Figure:
|
60
|
-
"""
|
61
|
-
Create a basic time series plot.
|
62
|
-
|
63
|
-
Args:
|
64
|
-
data (pd.DataFrame): Data with 'datetime' and 'value' columns
|
65
|
-
parameter_name (str): Name of the parameter being plotted
|
66
|
-
title (str, optional): Plot title
|
67
|
-
ylabel (str, optional): Y-axis label
|
68
|
-
color (str): Line color
|
69
|
-
save_path (str, optional): Path to save the plot
|
70
|
-
show_stats (bool): Whether to show statistics on the plot
|
71
|
-
figsize (tuple, optional): Figure size
|
72
|
-
|
73
|
-
Returns:
|
74
|
-
matplotlib.figure.Figure: The created figure
|
75
|
-
"""
|
76
|
-
if len(data) == 0:
|
77
|
-
raise ValueError("No data to plot")
|
78
|
-
|
79
|
-
figsize = figsize or self.default_figsize
|
80
|
-
fig, ax = plt.subplots(figsize=figsize)
|
81
|
-
|
82
|
-
# Plot the data
|
83
|
-
ax.plot(data['datetime'], data['value'], color=color, linewidth=1.5, alpha=0.8)
|
84
|
-
|
85
|
-
# Customize the plot
|
86
|
-
if title is None:
|
87
|
-
title = f"{parameter_name} Time Series"
|
88
|
-
ax.set_title(title, fontsize=14, fontweight='bold', pad=20)
|
89
|
-
|
90
|
-
if ylabel is None:
|
91
|
-
ylabel = parameter_name
|
92
|
-
ax.set_ylabel(ylabel, fontsize=12)
|
93
|
-
ax.set_xlabel('Date', fontsize=12)
|
94
|
-
|
95
|
-
# Format dates on x-axis
|
96
|
-
self._format_date_axis(ax, data['datetime'])
|
97
|
-
|
98
|
-
# Add grid
|
99
|
-
ax.grid(True, alpha=0.3, linestyle='--')
|
100
|
-
|
101
|
-
# Add statistics if requested
|
102
|
-
if show_stats:
|
103
|
-
self._add_statistics_text(ax, data['value'], parameter_name)
|
104
|
-
|
105
|
-
# Improve layout
|
106
|
-
plt.tight_layout()
|
107
|
-
|
108
|
-
# Save if requested
|
109
|
-
if save_path:
|
110
|
-
plt.savefig(save_path, dpi=300, bbox_inches='tight')
|
111
|
-
print(f"📊 Plot saved to: {save_path}")
|
112
|
-
|
113
|
-
return fig
|
114
|
-
|
115
|
-
def plot_multiple_parameters(
|
116
|
-
self,
|
117
|
-
data_dict: Dict[str, pd.DataFrame],
|
118
|
-
title: str = "Multiple Parameters Time Series",
|
119
|
-
save_path: Optional[str] = None,
|
120
|
-
figsize: Optional[Tuple[int, int]] = None
|
121
|
-
) -> plt.Figure:
|
122
|
-
"""
|
123
|
-
Plot multiple parameters on separate subplots.
|
124
|
-
|
125
|
-
Args:
|
126
|
-
data_dict (dict): Dictionary with parameter names as keys and DataFrames as values
|
127
|
-
title (str): Main plot title
|
128
|
-
save_path (str, optional): Path to save the plot
|
129
|
-
figsize (tuple, optional): Figure size
|
130
|
-
|
131
|
-
Returns:
|
132
|
-
matplotlib.figure.Figure: The created figure
|
133
|
-
"""
|
134
|
-
n_params = len(data_dict)
|
135
|
-
if n_params == 0:
|
136
|
-
raise ValueError("No data provided")
|
137
|
-
|
138
|
-
figsize = figsize or (12, 4 * n_params)
|
139
|
-
fig, axes = plt.subplots(n_params, 1, figsize=figsize, sharex=True)
|
140
|
-
|
141
|
-
if n_params == 1:
|
142
|
-
axes = [axes]
|
143
|
-
|
144
|
-
colors = self.colors[:n_params]
|
145
|
-
|
146
|
-
for i, (param_name, data) in enumerate(data_dict.items()):
|
147
|
-
if len(data) == 0:
|
148
|
-
continue
|
149
|
-
|
150
|
-
ax = axes[i]
|
151
|
-
ax.plot(data['datetime'], data['value'],
|
152
|
-
color=colors[i], linewidth=1.5, alpha=0.8, label=param_name)
|
153
|
-
|
154
|
-
ax.set_ylabel(param_name, fontsize=11)
|
155
|
-
ax.grid(True, alpha=0.3, linestyle='--')
|
156
|
-
ax.legend(loc='upper right')
|
157
|
-
|
158
|
-
# Add basic stats
|
159
|
-
mean_val = data['value'].mean()
|
160
|
-
ax.axhline(y=mean_val, color=colors[i], linestyle=':', alpha=0.6,
|
161
|
-
label=f'Mean: {mean_val:.2f}')
|
162
|
-
|
163
|
-
# Format the bottom subplot x-axis
|
164
|
-
if data_dict:
|
165
|
-
sample_data = next(iter(data_dict.values()))
|
166
|
-
self._format_date_axis(axes[-1], sample_data['datetime'])
|
167
|
-
|
168
|
-
axes[-1].set_xlabel('Date', fontsize=12)
|
169
|
-
fig.suptitle(title, fontsize=14, fontweight='bold')
|
170
|
-
|
171
|
-
plt.tight_layout()
|
172
|
-
|
173
|
-
if save_path:
|
174
|
-
plt.savefig(save_path, dpi=300, bbox_inches='tight')
|
175
|
-
print(f"📊 Plot saved to: {save_path}")
|
176
|
-
|
177
|
-
return fig
|
178
|
-
|
179
|
-
def plot_comparison(
|
180
|
-
self,
|
181
|
-
data_list: List[Tuple[pd.DataFrame, str]],
|
182
|
-
title: str = "Data Comparison",
|
183
|
-
ylabel: str = "Value",
|
184
|
-
save_path: Optional[str] = None,
|
185
|
-
figsize: Optional[Tuple[int, int]] = None
|
186
|
-
) -> plt.Figure:
|
187
|
-
"""
|
188
|
-
Plot multiple datasets on the same axes for comparison.
|
189
|
-
|
190
|
-
Args:
|
191
|
-
data_list (list): List of tuples (DataFrame, label)
|
192
|
-
title (str): Plot title
|
193
|
-
ylabel (str): Y-axis label
|
194
|
-
save_path (str, optional): Path to save the plot
|
195
|
-
figsize (tuple, optional): Figure size
|
196
|
-
|
197
|
-
Returns:
|
198
|
-
matplotlib.figure.Figure: The created figure
|
199
|
-
"""
|
200
|
-
if not data_list:
|
201
|
-
raise ValueError("No data provided")
|
202
|
-
|
203
|
-
figsize = figsize or self.default_figsize
|
204
|
-
fig, ax = plt.subplots(figsize=figsize)
|
205
|
-
|
206
|
-
colors = self.colors[:len(data_list)]
|
207
|
-
|
208
|
-
for i, (data, label) in enumerate(data_list):
|
209
|
-
if len(data) == 0:
|
210
|
-
continue
|
211
|
-
|
212
|
-
ax.plot(data['datetime'], data['value'],
|
213
|
-
color=colors[i], linewidth=1.5, alpha=0.8, label=label)
|
214
|
-
|
215
|
-
ax.set_title(title, fontsize=14, fontweight='bold', pad=20)
|
216
|
-
ax.set_ylabel(ylabel, fontsize=12)
|
217
|
-
ax.set_xlabel('Date', fontsize=12)
|
218
|
-
ax.grid(True, alpha=0.3, linestyle='--')
|
219
|
-
ax.legend()
|
220
|
-
|
221
|
-
# Format dates
|
222
|
-
if data_list and len(data_list[0][0]) > 0:
|
223
|
-
self._format_date_axis(ax, data_list[0][0]['datetime'])
|
224
|
-
|
225
|
-
plt.tight_layout()
|
226
|
-
|
227
|
-
if save_path:
|
228
|
-
plt.savefig(save_path, dpi=300, bbox_inches='tight')
|
229
|
-
print(f"📊 Plot saved to: {save_path}")
|
230
|
-
|
231
|
-
return fig
|
232
|
-
|
233
|
-
def plot_statistics(
|
234
|
-
self,
|
235
|
-
data: pd.DataFrame,
|
236
|
-
parameter_name: str = "Parameter",
|
237
|
-
save_path: Optional[str] = None
|
238
|
-
) -> plt.Figure:
|
239
|
-
"""
|
240
|
-
Create statistical plots (histogram and box plot).
|
241
|
-
|
242
|
-
Args:
|
243
|
-
data (pd.DataFrame): Data with 'datetime' and 'value' columns
|
244
|
-
parameter_name (str): Name of the parameter
|
245
|
-
save_path (str, optional): Path to save the plot
|
246
|
-
|
247
|
-
Returns:
|
248
|
-
matplotlib.figure.Figure: The created figure
|
249
|
-
"""
|
250
|
-
if len(data) == 0:
|
251
|
-
raise ValueError("No data to plot")
|
252
|
-
|
253
|
-
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
|
254
|
-
|
255
|
-
# Histogram
|
256
|
-
ax1.hist(data['value'], bins=30, alpha=0.7, color=self.colors[0], edgecolor='black')
|
257
|
-
ax1.set_title(f'{parameter_name} Distribution', fontweight='bold')
|
258
|
-
ax1.set_xlabel(parameter_name)
|
259
|
-
ax1.set_ylabel('Frequency')
|
260
|
-
ax1.grid(True, alpha=0.3)
|
261
|
-
|
262
|
-
# Add statistics to histogram
|
263
|
-
mean_val = data['value'].mean()
|
264
|
-
median_val = data['value'].median()
|
265
|
-
ax1.axvline(mean_val, color='red', linestyle='--', alpha=0.8, label=f'Mean: {mean_val:.2f}')
|
266
|
-
ax1.axvline(median_val, color='orange', linestyle='--', alpha=0.8, label=f'Median: {median_val:.2f}')
|
267
|
-
ax1.legend()
|
268
|
-
|
269
|
-
# Box plot
|
270
|
-
box_data = ax2.boxplot(data['value'], patch_artist=True)
|
271
|
-
box_data['boxes'][0].set_facecolor(self.colors[1])
|
272
|
-
box_data['boxes'][0].set_alpha(0.7)
|
273
|
-
|
274
|
-
ax2.set_title(f'{parameter_name} Box Plot', fontweight='bold')
|
275
|
-
ax2.set_ylabel(parameter_name)
|
276
|
-
ax2.grid(True, alpha=0.3)
|
277
|
-
|
278
|
-
plt.tight_layout()
|
279
|
-
|
280
|
-
if save_path:
|
281
|
-
plt.savefig(save_path, dpi=300, bbox_inches='tight')
|
282
|
-
print(f"📊 Plot saved to: {save_path}")
|
283
|
-
|
284
|
-
return fig
|
285
|
-
|
286
|
-
def _format_date_axis(self, ax, dates):
|
287
|
-
"""Format the date axis based on the date range."""
|
288
|
-
date_range = (dates.max() - dates.min()).days
|
289
|
-
|
290
|
-
if date_range <= 7: # Less than a week
|
291
|
-
ax.xaxis.set_major_formatter(mdates.DateFormatter('%m/%d %H:%M'))
|
292
|
-
ax.xaxis.set_major_locator(mdates.HourLocator(interval=6))
|
293
|
-
elif date_range <= 31: # Less than a month
|
294
|
-
ax.xaxis.set_major_formatter(mdates.DateFormatter('%m/%d'))
|
295
|
-
ax.xaxis.set_major_locator(mdates.DayLocator(interval=2))
|
296
|
-
elif date_range <= 365: # Less than a year
|
297
|
-
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
|
298
|
-
ax.xaxis.set_major_locator(mdates.MonthLocator())
|
299
|
-
else: # More than a year
|
300
|
-
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y'))
|
301
|
-
ax.xaxis.set_major_locator(mdates.YearLocator())
|
302
|
-
|
303
|
-
plt.setp(ax.xaxis.get_majorticklabels(), rotation=45, ha='right')
|
304
|
-
|
305
|
-
def _add_statistics_text(self, ax, values, parameter_name):
|
306
|
-
"""Add statistics text box to the plot."""
|
307
|
-
stats_text = (
|
308
|
-
f"Statistics:\n"
|
309
|
-
f"Mean: {values.mean():.2f}\n"
|
310
|
-
f"Median: {values.median():.2f}\n"
|
311
|
-
f"Min: {values.min():.2f}\n"
|
312
|
-
f"Max: {values.max():.2f}\n"
|
313
|
-
f"Std: {values.std():.2f}"
|
314
|
-
)
|
315
|
-
|
316
|
-
# Position the text box
|
317
|
-
ax.text(0.02, 0.98, stats_text, transform=ax.transAxes, fontsize=9,
|
318
|
-
verticalalignment='top', bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.8))
|
319
|
-
|
320
|
-
|
321
|
-
# Convenience functions for easy plotting
|
322
|
-
def plot_usgs_data(
|
323
|
-
data: pd.DataFrame,
|
324
|
-
parameter_name: str = "Value",
|
325
|
-
title: Optional[str] = None,
|
326
|
-
save_path: Optional[str] = None,
|
327
|
-
show_stats: bool = True
|
328
|
-
) -> plt.Figure:
|
329
|
-
"""
|
330
|
-
Quick function to plot USGS time series data.
|
331
|
-
|
332
|
-
Args:
|
333
|
-
data (pd.DataFrame): Data with 'datetime' and 'value' columns
|
334
|
-
parameter_name (str): Name of the parameter being plotted
|
335
|
-
title (str, optional): Plot title
|
336
|
-
save_path (str, optional): Path to save the plot
|
337
|
-
show_stats (bool): Whether to show statistics on the plot
|
338
|
-
|
339
|
-
Returns:
|
340
|
-
matplotlib.figure.Figure: The created figure
|
341
|
-
|
342
|
-
Example:
|
343
|
-
>>> import hydroanomaly
|
344
|
-
>>> data = hydroanomaly.get_discharge("08158000", "2023-01-01", "2023-01-31")
|
345
|
-
>>> hydroanomaly.plot_usgs_data(data, "Discharge (cfs)", "Colorado River Discharge")
|
346
|
-
"""
|
347
|
-
plotter = WaterDataPlotter()
|
348
|
-
return plotter.plot_timeseries(
|
349
|
-
data=data,
|
350
|
-
parameter_name=parameter_name,
|
351
|
-
title=title,
|
352
|
-
save_path=save_path,
|
353
|
-
show_stats=show_stats
|
354
|
-
)
|
355
|
-
|
356
|
-
|
357
|
-
def plot_multiple_gages(
|
358
|
-
data_dict: Dict[str, pd.DataFrame],
|
359
|
-
title: str = "Multiple Gage Comparison",
|
360
|
-
parameter_name: str = "Value",
|
361
|
-
save_path: Optional[str] = None
|
362
|
-
) -> plt.Figure:
|
363
|
-
"""
|
364
|
-
Plot data from multiple gages for comparison.
|
365
|
-
|
366
|
-
Args:
|
367
|
-
data_dict (dict): Dictionary with gage IDs as keys and DataFrames as values
|
368
|
-
title (str): Plot title
|
369
|
-
parameter_name (str): Y-axis label
|
370
|
-
save_path (str, optional): Path to save the plot
|
371
|
-
|
372
|
-
Returns:
|
373
|
-
matplotlib.figure.Figure: The created figure
|
374
|
-
"""
|
375
|
-
plotter = WaterDataPlotter()
|
376
|
-
data_list = [(data, f"Gage {gage_id}") for gage_id, data in data_dict.items()]
|
377
|
-
return plotter.plot_comparison(data_list, title, parameter_name, save_path)
|
378
|
-
|
379
|
-
|
380
|
-
def quick_plot(data: pd.DataFrame, title: str = "USGS Data") -> None:
|
381
|
-
"""
|
382
|
-
Create a quick plot and show it immediately.
|
383
|
-
|
384
|
-
Args:
|
385
|
-
data (pd.DataFrame): Data to plot
|
386
|
-
title (str): Plot title
|
387
|
-
"""
|
388
|
-
plot_usgs_data(data, title=title)
|
389
|
-
plt.show()
|