Using pyobis to query for known datasets#
Created: 2022-11-23
Updated: 2026-05-04
Author: Mathew Biddle
This notebook uses the pyobis Python package to query the OBIS API for datasets associated with projects funded under the United States Marine Biodiversity Observation Network. The notebook walks through the process for querying the OBIS api for a specific institution, then using the resultant datasets to gather the locations of all the occurrences using the pyobis package.
The US Marine Biodiversity Observation Network (US MBON) is an interagency initiative that seeks to coordinate across sectors and government to characterize biodiversity and understand drivers of change. US MBON represents a broad, collaborative effort to address the need for systematic collection and sharing of marine life information, ensure that information is available for decision-making and management from local to national levels, and document marine biodiversity status and trends in the face of human- and climate-induced change using a range of technologies and approaches. Through the National Oceanographic Partnership Program, NOAA, NASA, Office of Naval Research, and BOEM have invested in US MBON since 2014, most recently announcing new five year projects in 2022.
import pyobis
pyobis.__version__
'1.6.1'
First off, let’s look through the available institutions at OBIS and find the US MBON one.
Hint: We know the name of the institution is United States Marine Biodiversity Observation Network.
import pandas as pd
pd.set_option("max_colwidth", 400)
url = "https://api.obis.org/v3/institute"
institutes = pd.read_json(url)
df_inst = pd.DataFrame.from_records(institutes["results"])
df_inst.dropna(inplace=True)
df_inst["id"] = df_inst["id"].astype(int)
institution = df_inst.loc[
df_inst["name"].str.contains(
"United States Marine Biodiversity Observation", na=False
)
]
institution
| id | name | country | records | |
|---|---|---|---|---|
| 18 | 23070 | United States Marine Biodiversity Observation Network | United States of America | 7827785 |
Well that looks like the institution we’re after!
Using the id we can check it out on the OBIS website: https://obis.org/organization/23070
Yes, that does look like what we want. Now let’s use that id to query OBIS for all associated datasets.
institution_id = institution["id"].tolist()[0]
institution_id
23070
Collect metadata about all of the datasets#
Here we use the institution_id collected from OBIS to query the API for metadata on the datasets associated with that id.
First, we build the API call, then we bring it into a Pandas DataFrame and create additional fields for use later.
import pyobis
query = pyobis.dataset.search(instituteid=institution_id)
df = pd.DataFrame(query.execute())
df_meta = pd.DataFrame.from_records(df["results"])
# create a column for the human readable short name of the datasets
df_meta["short_name"] = df_meta["url"].str.split("=", expand=True)[1]
# create another column to group datasets by, which removes the year from the short names
df_meta["short_name_group"] = df_meta["short_name"].replace(r"\d", "", regex=True)
df_meta.head(5)
| id | url | archive | published | created | updated | core | extensions | statistics | extent | ... | institutes | contacts | nodes | keywords | downloads | tags | disable_extensions | records | short_name | short_name_group | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | a6155d3d-48b8-4778-83aa-776b459a75d1 | https://ipt-obis.gbif.us/resource?r=mlpa_kelp_forest_monitoring | https://ipt-obis.gbif.us/archive.do?r=mlpa_kelp_forest_monitoring | 2025-09-22T18:54:42.000Z | 2025-08-06T19:55:42.733Z | 2025-10-19T14:52:41.717Z | event | [measurementorfact, occurrence] | {'Event': 123604, 'absence': 26703147, 'dropped': 0, 'Occurrence': 32015548, 'DNADerivedData': 0, 'MeasurementOrFact': 3161067} | POLYGON((-124.24377 32.6580521,-117.2472125 32.6580521,-117.2472125 42.00195,-124.24377 42.00195,-124.24377 32.6580521)) | ... | [{'name': 'United States Marine Biodiversity Observation Network', 'oceanexpert_id': 23070, 'oceanexpert_parent_id': None}, {'name': 'Central & Northern California Ocean Observing System', 'oceanexpert_id': 23204, 'oceanexpert_parent_id': None}] | [{'role': None, 'type': 'creator', 'givenname': 'Mark', 'surname': 'Carr', 'organization': 'UC Santa Cruz ', 'position': None, 'email': 'mhcarr@ucsc.edu', 'url': None, 'organization_oceanexpert_id': None, 'type_display': 'Creator'}, {'role': None, 'type': 'creator', 'givenname': 'Jennifer', 'surname': 'Caselle', 'organization': 'UC Santa Barbara', 'position': None, 'email': 'caselle@ucsb.edu',... | [{'id': 'b7c47783-a020-4173-b390-7b57c4fa1426', 'name': 'OBIS USA'}] | [{'keyword': 'Samplingevent', 'thesaurus': 'GBIF Dataset Type Vocabulary: http://rs.gbif.org/vocabulary/gbif/dataset_type_2015-07-10.xml'}, {'keyword': 'Biological data', 'thesaurus': 'California Ocean Protection Council'}, {'keyword': 'Kelp forest/shallow subtidal', 'thesaurus': 'California Ocean Protection Council'}, {'keyword': 'Rocky reef', 'thesaurus': 'California Ocean Protection Council... | [{'year': 2026, 'downloads': 10125, 'records': 1049977035}, {'year': 2025, 'downloads': 5012, 'records': 2332268600}] | [https://goosocean.org/eov/fish, https://goosocean.org/eov/invertebrate, https://goosocean.org/eov/macroalgal, https://ror.org/028paz341] | None | 5312401 | mlpa_kelp_forest_monitoring | mlpa_kelp_forest_monitoring |
| 1 | cfceb150-bbe2-4efb-8682-14cfc7167e7c | https://ipt-obis.gbif.us/resource?r=rcca_transects | https://ipt-obis.gbif.us/archive.do?r=rcca_transects | 2025-06-03T16:53:10.000Z | 2020-12-08T01:07:21.790Z | 2026-09-28T07:28:31.521Z | event | [measurementorfact, occurrence] | {'Event': 19375, 'absence': 487162, 'dropped': 0, 'Occurrence': 724871, 'DNADerivedData': 0, 'MeasurementOrFact': 255300} | POLYGON((-124.294724 32.694233000000004,-117.264999 32.694233000000004,-117.264999 42.045155,-124.294724 42.045155,-124.294724 32.694233000000004)) | ... | [{'name': 'U.S. Geological Survey HQ', 'oceanexpert_id': 12976, 'oceanexpert_parent_id': None}, {'name': 'United States Marine Biodiversity Observation Network', 'oceanexpert_id': 23070, 'oceanexpert_parent_id': None}, {'name': 'Central & Northern California Ocean Observing System', 'oceanexpert_id': 23204, 'oceanexpert_parent_id': None}, {'name': 'Reef Check Foundation', 'oceanexpert_id': 232... | [{'role': None, 'type': 'creator', 'givenname': 'Jan', 'surname': 'Friewald', 'organization': 'Reef Check Foundation', 'position': 'Executive Director', 'email': 'jfreiwald@reefcheck.org', 'url': None, 'organization_oceanexpert_id': 23205, 'type_display': 'Creator'}, {'role': None, 'type': 'contact', 'givenname': 'Jan', 'surname': 'Friewald', 'organization': 'Reef Check Foundation', 'position'... | [{'id': 'b7c47783-a020-4173-b390-7b57c4fa1426', 'name': 'OBIS USA'}] | [{'keyword': 'Samplingevent', 'thesaurus': 'GBIF Dataset Type Vocabulary: http://rs.gbif.org/vocabulary/gbif/dataset_type_2015-07-10.xml'}] | [{'year': 2026, 'downloads': 3475, 'records': 54337895}, {'year': 2025, 'downloads': 5478, 'records': 553557433}, {'year': 2024, 'downloads': 5514, 'records': 767206545}, {'year': 2023, 'downloads': 3648, 'records': 588494102}, {'year': 2022, 'downloads': 4191, 'records': 426899064}, {'year': 2021, 'downloads': 13776, 'records': 497036713}, {'year': 2020, 'downloads': 747, 'records': 26199396}] | [https://goosocean.org/eov/fish, https://goosocean.org/eov/invertebrate, https://goosocean.org/eov/macroalgal, https://ror.org/028paz341, https://rs.obis.org/obis/vocabulary/DatasetType/CitizenScience] | None | 724871 | rcca_transects | rcca_transects |
| 2 | fbf4cb59-cef3-43be-b0ab-0dbab5f51740 | https://ipt-obis.gbif.us/resource?r=marine_ltm_photoplot_surveys | https://ipt-obis.gbif.us/archive.do?r=marine_ltm_photoplot_surveys | 2025-06-11T19:34:34.000Z | 2021-09-28T13:21:46.553Z | 2026-09-27T12:12:28.016Z | occurrence | [measurementorfact] | {'Event': 0, 'absence': 148598, 'dropped': 24793, 'Occurrence': 209528, 'DNADerivedData': 0, 'MeasurementOrFact': 628584} | POLYGON((-135.37755 33.38499069,-117.71475 33.38499069,-117.71475 57.04869843,-135.37755 57.04869843,-135.37755 33.38499069)) | ... | [{'name': 'University of California, Marine Science Institute , Santa Barbara', 'oceanexpert_id': 7227, 'oceanexpert_parent_id': None}, {'name': 'University of California, Santa Barbara', 'oceanexpert_id': 16187, 'oceanexpert_parent_id': None}, {'name': 'Univserity of California, Santa Cruz', 'oceanexpert_id': 17545, 'oceanexpert_parent_id': None}, {'name': 'University of California Los Angele... | [{'role': None, 'type': 'creator', 'givenname': None, 'surname': None, 'organization': 'Multi-Agency Rocky Intertidal Network (MARINe)', 'position': None, 'email': None, 'url': 'https://pacificrockyintertidal.org', 'organization_oceanexpert_id': None, 'type_display': 'Creator'}, {'role': None, 'type': 'creator', 'givenname': None, 'surname': None, 'organization': 'Partnership for Interdiscipli... | [{'id': 'b7c47783-a020-4173-b390-7b57c4fa1426', 'name': 'OBIS USA'}] | [{'keyword': 'Ocean', 'thesaurus': 'Global Change Master Directory'}, {'keyword': 'Intertidal Processes', 'thesaurus': 'Global Change Master Directory'}, {'keyword': 'Intertidal Zone', 'thesaurus': 'Global Change Master Directory'}, {'keyword': 'Marine Biology', 'thesaurus': 'Global Change Master Directory'}, {'keyword': 'Marine Invertebrates', 'thesaurus': 'Global Change Master Directory'}, {... | [{'year': 2026, 'downloads': 1680, 'records': 7338822}, {'year': 2025, 'downloads': 3436, 'records': 113726768}, {'year': 2024, 'downloads': 3565, 'records': 175165447}, {'year': 2023, 'downloads': 2830, 'records': 130576843}, {'year': 2022, 'downloads': 2621, 'records': 92885821}, {'year': 2021, 'downloads': 1173, 'records': 26122975}] | [https://goosocean.org/eov/invertebrate, https://goosocean.org/eov/macroalgal, https://ror.org/028paz341] | None | 184735 | marine_ltm_photoplot_surveys | marine_ltm_photoplot_surveys |
| 3 | bb638343-bbc9-464d-88d8-18d9ba29ecdd | https://ipt-obis.gbif.us/resource?r=marine_cbs_point_contact_surveys | https://ipt-obis.gbif.us/archive.do?r=marine_cbs_point_contact_surveys | 2025-09-08T16:00:04.000Z | 2023-05-02T17:01:15.060Z | 2026-09-27T12:21:29.087Z | event | [measurementorfact, occurrence] | {'Event': 429, 'absence': 91528, 'dropped': 1716, 'Occurrence': 111111, 'DNADerivedData': 0, 'MeasurementOrFact': 111111} | POLYGON((-136.73148 26.70548058,-113.5763 26.70548058,-113.5763 58.27082062,-136.73148 58.27082062,-136.73148 26.70548058)) | ... | [{'name': 'Univserity of California, Santa Cruz', 'oceanexpert_id': 17545, 'oceanexpert_parent_id': None}, {'name': 'United States Marine Biodiversity Observation Network', 'oceanexpert_id': 23070, 'oceanexpert_parent_id': None}, {'name': 'Central & Northern California Ocean Observing System', 'oceanexpert_id': 23204, 'oceanexpert_parent_id': None}] | [{'role': None, 'type': 'creator', 'givenname': 'Pete', 'surname': 'Raimondi', 'organization': 'Univeristy of California, Santa Cruz', 'position': None, 'email': 'raimondi@ucsc.edu', 'url': None, 'organization_oceanexpert_id': 17545, 'type_display': 'Creator'}, {'role': None, 'type': 'contact', 'givenname': None, 'surname': None, 'organization': 'Multi-Agency Rocky Intertidal Network (MARINe)'... | [{'id': 'b7c47783-a020-4173-b390-7b57c4fa1426', 'name': 'OBIS USA'}] | [{'keyword': 'Ocean', 'thesaurus': 'Global Change Master Directory'}, {'keyword': 'Intertidal Processes', 'thesaurus': 'Global Change Master Directory'}, {'keyword': 'Intertidal Zone', 'thesaurus': 'Global Change Master Directory'}, {'keyword': 'Marine Biology', 'thesaurus': 'Global Change Master Directory'}, {'keyword': 'Marine Invertebrates', 'thesaurus': 'Global Change Master Directory'}, {... | [{'year': 2026, 'downloads': 4296, 'records': 3464087}, {'year': 2025, 'downloads': 5479, 'records': 43235336}, {'year': 2024, 'downloads': 5621, 'records': 62198910}, {'year': 2023, 'downloads': 2444, 'records': 30346241}] | [https://goosocean.org/eov/invertebrate, https://goosocean.org/eov/macroalgal, https://ror.org/028paz341] | None | 109395 | marine_cbs_point_contact_surveys | marine_cbs_point_contact_surveys |
| 4 | d50e0443-4d84-4bd9-a5ad-4d417a7607e2 | https://ipt-obis.gbif.us/resource?r=2009floridakeysrvc | https://ipt-obis.gbif.us/archive.do?r=2009floridakeysrvc | 2025-06-03T16:55:55.000Z | NaN | 2025-11-12T11:47:36.075Z | event | [measurementorfact, occurrence] | {'Event': 2542, 'absence': 353964, 'dropped': 0, 'Occurrence': 451581, 'DNADerivedData': 0, 'MeasurementOrFact': 905704} | POLYGON((-81.9975333 24.4313,-80.08755 24.4313,-80.08755 25.7526,-81.9975333 25.7526,-81.9975333 24.4313)) | ... | [{'name': 'NOAA, Southeast Fisheries Science Center, National Marine Fisheries Service Southeast Miami Laboratory', 'oceanexpert_id': 7534, 'oceanexpert_parent_id': 11379}, {'name': 'Rosenstiel School of Marine & Atmospheric Science, University of Miami', 'oceanexpert_id': 8124, 'oceanexpert_parent_id': None}, {'name': 'U.S. Geological Survey HQ', 'oceanexpert_id': 12976, 'oceanexpert_parent_i... | [{'role': None, 'type': 'creator', 'givenname': 'Jerald', 'surname': 'Ault', 'organization': 'University of Miami, Rosenstiel School of Marine & Atmospheric Science', 'position': 'Program Lead', 'email': 'jault@rsmas.miami.edu', 'url': None, 'organization_oceanexpert_id': 8124, 'type_display': 'Creator'}, {'role': None, 'type': 'creator', 'givenname': 'Jim', 'surname': 'Bohnsack', 'organiz... | [{'id': 'b7c47783-a020-4173-b390-7b57c4fa1426', 'name': 'OBIS USA'}] | [{'keyword': 'Samplingevent', 'thesaurus': 'GBIF Dataset Type Vocabulary: http://rs.gbif.org/vocabulary/gbif/dataset_type.xml'}, {'keyword': 'Samplingevent', 'thesaurus': 'GBIF Dataset Type Vocabulary: http://rs.gbif.org/vocabulary/gbif/dataset_type_2015-07-10.xml'}] | [{'year': 2026, 'downloads': 12848, 'records': 22931680}, {'year': 2025, 'downloads': 17979, 'records': 227140705}, {'year': 2024, 'downloads': 15632, 'records': 317641880}, {'year': 2023, 'downloads': 7679, 'records': 242648438}, {'year': 2022, 'downloads': 7478, 'records': 175113790}, {'year': 2021, 'downloads': 17549, 'records': 204507663}, {'year': 2020, 'downloads': 3443, 'records': 68153... | None | None | 97617 | 2009floridakeysrvc | floridakeysrvc |
5 rows × 26 columns
df_meta[["short_name", "short_name_group"]].describe()
| short_name | short_name_group | |
|---|---|---|
| count | 344 | 344 |
| unique | 344 | 41 |
| top | mlpa_kelp_forest_monitoring | atn__ribbon-seal_trajectory_- |
| freq | 1 | 147 |
Read to geopandas and as a geojson#
Since we have the data, why don’t we write this out to a geoJson file to interact with on GitHub.
from datetime import datetime
import geopandas
gdf = geopandas.GeoDataFrame(
df_meta, geometry=geopandas.GeoSeries.from_wkt(df_meta["extent"])
)
gdf.set_crs("epsg:4326", inplace=True)
today = datetime.now().strftime("%Y-%m-%d")
fname = f"US_MBON_bounding_boxes_{today.replace('-','')}.geojson"
# can"t export fields with lists or dictionaries - breaks the format.
gdf[["id", "title", "url", "geometry"]].to_file(fname, driver="GeoJSON")
Query the OBIS aggregated points#
Now that we know the general area for where our observations are made, let’s dig a little deeper into each dataset.
We can use pyobis.occurrences.getpoints() to return all of the points for each dataset, aggregated to Geohash precision 8.
If we’d like a little more metadata about each of the aggregated points, we can query by dataset to collect the aggregated points and some useful metadata.
combined = pd.DataFrame()
query = pyobis.dataset.search(instituteid=institution_id)
df = pd.DataFrame(query.execute())
df_meta = pd.DataFrame.from_records(df["results"])
for datasetid in df_meta["id"]:
dset = pyobis.occurrences.getpoints(datasetid=datasetid).execute()
meta = pyobis.dataset.get(id=datasetid).execute()["results"][0]
short_name = meta["url"].split("=")[-1]
df = pd.DataFrame(dset)
df["dataset_id"] = datasetid
df["short_name"] = meta["url"].split("=")[-1]
df["short_name_group"] = df["short_name"].replace(r"\d", "", regex=True)
df["url"] = meta["url"]
df["metadata"] = str(meta)
df[["decimalLongitude", "decimalLatitude"]] = pd.DataFrame(
df["coordinates"].tolist()
)
combined = pd.concat([combined, df], ignore_index=True)
gdf_ghsh = geopandas.GeoDataFrame(
combined[
["dataset_id", "url", "short_name_group", "decimalLatitude", "decimalLongitude"]
],
geometry=geopandas.points_from_xy(
combined.decimalLongitude, combined.decimalLatitude
),
crs="epsg:4326",
)
import cartopy.io.shapereader as shpreader
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(20, 15))
gdf_ghsh.plot(
ax=ax,
zorder=10,
alpha=0.5,
column="short_name_group",
legend=True,
)
shpfilename = shpreader.natural_earth(
resolution="110m",
category="cultural",
name="admin_0_countries",
)
world = geopandas.read_file(shpfilename)
world = world.to_crs(gdf.crs)
# bound map to US.
world.loc[world["ISO_A3"] == "USA"].plot(
ax=ax, color="lightgrey", edgecolor="black", zorder=1
)
now = datetime.now()
today = now.strftime("%Y-%m-%d")
ax.set_title(
f"Total aggregated occurrence records contributed to OBIS from the US MBON network as of {today}: {gdf_ghsh.shape[0]}"
)
ax.set_axis_off();
It is visualize all those data points and make sense of them. So let’s return all occurences and build a histogram for the data occurrences.
Use pyobis to return all occurrences.#
If you are really interested in all of the records, the code below exemplifies how one might use pyobis to iterate through each dataset and collect only the coordinates to create a map.
Note: This takes a while to run!
First, let’s get all the occurrences as one huge DataFrame.
from pathlib import Path
fname = "combined_high_res.zip"
if Path(fname).exists():
print(
f"Loading cached {fname}. Remove this file if you want to fetch all the points again."
)
combined_high_res = pd.read_csv(fname, compression="zip")
else:
combined_high_res = pd.DataFrame()
query = pyobis.dataset.search(instituteid=institution_id)
df = pd.DataFrame(query.execute())
df_meta = pd.DataFrame.from_records(df["results"])
fields = ["decimalLatitude", "decimalLongitude", "id"]
for datasetid in df_meta["id"]:
query = pyobis.occurrences.search(datasetid=datasetid, fields=fields)
dset = query.execute()
df = pd.DataFrame(dset)
combined_high_res = pd.concat([combined_high_res, df], ignore_index=True)
combined_high_res[["decimalLongitude", "decimalLatitude"]].to_csv(
fname, index=False, compression="zip"
)
Loading cached combined_high_res.zip. Remove this file if you want to fetch all the points again.
Next, let’s convert that DataFrame to a GeoDataFrame and plot the points on a map.
gdf_high_res = geopandas.GeoDataFrame(
combined_high_res[["decimalLatitude", "decimalLongitude"]],
geometry=geopandas.points_from_xy(
combined_high_res.decimalLongitude, combined_high_res.decimalLatitude
),
crs="epsg:4326",
)
fig, ax = plt.subplots(figsize=(20, 15))
gdf_high_res.plot.scatter(
x="decimalLongitude",
y="decimalLatitude",
ax=ax,
s=5,
zorder=10,
rasterized=True,
)
world.loc[world["ISO_A3"] == "USA"].plot(
ax=ax, color="lightgrey", edgecolor="white", zorder=1
)
now = datetime.now()
today = now.strftime("%Y-%m-%d")
ax.set_title(
f"Total occurrence records contributed to OBIS from the US MBON network as of {today}: {gdf_high_res.shape[0]}"
)
ax.set_axis_off();
Let’s make some histograms of occurrence record distributions across latitude and longitude#
First, we need to create the bins by which we will calculate the number of occurrences. For this example we will use 1 degree bins.
bins_lat = [i for i in range(-90, 90, 1)]
bins_lon = [i for i in range(-180, 180, 1)]
fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(16, 8))
gdf_high_res.hist(
column="decimalLatitude", bins=bins_lat, orientation="horizontal", ax=ax[0]
)
gdf_high_res.hist(
column="decimalLongitude", bins=bins_lon, orientation="vertical", ax=ax[1]
)
array([<Axes: title={'center': 'decimalLongitude'}>], dtype=object)
Create a grid of observations#
1 degree x 1 degree
import numpy as np
import shapely
# total area for the grid
xmin, ymin, xmax, ymax = [-180.0, -90.0, 180.0, 90]
# how many cells across and down
n_cells = 360
# calculate the size of the cells
cell_size = (xmax - xmin) / n_cells
# create an evenly distributed GeoDataFrame for the grid described above
grid_cells = []
for x0 in np.arange(xmin, xmax + cell_size, cell_size):
for y0 in np.arange(ymin, ymax + cell_size, cell_size):
# bounds
x1 = x0 - cell_size
y1 = y0 + cell_size
grid_cells.append(shapely.geometry.box(x0, y0, x1, y1))
cell = geopandas.GeoDataFrame(grid_cells, columns=["geometry"], crs="EPSG:4326")
# spatial join the OBIS data with the one-degree cells for where OBIS data are within cells.
merged = geopandas.sjoin(gdf_high_res, cell, how="left", predicate="within")
# make a simple count variable that we can sum for each cell
merged["n_occur"] = 1
# Compute stats per grid cell -- aggregate occurences to grid cells with dissolve
dissolve = merged.dissolve(by="index_right", aggfunc="count")
# put this into cell
cell.loc[dissolve.index, "n_occur"] = dissolve.n_occur.values
# calculate occurrence density in km^2 for each cell.
cell["area (km2)"] = cell.to_crs(epsg=3763).area / (1000**2)
cell.plot(column="n_occur", legend=True);
/home/filipe/micromamba/envs/IOOS/lib/python3.14/site-packages/shapely/measurement.py:50: RuntimeWarning: invalid value encountered in area
return lib.area(geometry, **kwargs)
Next, let’s check out the distribution of the values for n_occur so we can make an appropriatly legible map of the distribution.
cell.describe()
/home/filipe/micromamba/envs/IOOS/lib/python3.14/site-packages/pandas/core/nanops.py:1028: RuntimeWarning: invalid value encountered in subtract
sqr = _ensure_numeric((avg - values) ** 2)
| n_occur | area (km2) | |
|---|---|---|
| count | 80.000000 | 6.449600e+04 |
| mean | 25000.212500 | inf |
| std | 84580.460311 | NaN |
| min | 10.000000 | 1.088584e+02 |
| 25% | 156.500000 | 5.065842e+03 |
| 50% | 594.000000 | 1.071955e+04 |
| 75% | 2487.500000 | 1.742916e+04 |
| max | 477661.000000 | inf |
Since the distribution of n_occur covers a wide range, we will present the concentration and histogram figures as log normal distributions.
So, let’s combine the latitude and longitude histograms with the map to recreate this figure https://bbest.github.io/obis-lat-time-fig.
import geoplot
import geoplot.crs as gcrs
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
norm = mpl.colors.LogNorm(vmin=cell["n_occur"].min(), vmax=cell["n_occur"].max())
projection = gcrs.PlateCarree(central_longitude=-105)
world = world.dissolve()
fig, ax = plt.subplots(
figsize=(16, 8), facecolor="white", subplot_kw={"projection": projection}
)
geoplot.polyplot(
world,
facecolor="lightgray",
edgecolor=None,
projection=projection,
ax=ax,
)
cax = fig.add_axes([0.225, 0.08, 0.575, 0.03])
cell = cell[~cell["n_occur"].isna()]
geoplot.choropleth(
cell,
hue="n_occur",
edgecolor=None,
projection=projection,
ax=ax,
cmap="Spectral_r",
legend=True,
legend_kwargs={
"orientation": "horizontal",
"cax": cax,
"label": "occurrence record log(count)",
},
norm=norm,
)
gl = ax.gridlines(draw_labels=True)
gl.ylocator = mticker.FixedLocator(range(0, 90, 5))
gl.xlocator = mticker.FixedLocator(range(-170, -40, 10))
# Add histogram for distribution
ax2 = fig.add_axes([0.95, 0.090, 0.05, 0.8])
bins_lat = [i for i in range(20, 77, 1)]
gdf_high_res.hist(
column="decimalLatitude", bins=bins_lat, orientation="horizontal", ax=ax2, log=True
)
ax3 = fig.add_axes([0.09, 1, 0.825, 0.08])
bins_lon = [i for i in range(-179, -61, 1)]
gdf_high_res.hist(
column="decimalLongitude", bins=bins_lon, orientation="vertical", ax=ax3, log=True
);