github-actions[bot] commited on
Commit ·
245b5cd
1
Parent(s): 4f7ea6b
Deploy from GitHub Actions
Browse files- analysis.py +54 -18
- pages.py +14 -10
- ui/pages/dissolved_oxygen.py +17 -9
analysis.py
CHANGED
|
@@ -1224,39 +1224,75 @@ def plot_do_temp_relationship(df: pd.DataFrame) -> Figure:
|
|
| 1224 |
.dropna(subset=["Dissolved Oxygen", "Temperature, Water"])
|
| 1225 |
)
|
| 1226 |
|
| 1227 |
-
|
|
|
|
| 1228 |
|
| 1229 |
-
# Create plot with regression line
|
| 1230 |
g = sns.lmplot(
|
| 1231 |
data=do_temp_data,
|
| 1232 |
x="Temperature, Water",
|
| 1233 |
y="Dissolved Oxygen",
|
| 1234 |
hue="Sample_Position",
|
| 1235 |
-
hue_order=["
|
| 1236 |
-
|
|
|
|
|
|
|
| 1237 |
height=8,
|
| 1238 |
aspect=1.5,
|
| 1239 |
legend=False,
|
| 1240 |
)
|
| 1241 |
|
| 1242 |
-
# Add DO threshold and
|
| 1243 |
ax = g.axes[0, 0]
|
| 1244 |
-
ax.axhline(
|
|
|
|
|
|
|
| 1245 |
ax.text(
|
| 1246 |
ax.get_xlim()[0],
|
| 1247 |
-
|
| 1248 |
-
"
|
| 1249 |
ha="left",
|
| 1250 |
va="bottom",
|
| 1251 |
-
color="
|
| 1252 |
-
alpha=0.
|
| 1253 |
)
|
| 1254 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1255 |
g.set_axis_labels("Water Temperature (°C)", "Dissolved Oxygen (mg/L)")
|
| 1256 |
ax.set_title("Dissolved Oxygen vs Water Temperature", pad=20, fontsize=16)
|
| 1257 |
-
|
| 1258 |
-
#
|
| 1259 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1260 |
|
| 1261 |
return g.figure
|
| 1262 |
|
|
@@ -1348,16 +1384,16 @@ def plotly_plot_do_temp_relationship(df: pd.DataFrame) -> go.Figure:
|
|
| 1348 |
|
| 1349 |
# Add DO threshold line
|
| 1350 |
fig.add_hline(
|
| 1351 |
-
y=
|
| 1352 |
-
line=dict(color="
|
| 1353 |
opacity=0.5,
|
| 1354 |
-
annotation_text="
|
| 1355 |
annotation_position="left",
|
| 1356 |
annotation=dict(
|
| 1357 |
-
font=dict(color="
|
| 1358 |
xanchor="left",
|
| 1359 |
yanchor="bottom",
|
| 1360 |
-
opacity=0.
|
| 1361 |
),
|
| 1362 |
)
|
| 1363 |
|
|
|
|
| 1224 |
.dropna(subset=["Dissolved Oxygen", "Temperature, Water"])
|
| 1225 |
)
|
| 1226 |
|
| 1227 |
+
# Create custom color palette matching DO timeseries
|
| 1228 |
+
custom_palette = {"Surface": "#5AA4D8", "Bottom": "#1B4B8A"}
|
| 1229 |
|
| 1230 |
+
# Create plot with regression line and adjust the hue order
|
| 1231 |
g = sns.lmplot(
|
| 1232 |
data=do_temp_data,
|
| 1233 |
x="Temperature, Water",
|
| 1234 |
y="Dissolved Oxygen",
|
| 1235 |
hue="Sample_Position",
|
| 1236 |
+
hue_order=["Bottom", "Surface"], # Plot 'Bottom' first
|
| 1237 |
+
palette=custom_palette,
|
| 1238 |
+
scatter_kws={"alpha": 0.5, "zorder": 2, "s": 20}, # Scatter plots at zorder=2
|
| 1239 |
+
line_kws={"zorder": 3, "linewidth": 1}, # Trend lines at zorder=3
|
| 1240 |
height=8,
|
| 1241 |
aspect=1.5,
|
| 1242 |
legend=False,
|
| 1243 |
)
|
| 1244 |
|
| 1245 |
+
# Add DO threshold and set z-order
|
| 1246 |
ax = g.axes[0, 0]
|
| 1247 |
+
ax.axhline(
|
| 1248 |
+
y=4.8, color="#FF8C00", linestyle="--", alpha=0.9, zorder=1, linewidth=1
|
| 1249 |
+
) # Threshold line at zorder=1
|
| 1250 |
ax.text(
|
| 1251 |
ax.get_xlim()[0],
|
| 1252 |
+
4.9,
|
| 1253 |
+
" 4.8 mg/L DO threshold",
|
| 1254 |
ha="left",
|
| 1255 |
va="bottom",
|
| 1256 |
+
color="#FF8C00",
|
| 1257 |
+
alpha=0.9,
|
| 1258 |
)
|
| 1259 |
|
| 1260 |
+
# Customize spines - only show bottom spine
|
| 1261 |
+
ax.spines["top"].set_visible(False)
|
| 1262 |
+
ax.spines["right"].set_visible(False)
|
| 1263 |
+
ax.spines["left"].set_visible(False)
|
| 1264 |
+
ax.spines["bottom"].set_color("black")
|
| 1265 |
+
ax.spines["bottom"].set_linewidth(0.5)
|
| 1266 |
+
|
| 1267 |
g.set_axis_labels("Water Temperature (°C)", "Dissolved Oxygen (mg/L)")
|
| 1268 |
ax.set_title("Dissolved Oxygen vs Water Temperature", pad=20, fontsize=16)
|
| 1269 |
+
|
| 1270 |
+
# Adjust legend to show 'Surface' first
|
| 1271 |
+
handles, labels = ax.get_legend_handles_labels()
|
| 1272 |
+
# Reverse the order of handles and labels
|
| 1273 |
+
handles = handles[::-1]
|
| 1274 |
+
labels = labels[::-1]
|
| 1275 |
+
ax.legend(
|
| 1276 |
+
handles,
|
| 1277 |
+
labels,
|
| 1278 |
+
bbox_to_anchor=(1.0, 1.0),
|
| 1279 |
+
loc="upper right",
|
| 1280 |
+
frameon=False,
|
| 1281 |
+
handletextpad=0.5,
|
| 1282 |
+
)
|
| 1283 |
+
|
| 1284 |
+
# Add grid with matching style
|
| 1285 |
+
ax.grid(True, axis="y", alpha=0.15, linestyle="-", color="gray")
|
| 1286 |
+
|
| 1287 |
+
# Remove tick marks but keep labels
|
| 1288 |
+
ax.tick_params(axis="y", which="both", length=0)
|
| 1289 |
+
|
| 1290 |
+
# Set y-axis limits with some padding
|
| 1291 |
+
ymin = max(int(min(do_temp_data["Dissolved Oxygen"].min(), 4.8) * 0.9) - 1, 0)
|
| 1292 |
+
ymax = do_temp_data["Dissolved Oxygen"].max() * 1.1
|
| 1293 |
+
ax.set_ylim(ymin, ymax)
|
| 1294 |
+
yticks = np.arange(ymin, ymax, 2)
|
| 1295 |
+
ax.set_yticks(yticks)
|
| 1296 |
|
| 1297 |
return g.figure
|
| 1298 |
|
|
|
|
| 1384 |
|
| 1385 |
# Add DO threshold line
|
| 1386 |
fig.add_hline(
|
| 1387 |
+
y=4.8,
|
| 1388 |
+
line=dict(color="#FF8C00", width=1, dash="dash"),
|
| 1389 |
opacity=0.5,
|
| 1390 |
+
annotation_text="4.8 mg/L DO threshold",
|
| 1391 |
annotation_position="left",
|
| 1392 |
annotation=dict(
|
| 1393 |
+
font=dict(color="#FF8C00", size=12),
|
| 1394 |
xanchor="left",
|
| 1395 |
yanchor="bottom",
|
| 1396 |
+
opacity=0.8,
|
| 1397 |
),
|
| 1398 |
)
|
| 1399 |
|
pages.py
CHANGED
|
@@ -332,16 +332,20 @@ def data_summary_section():
|
|
| 332 |
@log_page_visit()
|
| 333 |
def do_temp_relationship_section():
|
| 334 |
st.sidebar.toggle("Interactive Plots", key="ENABLE_ALTAIR")
|
| 335 |
-
|
| 336 |
-
st.
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 345 |
|
| 346 |
|
| 347 |
@log_page_visit()
|
|
|
|
| 332 |
@log_page_visit()
|
| 333 |
def do_temp_relationship_section():
|
| 334 |
st.sidebar.toggle("Interactive Plots", key="ENABLE_ALTAIR")
|
| 335 |
+
|
| 336 |
+
container = st.container()
|
| 337 |
+
with container:
|
| 338 |
+
left_spacer, content, right_spacer = st.columns([1, 8, 1])
|
| 339 |
+
with content:
|
| 340 |
+
if st.session_state.ENABLE_ALTAIR:
|
| 341 |
+
fig = plotly_plot_do_temp_relationship(st.session_state.data["raw_df"])
|
| 342 |
+
st.plotly_chart(fig, use_container_width=True) # type: ignore
|
| 343 |
+
else:
|
| 344 |
+
fig = plot_do_temp_relationship(st.session_state.data["raw_df"])
|
| 345 |
+
st.pyplot(fig)
|
| 346 |
+
st.info(
|
| 347 |
+
"This plot shows the relationship between dissolved oxygen and water temperature for all data."
|
| 348 |
+
)
|
| 349 |
|
| 350 |
|
| 351 |
@log_page_visit()
|
ui/pages/dissolved_oxygen.py
CHANGED
|
@@ -20,12 +20,20 @@ def dissolved_oxygen():
|
|
| 20 |
return plot_do_timeseries(filtered_df, period=period)
|
| 21 |
|
| 22 |
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
st.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
return plot_do_timeseries(filtered_df, period=period)
|
| 21 |
|
| 22 |
|
| 23 |
+
# Create a centered container with custom width
|
| 24 |
+
container = st.container()
|
| 25 |
+
with container:
|
| 26 |
+
# Create three columns with ratios 1:8:1 (10% : 80% : 10%)
|
| 27 |
+
left_spacer, content, right_spacer = st.columns([1, 8, 1])
|
| 28 |
+
|
| 29 |
+
# Put content in middle column
|
| 30 |
+
with content:
|
| 31 |
+
st.info(
|
| 32 |
+
"""
|
| 33 |
+
:material/info: This is a half-baked attempt to replicate the Hudson River Estuary Program's excellent
|
| 34 |
+
[Long Term Findings chart](https://www.hudsonriver.org/ccmp/soe/water-quality/do) for dissolved oxygen.
|
| 35 |
+
The EPA threshold is copied from the reference image and not specific to the water quality standards for our area.
|
| 36 |
+
"""
|
| 37 |
+
)
|
| 38 |
+
fig = dissolved_oxygen()
|
| 39 |
+
st.pyplot(fig)
|