Kobe plot: North Atlantic swordfish stock status 1970-2020
Fisheries
Example from the compendium of canonical charts
Python Code
"""Fisheries — Kobe plot: North Atlantic swordfish stock status 1970-2020."""
from pathlib import Path
# ── Palette + theme (matching the Plotly Studio gallery these charts ship in) ──
VIOLET, TEAL, GREEN, PINK, ORANGE = "#845EEE", "#52B3D0", "#55B685", "#DA5597", "#E9A23B"
PRIMARY, SECONDARY = VIOLET, TEAL
COLORWAY = [VIOLET, TEAL, GREEN, PINK, ORANGE]
BG, TEXT, GRID, MUTED = "#ffffff", "#1c2024", "#d9d9e0", "#60646c"
FONT = "Inter, -apple-system, BlinkMacSystemFont, sans-serif"
COLORSCALE = [[0, "rgba(132, 94, 238, 0.05)"], [1, "rgba(132, 94, 238, 0.9)"]]
def apply_theme(fig):
"""Light gallery theme: white background, Inter font, soft gridlines."""
fig.update_layout(
paper_bgcolor=BG, plot_bgcolor=BG, colorway=COLORWAY,
font=dict(family=FONT, color=TEXT, size=12),
legend=dict(font=dict(color=TEXT)),
hoverlabel=dict(bgcolor="#f0f0f3", font=dict(color=TEXT, family=FONT), bordercolor=GRID),
)
fig.update_xaxes(gridcolor=GRID, linecolor=GRID, zerolinecolor=GRID)
fig.update_yaxes(gridcolor=GRID, linecolor=GRID, zerolinecolor=GRID)
def fetch_csv(url, **kwargs):
import io
import pandas as pd
import requests
r = requests.get(url, timeout=60)
r.raise_for_status()
return pd.read_csv(io.StringIO(r.text), **kwargs)
def fetch_json(url):
import requests
r = requests.get(url, timeout=60)
r.raise_for_status()
return r.json()
import numpy as np
import plotly.graph_objects as go
def generate():
years = np.arange(1970, 2021)
n = len(years)
def make_trajectory(yr, vals):
return np.interp(years, yr, vals)
B_Bmsy = make_trajectory(
[1970, 1980, 1990, 2000, 2005, 2012, 2020],
[1.20, 0.95, 0.72, 0.60, 0.65, 0.85, 1.10],
)
U_Umsy = make_trajectory(
[1970, 1980, 1990, 1995, 2000, 2005, 2012, 2020],
[0.80, 1.05, 1.35, 1.60, 1.45, 1.20, 1.00, 0.88],
)
RNG = np.random.default_rng(17)
B_Bmsy += RNG.normal(0, 0.03, n)
U_Umsy += RNG.normal(0, 0.04, n)
fig = go.Figure()
fig.add_shape(type="rect", x0=1, x1=2.5, y0=0, y1=1,
fillcolor="rgba(85,182,133,0.20)", line_width=0, layer="below")
fig.add_shape(type="rect", x0=0, x1=1, y0=1, y1=2.5,
fillcolor="rgba(220,50,50,0.18)", line_width=0, layer="below")
fig.add_shape(type="rect", x0=1, x1=2.5, y0=1, y1=2.5,
fillcolor="rgba(233,162,59,0.18)", line_width=0, layer="below")
fig.add_shape(type="rect", x0=0, x1=1, y0=0, y1=1,
fillcolor="rgba(233,162,59,0.13)", line_width=0, layer="below")
fig.add_vline(x=1, line_dash="dash", line_color=MUTED, line_width=1.5)
fig.add_hline(y=1, line_dash="dash", line_color=MUTED, line_width=1.5)
label_kw = dict(showarrow=False, font=dict(size=12, color=MUTED))
fig.add_annotation(x=1.8, y=0.5, text="Sustainable", **label_kw)
fig.add_annotation(x=0.35, y=1.6, text="Overfished &<br>Overfishing", **label_kw)
fig.add_annotation(x=1.8, y=1.7, text="Overfishing", **label_kw)
fig.add_annotation(x=0.35, y=0.35, text="Recovering", **label_kw)
fig.add_trace(go.Scatter(
x=B_Bmsy,
y=U_Umsy,
mode="lines",
line=dict(color=MUTED, width=1.5, dash="dot"),
showlegend=False,
hoverinfo="skip",
))
fig.add_trace(go.Scatter(
x=B_Bmsy,
y=U_Umsy,
mode="markers",
marker=dict(
size=9,
color=years,
colorscale="Plasma",
line=dict(color="white", width=0.8),
showscale=False,
),
customdata=years,
hovertemplate="Year: %{customdata}<br>B/Bmsy: %{x:.2f}<br>U/Umsy: %{y:.2f}<extra></extra>",
showlegend=False,
))
fig.add_trace(go.Scatter(
x=[B_Bmsy[0], B_Bmsy[-1]],
y=[U_Umsy[0], U_Umsy[-1]],
mode="markers+text",
marker=dict(size=14, color=["#1f77b4", "#d62728"], symbol=["circle", "star"],
line=dict(color="white", width=1)),
text=["1970", "2020"],
textposition=["top right", "top right"],
showlegend=False,
hoverinfo="skip",
))
fig.update_layout(
xaxis=dict(title="B / B<sub>MSY</sub>", range=[0.1, 2.2], zeroline=False),
yaxis=dict(title="U / U<sub>MSY</sub>", range=[0.1, 2.0], zeroline=False),
showlegend=False,
margin=dict(t=20, b=60, l=70, r=20),
)
apply_theme(fig)
return fig
fig = generate()
fig.show()
Made with Plotly