Carbon analysis¶
In this notebook, we will show a use case of visualizing carbon emissions and metrics for a fleet.¶
We will choose our fleet as a list of vessels, get their historical voyage emissions using Signal Ocean's Vessel Emissions APIn and plot CO2 emissions and CII for the previous and current year.¶
Setup¶
Install the Signal Ocean SDK:
pip install signal-ocean
Set your subscription key acquired here: https://apis.signalocean.com/profile
In [1]:
Copied!
!pip install signal-ocean
!pip install signal-ocean
Collecting signal-ocean Downloading signal_ocean-13.3.0-py3-none-any.whl.metadata (2.2 kB) Requirement already satisfied: requests<3,>=2.23.0 in /usr/local/lib/python3.11/dist-packages (from signal-ocean) (2.32.3) Requirement already satisfied: python-dateutil<3,>=2.8.1 in /usr/local/lib/python3.11/dist-packages (from signal-ocean) (2.8.2) Requirement already satisfied: pandas<3,>=1.0.3 in /usr/local/lib/python3.11/dist-packages (from signal-ocean) (2.2.2) Requirement already satisfied: numpy>=1.18.5 in /usr/local/lib/python3.11/dist-packages (from signal-ocean) (1.26.4) Collecting strictly-typed-pandas==0.1.4 (from signal-ocean) Downloading strictly_typed_pandas-0.1.4-py3-none-any.whl.metadata (3.0 kB) Collecting typeguard<3.0.0,>=2.13.3 (from signal-ocean) Downloading typeguard-2.13.3-py3-none-any.whl.metadata (3.6 kB) Requirement already satisfied: pandas-stubs in /usr/local/lib/python3.11/dist-packages (from strictly-typed-pandas==0.1.4->signal-ocean) (2.2.2.240909) Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.11/dist-packages (from pandas<3,>=1.0.3->signal-ocean) (2025.1) Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.11/dist-packages (from pandas<3,>=1.0.3->signal-ocean) (2025.1) Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.11/dist-packages (from python-dateutil<3,>=2.8.1->signal-ocean) (1.17.0) Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.11/dist-packages (from requests<3,>=2.23.0->signal-ocean) (3.4.1) Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.11/dist-packages (from requests<3,>=2.23.0->signal-ocean) (3.10) Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.11/dist-packages (from requests<3,>=2.23.0->signal-ocean) (2.3.0) Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.11/dist-packages (from requests<3,>=2.23.0->signal-ocean) (2025.1.31) Requirement already satisfied: types-pytz>=2022.1.1 in /usr/local/lib/python3.11/dist-packages (from pandas-stubs->strictly-typed-pandas==0.1.4->signal-ocean) (2025.1.0.20250204) Downloading signal_ocean-13.3.0-py3-none-any.whl (155 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 155.6/155.6 kB 2.7 MB/s eta 0:00:00 Downloading strictly_typed_pandas-0.1.4-py3-none-any.whl (9.6 kB) Downloading typeguard-2.13.3-py3-none-any.whl (17 kB) Installing collected packages: typeguard, strictly-typed-pandas, signal-ocean Attempting uninstall: typeguard Found existing installation: typeguard 4.4.2 Uninstalling typeguard-4.4.2: Successfully uninstalled typeguard-4.4.2 ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts. inflect 7.5.0 requires typeguard>=4.0.1, but you have typeguard 2.13.3 which is incompatible. Successfully installed signal-ocean-13.3.0 strictly-typed-pandas-0.1.4 typeguard-2.13.3
Initialize Vessel Emissions API¶
In [2]:
Copied!
signal_ocean_api_key = '' #replace with your subscription key
from signal_ocean import Connection
from signal_ocean.vessel_emissions import VesselEmissionsAPI
connection = Connection(signal_ocean_api_key)
emissions_api = VesselEmissionsAPI(connection)
signal_ocean_api_key = '' #replace with your subscription key
from signal_ocean import Connection
from signal_ocean.vessel_emissions import VesselEmissionsAPI
connection = Connection(signal_ocean_api_key)
emissions_api = VesselEmissionsAPI(connection)
Requirement already satisfied: signal-ocean in /usr/local/lib/python3.11/dist-packages (13.3.0) Requirement already satisfied: requests<3,>=2.23.0 in /usr/local/lib/python3.11/dist-packages (from signal-ocean) (2.32.3) Requirement already satisfied: python-dateutil<3,>=2.8.1 in /usr/local/lib/python3.11/dist-packages (from signal-ocean) (2.8.2) Requirement already satisfied: pandas<3,>=1.0.3 in /usr/local/lib/python3.11/dist-packages (from signal-ocean) (2.2.2) Requirement already satisfied: numpy>=1.18.5 in /usr/local/lib/python3.11/dist-packages (from signal-ocean) (1.26.4) Requirement already satisfied: strictly-typed-pandas==0.1.4 in /usr/local/lib/python3.11/dist-packages (from signal-ocean) (0.1.4) Requirement already satisfied: typeguard<3.0.0,>=2.13.3 in /usr/local/lib/python3.11/dist-packages (from signal-ocean) (2.13.3) Requirement already satisfied: pandas-stubs in /usr/local/lib/python3.11/dist-packages (from strictly-typed-pandas==0.1.4->signal-ocean) (2.2.2.240909) Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.11/dist-packages (from pandas<3,>=1.0.3->signal-ocean) (2025.1) Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.11/dist-packages (from pandas<3,>=1.0.3->signal-ocean) (2025.1) Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.11/dist-packages (from python-dateutil<3,>=2.8.1->signal-ocean) (1.17.0) Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.11/dist-packages (from requests<3,>=2.23.0->signal-ocean) (3.4.1) Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.11/dist-packages (from requests<3,>=2.23.0->signal-ocean) (3.10) Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.11/dist-packages (from requests<3,>=2.23.0->signal-ocean) (2.3.0) Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.11/dist-packages (from requests<3,>=2.23.0->signal-ocean) (2025.1.31) Requirement already satisfied: types-pytz>=2022.1.1 in /usr/local/lib/python3.11/dist-packages (from pandas-stubs->strictly-typed-pandas==0.1.4->signal-ocean) (2025.1.0.20250204)
Import helpful modules¶
In [3]:
Copied!
from datetime import date, timedelta
from tqdm import tqdm
import requests
import json
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib.collections import PolyCollection
import numpy as np
sns.set_theme()
sns.set_style("whitegrid")
from datetime import date, timedelta
from tqdm import tqdm
import requests
import json
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib.collections import PolyCollection
import numpy as np
sns.set_theme()
sns.set_style("whitegrid")
Input Data¶
Choose a fleet as list of IMOs.¶
In [4]:
Copied!
vessel_imos = [ 9299111, 9314193, 9411197, 9425526, 9440473, 9459096, 9461659, 9462354, 9486922, 9486934, 9487469, 9487471, 9487483, 9505819,
9524982, 9543536, 9580405, 9688336, 9688348, 9723007, 9726619, 9767340, 9773947, 9780251, 9783992, 9833723 ]
vessel_imos = [ 9299111, 9314193, 9411197, 9425526, 9440473, 9459096, 9461659, 9462354, 9486922, 9486934, 9487469, 9487471, 9487483, 9505819,
9524982, 9543536, 9580405, 9688336, 9688348, 9723007, 9726619, 9767340, 9773947, 9780251, 9783992, 9833723 ]
Use the emissions api to get historical emissions and metrics data for the list of selected vessels, including EU emissions. We will store the data as pandas dataframe and keep only data from 2023 and on.¶
In [5]:
Copied!
emissions_list = []
metrics_list = []
for imo in tqdm(vessel_imos):
vessel_emissions = emissions_api.get_emissions_by_imo(imo)
vessel_emissions = [emissions.to_dict() for emissions in vessel_emissions]
emissions_list.extend(vessel_emissions)
vessel_metrics = emissions_api.get_metrics_by_imo(imo)
vessel_metrics = [metrics.to_dict() for metrics in vessel_metrics]
metrics_list.extend(vessel_metrics)
emissions_df = pd.json_normalize(emissions_list, sep='')
emissions_df = emissions_df[emissions_df['StartDate'] >= '2023']
metrics_df = pd.json_normalize(metrics_list, sep='')
metrics_df = metrics_df[metrics_df['Year'] >= 2023]
emissions_list = []
metrics_list = []
for imo in tqdm(vessel_imos):
vessel_emissions = emissions_api.get_emissions_by_imo(imo)
vessel_emissions = [emissions.to_dict() for emissions in vessel_emissions]
emissions_list.extend(vessel_emissions)
vessel_metrics = emissions_api.get_metrics_by_imo(imo)
vessel_metrics = [metrics.to_dict() for metrics in vessel_metrics]
metrics_list.extend(vessel_metrics)
emissions_df = pd.json_normalize(emissions_list, sep='')
emissions_df = emissions_df[emissions_df['StartDate'] >= '2023']
metrics_df = pd.json_normalize(metrics_list, sep='')
metrics_df = metrics_df[metrics_df['Year'] >= 2023]
100%|██████████| 26/26 [00:55<00:00, 2.13s/it]
In [6]:
Copied!
emissions_df.groupby("VesselClass")['EmissionsVoyageCO2InTons'].mean()
emissions_df.groupby("VesselClass")['EmissionsVoyageCO2InTons'].mean()
Out[6]:
EmissionsVoyageCO2InTons | |
---|---|
VesselClass | |
Aframax | 1432.394487 |
MR1 | 719.735044 |
MR2 | 605.297518 |
Panamax | 850.613621 |
Suezmax | 1671.113432 |
In the next plots we see total CO2 emissions per vessel for the previous and current year, and for 2023 and 2024 separately.¶
In [7]:
Copied!
emissions_per_vessel = emissions_df.groupby("VesselName")['EmissionsVoyageCO2InTons'].mean().reset_index()
plt.figure(figsize=(16,8))
emissions_per_vessel.sort_values('EmissionsVoyageCO2InTons', inplace=True)
sns.barplot(x='VesselName', y='EmissionsVoyageCO2InTons', data=emissions_per_vessel)
plt.xticks(rotation=90)
plt.title('CO2 Emitted per Vessel', fontsize=14)
plt.show()
emissions_per_vessel = emissions_df.groupby("VesselName")['EmissionsVoyageCO2InTons'].mean().reset_index()
plt.figure(figsize=(16,8))
emissions_per_vessel.sort_values('EmissionsVoyageCO2InTons', inplace=True)
sns.barplot(x='VesselName', y='EmissionsVoyageCO2InTons', data=emissions_per_vessel)
plt.xticks(rotation=90)
plt.title('CO2 Emitted per Vessel', fontsize=14)
plt.show()
In [8]:
Copied!
emissions_per_vessel_2023 = emissions_df[emissions_df['EndDate'] < "2024"].groupby("VesselName")['EmissionsVoyageCO2InTons'].mean().reset_index()
plt.figure(figsize=(16,8))
emissions_per_vessel_2023.sort_values('EmissionsVoyageCO2InTons', inplace=True)
sns.barplot(x='VesselName', y='EmissionsVoyageCO2InTons', data=emissions_per_vessel_2023)
plt.xticks(rotation=90)
plt.title('CO2 Emitted per Vessel (2023)', fontsize=14)
plt.show()
emissions_per_vessel_2023 = emissions_df[emissions_df['EndDate'] < "2024"].groupby("VesselName")['EmissionsVoyageCO2InTons'].mean().reset_index()
plt.figure(figsize=(16,8))
emissions_per_vessel_2023.sort_values('EmissionsVoyageCO2InTons', inplace=True)
sns.barplot(x='VesselName', y='EmissionsVoyageCO2InTons', data=emissions_per_vessel_2023)
plt.xticks(rotation=90)
plt.title('CO2 Emitted per Vessel (2023)', fontsize=14)
plt.show()
In [9]:
Copied!
emissions_per_vessel_2024 = emissions_df[emissions_df['EndDate'] >= "2024"].groupby("VesselName")['EmissionsVoyageCO2InTons'].mean().reset_index()
plt.figure(figsize=(16,8))
emissions_per_vessel_2024.sort_values('EmissionsVoyageCO2InTons', inplace=True)
sns.barplot(x='VesselName', y='EmissionsVoyageCO2InTons', data=emissions_per_vessel_2024)
plt.xticks(rotation=90)
plt.title('CO2 Emitted per Vessel (2024)', fontsize=14)
plt.show()
emissions_per_vessel_2024 = emissions_df[emissions_df['EndDate'] >= "2024"].groupby("VesselName")['EmissionsVoyageCO2InTons'].mean().reset_index()
plt.figure(figsize=(16,8))
emissions_per_vessel_2024.sort_values('EmissionsVoyageCO2InTons', inplace=True)
sns.barplot(x='VesselName', y='EmissionsVoyageCO2InTons', data=emissions_per_vessel_2024)
plt.xticks(rotation=90)
plt.title('CO2 Emitted per Vessel (2024)', fontsize=14)
plt.show()
We can also color our plot with vessel class.¶
In [10]:
Copied!
emissions_per_class = emissions_df.groupby("VesselName").agg(
CO2_Emissions=("EmissionsVoyageCO2InTons", "mean"), Number_of_Voyages=("VoyageNumber","size"), VesselClass=("VesselClass", "first")).reset_index()
emissions_per_class = emissions_df.groupby("VesselName").agg(
CO2_Emissions=("EmissionsVoyageCO2InTons", "mean"), Number_of_Voyages=("VoyageNumber","size"), VesselClass=("VesselClass", "first")).reset_index()
In [11]:
Copied!
plt.figure(figsize=(16,8))
emissions_per_class.sort_values('CO2_Emissions', inplace=True)
ax = sns.barplot(x='VesselName', y='CO2_Emissions', hue='VesselClass', data=emissions_per_class)
# ax.bar_label(ax.containers[0], labels=emissions_per_vessel_2023['Number_of_Voyages'])
plt.xticks(rotation=90)
plt.title('CO2 Emitted per Vessel Class', fontsize=14)
plt.show()
plt.figure(figsize=(16,8))
emissions_per_class.sort_values('CO2_Emissions', inplace=True)
ax = sns.barplot(x='VesselName', y='CO2_Emissions', hue='VesselClass', data=emissions_per_class)
# ax.bar_label(ax.containers[0], labels=emissions_per_vessel_2023['Number_of_Voyages'])
plt.xticks(rotation=90)
plt.title('CO2 Emitted per Vessel Class', fontsize=14)
plt.show()
We repeat for the metrics data, using CII.¶
In [12]:
Copied!
metrics_2023 = metrics_df[metrics_df['Year']==2023].copy()
plt.figure(figsize=(16,8))
metrics_2023.sort_values('CiiValue', inplace=True)
ax = sns.barplot(x='IMO', y='CiiValue', hue='VesselClass', data=metrics_2023, order=metrics_2023.IMO)
plt.xticks(rotation=90)
plt.title('Cii per Vessel (2023)', fontsize=14)
plt.show()
metrics_2023 = metrics_df[metrics_df['Year']==2023].copy()
plt.figure(figsize=(16,8))
metrics_2023.sort_values('CiiValue', inplace=True)
ax = sns.barplot(x='IMO', y='CiiValue', hue='VesselClass', data=metrics_2023, order=metrics_2023.IMO)
plt.xticks(rotation=90)
plt.title('Cii per Vessel (2023)', fontsize=14)
plt.show()
In [13]:
Copied!
metrics_2024 = metrics_df[metrics_df['Year']==2024].copy()
plt.figure(figsize=(16,8))
metrics_2024.sort_values('CiiValue', inplace=True)
ax = sns.barplot(x='IMO', y='CiiValue', hue='VesselClass', data=metrics_2024, order=metrics_2024.IMO)
plt.xticks(rotation=90)
plt.title('Cii per Vessel (2024)', fontsize=14)
plt.show()
metrics_2024 = metrics_df[metrics_df['Year']==2024].copy()
plt.figure(figsize=(16,8))
metrics_2024.sort_values('CiiValue', inplace=True)
ax = sns.barplot(x='IMO', y='CiiValue', hue='VesselClass', data=metrics_2024, order=metrics_2024.IMO)
plt.xticks(rotation=90)
plt.title('Cii per Vessel (2024)', fontsize=14)
plt.show()