Voyages Metrics - Top 3 Loading Locations, Discharge Locations, Cargo Types¶
Run this example in Colab.¶
APIs Used: Voyages
Description:
The goal of this example is to create key metrics using the Voyages API. We retrieve data for a specified time period and vessel class. After selecting a Starting Area, we generate charts showcasing the top 3 Loading Countries, Discharge Countries, and Cargo Types based on voyage data.
Output: Charts By loading Country, Discharge Country, Cargo Type
Setup¶
Install the Signal Ocean SDK:
pip install signal-ocean
Set your subscription key acquired here: https://apis.signalocean.com/profile
!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
signal_ocean_api_key = '' #replace with your subscription key
from signal_ocean import Connection
from signal_ocean.voyages import VoyagesAPI, VesselClass, VesselClassFilter
import pandas as pd
import pandas as pd
from datetime import date, timedelta
import matplotlib.pyplot as plt
connection = Connection(signal_ocean_api_key)
api = VoyagesAPI(connection)
We need to specify the following parameters for our queries:
startingArea - The Starting Area of our Voyages
vesselClass - the class of queried vessels.
dateFrom - The starting date of queried voyages.
startingArea = 'Arabian Gulf'
vesselClass = 'Aframax'
dateFrom = date(2024, 6, 1) # Define a specific date as the minimum Starting Voyage Date (e.g., "2024-01-01")
Declare helper functions
def get_voyage_start_area(voyage_events):
return next((e.area_name_level0 for e in voyage_events or [] if e.event_type =='VoyageStart' ), None)
def get_voyage_discharge_countries(voyage_events): # Returns a list of all discharge countries from voyage events.
return [e.country for e in reversed(voyage_events or []) if e.purpose == 'Discharge']
def get_voyage_load_countries(voyage_events): # Returns a list of all load countries from voyage events.
return [e.country for e in voyage_events or [] if e.purpose == 'Load']
Get voyages¶
vesselClass = api.get_vessel_classes(VesselClassFilter(vesselClass))
aframaxId = vesselClass[0].vessel_class_id
aframaxId
86
Calling Voyages API for the Vessel Class & the dates of our preference
#Calling the API for the Vessel Class and after a specifc Starting Voyage Date
voyages = api.get_voyages_by_advanced_search(vessel_class_id=aframaxId, start_date_from=dateFrom)
voyages = pd.DataFrame(v.__dict__ for v in voyages)
voyages['discharge_country'] = voyages['events'].apply(get_voyage_discharge_countries)
voyages['load_country'] = voyages['events'].apply(get_voyage_load_countries)
voyages['start_area']= voyages['events'].apply(get_voyage_start_area)
# Filter voyages in Starting Area
voyagesStartingArea = voyages[voyages['start_area'] == startingArea]
Creating a chart for the top 3 Load Countries
# Get value counts for load countries
flattened_load_countries = [country for sublist in voyagesStartingArea['load_country'] for country in sublist]
load_counts = pd.Series(flattened_load_countries).value_counts()
load_percentages = ((load_counts / load_counts.sum()) * 100).round(2)
# Get the top 3 load countries by percentage
top_3_load_countries = load_percentages.head(3)
# Plot a bar chart
plt.figure(figsize=(8, 5))
top_3_load_countries.plot(kind='bar', color=['#76c7c0', '#ff6f61', '#ffa07a'], edgecolor='black')
# Add chart details
plt.title('Top 3 Load Countries (Percent of Total)', fontsize=14, fontweight='bold')
plt.ylabel('Percentage (%)', fontsize=12)
plt.xlabel('Load Country', fontsize=12)
plt.xticks(rotation=45, ha='right', fontsize=10)
plt.yticks(fontsize=10)
# Show percentage values on top of bars
for index, value in enumerate(top_3_load_countries):
plt.text(index, value + 1, f'{value}%', ha='center', fontsize=10, color='black')
plt.tight_layout()
plt.show()
Creating a chart for the top 3 Discharge Countries
# Get value counts for discharge countries
flattened_discharge_countries = [country for sublist in voyagesStartingArea['discharge_country'] for country in sublist]
discharge_counts = pd.Series(flattened_discharge_countries).value_counts()
discharge_percentages = ((discharge_counts / discharge_counts.sum()) * 100).round(2)
# Get the top 3 discharge countries by percentage
top_3_discharge_countries = discharge_percentages.head(3)
# Plot a bar chart
plt.figure(figsize=(8, 5))
top_3_discharge_countries.plot(kind='bar', color=['#76c7c0', '#ff6f61', '#ffa07a'], edgecolor='black')
# Add chart details
plt.title('Top 3 Discharge Countries (Percent of Total)', fontsize=14, fontweight='bold')
plt.ylabel('Percentage (%)', fontsize=12)
plt.xlabel('Discharge Country', fontsize=12)
plt.xticks(rotation=45, ha='right', fontsize=10)
plt.yticks(fontsize=10)
# Show percentage values on top of bars
for index, value in enumerate(top_3_discharge_countries):
plt.text(index, value + 1, f'{value}%', ha='center', fontsize=10, color='black')
plt.tight_layout()
plt.show()
Creating a chart for the top 3 Cargo Types
# Get value counts for cargo types
cargo_types_counts = voyagesStartingArea['cargo_type'].value_counts()
cargo_types_percentages = ((cargo_types_counts / cargo_types_counts.sum()) * 100).round(2)
# Get the top 3 load cargo types
top_3_cargo_types = cargo_types_percentages.head(3)
# Plot a bar chart
plt.figure(figsize=(8, 5))
top_3_cargo_types.plot(kind='bar', color=['#76c7c0', '#ff6f61', '#ffa07a'], edgecolor='black')
# Add chart details
plt.title('Top 3 Cargo Types (Percent of Total)', fontsize=14, fontweight='bold')
plt.ylabel('Percentage (%)', fontsize=12)
plt.xlabel('Cargo Type', fontsize=12)
plt.xticks(rotation=45, ha='right', fontsize=10)
plt.yticks(fontsize=10)
# Show percentage values on top of bars
for index, value in enumerate(top_3_cargo_types):
plt.text(index, value + 1, f'{value}%', ha='center', fontsize=10, color='black')
plt.tight_layout()
plt.show()