Range movement
In [1]:
Copied!
# Uncomment below to run in Google Collab
# pip install ecospat
# Uncomment below to run in Google Collab
# pip install ecospat
Classifying the range movement patterns¶
In [2]:
Copied!
import ecospat.ecospat as ecospat_full
from ecospat.stand_alone_functions import (
process_species_historical_range,
analyze_species_distribution,
analyze_northward_shift,
categorize_species,
analyze_species_distribution_south,
categorize_species_south,
)
import ecospat.ecospat as ecospat_full
from ecospat.stand_alone_functions import (
process_species_historical_range,
analyze_species_distribution,
analyze_northward_shift,
categorize_species,
analyze_species_distribution_south,
categorize_species_south,
)
Classify historical range edges.¶
In [3]:
Copied!
hist_pipeline = ecospat_full.Map()
hist_range = process_species_historical_range(
new_map=hist_pipeline, species_name="Populus angustifolia"
)
hist_range.head()
hist_pipeline = ecospat_full.Map()
hist_range = process_species_historical_range(
new_map=hist_pipeline, species_name="Populus angustifolia"
)
hist_range.head()
No overlapping polygons found — returning original classifications.
Out[3]:
| AREA | PERIMETER | POPUANGU_ | POPUANGU_I | CODE | geometry | cluster | category | |
|---|---|---|---|---|---|---|---|---|
| 0 | 13.195793 | 57.362450 | 225.0 | 244.0 | 2 | POLYGON ((-105.01138 39.68628, -104.99014 39.6... | 0 | core |
| 1 | 8.995920 | 25.194981 | 52.0 | 49.0 | 2 | MULTIPOLYGON (((-111.83473 43.34059, -111.8263... | 1 | core |
| 2 | 3.241327 | 15.342440 | 79.0 | 78.0 | 1 | POLYGON ((-112.46986 35.16809, -112.45182 35.1... | 2 | core |
| 3 | 3.137328 | 19.063100 | 41.0 | 38.0 | 1 | POLYGON ((-111.4987 42.28642, -111.48959 42.35... | 3 | core |
| 4 | 0.851035 | 6.108692 | 60.0 | 57.0 | 1 | POLYGON ((-111.88084 39.45175, -111.83646 39.4... | 3 | trailing (0.05) |
Classify modern range edges.¶
In [4]:
Copied!
classified_modern, classified_historic = analyze_species_distribution(
"Populus angustifolia", record_limit=1000, continent="north_america"
)
classified_modern.head()
classified_modern, classified_historic = analyze_species_distribution(
"Populus angustifolia", record_limit=1000, continent="north_america"
)
classified_modern.head()
Modern records (>= 1976): 1000 Historic records (< 1976): 254
Out[4]:
| point_geometry | year | eventDate | geometry | geometry_id | cluster | AREA | category | density | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | POINT (-111.840163 40.880712) | 2025 | 2025-01-05 | POLYGON ((-112.16175 40.79402, -112.1358 40.81... | cb869cb2640256dc655c5ffd650a009f | 1 | 52264.281507 | core | 0.003597 |
| 1 | POINT (-111.467794 40.775008) | 2025 | 2025-02-07 | POLYGON ((-112.16175 40.79402, -112.1358 40.81... | cb869cb2640256dc655c5ffd650a009f | 1 | 52264.281507 | core | 0.003597 |
| 2 | POINT (-110.869423 39.731437) | 2025 | 2025-03-29 | POLYGON ((-112.16175 40.79402, -112.1358 40.81... | cb869cb2640256dc655c5ffd650a009f | 1 | 52264.281507 | core | 0.003597 |
| 3 | POINT (-111.826781 40.765911) | 2025 | 2025-04-27 | POLYGON ((-112.16175 40.79402, -112.1358 40.81... | cb869cb2640256dc655c5ffd650a009f | 1 | 52264.281507 | core | 0.003597 |
| 4 | POINT (-111.830586 40.497927) | 2025 | 2025-05-25 | POLYGON ((-112.16175 40.79402, -112.1358 40.81... | cb869cb2640256dc655c5ffd650a009f | 1 | 52264.281507 | core | 0.003597 |
Calculate northward rate of movement.¶
In [5]:
Copied!
# It's important to note that although relict populations are given a northward movement rate - this rate is only biologically relevant for leading, core, and trailing populations. Relict populations are not considered part of the noncontiguous, moving range.
northward_rate_df = analyze_northward_shift(
gdf_hist=hist_range,
gdf_new=classified_modern,
species_name="Populus angustifolia",
)
northward_rate_df
# It's important to note that although relict populations are given a northward movement rate - this rate is only biologically relevant for leading, core, and trailing populations. Relict populations are not considered part of the noncontiguous, moving range.
northward_rate_df = analyze_northward_shift(
gdf_hist=hist_range,
gdf_new=classified_modern,
species_name="Populus angustifolia",
)
northward_rate_df
Out[5]:
| species | category | northward_change_km | northward_rate_km_per_year | |
|---|---|---|---|---|
| 0 | Populus angustifolia | core | -85.070546 | -1.736134 |
| 1 | Populus angustifolia | leading | -20.567392 | -0.419743 |
| 2 | Populus angustifolia | relict | -723.304175 | -14.761310 |
| 3 | Populus angustifolia | trailing | -84.163976 | -1.717632 |
Classify range movement pattern¶
In [6]:
Copied!
# If the northward rate of movement is categorized for leading, core, and trailing edges then it will be classified as one of the following: Moving together (positive or negative), Stability, Pull Apart, Reabsorption. If the northward rate of movement is only categorized for 2 of the 3 range edges then all patterns are "likely".
# If the northward rate of movement is categorized for leading, core, and trailing edges then it will be classified as one of the following: Moving together (positive or negative), Stability, Pull Apart, Reabsorption. If the northward rate of movement is only categorized for 2 of the 3 range edges then all patterns are "likely".
In [7]:
Copied!
# Populus angustifolia's range is stable
range_pattern = categorize_species(northward_rate_df)
print(range_pattern)
# Populus angustifolia's range is stable
range_pattern = categorize_species(northward_rate_df)
print(range_pattern)
species leading core trailing category 0 Populus angustifolia -0.419743 -1.736134 -1.717632 stability
In [8]:
Copied!
# Acer rubrum's range is reabsorbing (core moving into edges or vice-versa)
acer_map = ecospat_full.Map()
acer_range = process_species_historical_range(
new_map=hist_pipeline, species_name="Acer saccharum"
)
modern_acer, historic_acer = analyze_species_distribution(
"Acer saccharum", record_limit=1000, continent="north_america"
)
northward_rate_acer = analyze_northward_shift(
gdf_hist=acer_range,
gdf_new=modern_acer,
species_name="Acer saccharum",
)
range_pattern_acer = categorize_species(northward_rate_acer)
print(range_pattern_acer)
# Acer rubrum's range is reabsorbing (core moving into edges or vice-versa)
acer_map = ecospat_full.Map()
acer_range = process_species_historical_range(
new_map=hist_pipeline, species_name="Acer saccharum"
)
modern_acer, historic_acer = analyze_species_distribution(
"Acer saccharum", record_limit=1000, continent="north_america"
)
northward_rate_acer = analyze_northward_shift(
gdf_hist=acer_range,
gdf_new=modern_acer,
species_name="Acer saccharum",
)
range_pattern_acer = categorize_species(northward_rate_acer)
print(range_pattern_acer)
Modern records (>= 1971): 1000 Historic records (< 1971): 548
species leading core trailing category 0 Acer saccharum -1.297308 7.282416 9.815578 reabsorption
Species in the Global South¶
In [9]:
Copied!
# Eucalyptus globulus's range is likely reabsorption
euc_modern, euc_historic = analyze_species_distribution_south(
"Eucalyptus globulus", record_limit=1000, continent="oceania", user_start_year=1980
)
northward_rate_euc = analyze_northward_shift(
gdf_hist=euc_historic,
gdf_new=euc_modern,
species_name="Eucalyptus globulus",
user_start_year=1980,
)
euc_range_pattern = categorize_species_south(northward_rate_euc)
print(euc_range_pattern)
# Eucalyptus globulus's range is likely reabsorption
euc_modern, euc_historic = analyze_species_distribution_south(
"Eucalyptus globulus", record_limit=1000, continent="oceania", user_start_year=1980
)
northward_rate_euc = analyze_northward_shift(
gdf_hist=euc_historic,
gdf_new=euc_modern,
species_name="Eucalyptus globulus",
user_start_year=1980,
)
euc_range_pattern = categorize_species_south(northward_rate_euc)
print(euc_range_pattern)
Modern records (>= 1980): 1000 Historic records (< 1980): 1000
species leading core trailing category 0 Eucalyptus globulus None 0.385176 -3.823545 likely reabsorption