Skip to main content
Ctrl+K
 - Home

Review Material

  • 1. GitHub Quickstart
  • 2. Pandas and Geopandas Review
    • 2.4. Case Study Solution

Establishing the Analysis Zone

  • 3. Establishing the Analysis Zone

Mapping and Monitoring the Extent of the Crisis

  • 4. Introduction
  • 5. Mapping and Monitoring Conflict
  • 6. Mapping and Monitoring Earthquake Intensity
  • 7. Mapping and Monitoring Floods
  • 8. Mapping and Monitoring Drought
  • 9. Mapping and Monitoring Surface Change (Illustrative)

Mapping Exposure

  • 10. Introduction
  • 11. Mapping Exposure - Infrastructure
    • 11.4. Open Street Maps - OSM
    • 11.5. Microsoft Building Footprints (MBF)
    • 11.6. Open Buildings from Google
    • 11.7. Overture Maps Foundation
  • 12. Mapping Exposure - Population
  • 13. Agricultural Area (Illustrative)

Estimating Physical Impact

  • 14. Infrastructural damage assessment (Illustrative)
  • 15. Internet Connectivity during Crisis
    • 15.4. Meta Mobile Network Connectivity (Illustrative)
    • 15.5. Ookla
  • 16. Nighttime Lights from NASA Black Marble during Crisis
  • 17. Solar Panel Detection (Illustrative)

Estimating Disruption to Business and Trade

  • 18. Night Time Lights to estimate Disruptions to Business and Trade
  • 19. Business Activity Trends
  • 20. Maritime Port Activity
    • 20.4. Chokepoints Monitor - Methodology
    • 20.5. Port Call Trends Monitor - Methodology
    • 20.6. Practice
  • 21. Estimating Disruption to Business and Trade Through Point of Interest Visits Using Mobility Data (Illustrative)
  • 22. Cross Border Activity (Illustrative)
  • 23. Online Search Trends (Illustrative)

Pulling it all Together

  • 24. Pulling it Together - Indicators
    • 24.3. Impact on Housing and Community
    • 24.4. Impact on Industry and Commerce
    • 24.5. Practice
  • 25. File Management and Code Dissemination

Final Project

  • 26. Final Project
  • Repository
  • Suggest edit
  • Open issue
  • .ipynb

Port Call Trends Monitor - Methodology

Contents

  • 20.5.1. Ports of interest
  • 20.5.2. Retrieve the data
  • 20.5.3. Resample to Weekly
  • 20.5.4. Weekly Trade Charts

20.5. Port Call Trends Monitor - Methodology#

This section examines how AIS-derived trade estimates have evolved in ports along the Red Sea given the conflict in the Middle East and the escalating attacks in the Red Sea.

20.5.1. Ports of interest#

The first step is to detect the ports of interest, which for this study are the ones in Egypt, Yemen, Djibouti, Jordan and Saudi Arabia.

countries = ["Egypt", "Yemen", "Djibouti", "Jordan", "Saudi Arabia"]
ports = api_call("https://services9.arcgis.com/weJ1QsnbMYJlCHdG/arcgis/rest/services/PortWatch_ports_database/FeatureServer/0/query?where=1%3D1&outFields=*&outSR=4326&f=json")
ports_oi = ports[ports['country'].isin(countries)]
ports_red_sea = create_gdf(ports_oi, 'EPSG:4326', 'lat', 'lon')
ports_red_sea.head()
portid portname country ISO3 continent fullname lat lon vessel_count_total vessel_count_container vessel_count_dry_bulk vessel_count_general_cargo vessel_count_RoRo vessel_count_tanker industry_top1 industry_top2 industry_top3 share_country_maritime_import share_country_maritime_export LOCODE pageid countrynoaccents ObjectId geometry
14 port23 Alexandria Egypt EGY Africa Alexandria, Egypt 31.155365 29.837591 2942 835 447 1147 141 369 Mineral Products Vegetable Products Metals 21.61 8.24 None a068e6e45ebc45ecb3aeda0a5ea19a38 Egypt 15 POINT (29.83759 31.15537)
19 port71 As Suways Egypt EGY Africa As Suways, Egypt 29.951008 32.530639 221 1 6 87 3 123 Mineral Products Chemical & Allied Industries Stone & Glass 0.15 0.46 None 985a636a613c43919bfb65a922bd6e6b Egypt 20 POINT (32.53064 29.95101)
33 port191 Safaga Egypt EGY Africa Safaga, Egypt 26.725556 33.942231 213 0 81 127 0 2 Vegetable Products Mineral Products Prepared Foodstuffs & Beverages 1.92 2.57 EG SGA 11463b58a3054345a897c7484b631d20 Egypt 34 POINT (33.94223 26.72556)
41 port192 Port Said Egypt EGY Africa Port Said, Egypt 31.249586 32.307719 796 499 33 158 10 94 Mineral Products Vegetable Products Chemical & Allied Industries 3.38 3.94 None 87392bda133f4d98ac6a7d3ed8d4a6b5 Egypt 42 POINT (32.30772 31.24959)
47 port274 Damietta Egypt EGY Africa Damietta, Egypt 31.462137 31.759472 2104 692 603 679 2 126 Mineral Products Chemical & Allied Industries Vegetable Products 19.27 19.42 None 1b9285ff906f45c0aa30f0a6f15199d4 Egypt 48 POINT (31.75947 31.46214)
ports_red_sea[
    [
        "geometry",
        "portname",
        "country",
        "vessel_count_total",
        "vessel_count_container",
        "vessel_count_dry_bulk",
        "vessel_count_general_cargo",
        "vessel_count_RoRo",
        "vessel_count_tanker",
        "industry_top1",
        "industry_top2",
        "industry_top3",
        "share_country_maritime_import",
        "share_country_maritime_export",
    ]
].explore(
    column="country",
    cmap="Dark2",
    marker_kwds={"radius": 7},
    tiles="Esri.WorldGrayCanvas",
    legend_kwds={"loc": "upper right", "caption": "Ports"},
    # attribution = 'chec'
)
Make this Notebook Trusted to load map: File -> Trust Notebook

20.5.2. Retrieve the data#

This example processes daily estimated trade (imports and exports) since 2019 from the IMF’s PortWatch platform. The process is similar to what was done for chokepoints. First, the URL for the API call needs to be build. After that, successive calls that return 1000 registers need to be done to download the complete dataset.

Sample URL to download portid 23 and 31:

https://services9.arcgis.com/weJ1QsnbMYJlCHdG/arcgis/rest/services/Daily_Trade_Data/FeatureServer/0/query?where=portid %3D ‘PORT23’ OR portid %3D ‘PORT31’&outFields=*&outSR=4326&f=json

def get_port_data(ports, url_base):
    for port in ports:
        if port == ports[-1]:
            url_base += (
                f"portid%3D%27{port}%27&outFields=*&outSR=4326&f=json&resultOffset=0"
            )
        else:
            url_base += f"portid%3D%27{port}%27+OR+"
    res = requests.get(url_base)
    df = pd.DataFrame([d["attributes"] for d in res.json()["features"]])
    offset = 1000
    while len(df) % 1000 == 0:
        res = requests.get(url_base.replace("resultOffset=0", f"resultOffset={offset}"))
        df2 = pd.DataFrame([d["attributes"] for d in res.json()["features"]])
        df = pd.concat([df, df2])
        offset += 1000
    df.reset_index(inplace=True, drop=True)
    df["date"] = df.date.apply(lambda x: datetime.fromtimestamp(x / 1000))
    df.sort_values(["portid", "date"], inplace=True)
    return df
url_base = "https://services9.arcgis.com/weJ1QsnbMYJlCHdG/arcgis/rest/services/Daily_Trade_Data/FeatureServer/0/query?where="
ports = list(ports_red_sea.portid)
df_ports = get_port_data(ports, url_base)
df_ports.to_csv('ports_call.csv') # Save the call locally to avoid repeating it
# Reload the save data. 
df = pd.read_csv('ports_call.csv')
df.date = pd.to_datetime(df.date)
df = df.loc[df.date >= "2019-01-01"].copy()

20.5.3. Resample to Weekly#

Since the data is daily and there are multiple days with no trade, first data was weekly resampled.

df_week = df.groupby(["portname", "portid"])[
        [
            "portcalls_cargo",
            "portcalls_tanker",
            "portcalls",
            "import_cargo",
            "export_cargo",
            "import_tanker",
            "export_tanker",
            "import",
            "export",
            "date",
        ]
    ].resample("W-Mon", on="date").sum().reset_index()

df_week.loc[:, "ymd"] = df_week.date.dt.strftime("%Y-%m-%d")
df_week.loc[:, "w"] = df_week.date.dt.strftime("%W") # Week number of the year
df_week.head()
portname portid date portcalls_cargo portcalls_tanker portcalls import_cargo export_cargo import_tanker export_tanker import export ymd w
0 Aden port9 2019-01-07 9 1 10 67364.559011 6774.851456 0.000000 0.000000 67364.559011 6774.851456 2019-01-07 01
1 Aden port9 2019-01-14 9 0 9 57414.729623 0.000000 0.000000 0.000000 57414.729623 0.000000 2019-01-14 02
2 Aden port9 2019-01-21 9 3 12 62216.623451 494.407228 23018.373940 3160.095388 85234.997391 3654.502616 2019-01-21 03
3 Aden port9 2019-01-28 5 2 7 32504.098000 0.000000 2391.884286 0.000000 34895.982286 0.000000 2019-01-28 04
4 Aden port9 2019-02-04 11 2 13 69051.590774 13218.182961 36085.859781 3160.095388 105137.450555 16378.278349 2019-02-04 05

20.5.4. Weekly Trade Charts#

start_reference_date = "2022-01-01"
conflict_date = "2023-10-07"
crisis_date = "2023-11-17"
df_filt = df_week.loc[(df_week.date >= "2023-01-01")].copy() # Obtain the recent data

Calculate the reference period and add it to the recent data. The merging will occur on the week of the year.

df_ref = df_week.loc[(df_week.date >= start_reference_date) & (df_week.date < conflict_date)].copy()
df_ref = df_ref.groupby(["portname", "portid", "w"])[
    [
        "portcalls_cargo",
        "portcalls_tanker",
        "portcalls",
        "import_cargo",
        "export_cargo",
        "import_tanker",
        "export_tanker",
        "import",
        "export",
    ]
].mean()
df_ref.reset_index(inplace=True)
df_ref.rename(
    columns={
        "portcalls_cargo": "portcalls_cargo_ref",
        "portcalls_tanker": "portcalls_tanker_ref",
        "portcalls": "portcalls_ref",
        "import_cargo": "import_cargo_ref",
        "export_cargo": "export_cargo_ref",
        "import_tanker": "import_tanker_ref",
        "export_tanker": "export_tanker_ref",
        "import": "import_ref",
        "export": "export_ref",
    },
    inplace=True,
)
df_filt = df_filt.merge(
    df_ref, on=["portname", "portid", "w"], how="left", validate="m:1"
)
for port_id in df_filt.portid.unique():
    port_info = ports_red_sea.loc[ports_red_sea.portid == port_id].iloc[0]
    country = port_info.country
    df_port = df_filt.loc[df_filt.portid == port_id].copy()
    df_port_copy = df_port.copy()
    port = df_port_copy.iloc[0].portname
    df_port = df_port.melt(
        id_vars="date",
        value_vars=["import", "export"],
        var_name="direction",
        value_name="trade",
    )
    df_port.loc[:, "direction"] = df_port.direction.str.capitalize()
    p0 = (
        ggplot(df_port_copy, aes(x="date", y="import_ref"))  #
        + geom_bar(
            mapping=aes(x="date", y="trade", fill="direction"),
            data=df_port,
            alpha=3 / 4,
            stat="identity",
            position="dodge2",
        )
        + geom_smooth(
            mapping=aes(x="date", y="import_ref"), color="#00BFC4", size=1, alpha=3 / 4
        )
        + geom_smooth(
            mapping=aes(x="date", y="export_ref"), color="#F8766D", size=1, alpha=3 / 4
        )
        + geom_vline(xintercept=conflict_date, linetype="dashed", color="black")
        + geom_vline(xintercept=crisis_date, linetype="dashed", color="black")
        + labs(
            x="",
            y="",
            subtitle="Metric Tons",
            title=f"Weekly Trade Volume - {port}, {country}",
            fill="Trade Flow",
        )
        + theme_minimal()
        + theme(
            text=element_text(size=13),
            plot_title=element_text(size=16, weight="bold"),
            axis_text_x=element_text(rotation=45, hjust=1),
            legend_position="none",
        )
        + scale_x_datetime(breaks=date_breaks("2 month"), labels=date_format("%Y-%m"))
        + scale_y_continuous(labels=comma_format())
    )
    display(p0)
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
../../_images/3703e3a581a17cfd8e24a8185e0171bc4f400796e95116931396e295e48d32cb.png
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
../../_images/73e9bce7a994ed09b1c9ff47fc686c96a196baa67a8b72e49bee995d46775e17.png
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
../../_images/645502b29d97371f6b0236cfb833fc1ce81783309cde0f444da6133df3b5fff4.png
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
../../_images/7ee2f2b15eebb44fe1dfafa66abc18aae75efad333f72cb33aeaf1222313d44f.png
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
../../_images/8a9e92a0edd10e027aa730f4bd0a8dae712cc10792cb4b0cade2c27af0520e49.png
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
../../_images/e565ccd601c0d81b10a05613ebdd44f9983bf26a5b783d287ffe375fa82607a8.png
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
../../_images/a2841358c1f87d031580bb98e3d11a6b4269bbedfb9f7890100b006dbe001f44.png
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
../../_images/2e09017170848ff80afbb0febb542eda65563c377e31e2b59bc382176a94ce0b.png
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
../../_images/69ee4ba9936e3912e2428ab6d7e189d5ff7974fac5ba1c16c44540aa96a2554a.png
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
../../_images/c4db7f4185df3fcfb82fca447710296af37cbcc5cebb7e6034f3d92f52c872fd.png
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
../../_images/6402123d2bf03097658322635b2f7944dc1acf4f5fdc030967629a6ff59eb9dd.png
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
../../_images/3d1fa6a330e77298e0e3a07dd9e942fa3b0c35118b873fbd6f64410fd199f932.png
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
../../_images/68cc1d9f35a311e49f7156a7ae191280dc606b1d839fee8a0fb48a1ea14d9e9e.png
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
../../_images/f73b0603742aaca48ec150ed27cda6830aadd5dd8c7424cdf7b00d0efddc7b69.png
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
../../_images/febe15238aca553485ae8abedaf0c47d9fa3d0b6e9d8baedd5eff48fb336f237.png
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
../../_images/3450b582641a795c6e326a0572ecca57d0212ce31a870724910aec6172153234.png
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
../../_images/1f9b96c20b277750f501039afaf400b2b36b840778c28bc516d592ed5307c0ae.png
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
../../_images/13de4322aa3abfc8a9d4c1dc4bde2ac54f8541e095f3de80f39dac35cad999d0.png
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
../../_images/03b4d34c10c52ec1af948cca40f787fcc2941720303783d261e1605e63efa2a6.png
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
../../_images/b2bbf1c872d1feee0b0a5c34df7fb677423ce8e80bb616227ecc325040c912ed.png
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
../../_images/8255d0a79eecb5a7b8de8b8565abc6342aa05c5056e1a2aeb02dbf5c8dbc3c5c.png
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
../../_images/9821c95f9d7b845a5e21f92564db0ff35100f9731c0ca3fa0c39da5286d12562.png
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
../../_images/3e84e0cebc02b2b2fa2c2efa1bc6d9db57457738c359519d1108bd2ccad96bb3.png
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
/home/sol/venv/lib/python3.10/site-packages/plotnine/stats/smoothers.py:347: PlotnineWarning: Confidence intervals are not yet implemented for lowess smoothings.
../../_images/010310f29292d05271318f0486a6f9092f1fa5949aec447610b7e261295db61a.png

previous

20.4. Chokepoints Monitor - Methodology

next

20.6. Practice

Contents
  • 20.5.1. Ports of interest
  • 20.5.2. Retrieve the data
  • 20.5.3. Resample to Weekly
  • 20.5.4. Weekly Trade Charts

By Development Data Partnership

Last updated on Oct 21, 2025.

Country borders or names do not necessarily reflect the World Bank Group’s official position. All maps are for illustrative purposes and do not imply the expression of any opinion on the part of the World Bank, concerning the legal status of any country or territory or concerning the delimitation of frontiers or boundaries
All content (unless otherwise specified) is subject to the Mozilla Public License.