github-actions[bot] commited on
Commit ·
fecd908
1
Parent(s): 89cc124
Deploy from GitHub Actions
Browse files- analysis.py +222 -13
- app.py +2 -0
- pages.py +7 -0
- ui/pages/grouped_bar_charts.py +53 -1
- ui/pages/sector_compare.py +112 -0
analysis.py
CHANGED
|
@@ -501,7 +501,7 @@ def plot_sector_trends(
|
|
| 501 |
|
| 502 |
# For Salinity, exclude Fresh Water Lakes
|
| 503 |
if analyte_name == "Salinity":
|
| 504 |
-
analyte_data = analyte_data[analyte_data["Sector"] != "
|
| 505 |
|
| 506 |
# Plot each sector with custom colors
|
| 507 |
for sector, color in zip(df["Sector"].unique(), custom_colors):
|
|
@@ -2207,7 +2207,7 @@ def plot_grouped_bars(
|
|
| 2207 |
parameter: str,
|
| 2208 |
year_range: tuple[int, int],
|
| 2209 |
group_by: str = "sector",
|
| 2210 |
-
) -> Figure:
|
| 2211 |
"""
|
| 2212 |
Create a grouped bar chart showing means by sector or year for a selected parameter.
|
| 2213 |
|
|
@@ -2224,8 +2224,9 @@ def plot_grouped_bars(
|
|
| 2224 |
|
| 2225 |
Returns:
|
| 2226 |
--------
|
| 2227 |
-
Figure
|
| 2228 |
-
Matplotlib figure containing the grouped bar chart
|
|
|
|
| 2229 |
"""
|
| 2230 |
# Filter data for parameter and year range
|
| 2231 |
plot_df = df[
|
|
@@ -2330,9 +2331,9 @@ def plot_grouped_bars(
|
|
| 2330 |
|
| 2331 |
# Customize plot
|
| 2332 |
unit = plot_df["Org_Result_Unit"].iloc[0]
|
| 2333 |
-
# ax.set_ylabel(f"{parameter} ({unit})")
|
| 2334 |
ax.set_xlabel(x_label)
|
| 2335 |
-
|
|
|
|
| 2336 |
|
| 2337 |
# Function to wrap text
|
| 2338 |
def wrap_labels(text, width=10):
|
|
@@ -2359,7 +2360,7 @@ def plot_grouped_bars(
|
|
| 2359 |
# Add error bar note with adjusted position
|
| 2360 |
ax.text(
|
| 2361 |
0.99,
|
| 2362 |
-
-0.15,
|
| 2363 |
"Error bars represent ±1 standard error of the mean",
|
| 2364 |
ha="right",
|
| 2365 |
va="top",
|
|
@@ -2369,7 +2370,7 @@ def plot_grouped_bars(
|
|
| 2369 |
)
|
| 2370 |
|
| 2371 |
# Adjust layout with more vertical space for wrapped labels
|
| 2372 |
-
plt.tight_layout(rect=(0, 0.2, 1, 1))
|
| 2373 |
|
| 2374 |
# Add grid
|
| 2375 |
ax.grid(True, axis="y", alpha=0.2, linestyle="-", zorder=1)
|
|
@@ -2377,12 +2378,10 @@ def plot_grouped_bars(
|
|
| 2377 |
# Customize spines
|
| 2378 |
ax.spines["top"].set_visible(False)
|
| 2379 |
ax.spines["right"].set_visible(False)
|
| 2380 |
-
ax.spines["left"].set_visible(False)
|
| 2381 |
|
| 2382 |
# Remove tick marks but keep labels
|
| 2383 |
-
ax.tick_params(
|
| 2384 |
-
axis="y", which="both", length=0
|
| 2385 |
-
) # Add this line to remove tick marks
|
| 2386 |
|
| 2387 |
ax.legend(
|
| 2388 |
bbox_to_anchor=(1.02, 1), # Position at top-right
|
|
@@ -2403,7 +2402,8 @@ def plot_grouped_bars(
|
|
| 2403 |
ax.set_yscale("log")
|
| 2404 |
ax.yaxis.set_major_formatter(plt.ScalarFormatter()) # type: ignore
|
| 2405 |
|
| 2406 |
-
|
|
|
|
| 2407 |
|
| 2408 |
|
| 2409 |
def plot_seasonal_line(
|
|
@@ -2687,3 +2687,212 @@ def plot_seasonal_line(
|
|
| 2687 |
plt.tight_layout(rect=(0, 0, 0.9, 1))
|
| 2688 |
stats_df.insert(0, "parameter", parameter)
|
| 2689 |
return fig, param_data, stats_df
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 501 |
|
| 502 |
# For Salinity, exclude Fresh Water Lakes
|
| 503 |
if analyte_name == "Salinity":
|
| 504 |
+
analyte_data = analyte_data[analyte_data["Sector"] != "Freshwater Lakes"]
|
| 505 |
|
| 506 |
# Plot each sector with custom colors
|
| 507 |
for sector, color in zip(df["Sector"].unique(), custom_colors):
|
|
|
|
| 2207 |
parameter: str,
|
| 2208 |
year_range: tuple[int, int],
|
| 2209 |
group_by: str = "sector",
|
| 2210 |
+
) -> tuple[Figure, pd.DataFrame]:
|
| 2211 |
"""
|
| 2212 |
Create a grouped bar chart showing means by sector or year for a selected parameter.
|
| 2213 |
|
|
|
|
| 2224 |
|
| 2225 |
Returns:
|
| 2226 |
--------
|
| 2227 |
+
tuple[Figure, pd.DataFrame]
|
| 2228 |
+
- Figure: Matplotlib figure containing the grouped bar chart
|
| 2229 |
+
- DataFrame: Contains the plotted data points with means and standard errors
|
| 2230 |
"""
|
| 2231 |
# Filter data for parameter and year range
|
| 2232 |
plot_df = df[
|
|
|
|
| 2331 |
|
| 2332 |
# Customize plot
|
| 2333 |
unit = plot_df["Org_Result_Unit"].iloc[0]
|
|
|
|
| 2334 |
ax.set_xlabel(x_label)
|
| 2335 |
+
title = f"{parameter} (Mean Annual{' ' + unit if unit else ''})"
|
| 2336 |
+
ax.set_title(title)
|
| 2337 |
|
| 2338 |
# Function to wrap text
|
| 2339 |
def wrap_labels(text, width=10):
|
|
|
|
| 2360 |
# Add error bar note with adjusted position
|
| 2361 |
ax.text(
|
| 2362 |
0.99,
|
| 2363 |
+
-0.15,
|
| 2364 |
"Error bars represent ±1 standard error of the mean",
|
| 2365 |
ha="right",
|
| 2366 |
va="top",
|
|
|
|
| 2370 |
)
|
| 2371 |
|
| 2372 |
# Adjust layout with more vertical space for wrapped labels
|
| 2373 |
+
plt.tight_layout(rect=(0, 0.2, 1, 1))
|
| 2374 |
|
| 2375 |
# Add grid
|
| 2376 |
ax.grid(True, axis="y", alpha=0.2, linestyle="-", zorder=1)
|
|
|
|
| 2378 |
# Customize spines
|
| 2379 |
ax.spines["top"].set_visible(False)
|
| 2380 |
ax.spines["right"].set_visible(False)
|
| 2381 |
+
ax.spines["left"].set_visible(False)
|
| 2382 |
|
| 2383 |
# Remove tick marks but keep labels
|
| 2384 |
+
ax.tick_params(axis="y", which="both", length=0)
|
|
|
|
|
|
|
| 2385 |
|
| 2386 |
ax.legend(
|
| 2387 |
bbox_to_anchor=(1.02, 1), # Position at top-right
|
|
|
|
| 2402 |
ax.set_yscale("log")
|
| 2403 |
ax.yaxis.set_major_formatter(plt.ScalarFormatter()) # type: ignore
|
| 2404 |
|
| 2405 |
+
means_df.insert(0, "parameter", parameter)
|
| 2406 |
+
return fig, means_df
|
| 2407 |
|
| 2408 |
|
| 2409 |
def plot_seasonal_line(
|
|
|
|
| 2687 |
plt.tight_layout(rect=(0, 0, 0.9, 1))
|
| 2688 |
stats_df.insert(0, "parameter", parameter)
|
| 2689 |
return fig, param_data, stats_df
|
| 2690 |
+
|
| 2691 |
+
|
| 2692 |
+
@timer(include_params=True)
|
| 2693 |
+
def plot_sector_line_charts(
|
| 2694 |
+
df: pd.DataFrame,
|
| 2695 |
+
parameter: str,
|
| 2696 |
+
show_sem: bool = True,
|
| 2697 |
+
panel_chart: bool = False,
|
| 2698 |
+
) -> tuple[Figure, pd.DataFrame, pd.DataFrame]:
|
| 2699 |
+
"""
|
| 2700 |
+
Create a plot of mean annual parameter trends by sector.
|
| 2701 |
+
|
| 2702 |
+
Parameters:
|
| 2703 |
+
-----------
|
| 2704 |
+
df : pd.DataFrame
|
| 2705 |
+
Input dataframe
|
| 2706 |
+
parameter : str
|
| 2707 |
+
Name of the parameter to plot
|
| 2708 |
+
show_sem : bool, default=True
|
| 2709 |
+
Whether to show the standard error of the mean bands
|
| 2710 |
+
panel_chart : bool, default=False
|
| 2711 |
+
If True, creates a grid of individual sector charts instead of overlapping lines
|
| 2712 |
+
|
| 2713 |
+
Returns:
|
| 2714 |
+
--------
|
| 2715 |
+
tuple[Figure, pd.DataFrame, pd.DataFrame]
|
| 2716 |
+
- Figure: Matplotlib figure containing the line chart(s)
|
| 2717 |
+
- DataFrame: Filtered dataframe containing the data used in the plot
|
| 2718 |
+
- DataFrame: Contains the plotted data points with means and standard errors
|
| 2719 |
+
"""
|
| 2720 |
+
# Filter data for parameter
|
| 2721 |
+
param_data = df[df["Org_Analyte_Name"] == parameter].copy()
|
| 2722 |
+
|
| 2723 |
+
# For Salinity, exclude Freshwater Lakes
|
| 2724 |
+
if parameter == "Salinity":
|
| 2725 |
+
param_data = param_data[param_data["Sector"] != "Freshwater Lakes"]
|
| 2726 |
+
|
| 2727 |
+
# Get unique sectors and years
|
| 2728 |
+
sectors = sorted(param_data["Sector"].unique())
|
| 2729 |
+
years = sorted(param_data["Reporting_Year"].unique())
|
| 2730 |
+
n_sectors = len(sectors)
|
| 2731 |
+
|
| 2732 |
+
# Calculate grid dimensions for panel chart
|
| 2733 |
+
if panel_chart:
|
| 2734 |
+
n_cols = min(3, n_sectors) # Maximum 3 columns
|
| 2735 |
+
n_rows = (n_sectors + n_cols - 1) // n_cols
|
| 2736 |
+
fig = plt.figure(figsize=(5 * n_cols, 3 * n_rows))
|
| 2737 |
+
else:
|
| 2738 |
+
fig, ax = plt.subplots(figsize=(14, 4))
|
| 2739 |
+
|
| 2740 |
+
custom_colors = [
|
| 2741 |
+
"#1f77b4", # blue
|
| 2742 |
+
"#ff7f0e", # orange
|
| 2743 |
+
"#2ca02c", # green
|
| 2744 |
+
"#d62728", # red
|
| 2745 |
+
"#9467bd", # purple
|
| 2746 |
+
"#8c564b", # brown
|
| 2747 |
+
"#e377c2", # pink
|
| 2748 |
+
"#7f7f7f", # gray
|
| 2749 |
+
]
|
| 2750 |
+
|
| 2751 |
+
# Initialize empty list to store sector data
|
| 2752 |
+
all_sector_data = []
|
| 2753 |
+
|
| 2754 |
+
# Get global y-axis limits for consistent scaling
|
| 2755 |
+
y_min = float("inf")
|
| 2756 |
+
y_max = float("-inf")
|
| 2757 |
+
|
| 2758 |
+
# First pass to get y-axis limits
|
| 2759 |
+
for sector in sectors:
|
| 2760 |
+
sector_data = (
|
| 2761 |
+
param_data[param_data["Sector"] == sector]
|
| 2762 |
+
.groupby("Reporting_Year", observed=True)["Org_Result_Value"]
|
| 2763 |
+
.agg(["mean", "sem"])
|
| 2764 |
+
.reset_index()
|
| 2765 |
+
)
|
| 2766 |
+
if not sector_data.empty:
|
| 2767 |
+
y_min = min(y_min, (sector_data["mean"] - sector_data["sem"]).min())
|
| 2768 |
+
y_max = max(y_max, (sector_data["mean"] + sector_data["sem"]).max())
|
| 2769 |
+
|
| 2770 |
+
# Plot each sector
|
| 2771 |
+
for i, (sector, color) in enumerate(zip(sectors, custom_colors)):
|
| 2772 |
+
sector_data = (
|
| 2773 |
+
param_data[param_data["Sector"] == sector]
|
| 2774 |
+
.groupby("Reporting_Year", observed=True)["Org_Result_Value"]
|
| 2775 |
+
.agg(["mean", "sem"])
|
| 2776 |
+
.reset_index()
|
| 2777 |
+
)
|
| 2778 |
+
|
| 2779 |
+
# Add sector column to the data
|
| 2780 |
+
sector_data["Sector"] = sector
|
| 2781 |
+
all_sector_data.append(sector_data)
|
| 2782 |
+
|
| 2783 |
+
if not sector_data.empty:
|
| 2784 |
+
if panel_chart:
|
| 2785 |
+
ax = fig.add_subplot(n_rows, n_cols, i + 1)
|
| 2786 |
+
|
| 2787 |
+
# Plot mean line with error bands for this sector
|
| 2788 |
+
ax.plot(
|
| 2789 |
+
sector_data["Reporting_Year"],
|
| 2790 |
+
sector_data["mean"],
|
| 2791 |
+
"-o",
|
| 2792 |
+
color=color,
|
| 2793 |
+
markersize=4,
|
| 2794 |
+
linewidth=2,
|
| 2795 |
+
)
|
| 2796 |
+
|
| 2797 |
+
if show_sem:
|
| 2798 |
+
ax.fill_between(
|
| 2799 |
+
sector_data["Reporting_Year"],
|
| 2800 |
+
sector_data["mean"] - sector_data["sem"],
|
| 2801 |
+
sector_data["mean"] + sector_data["sem"],
|
| 2802 |
+
color=color,
|
| 2803 |
+
alpha=0.15,
|
| 2804 |
+
)
|
| 2805 |
+
|
| 2806 |
+
# Customize individual panel
|
| 2807 |
+
ax.set_title(sector, pad=10, fontsize=10)
|
| 2808 |
+
ax.grid(True, axis="y", alpha=0.2, linestyle="--")
|
| 2809 |
+
ax.spines["top"].set_visible(False)
|
| 2810 |
+
ax.spines["right"].set_visible(False)
|
| 2811 |
+
ax.spines["left"].set_visible(False)
|
| 2812 |
+
ax.tick_params(axis="y", which="both", length=0)
|
| 2813 |
+
ax.set_xticks(years)
|
| 2814 |
+
ax.set_xticklabels(years, rotation=45)
|
| 2815 |
+
|
| 2816 |
+
# Set consistent y-axis limits
|
| 2817 |
+
if parameter in [
|
| 2818 |
+
"Turbidity",
|
| 2819 |
+
"Fecal Coliform (MPN)",
|
| 2820 |
+
"Total Nitrogen",
|
| 2821 |
+
"Total Phosphorus",
|
| 2822 |
+
]:
|
| 2823 |
+
ax.set_yscale("log")
|
| 2824 |
+
else:
|
| 2825 |
+
ax.set_ylim(y_min * 0.95, y_max * 1.05)
|
| 2826 |
+
|
| 2827 |
+
else:
|
| 2828 |
+
# Original single-panel plotting code
|
| 2829 |
+
ax.plot(
|
| 2830 |
+
sector_data["Reporting_Year"],
|
| 2831 |
+
sector_data["mean"],
|
| 2832 |
+
"-o",
|
| 2833 |
+
color=color,
|
| 2834 |
+
label=sector,
|
| 2835 |
+
markersize=4,
|
| 2836 |
+
linewidth=2,
|
| 2837 |
+
)
|
| 2838 |
+
|
| 2839 |
+
if show_sem:
|
| 2840 |
+
ax.fill_between(
|
| 2841 |
+
sector_data["Reporting_Year"],
|
| 2842 |
+
sector_data["mean"] - sector_data["sem"],
|
| 2843 |
+
sector_data["mean"] + sector_data["sem"],
|
| 2844 |
+
color=color,
|
| 2845 |
+
alpha=0.15,
|
| 2846 |
+
)
|
| 2847 |
+
|
| 2848 |
+
# Get parameter unit
|
| 2849 |
+
param_unit = param_data["Org_Result_Unit"].iloc[0] if not param_data.empty else ""
|
| 2850 |
+
|
| 2851 |
+
if panel_chart:
|
| 2852 |
+
# Add common title and labels
|
| 2853 |
+
fig.suptitle(f"{parameter} ({param_unit})", fontsize=14, y=1.02)
|
| 2854 |
+
fig.text(0.5, 0.02, "Year", ha="center", fontsize=12)
|
| 2855 |
+
fig.text(
|
| 2856 |
+
0.02,
|
| 2857 |
+
0.5,
|
| 2858 |
+
f"Value ({param_unit})",
|
| 2859 |
+
va="center",
|
| 2860 |
+
rotation="vertical",
|
| 2861 |
+
fontsize=12,
|
| 2862 |
+
)
|
| 2863 |
+
else:
|
| 2864 |
+
# Original single-panel customization
|
| 2865 |
+
ax.set_title(parameter, pad=10, fontsize=14, fontweight="normal")
|
| 2866 |
+
ax.set_xlabel("Year", fontsize=12)
|
| 2867 |
+
ax.set_ylabel(f"{parameter} ({param_unit})", fontsize=12)
|
| 2868 |
+
ax.set_xticks(years)
|
| 2869 |
+
ax.set_xticklabels(years)
|
| 2870 |
+
ax.grid(True, axis="y", alpha=0.2, linestyle="--")
|
| 2871 |
+
ax.spines["top"].set_visible(False)
|
| 2872 |
+
ax.spines["right"].set_visible(False)
|
| 2873 |
+
ax.spines["left"].set_visible(False)
|
| 2874 |
+
ax.tick_params(axis="y", which="both", length=0)
|
| 2875 |
+
ax.legend(
|
| 2876 |
+
bbox_to_anchor=(1.05, 1),
|
| 2877 |
+
loc="upper left",
|
| 2878 |
+
borderaxespad=0.0,
|
| 2879 |
+
frameon=False,
|
| 2880 |
+
fontsize=9,
|
| 2881 |
+
)
|
| 2882 |
+
|
| 2883 |
+
if parameter in [
|
| 2884 |
+
"Turbidity",
|
| 2885 |
+
"Fecal Coliform (MPN)",
|
| 2886 |
+
"Total Nitrogen",
|
| 2887 |
+
"Total Phosphorus",
|
| 2888 |
+
]:
|
| 2889 |
+
ax.set_yscale("log")
|
| 2890 |
+
|
| 2891 |
+
# Adjust layout
|
| 2892 |
+
plt.tight_layout()
|
| 2893 |
+
|
| 2894 |
+
# Combine all sector data into a single dataframe
|
| 2895 |
+
plot_data = pd.concat(all_sector_data, ignore_index=True)
|
| 2896 |
+
plot_data.insert(0, "parameter", parameter)
|
| 2897 |
+
|
| 2898 |
+
return fig, param_data, plot_data
|
app.py
CHANGED
|
@@ -16,6 +16,7 @@ from pages import (
|
|
| 16 |
scatter_plots_page,
|
| 17 |
seasonal_line_charts_page,
|
| 18 |
seasonal_trends_page,
|
|
|
|
| 19 |
sector_trends_page,
|
| 20 |
settings_page,
|
| 21 |
trends_by_station_page,
|
|
@@ -47,6 +48,7 @@ page_dict = {}
|
|
| 47 |
|
| 48 |
page_dict["Annual Report Draft Charts/Tables"] = [
|
| 49 |
seasonal_line_charts_page,
|
|
|
|
| 50 |
scatter_plots_page,
|
| 51 |
grouped_bar_charts_page,
|
| 52 |
parameter_summary_tables_page,
|
|
|
|
| 16 |
scatter_plots_page,
|
| 17 |
seasonal_line_charts_page,
|
| 18 |
seasonal_trends_page,
|
| 19 |
+
sector_compare_page,
|
| 20 |
sector_trends_page,
|
| 21 |
settings_page,
|
| 22 |
trends_by_station_page,
|
|
|
|
| 48 |
|
| 49 |
page_dict["Annual Report Draft Charts/Tables"] = [
|
| 50 |
seasonal_line_charts_page,
|
| 51 |
+
sector_compare_page,
|
| 52 |
scatter_plots_page,
|
| 53 |
grouped_bar_charts_page,
|
| 54 |
parameter_summary_tables_page,
|
pages.py
CHANGED
|
@@ -855,6 +855,12 @@ def trends_by_station_section():
|
|
| 855 |
)
|
| 856 |
|
| 857 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 858 |
home_page = st.Page(
|
| 859 |
home_section,
|
| 860 |
title="Home",
|
|
@@ -953,4 +959,5 @@ __all__ = [
|
|
| 953 |
"scatter_plots_page",
|
| 954 |
"grouped_bar_charts_page",
|
| 955 |
"seasonal_line_charts_page",
|
|
|
|
| 956 |
]
|
|
|
|
| 855 |
)
|
| 856 |
|
| 857 |
|
| 858 |
+
sector_compare_page = st.Page(
|
| 859 |
+
"ui/pages/sector_compare.py",
|
| 860 |
+
title="Sector Comparison",
|
| 861 |
+
icon=":material/ssid_chart:",
|
| 862 |
+
)
|
| 863 |
+
|
| 864 |
home_page = st.Page(
|
| 865 |
home_section,
|
| 866 |
title="Home",
|
|
|
|
| 959 |
"scatter_plots_page",
|
| 960 |
"grouped_bar_charts_page",
|
| 961 |
"seasonal_line_charts_page",
|
| 962 |
+
"sector_compare_page",
|
| 963 |
]
|
ui/pages/grouped_bar_charts.py
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
|
|
|
|
|
| 1 |
import pandas as pd
|
| 2 |
import streamlit as st
|
| 3 |
|
| 4 |
from analysis import plot_grouped_bars
|
|
|
|
| 5 |
from dashboard_analytics import log_visit
|
| 6 |
|
| 7 |
st.title("Grouped Bar Charts")
|
|
@@ -46,7 +49,56 @@ else:
|
|
| 46 |
year_range = (min(years), max(years))
|
| 47 |
|
| 48 |
try:
|
| 49 |
-
fig = plot_grouped_bars(raw_df, selected_parameter, year_range, group_by)
|
| 50 |
st.pyplot(fig)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
except ValueError as e:
|
| 52 |
st.warning(str(e))
|
|
|
|
| 1 |
+
import io
|
| 2 |
+
|
| 3 |
import pandas as pd
|
| 4 |
import streamlit as st
|
| 5 |
|
| 6 |
from analysis import plot_grouped_bars
|
| 7 |
+
from components import render_filtered_data_preview
|
| 8 |
from dashboard_analytics import log_visit
|
| 9 |
|
| 10 |
st.title("Grouped Bar Charts")
|
|
|
|
| 49 |
year_range = (min(years), max(years))
|
| 50 |
|
| 51 |
try:
|
| 52 |
+
fig, means_df = plot_grouped_bars(raw_df, selected_parameter, year_range, group_by)
|
| 53 |
st.pyplot(fig)
|
| 54 |
+
|
| 55 |
+
# Add Chart Data expander
|
| 56 |
+
with st.expander("Chart Data"):
|
| 57 |
+
render_filtered_data_preview(
|
| 58 |
+
means_df, display_columns=means_df.columns.tolist()
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
# Add CSV download button for chart data
|
| 62 |
+
csv_buffer = io.StringIO()
|
| 63 |
+
means_df.to_csv(csv_buffer, index=False)
|
| 64 |
+
st.download_button(
|
| 65 |
+
label=f"Download Chart Data for {selected_parameter} (CSV)",
|
| 66 |
+
data=csv_buffer.getvalue(),
|
| 67 |
+
file_name=f"{selected_parameter}_grouped_by_{group_by}_year-{year_range[0]}-{year_range[1]}_chart_data.csv",
|
| 68 |
+
mime="text/csv",
|
| 69 |
+
)
|
| 70 |
+
|
| 71 |
+
# Add Raw Data expander
|
| 72 |
+
with st.expander("Raw Data"):
|
| 73 |
+
# Filter raw data for the selected parameter and year range
|
| 74 |
+
filtered_df = raw_df[
|
| 75 |
+
(raw_df["Org_Analyte_Name"] == selected_parameter)
|
| 76 |
+
& (raw_df["Activity_Start_Date_Time"].dt.year >= year_range[0])
|
| 77 |
+
& (raw_df["Activity_Start_Date_Time"].dt.year <= year_range[1])
|
| 78 |
+
]
|
| 79 |
+
|
| 80 |
+
# Display filtered data preview
|
| 81 |
+
render_filtered_data_preview(
|
| 82 |
+
filtered_df,
|
| 83 |
+
[
|
| 84 |
+
"Date",
|
| 85 |
+
"Sector",
|
| 86 |
+
"Station_Number",
|
| 87 |
+
"Sample_Position",
|
| 88 |
+
"Org_Analyte_Name",
|
| 89 |
+
"Org_Result_Value",
|
| 90 |
+
],
|
| 91 |
+
)
|
| 92 |
+
|
| 93 |
+
# Add CSV download button for raw data
|
| 94 |
+
csv_buffer = io.StringIO()
|
| 95 |
+
filtered_df.to_csv(csv_buffer, index=False)
|
| 96 |
+
st.download_button(
|
| 97 |
+
label=f"Download Raw Data for {selected_parameter} (CSV)",
|
| 98 |
+
data=csv_buffer.getvalue(),
|
| 99 |
+
file_name=f"{selected_parameter}_grouped_by_{group_by}_year-{year_range[0]}-{year_range[1]}_raw_data.csv",
|
| 100 |
+
mime="text/csv",
|
| 101 |
+
)
|
| 102 |
+
|
| 103 |
except ValueError as e:
|
| 104 |
st.warning(str(e))
|
ui/pages/sector_compare.py
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import io
|
| 2 |
+
|
| 3 |
+
import pandas as pd
|
| 4 |
+
import streamlit as st
|
| 5 |
+
|
| 6 |
+
from analysis import plot_sector_line_charts
|
| 7 |
+
from components import render_filtered_data_preview
|
| 8 |
+
from dashboard_analytics import log_visit
|
| 9 |
+
|
| 10 |
+
st.title("Sector Comparison Charts")
|
| 11 |
+
log_visit("Sector Comparison Charts")
|
| 12 |
+
|
| 13 |
+
# Get data from session state
|
| 14 |
+
raw_df = st.session_state.data["raw_df"]
|
| 15 |
+
raw_df["Date"] = pd.to_datetime(raw_df["Activity_Start_Date_Time"]).dt.date
|
| 16 |
+
|
| 17 |
+
# Create sidebar controls
|
| 18 |
+
selected_parameter = st.sidebar.selectbox(
|
| 19 |
+
"Parameter:",
|
| 20 |
+
options=sorted(raw_df["Org_Analyte_Name"].unique()),
|
| 21 |
+
index=sorted(raw_df["Org_Analyte_Name"].unique()).index("Temperature, Water")
|
| 22 |
+
if "Temperature, Water" in raw_df["Org_Analyte_Name"].unique()
|
| 23 |
+
else 0,
|
| 24 |
+
help="Select a parameter to display.",
|
| 25 |
+
key="sector_compare_parameter_select",
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
# Add show_sem checkbox
|
| 29 |
+
show_sem = st.sidebar.checkbox(
|
| 30 |
+
"Show Standard Error",
|
| 31 |
+
value=True,
|
| 32 |
+
help="Display standard error bands around the mean lines",
|
| 33 |
+
key="sector_compare_show_sem",
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
# Add year filter slider
|
| 37 |
+
years = sorted(raw_df["Activity_Start_Date_Time"].dt.year.unique())
|
| 38 |
+
default_min_year = min(years)
|
| 39 |
+
if len(years) >= 2:
|
| 40 |
+
year_range = st.sidebar.slider(
|
| 41 |
+
"Year Range:",
|
| 42 |
+
min_value=min(years),
|
| 43 |
+
max_value=max(years),
|
| 44 |
+
value=(default_min_year, max(years)),
|
| 45 |
+
key="sector_compare_year_range",
|
| 46 |
+
)
|
| 47 |
+
else:
|
| 48 |
+
year_range = (min(years), max(years))
|
| 49 |
+
|
| 50 |
+
# Filter data based on year range
|
| 51 |
+
filtered_raw_df = raw_df[
|
| 52 |
+
(raw_df["Activity_Start_Date_Time"].dt.year >= year_range[0])
|
| 53 |
+
& (raw_df["Activity_Start_Date_Time"].dt.year <= year_range[1])
|
| 54 |
+
]
|
| 55 |
+
|
| 56 |
+
try:
|
| 57 |
+
fig, param_data, plot_data = plot_sector_line_charts(
|
| 58 |
+
filtered_raw_df, selected_parameter, show_sem=show_sem
|
| 59 |
+
)
|
| 60 |
+
st.pyplot(fig)
|
| 61 |
+
|
| 62 |
+
fig, _, _ = plot_sector_line_charts(
|
| 63 |
+
filtered_raw_df, selected_parameter, show_sem=show_sem, panel_chart=True
|
| 64 |
+
)
|
| 65 |
+
st.pyplot(fig)
|
| 66 |
+
|
| 67 |
+
# Add Chart Data expander
|
| 68 |
+
with st.expander("Chart Data"):
|
| 69 |
+
render_filtered_data_preview(
|
| 70 |
+
plot_data, display_columns=plot_data.columns.tolist()
|
| 71 |
+
)
|
| 72 |
+
|
| 73 |
+
# Add CSV download button for chart data
|
| 74 |
+
csv_buffer = io.StringIO()
|
| 75 |
+
plot_data.to_csv(csv_buffer, index=False)
|
| 76 |
+
st.download_button(
|
| 77 |
+
label=f"Download Chart Data for {selected_parameter} (CSV)",
|
| 78 |
+
data=csv_buffer.getvalue(),
|
| 79 |
+
file_name=f"{selected_parameter}_sector_comparison_year-{year_range[0]}-{year_range[1]}_chart_data.csv",
|
| 80 |
+
mime="text/csv",
|
| 81 |
+
)
|
| 82 |
+
|
| 83 |
+
# Add Raw Data expander
|
| 84 |
+
with st.expander("Raw Data"):
|
| 85 |
+
# Filter raw data for the selected parameter
|
| 86 |
+
filtered_df = param_data.copy()
|
| 87 |
+
|
| 88 |
+
# Display filtered data preview
|
| 89 |
+
render_filtered_data_preview(
|
| 90 |
+
filtered_df,
|
| 91 |
+
[
|
| 92 |
+
"Date",
|
| 93 |
+
"Sector",
|
| 94 |
+
"Station_Number",
|
| 95 |
+
"Sample_Position",
|
| 96 |
+
"Org_Analyte_Name",
|
| 97 |
+
"Org_Result_Value",
|
| 98 |
+
],
|
| 99 |
+
)
|
| 100 |
+
|
| 101 |
+
# Add CSV download button for raw data
|
| 102 |
+
csv_buffer = io.StringIO()
|
| 103 |
+
filtered_df.to_csv(csv_buffer, index=False)
|
| 104 |
+
st.download_button(
|
| 105 |
+
label=f"Download Raw Data for {selected_parameter} (CSV)",
|
| 106 |
+
data=csv_buffer.getvalue(),
|
| 107 |
+
file_name=f"{selected_parameter}_sector_comparison_year-{year_range[0]}-{year_range[1]}_raw_data.csv",
|
| 108 |
+
mime="text/csv",
|
| 109 |
+
)
|
| 110 |
+
|
| 111 |
+
except ValueError as e:
|
| 112 |
+
st.warning(str(e))
|