Network Analysis of Paris’s Street: A Powerful Python Approach to Hidden City Patterns
Ever wondered what secrets lie within the streets of Paris? Beneath the city's romantic boulevards and historic avenues lies a complex web of connections that shape how people move, interact, and experience urban life. In this blog post, we’ll uncover the hidden structure of Paris’s road network using Network Analysis a powerful tool that turns maps into data-rich insights. From identifying central hubs to spotting potential traffic bottlenecks, this smart guide will walk you through how graph theory can reveal the unseen logic of one of the world’s most iconic cities.
Table of Contents
What is Network Analysis?
Network analysis is a method used to study the structure and behavior of networks; systems made up of nodes (or points) and edges (or connections). In the context of transportation, a network often represents things like intersections (nodes) and roads (edges). By analyzing how these elements are connected, we can uncover patterns, measure efficiency, detect bottlenecks, and optimize movement through the system.
At its core, network analysis comes from graph theory, a field of mathematics that models relationships between objects. In urban planning and transportation, it helps answer critical questions like:
- What is the shortest or fastest route between two locations?
- Which roads are the most central or heavily used?
- Where are the weak points or most vulnerable parts of the road network?
Whether you’re optimizing traffic flow, planning new infrastructure, or simply exploring how cities work, network analysis is a key tool for understanding complex spatial systems.
Why Paris? A Unique Case for Road Network Analysis
Paris is much more than the City of Light, it’s a living example of complex urban design shaped by centuries of history. Unlike many modern cities built on grid systems, Paris features a unique radial-concentric road network, largely influenced by the sweeping urban renovations led by Baron Haussmann in the 19th century. This mix of wide boulevards, winding medieval streets, and ring roads creates a fascinating pattern for network analysis.
The geometric and topological complexity of Paris’s roads makes it an ideal case study. The city’s layout combines narrow alleys in historic neighborhoods with broad avenues designed for modern traffic flow. This blend results in a rich network with central hubs, key connectors, and inevitable bottlenecks.
Analyzing Paris’s road network through network analysis tools allows urban planners and researchers to better understand traffic dynamics, improve emergency response routes, and plan smarter infrastructure. In this post, we’ll explore how these network patterns reveal the hidden logic of Paris’s streets offering insights that go beyond the map.
The Digital Toolbox: Python Libraries for Urban Network Analysis
For this transportation network analysis of Paris, we rely heavily on Python, a versatile programming language favored by data scientists and urban planners alike. Python offers powerful libraries that make it easier to collect, visualize, and analyze complex road network data.
Some of the key Python packages we use include:
- OSMnx: This library allows us to download, model, and visualize street networks directly from OpenStreetMap data, tailored specifically for urban network analysis.
- NetworkX: A core package for building and analyzing graphs, NetworkX helps us calculate important network metrics like shortest paths, centrality, and connectivity.
- GeoPandas: Extends Pandas to allow spatial operations on geographic data, useful for handling the road network’s spatial features.
- Matplotlib and Folium: These visualization libraries help us create clear and interactive maps to better understand the network’s structure.
By combining these Python tools, we can efficiently transform raw geographic data into meaningful insights about Paris’s road network, revealing patterns that inform urban mobility and planning.
Methodology: Extracting and Processing Paris's Road Network
To transform Paris from a beautiful city on a map into a mathematical network that we can analyze, we need a systematic approach that preserves the essential characteristics of its road structure while converting it into data suitable for computation.
This section walks through the process of capturing Paris's complex urban environment as a graph where intersections become nodes and streets become edges connecting them.
Our methodology combines geographical information systems with network science principles, creating a digital twin of Paris's road network that we can interrogate using algorithms. This transformation isn't just a technical exercise; it's fundamentally about representing a living, breathing city in a way that reveals its underlying patterns and logic.
The journey from raw geographical data to meaningful network insights involves several carefully orchestrated steps. We'll explore how we defined Paris's boundaries, extracted its road network using OpenStreetMap data through Python, cleaned and processed this information to ensure accuracy, and prepared it for the various network analyses that will reveal the city's hidden transportation dynamics.
Extracting Paris’s Road Network with OSMnx
Analyzing Paris’s road network require obtaining accurate and detailed data representing the city’s streets and intersections. For this purpose, we use OSMnx, a powerful Python library designed to download and model street networks directly from OpenStreetMap.
OSMnx simplifies the process of extracting road data within defined geographic boundaries, allowing us to quickly convert real-world maps into analyzable graph structures.
import osmnx as ox
city= 'Paris, France'
#Loading Paris driving road network
paris_road_network= ox.graph_from_place(city, network_type='drive')
The resulting network is stored in the variable paris_road_network
as a MultiDiGraph with 9476 items where nodes represent intersections or endpoints, and edges represent road segments with attributes like length, speed limit, and road type. This data can later be used for route optimization, traffic analysis and visualization.
Examining Nodes and Edges Using GeoPandas
Once we’ve extracted Paris’s road network, the next crucial step is to explore and verify the underlying data. Using GeoPandas, a powerful Python library for spatial data manipulation, we convert the raw network graph into geospatial dataframes representing nodes (intersections) and edges (road segments).
This process allows us to:
- Understand the network's structure (e.g., connectivity, density).
- Verify key attributes (e.g., road types, one-way restrictions).
- Detect anomalies or missing data that could affect analysis.
- Visualize spatial patterns in roads and intersections.
By thoroughly inspecting the data at this stage, we ensure accuracy before proceeding to advanced network analysis, routing algorithms, or urban planning applications.
# Extract Road nodes as a GeodataFrame
paris_road_nodes= ox.graph_to_gdfs(paris_road_network, edges=False, nodes=True)
print(f'{paris_road_nodes.info()} \n')
paris_road_nodes.head()
#Output:
# <class 'geopandas.geodataframe.GeoDataFrame'>
# Index: 9476 entries, 125730 to 12833072255
# Data columns (total 8 columns):
# # Column Non-Null Count Dtype
# --- ------ -------------- -----
# 0 y 9476 non-null float64
# 1 x 9476 non-null float64
# 2 street_count 9476 non-null int64
# 3 highway 1589 non-null object
# 4 junction 60 non-null object
# 5 railway 3 non-null object
# 6 ref 3 non-null object
# 7 geometry 9476 non-null geometry
# dtypes: float64(2), geometry(1), int64(1), object(4)
# osmid y x street_count highway junction railway ref geometry
# 125730 48.863523 2.410732 4 NaN NaN NaN NaN POINT (2.41073 48.86352)
# 125742 48.851852 2.401373 4 NaN NaN NaN NaN POINT (2.40137 48.85185)
# 125743 48.854563 2.413409 3 NaN NaN NaN NaN POINT (2.41341 48.85456)
# 125745 48.854524 2.414352 3 NaN NaN NaN NaN POINT (2.41435 48.85452)
# 125747 48.854261 2.414664 3 NaN NaN NaN NaN POINT (2.41466 48.85426)
This code snippet shows that paris_road_nodes
is a GeoDataFrame containing 9,476 nodes (intersections) from the Paris road network. Each node has geographic coordinates (x and y), a count of connected streets, and various optional attributes like highway type, junction type, railway information, and reference codes. The geometry column stores the spatial point data for each intersection.
The data reveals that while all nodes have coordinates and street counts, only some have additional attributes; for example, only 1,589 nodes have highway information and just 60 are marked as junctions.
# Extract Road Netwwork Edges as a GeodataFrame
paris_road_edges= ox.graph_to_gdfs(paris_road_network, edges=True, nodes=False)
print(f'{paris_road_edges.info()} \n')
print(paris_road_edges.head())
# <class 'geopandas.geodataframe.GeoDataFrame'>
# MultiIndex: 18220 entries, (np.int64(125730), np.int64(12179625841), np.int64(0)) to (np.int64(12833072255), np.int64(24910428), np.int64(0))
# Data columns (total 15 columns):
# # Column Non-Null Count Dtype
# --- ------ -------------- -----
# 0 osmid 18220 non-null object
# 1 highway 18220 non-null object
# 2 lanes 8565 non-null object
# 3 maxspeed 18016 non-null object
# 4 name 17753 non-null object
# 5 oneway 18220 non-null bool
# 6 reversed 18220 non-null object
# 7 length 18220 non-null float64
# 8 geometry 18220 non-null geometry
# 9 junction 523 non-null object
# 10 width 1298 non-null object
# 11 bridge 261 non-null object
# 12 tunnel 225 non-null object
# 13 access 77 non-null object
# 14 ref 90 non-null object
# dtypes: bool(1), float64(1), geometry(1), object(12)
# osmid highway lanes maxspeed \
# u v key
# 125730 12179625841 0 31093720 residential 2 30
# 244455548 0 85735969 residential 2 30
# 567367734 0 315818975 residential NaN 30
# 1089076182 0 [266463585, 611974677] residential 2 30
# 125742 60912980 0 4039102 residential NaN 30
# name oneway reversed length \
# u v key
# 125730 12179625841 0 Rue Louis Lumière False False 4.347747
# 244455548 0 Rue Louis Lumière False True 105.925927
# 567367734 0 Rue Henri Duvernois False True 35.154556
# 1089076182 0 Rue Louis Ganne False True 128.654471
# 125742 60912980 0 Rue de Buzenval True False 129.018946
# geometry \
# u v key
# 125730 12179625841 0 LINESTRING (2.41073 48.86352, 2.41072 48.86356)
# 244455548 0 LINESTRING (2.41073 48.86352, 2.41077 48.86337...
# 567367734 0 LINESTRING (2.41073 48.86352, 2.41083 48.86348...
# 1089076182 0 LINESTRING (2.41073 48.86352, 2.41062 48.86351...
# 125742 60912980 0 LINESTRING (2.40137 48.85185, 2.40133 48.85192...
This code output displays the road network edges (streets) of Paris extracted as a GeoDataFrame using OSMnx. The dataset contains 18,220 road segments with 15 different attributes. Each edge is uniquely identified by a MultiIndex of source node (u), target node (v), and a key.
The data shows that all edges have basic information like OSM ID, highway type, oneway status, and length, while other attributes are partially populated – for example, only 8,565 edges have lane information and 18,016 have speed limit data. The geometry column contains LineString objects representing the spatial path of each road segment.
The sample data shows five road segments connected to nodes 125730 and 125742. These are primarily residential streets with names like “Rue Louis Lumière” and “Rue de Buzenval,” most with 30 km/h speed limits. This comprehensive dataset enables detailed analysis of Paris's street network, including road types, connectivity, directionality, and physical characteristics.
Visualizing the Road Network Graph with OSMnx and GeoPandas
With the road network data extracted and inspected, the next step is to visualize it, turning abstract graph data into an intuitive, map-based representation. Mapping the network not only helps verify that the data aligns correctly with Paris’s geography, but also reveals key structural features such as major intersections, road densities, and spatial connectivity.
Using Python tools like OSMnx and GeoPandas, we can plot the road network directly over a geographic background, providing a powerful visual reference for both exploration and presentation. These visualizations serve as a bridge between data and insight, helping us spot irregularities, disconnected components, and early patterns before diving into deeper analysis.
m= paris_road_edges.explore(tiles="openstreetmap", color="skyblue", alpha=0.3)
# you can save a web map as an html file for reuse elsewhere
m.save("paris_roads.html")
m
In the code snippet above, we use the .explore()
method from GeoPandas to create an interactive map of the road network. By specifying tiles="openstreetmap"
, we overlay the network on a familiar and detailed basemap. The color="skyblue"
and alpha=0.3
settings help visually distinguish the road edges while allowing the underlying map to remain visible.
Finally, m.save("paris_roads.html")
lets us export the interactive map to an HTML file, making it easy to share or embed in a website. This simple yet powerful visualization confirms that our road network aligns correctly with Paris's urban layout and provides a spatial reference for further analysis.
Shortest Route Analysis in Paris with NetworkX and Folium
One of the most powerful applications of network analysis is calculating the shortest path between two locations in a city. In graph theory, this problem is efficiently solved using Dijkstra’s algorithm, which finds the minimum-cost path between nodes based on edge weights—in this case, street lengths.
In this section, we’ll apply Dijkstra’s algorithm using NetworkX, a robust Python library for graph analysis, to compute optimal routes through Paris’s road network. Then, we’ll use Folium to visualize these routes interactively on a map, combining analytical insight with real-world geography. Whether you're modeling traffic flows or building a routing tool, this technique lies at the heart of smart mobility solutions.
#starting and ending locations
Les_invaldes= (48.8568092105193, 2.3133769174527554)
Palace_de_La_Concorde= (48.8659388675436, 2.3213126933159716)
#nearest node to the location coordinates
Les_invaldes_node= ox.distance.nearest_nodes(paris_road_network, X= Les_invaldes[1], Y=Les_invaldes[0])
La_Concorde_node= ox.distance.nearest_nodes(paris_road_network, X= Palace_de_La_Concorde[1], Y=Palace_de_La_Concorde[0])
#Compute The shortest path using djikistra algorithm
shortest_path= nx.shortest_path(paris_road_network, source=Les_invaldes_node, target=La_Concorde_node, weight="length")
shortest_path_length= nx.shortest_path_length(paris_road_network, source=Les_invaldes_node, target=La_Concorde_node, weight="length")
print(f'The shortest path length between Les invades and La Concorde is {shortest_path_length:.2f} meters')
#Display the path on a Map
shortest_path_gdf = ox.routing.route_to_gdf(paris_road_network, shortest_path, weight="length")
m= shortest_path_gdf['geometry'].explore(tiles="openstreetmap", color="blue", style_kwds={"weight": 4})
folium.Marker(location=[paris_road_network.nodes[Les_invaldes_node]['y'], paris_road_network.nodes[Les_invaldes_node]['x']],
popup=folium.Popup('Les invalides', max_width=150), icon=folium.Icon(icon='flag', color='red')).add_to(m)
folium.Marker(location=[paris_road_network.nodes[La_Concorde_node]['y'], paris_road_network.nodes[La_Concorde_node]['x']],
popup=folium.Popup('La Concorde', max_width=150), icon=folium.Icon(icon='flag', color='red')).add_to(m)
m
#The shortest path length between Les invades and La Concorde is 1456.34 meters
We compute and visualize the shortest driving route between Les Invalides and Place de la Concorde using Dijkstra’s algorithm. We start by defining the latitude and longitude of both landmarks and then use ox.distance.nearest_nodes()
to find the closest nodes in the road network graph to those coordinates.
Once we’ve located the start and end nodes, we apply NetworkX’s shortest_path()
function with the length
attribute as the weight, ensuring that the algorithm finds the path with the shortest physical distance.
We also calculate the total path length with shortest_path_length()
, then convert the resulting node sequence into a GeoDataFrame using ox.routing.route_to_gdf()
. This path is visualized on a Folium map, styled in blue, with markers added to highlight both the origin and destination. The result is an interactive and geographically accurate visualization of the most efficient route between two key Paris landmarks.
Emergency Access in Paris: Closest Hospital via Network Analysis
In urban environments like Paris, quick access to essential services especially hospitals can be a matter of life and death. Closest Facility Analysis helps answer a critical question: From a given location, which facility (like a hospital) is the quickest to reach based on the actual road network? Unlike simple straight-line distances, this approach accounts for the real structure of the city turns, detours, and road hierarchy using network analysis.
In this section, we'll identify the nearest hospital to a specific point in Paris by calculating the shortest travel distance through the road network. By combining Python libraries like NetworkX and OSMnx, we'll simulate how emergency responders or navigation systems determine the optimal destination in a time-sensitive scenario. Let’s dive into how network science can help make smarter, potentially life-saving decisions in the city.
#Loading Paris hospitals and social facilities
paris_hospitals_path='/content/drive/MyDrive/EQUIPEMENT_EMPRISE_GRAND_EQUIPEMENT_SANTE.geojson'
paris_hospitals= gpd.read_file(paris_hospitals_path)
social_facilities_path='/content/drive/MyDrive/EQUIPEMENT_PONCTUEL_HEBERGEMENT_LOGEMENT_ADAPTE.geojson'
social_facilities= gpd.read_file(social_facilities_path)
# Clip the facilities to paris boundart
paris_boundary= ox.geocode_to_gdf(city)
paris_boundary= paris_boundary.to_crs(crs=4326)
paris_hospitals_clipped= paris_hospitals.clip(paris_boundary)
social_facilities_clipped= social_facilities.clip(paris_boundary)
paris_hospitals_clipped= paris_hospitals.to_crs(paris_boundary.crs)
social_facilities_clipped= social_facilities.to_crs(paris_boundary.crs)
#Project road netwotk to WGS84
paris_road_network_projected= ox.project_graph(paris_road_network, to_crs=paris_boundary.crs)
random_social_facility= social_facilities_clipped.sample(1)
random_social_facility_node= ox.distance.nearest_nodes(paris_road_network_projected,
X=random_social_facility.geometry.x,
Y=random_social_facility.geometry.y)
hospitals_nodes= []
paris_hospitals_clipped.apply(lambda row: hospitals_nodes.append(
(Point(row.geometry.centroid.x,row.geometry.centroid.y),
ox.distance.nearest_nodes(paris_road_network_projected,
X=row.geometry.centroid.x,
Y=row.geometry.centroid.y)))
, axis=1)
# Find the nearest hospital to the random social facility
closest_hospital= None
closest_dist= float('inf')
shortest_path= None
for point, hospital_node in hospitals_nodes:
try:
distance= nx.shortest_path_length(paris_road_network_projected, random_social_facility_node[0], hospital_node, weight='length')
if distance < closest_dist:
closest_dist= distance
closest_hospital= hospital_node
shortest_path= nx.shortest_path(paris_road_network_projected,
source=random_social_facility_node[0], target=closest_hospital,
weight='length')
except nx.NetworkXNoPath:
continue
# Mapping closest Hospital:
path_coords=[(paris_road_network_projected.nodes[node]['y'],
paris_road_network_projected.nodes[node]['x']) for node in shortest_path]
m= paris_hospitals_clipped.explore(location=[random_social_facility.geometry.y, random_social_facility.geometry.x],
tiles="openstreetmap", color="blue", tooltip=['l_ee_min'],zoom_start=17)
folium.Marker(location=[random_social_facility.geometry.y, random_social_facility.geometry.x],
popup=folium.Popup(random_social_facility['l_ep_min'].values[0], max_width=190, sticky=True,show=True),
icon=folium.Icon(icon='kit-medical', color='red', prefix='fa')).add_to(m)
folium.Marker(location=[paris_road_network_projected.nodes[closest_hospital]['y'], paris_road_network_projected.nodes[closest_hospital]['x']],
popup=folium.Popup('Closest Hospital', max_width=190, sticky=True, show=True),
icon=folium.Icon(icon='hospital', color='red', prefix='fa')).add_to(m)
folium.PolyLine(locations=path_coords, color='green', popup=folium.Popup(f"distance: {closest_dist:.2f} meters", max_width=120, sticky=True, show=True),
weight=4).add_to(m)
m
To identify the closest hospital to a given location in Paris, this code performs a network-based proximity search. It begins by loading hospital and social facility datasets in GeoJSON format and clipping them to fit within the Paris boundary.
A random social facility is selected as the origin point, and its nearest node on the road network is determined. Each hospital is also associated with its closest graph node based on centroid coordinates.
Next, the code computes the shortest network path from the selected facility to every hospital using Dijkstra’s algorithm, selecting the one with the minimum distance. The winning route is plotted on an interactive map using Folium, with custom markers showing both the origin and the nearest hospital. A green polyline visualizes the computed route, annotated with the total travel distance in meters. This analysis demonstrates how graph-based routing can support real-world decisions like emergency accessibility and healthcare planning in urban settings.
Conclusion
Through the lens of network analysis, we’ve peeled back the surface of Paris to reveal the intricate structure that governs its roads, routes, and accessibility. From extracting and visualizing the city’s road network to computing shortest paths and identifying the nearest hospitals, each step has shown how graph theory and Python tools can translate urban complexity into actionable insight.
This exploration is more than a technical exercise, it’s a demonstration of how data science and geography come together to make cities smarter. Whether you're optimizing traffic flow, improving emergency response, or enhancing infrastructure planning, network analysis offers a powerful framework to understand and shape urban systems.
As cities continue to grow and evolve, so does the need for intelligent, data-driven decision-making. By applying these methods to real-world challenges, we move closer to building cities that are not only more efficient, but also more equitable and responsive to the people who live in them.
If you're interested in how these spatial patterns relate to real-world safety and behavior, don’t miss our companion analysis: Comprehensive NYC Vehicle Collisions Data Analysis. It dives into the when, where, and why of urban collisions using geospatial clustering and temporal insights, offering a practical look at how street use and safety intersect.
This next section may contain affiliate links. If you click one of these links and make a purchase, I may earn a small commission at no extra cost to you. Thank you for supporting the blog!
References
OSMnx: Python for Street Networks
Geographic Data Science with Python (Chapman & Hall/CRC Texts in Statistical Science)
Working with spatial networks using NetworkX
Python for Data Analysis: Data Wrangling with pandas, NumPy, and IPython
Frequently Asked Questions (FAQs)
What is network analysis in urban planning?
Network analysis is a method used to study how elements like roads, intersections, and facilities are connected within a city. In urban planning, it helps uncover patterns in traffic flow, accessibility, and infrastructure efficiency using mathematical graph theory.
Why use Python for transportation network analysis?
Python offers powerful libraries like OSMnx, GeoPandas, NetworkX, and Folium that simplify tasks such as extracting road networks, computing shortest paths, and visualizing maps. It’s open-source, highly customizable, and widely supported by the data science community.
What algorithms are used in shortest path analysis?
One of the most common algorithms is Dijkstra’s algorithm, which finds the shortest route between two points in a weighted graph. It’s used here to calculate the most efficient travel path across Paris’s road network.
What kind of data do I need to perform this analysis?
You'll need:
-Road network data (e.g., from OpenStreetMap)
-Geographic boundaries (e.g., Paris administrative limits)
-Facility location data (e.g., hospitals, social services)
-A GIS-compatible format such as GeoJSON or Shapefiles
What are some real-world applications of this analysis?
-Emergency response planning (e.g., finding the nearest hospital)
–Urban mobility optimization (e.g., improving bike or bus routes)
–Infrastructure planning and investment
–Accessibility analysis for marginalized areas
–Smart city development and traffic simulations
No Comment! Be the first one.