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
signal_ocean_api_key = '' #replace with your subscription key
Voyages API Use Cases¶
from signal_ocean import Connection
from signal_ocean.voyages import VoyagesAPI
from signal_ocean.voyages import Vessel, VesselFilter
from signal_ocean.voyages import VesselType, VesselTypeFilter
from signal_ocean.voyages import VesselClass, VesselClassFilter
import pandas as pd
import numpy as np
from datetime import date, timedelta
import seaborn as sns
import matplotlib.pyplot as plt
sns.set_theme()
connection = Connection(signal_ocean_api_key)
api = VoyagesAPI(connection)
Declare helper functions
def get_voyage_load_area(voyage_events):
return next((e.area_name_level0 for e in voyage_events or [] if e.purpose=='Load'), None)
def get_voyage_discharge_country(voyage_events):
return next((e.country for e in reversed(voyage_events or []) if e.purpose=='Discharge'), None)
def get_voyage_load_country(voyage_events):
return next((e.country for e in voyage_events or [] if e.purpose=='Load'), None)
Get voyages¶
# get vessel class id for vlcc
vessel_class = api.get_vessel_classes(VesselClassFilter('vlcc'))
vlcc_id = vessel_class[0].vessel_class_id
vlcc_id
84
date_from = date.today() - timedelta(days=180)
voyages = api.get_voyages(vessel_class_id=vlcc_id, date_from=date_from)
voyages = pd.DataFrame(v.__dict__ for v in voyages)
events = pd.DataFrame(e.__dict__ for voyage_events in voyages['events'].dropna() for e in voyage_events)
historical_events = events[events['event_horizon']=='Historical']
voyages['load_area'] = voyages['events'].apply(get_voyage_load_area)
voyages['discharge_country'] = voyages['events'].apply(get_voyage_discharge_country)
voyages['load_country'] = voyages['events'].apply(get_voyage_load_country)
Number of exporting voyages¶
voyages_exports_usg = voyages[(voyages['load_area']=='US Gulf')&(voyages['discharge_country']!='United States')]
voyages_exports_usg.shape[0]
198
voyages_exports_usg['discharge_country'].value_counts()
Korea, Republic of 46 China 37 Netherlands 29 Taiwan 17 India 16 Germany 12 Singapore 9 France 8 Thailand 7 United Kingdom 6 Malaysia 4 Japan 2 Bahamas 1 Panama 1 Sweden 1 Norway 1 Italy 1 Name: discharge_country, dtype: int64
Port Delays¶
discharges_china = historical_events[(historical_events['country']=='China')&(historical_events['purpose']=='Discharge')].copy()
discharges_china['duration'] = discharges_china['sailing_date'] - discharges_china['arrival_date']
discharges_china['duration'].describe()
count 645 mean 4 days 17:15:01.378294573 std 3 days 19:24:41.541664624 min 0 days 23:50:45 25% 2 days 15:50:59 50% 3 days 12:00:49 75% 5 days 11:57:42 max 48 days 12:15:40 Name: duration, dtype: object
discharges_china['duration_in_hours'] = discharges_china['duration'] / np.timedelta64(1, 'h')
common_discharge_ports_china = discharges_china['port_name'].value_counts().head(8)
common_port_discharges_china = discharges_china[discharges_china['port_name'].isin(common_discharge_ports_china.index)]
sns.catplot(x="port_name", y="duration_in_hours", kind="box", data=common_port_discharges_china, aspect=2);
discharges_china['arrival_month'] = discharges_china['arrival_date'].dt.tz_localize(None).dt.to_period('M').dt.to_timestamp()
sns.lineplot(data=discharges_china, x='arrival_month', y='duration_in_hours')
plt.xticks(rotation=90);
Discharge destinations¶
discharge_destinations_brazil = voyages[voyages['load_country']=='Brazil'].dropna(subset=['discharge_country'])
discharge_destinations_brazil['discharge_country'].value_counts()
China 70 Korea, Republic of 6 United States 6 Portugal 4 Netherlands 4 Malaysia 2 Brazil 2 United Kingdom 1 France 1 Greece 1 Name: discharge_country, dtype: int64
sns.displot(discharge_destinations_brazil, x="start_date", hue="discharge_country", aspect=2);
Advanced Voyage Search: Discharge origins¶
This use-case demonstrates how to utilise the advanced search endpoint to extract historical voyages by vessel_class_id
, first_load_arrival_date
and a specific event_purpose
. The voyages with the provided purpose can then be merged and filtered with a specific load_area
and discharge_country
in order to visualize the vessel flows into the specified country.
# get vessel class id for vlcc
vessel_class = api.get_vessel_classes(VesselClassFilter('vlcc'))
vlcc_id = vessel_class[0].vessel_class_id
vlcc_id
84
date_from = date.today() - timedelta(days=60)
load_area = 'Arabian Gulf'
discharge_country = 'Japan'
In the following cell we extract the voyages with an event_purpose="Discharge"
, which essentially looks up for all the voyages with at least one discharge event. This implies, due to the nature of the shipping pipeline, that load events are also included in the discharge call and can therefore be omitted.
voyages = api.get_voyages_by_advanced_search(vessel_class_id=vlcc_id, first_load_arrival_date_from=date_from,
event_horizon='Historical', event_purpose='Discharge')
voyages = pd.DataFrame(v.__dict__ for v in voyages)
voyages['load_area'] = voyages['events'].apply(get_voyage_load_area)
voyages['load_country'] = voyages['events'].apply(get_voyage_load_country)
voyages['discharge_country'] = voyages['events'].apply(get_voyage_discharge_country)
voyages_filtered = voyages.loc[(voyages['load_area'] == load_area) & (voyages['discharge_country'] == discharge_country)].reset_index(drop=True)
fig, _ = plt.subplots(figsize=(12, 5))
ax = sns.countplot(x='load_country', data=voyages_filtered)
ax.set_title(f'Vessel Flows ({discharge_country})', fontsize=14)
ax.set_xlabel('Load Countries', fontsize=12)
ax.set_ylabel('Vessel Counts', fontsize=12);