Skip to main content

PhenoPhyto: A python package for detecting and visualizing phytoplankton bloom phenology events

Project description

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.

pip install PhenoPhyto

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

pip install geopandas shapely

Imports

These functions are available from the package root:

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:

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.

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.

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.

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.

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.

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.

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.

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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

phenophyto-0.3.1.tar.gz (26.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

phenophyto-0.3.1-py3-none-any.whl (24.3 kB view details)

Uploaded Python 3

File details

Details for the file phenophyto-0.3.1.tar.gz.

File metadata

  • Download URL: phenophyto-0.3.1.tar.gz
  • Upload date:
  • Size: 26.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.1

File hashes

Hashes for phenophyto-0.3.1.tar.gz
Algorithm Hash digest
SHA256 c1fdea416693c7cdb3d2f0d586ca2b44f676ec0f8847408dde92a0e5467f17a1
MD5 b13bf1553b6f92a0a180ad245bf59ef3
BLAKE2b-256 5c3a2b0be85fd2b57944afd8d4c39f1bb9365d5e3fe5e9aff8cbc0f5c0c537c1

See more details on using hashes here.

File details

Details for the file phenophyto-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: phenophyto-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 24.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.1

File hashes

Hashes for phenophyto-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 753580f338dff5472468dbae701ea284590ec3a619ed4bfcf543234faa29d161
MD5 a8605ce19153c30f80660c8747daf1b5
BLAKE2b-256 7bfd3194c68c12cc15d2834b24332fdef85adfaba88575e8458dc1926ce777a3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page