Metadata-Version: 2.4
Name: PhenoPhyto
Version: 0.3.1
Summary: PhenoPhyto: A python package for detecting and visualizing phytoplankton bloom phenology events
Author: Md Nahin Alam, Md. Leion Hassan, Md. Enamul Hoque
License: MIT
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pandas
Requires-Dist: numpy
Requires-Dist: scipy
Requires-Dist: matplotlib
Requires-Dist: seaborn
Requires-Dist: statsmodels
Dynamic: author
Dynamic: description
Dynamic: description-content-type
Dynamic: license
Dynamic: license-file
Dynamic: requires-dist
Dynamic: summary

# PhenoPhyto

PhenoPhyto is a Python package for detecting and visualizing phytoplankton bloom phenology from chlorophyll data. It supports three common workflows:

- spatial daily climatology bloom detection
- spatial weekly climatology bloom detection
- non-spatial time-series bloom detection and visualization

The package also includes helper functions for regional comparison plots and spatial mapping of phenology metrics.

## What the package does

PhenoPhyto can help you:

- detect bloom initiation, peak, termination, and duration from chlorophyll climatologies
- detect multiple bloom events in a single non-spatial time series
- highlight bloom periods on time-series plots
- create quick spatial maps from point results
- interpolate phenology variables to a grid with inverse distance weighting (IDW)
- build regional boxplots with Tukey HSD compact letter displays

## Installation

Use Python 3.

```bash
pip install PhenoPhyto
```

Optional geospatial overlays and masking in the mapping functions work best if you also install:

```bash
pip install geopandas shapely
```

## Imports

These functions are available from the package root:

```python
from PhenoPhyto import (
    detect_phyto_pheno_1,
    detect_phyto_pheno_2,
    detect_phyto_pheno_3,
    plot_bloom_events,
)
```

Additional plotting helpers live in `PhenoPhyto.plotting`:

```python
from PhenoPhyto.plotting import (
    apply_idw_interpolation,
    plot_bloom_map,
    map_phenology_variable,
    regional_bloom_days_boxplot,
    regional_number_of_bloom_boxplot,
)
```

## Quick start: non-spatial time series

Use `detect_phyto_pheno_3()` when you have a single chlorophyll time series without latitude and longitude columns.

```python
import numpy as np
import pandas as pd

from PhenoPhyto import detect_phyto_pheno_3, plot_bloom_events

df = pd.DataFrame({
    "day_of_year": np.arange(1, 366),
    "CHL": 1 + 0.3 * np.sin(np.linspace(0, 8 * np.pi, 365)) + np.random.rand(365) * 0.4,
})

bloom_data = detect_phyto_pheno_3(
    df,
    time_col="day_of_year",
    chl_col="CHL",
    threshold_percent=10,
    min_gap=2,
    min_duration=3,
)

print("Threshold:", bloom_data["threshold"])
print(bloom_data["bloom_events"])

plot_bloom_events(
    df,
    bloom_data,
    region_name="Sample Region",
    time_col="day_of_year",
    chl_col="CHL",
)
```

`detect_phyto_pheno_3()` returns a dictionary with:

- `median_chl`
- `threshold`
- `threshold_percent`
- `bloom_events`

The `bloom_events` value is a pandas DataFrame with these columns:

- `bloom_id`
- `bloom_start_day`
- `bloom_start_value`
- `bloom_end_day`
- `bloom_end_value`
- `bloom_peak_day`
- `bloom_peak_value`
- `bloom_duration`

## Spatial daily climatology

Use `detect_phyto_pheno_1()` when your data contain latitude, longitude, daily time steps, and chlorophyll values.

```python
import pandas as pd

from PhenoPhyto import detect_phyto_pheno_1

df_daily = pd.DataFrame({
    "latitude": [21.0, 21.0, 21.0, 21.5, 21.5, 21.5],
    "longitude": [91.0, 91.0, 91.0, 91.5, 91.5, 91.5],
    "day": [40, 80, 120, 40, 80, 120],
    "CHL": [0.8, 2.3, 1.1, 0.9, 2.8, 1.2],
})

daily_result = detect_phyto_pheno_1(
    df_daily,
    lat_col="latitude",
    lon_col="longitude",
    time_col="day",
    chl_col="CHL",
    threshold_percent=10,
    min_duration=0,
)

print(daily_result.head())
```

The output is a DataFrame with one row per location and columns such as:

- `latitude`, `longitude`
- `bloom_initiation`, `bloom_initiation_chl`
- `bloom_peak`, `bloom_peak_chl`
- `bloom_termination`, `bloom_termination_chl`
- `bloom_duration`
- `bloom_initiation_month`, `bloom_peak_month`, `bloom_termination_month`

## Spatial weekly climatology

Use `detect_phyto_pheno_2()` when the time column is weekly instead of daily.

```python
from PhenoPhyto import detect_phyto_pheno_2

weekly_result = detect_phyto_pheno_2(
    df_weekly,
    lat_col="latitude",
    lon_col="longitude",
    time_col="week",
    chl_col="CHL",
    threshold_percent=10,
    min_duration=0,
)
```

The returned columns match the daily workflow, but the timing is interpreted in weeks.

## Quick spatial map

If you already have spatial point results from `detect_phyto_pheno_1()` or `detect_phyto_pheno_2()`, you can map any phenology variable directly.

```python
from PhenoPhyto.plotting import map_phenology_variable

fig = map_phenology_variable(
    daily_result,
    var_col="bloom_peak",
    title="Bloom Peak Day",
    legend_label="Day of Year",
    color_palette="viridis",
)
```

Expected input columns:

- `longitude`
- `latitude`
- the variable you want to plot

## Interpolated phenology map

For a smoother map, interpolate a phenology variable to a grid with IDW and then plot the result.

```python
from PhenoPhyto.plotting import apply_idw_interpolation, plot_bloom_map

interp = apply_idw_interpolation(
    daily_result,
    var_column="bloom_initiation",
    grid_resolution=0.05,
    idp_value=2.0,
)

fig = plot_bloom_map(
    data=interp,
    x_col="x",
    y_col="y",
    fill_col="bloom_initiation",
    title="Bloom Initiation Day",
    fill_name="Day of Year",
    fill_palette="RdYlBu_r",
)
```

If you pass GeoDataFrames to the shapefile arguments, `plot_bloom_map()` can also overlay region boundaries and land layers.

## Regional comparison boxplots

`regional_bloom_days_boxplot()` is designed for comparing bloom timing variables across regions.

```python
from PhenoPhyto.plotting import regional_bloom_days_boxplot

fig = regional_bloom_days_boxplot(
    data=regional_df,
    region_var="region",
    y_vars=["bloom_initiation", "bloom_peak", "bloom_termination"],
    y_labels=["Bloom Start Day", "Bloom Peak Day", "Bloom Termination Day"],
    ncol=3,
)
```

`regional_number_of_bloom_boxplot()` is designed for long-format bloom-count data.

```python
from PhenoPhyto.plotting import regional_number_of_bloom_boxplot

fig = regional_number_of_bloom_boxplot(
    data=count_df,
    x_var="Zone",
    y_var="Count",
    x_label="Regions",
    y_label="Number of Blooms",
)
```

Both functions use Tukey HSD compact letter displays when the statistical calculation succeeds.

## Function overview

| Function | Purpose |
| --- | --- |
| `find_peaks_from_chl_data()` | Helper for identifying peaks above a threshold in a time series |
| `detect_phyto_pheno_1()` | Detect bloom phenology from daily spatial climatology data |
| `detect_phyto_pheno_2()` | Detect bloom phenology from weekly spatial climatology data |
| `detect_phyto_pheno_3()` | Detect bloom events from a non-spatial chlorophyll time series |
| `plot_bloom_events()` | Plot a chlorophyll time series and shade bloom periods |
| `map_phenology_variable()` | Create a quick spatial map from point phenology data |
| `apply_idw_interpolation()` | Interpolate a phenology variable to a regular grid |
| `plot_bloom_map()` | Plot an interpolated phenology surface with optional overlays |
| `regional_bloom_days_boxplot()` | Compare bloom timing metrics across regions |
| `regional_number_of_bloom_boxplot()` | Compare bloom counts across regions |

## Data requirements

### For `detect_phyto_pheno_1()`

- a pandas DataFrame
- latitude and longitude columns
- a daily time column such as `day`
- a numeric chlorophyll column such as `CHL`

### For `detect_phyto_pheno_2()`

- a pandas DataFrame
- latitude and longitude columns
- a weekly time column such as `week`
- a numeric chlorophyll column such as `CHL`

### For `detect_phyto_pheno_3()`

- a pandas DataFrame
- a time column such as `day_of_year`
- a numeric chlorophyll column such as `CHL`

### For mapping functions

- `map_phenology_variable()` expects `longitude`, `latitude`, and the variable to display
- `apply_idw_interpolation()` expects `latitude`, `longitude`, and the variable to interpolate
- `plot_bloom_map()` expects the interpolated output columns `x`, `y`, and the variable to plot

### For regional boxplots

- `regional_bloom_days_boxplot()` expects a region column plus one or more bloom timing variables
- `regional_number_of_bloom_boxplot()` expects long-format data with a grouping column and a numeric count column

## Notes

- Chlorophyll values must be numeric.
- In the spatial detection functions, duplicate observations at the same time step within a location are averaged before analysis.
- `min_duration` is interpreted in the same units as the supplied time column.
- `geopandas` and `shapely` are only needed for shapefile masking and land or boundary overlays.

## License

This project is released under the MIT License. See [LICENSE](LICENSE).
