01 · Data & graph construction

Builds the bilateral edge list for config.YEAR from the raw BACI files, turns it into the directed weighted graph that every later notebook rebuilds, stores nodes and edges in DuckDB, and draws the flow map.

The source, node set, and modelling decisions are on the Data & method page.

Show code
from __future__ import annotations

import pandas as pd

from eu_trade_network import config, data_loader, db, graph, viz

Edge list + directed graph

Build the bilateral edge list for config.YEAR, construct the directed weighted graph, and print a headline summary.

Show code
edgelist = data_loader.build_edgelist()
G = graph.build_graph(edgelist)
summary = graph.graph_summary(G)
summary

Persist nodes + edges in DuckDB

Show code
node_meta = (
    pd.DataFrame([{"iso3": n, **attrs} for n, attrs in G.nodes(data=True)])
    .sort_values("iso3")
    .reset_index(drop=True)
)

node_meta["out_strength"] = [float(G.out_degree(n, weight="weight")) for n in node_meta["iso3"]]
node_meta["in_strength"] = [float(G.in_degree(n, weight="weight")) for n in node_meta["iso3"]]
node_meta["degree"] = [int(G.degree(n)) for n in node_meta["iso3"]]

edges_db = edgelist.copy()
edges_db["year"] = config.YEAR

con = db.connect()
db.init_schema(con)
db.write_table(
    con,
    "nodes",
    node_meta[["iso3", "name", "grp", "out_strength", "in_strength", "degree"]],
)
db.write_table(con, "edges", edges_db[["exporter_iso3", "importer_iso3", "value_kusd", "year"]])

print(f"Wrote {len(node_meta)} nodes and {len(edges_db)} edges → {config.DB_PATH}")
con.close()

Flow map (hero figure)

Show code
# Cartesian lon/lat map is kaleido-safe (no Plotly topojson CDN) and matches
# the dark Quarto theme used for headline PNGs.
fig = viz.plot_lonlat_flows(
    edgelist,
    node_meta,
    top_n=150,
    title=f"European merchandise trade flows {config.YEAR} (top 150 edges)",
)
out = viz.save_fig(fig, "01_flow_map.png", headline=True)
print(f"Saved {out}")
fig.show()