Python for Transportation & Urban Planning

A Comprehensive Guide to Essential Commands and Syntaxes

🐍
πŸ™οΈ
🚊
πŸ“Š
πŸ—ΊοΈ
10+
Essential Libraries
100+
Code Examples
9
Key Sections

Python has revolutionized the field of transportation and urban planning by providing powerful tools for data analysis, spatial modeling, and visualization. This guide covers the essential Python commands and syntaxes that every transportation and urban planner should master.

πŸ”— Data Integration

Combine census data, traffic counts, GIS layers, and survey results seamlessly

πŸ—ΊοΈ Spatial Analysis

Perform complex geographic calculations and modeling with precision

⚑ Automation

Streamline repetitive planning tasks and increase productivity

πŸ“Š Visualization

Create compelling maps and charts for presentations and reports

πŸ’° Open Source

Cost-effective alternative to expensive proprietary software

🌐 Community

Access to vast libraries and active planning community support

Pandas - The Foundation of Planning Data Analysis

Pandas is your primary tool for handling structured data in planning projects. Think of it as Excel on steroids.

Essential DataFrame Operations
import pandas as pd

# Reading data from various sources
df = pd.read_csv('traffic_counts.csv')
df = pd.read_excel('population_data.xlsx', sheet_name='Summary')
df = pd.read_sql('SELECT * FROM parcels', connection)

# Basic data exploration
df.head()          # First 5 rows
df.info()          # Data types and null values
df.describe()      # Statistical summary
df.shape           # Dimensions (rows, columns)

Data Cleaning - Critical for Planning Data

# Handle missing values (common in planning datasets)
df.dropna(subset=['population'])           # Remove rows with missing population
df.fillna({'income': df['income'].median()})  # Fill with median income

# Data type conversions
df['census_tract'] = df['census_tract'].astype(str)
df['survey_date'] = pd.to_datetime(df['survey_date'])

# Remove duplicates
df.drop_duplicates(subset=['parcel_id'])

Aggregation and Grouping

# Group by transportation analysis zones
zone_summary = df.groupby('taz_id').agg({
    'population': 'sum',
    'employment': 'sum',
    'trip_generation': 'mean',
    'income': 'median'
}).round(2)

# Multiple grouping levels
mode_zone = df.groupby(['taz_id', 'mode_choice']).size().unstack(fill_value=0)

# Time-based aggregation
daily_counts = df.groupby(df['timestamp'].dt.date)['vehicle_count'].sum()

GeoPandas - GIS in Python

GeoPandas extends pandas to handle spatial data, making it perfect for planning applications.

import geopandas as gpd
from shapely.geometry import Point, Polygon

# Read various spatial formats
parcels = gpd.read_file('parcels.shp')
transit_stops = gpd.read_file('transit_stops.geojson')
census_tracts = gpd.read_file('census_tracts.gpkg')

# Create GeoDataFrame from coordinates
transit_df = pd.DataFrame({
    'stop_id': [1, 2, 3],
    'ridership': [150, 200, 300],
    'longitude': [-122.4, -122.3, -122.2],
    'latitude': [37.8, 37.7, 37.6]
})

geometry = [Point(xy) for xy in zip(transit_df.longitude, transit_df.latitude)]
transit_gdf = gpd.GeoDataFrame(transit_df, geometry=geometry, crs='EPSG:4326')

Spatial Operations

# Buffer analysis - critical for accessibility studies
transit_buffer = transit_stops.buffer(400)  # 400-meter walking distance
parcels_near_transit = parcels[parcels.within(transit_buffer.unary_union)]

# Spatial joins
parcels_with_zones = gpd.sjoin(
    parcels, 
    zoning_districts, 
    how='left', 
    predicate='within'
)

# Overlay operations
intersection = gpd.overlay(
    flood_zones, 
    development_parcels, 
    how='intersection'
)

Geometric Calculations

# Calculate areas and distances
parcels['area_sqft'] = parcels.geometry.area
parcels['perimeter'] = parcels.geometry.length

# Centroid calculations
parcels['centroid'] = parcels.geometry.centroid
parcels['centroid_x'] = parcels.centroid.x
parcels['centroid_y'] = parcels.centroid.y

# Distance calculations
parcels['dist_to_transit'] = parcels.geometry.distance(
    transit_stops.geometry.unary_union
)

NetworkX - Network Analysis and Routing

Perfect for analyzing transportation networks, accessibility, and connectivity.

import networkx as nx

# Create network from edge list
road_network = nx.from_pandas_edgelist(
    edges_df, 
    source='from_node', 
    target='to_node', 
    edge_attr=['length', 'speed_limit', 'capacity']
)

# Add node attributes
node_attrs = dict(zip(nodes_df.node_id, nodes_df.signal_type))
nx.set_node_attributes(road_network, node_attrs, 'signal_type')

Shortest Path Analysis

# Single shortest path
shortest_path = nx.shortest_path(
    road_network, 
    source=origin_node, 
    target=destination_node, 
    weight='travel_time'
)

# All shortest paths from one origin
distances = nx.single_source_shortest_path_length(
    road_network, 
    origin_node, 
    weight='travel_time'
)

# Travel time matrix
travel_times = dict(nx.all_pairs_shortest_path_length(
    road_network, 
    weight='travel_time'
))

Network Centrality Analysis

# Betweenness centrality - identifies critical corridors
betweenness = nx.betweenness_centrality(road_network, weight='length')

# Closeness centrality - measures accessibility
closeness = nx.closeness_centrality(road_network, distance='travel_time')

# Degree centrality - connectivity measure
degree = nx.degree_centrality(road_network)

# PageRank - importance in network
pagerank = nx.pagerank(road_network, weight='traffic_volume')

Matplotlib & Seaborn - Statistical Visualization

import matplotlib.pyplot as plt
import seaborn as sns

# Set planning-friendly style
plt.style.use('seaborn-v0_8')
sns.set_palette("husl")

# Mode share visualization
mode_counts = df['mode_choice'].value_counts()
plt.figure(figsize=(10, 6))
plt.pie(mode_counts.values, labels=mode_counts.index, autopct='%1.1f%%')
plt.title('Transportation Mode Share')
plt.show()

# Trip length distribution
plt.figure(figsize=(12, 6))
plt.hist(df['trip_distance'], bins=50, alpha=0.7, edgecolor='black')
plt.xlabel('Trip Distance (miles)')
plt.ylabel('Frequency')
plt.title('Distribution of Trip Distances')
plt.axvline(df['trip_distance'].mean(), color='red', linestyle='--', 
           label=f'Mean: {df["trip_distance"].mean():.1f} miles')
plt.legend()
plt.show()

Folium - Interactive Web Maps

import folium
from folium.plugins import HeatMap, MarkerCluster

# Create base map
m = folium.Map(
    location=[city_lat, city_lon], 
    zoom_start=12, 
    tiles='OpenStreetMap'
)

# Add transit stops with clustering
marker_cluster = MarkerCluster().add_to(m)
for idx, stop in transit_stops.iterrows():
    folium.Marker(
        location=[stop.geometry.y, stop.geometry.x],
        popup=f"Stop: {stop.stop_name}
Ridership: {stop.daily_boardings}"
, icon=folium.Icon(color='blue', icon='bus') ).add_to(marker_cluster) # Add choropleth map for demographics folium.Choropleth( geo_data=census_tracts, data=demographic_data, columns=['census_tract', 'median_income'], key_on='feature.properties.tract_id', fill_color='YlOrRd', fill_opacity=0.7, line_opacity=0.2, legend_name='Median Household Income' ).add_to(m) m.save('planning_map.html')

SciPy - Advanced Statistical Analysis

from scipy import stats
import scipy.spatial.distance as distance

# Hypothesis testing for planning studies
# Compare before/after traffic volumes
before_traffic = df[df['period'] == 'before']['volume']
after_traffic = df[df['period'] == 'after']['volume']

# Paired t-test for before/after comparison
t_stat, p_value = stats.ttest_rel(before_traffic, after_traffic)
print(f"T-statistic: {t_stat:.3f}, p-value: {p_value:.3f}")

# Chi-square test for mode choice independence
contingency_table = pd.crosstab(df['income_group'], df['mode_choice'])
chi2, p_val, dof, expected = stats.chi2_contingency(contingency_table)

# Correlation analysis
correlation, p_value = stats.pearsonr(df['density'], df['transit_ridership'])
spearman_corr, p_val_spearman = stats.spearmanr(df['density'], df['transit_ridership'])

Scikit-learn - Machine Learning for Planning

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.ensemble import RandomForestRegressor
from sklearn.cluster import KMeans
from sklearn.metrics import mean_absolute_error, r2_score

# Predict transit ridership based on built environment
features = ['population_density', 'job_density', 'walk_score', 
           'parking_spaces', 'distance_to_cbd']
X = df[features]
y = df['transit_ridership']

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Linear regression model
lr_model = LinearRegression()
lr_model.fit(X_train, y_train)
y_pred = lr_model.predict(X_test)

print(f"RΒ² Score: {r2_score(y_test, y_pred):.3f}")
print(f"Mean Absolute Error: {mean_absolute_error(y_test, y_pred):.0f}")

Requests - API Data Collection

import requests
import json

# Census API for demographic data
census_key = 'your_census_api_key'
census_url = f"https://api.census.gov/data/2021/acs/acs5"
params = {
    'get': 'B01003_001E,B19013_001E,B25001_001E',  # Population, income, housing
    'for': 'tract:*',
    'in': 'state:06 county:075',  # San Francisco County
    'key': census_key
}

response = requests.get(census_url, params=params)
census_data = response.json()
census_df = pd.DataFrame(census_data[1:], columns=census_data[0])

# GTFS Real-time transit data
gtfs_url = "https://api.511.org/transit/vehiclepositions"
headers = {'api_key': 'your_511_api_key'}
transit_response = requests.get(gtfs_url, headers=headers)
transit_data = transit_response.json()

Data Processing from APIs

# Process JSON responses into DataFrames
def process_census_data(json_data):
    df = pd.DataFrame(json_data[1:], columns=json_data[0])
    df['population'] = pd.to_numeric(df['B01003_001E'], errors='coerce')
    df['median_income'] = pd.to_numeric(df['B19013_001E'], errors='coerce')
    df['housing_units'] = pd.to_numeric(df['B25001_001E'], errors='coerce')
    df['fips'] = df['state'] + df['county'] + df['tract']
    return df[['fips', 'population', 'median_income', 'housing_units']]

# Batch API requests with error handling
def batch_api_requests(urls, max_retries=3):
    results = []
    for url in urls:
        for attempt in range(max_retries):
            try:
                response = requests.get(url, timeout=10)
                response.raise_for_status()
                results.append(response.json())
                break
            except requests.exceptions.RequestException as e:
                if attempt == max_retries - 1:
                    print(f"Failed to fetch {url}: {e}")
                    results.append(None)
    return results

List Comprehensions for Planning Data

# Filter and transform data efficiently
high_density_zones = [zone for zone in zones if zone['density'] > 10000]
trip_distances = [calculate_distance(trip['origin'], trip['destination']) 
                 for trip in trip_data]

# Conditional operations
mode_categories = ['sustainable' if mode in ['walk', 'bike', 'transit'] 
                  else 'auto' for mode in df['mode_choice']]

# Nested comprehensions for complex operations
zone_accessibility = {
    zone: [stop for stop in transit_stops 
           if distance(zone['centroid'], stop['location']) < 400]
    for zone in analysis_zones
}

Functions for Reusable Analysis

def calculate_gravity_model(origins, destinations, impedance_function):
    """
    Calculate gravity model for trip distribution
    """
    interactions = np.zeros((len(origins), len(destinations)))
    
    for i, origin in enumerate(origins):
        for j, destination in enumerate(destinations):
            distance = haversine_distance(origin['coords'], destination['coords'])
            impedance = impedance_function(distance)
            interactions[i, j] = (origin['production'] * 
                                destination['attraction'] * impedance)
    
    return interactions

def accessibility_index(zone_centroid, opportunities, decay_function):
    """
    Calculate accessibility index for a zone
    """
    accessibility = 0
    for opportunity in opportunities:
        distance = haversine_distance(zone_centroid, opportunity['location'])
        accessibility += opportunity['size'] * decay_function(distance)
    return accessibility

def mode_split_logit(utilities):
    """
    Calculate mode split using logit model
    """
    exp_utilities = np.exp(utilities)
    probabilities = exp_utilities / np.sum(exp_utilities)
    return probabilities

Planning-Specific Best Practices

πŸ“Š Data Quality

  • Always validate data sources and check for missing values
  • Document data transformations and assumptions
  • Use consistent coordinate reference systems
  • Implement data quality checks in your workflows

πŸ”„ Reproducibility

  • Use version control (Git) for all analysis code
  • Create virtual environments for each project
  • Document dependencies with requirements.txt
  • Write clear docstrings and comments

⚑ Performance

  • Use vectorized operations instead of loops
  • Leverage spatial indexing for large datasets
  • Consider using Dask for big data processing
  • Profile your code to identify bottlenecks

🎯 Planning Focus

  • Understand your audience and tailor visualizations
  • Use appropriate spatial scales for analysis
  • Consider equity implications in your analysis
  • Validate results against planning knowledge

Essential Planning Libraries Summary

pandas
Data Analysis
geopandas
Spatial Data
networkx
Network Analysis
folium
Interactive Maps
matplotlib
Visualization
scipy
Statistics

Common Planning Workflows

Accessibility Analysis Workflow:
1. Load transportation network and land use data
2. Calculate travel times between zones
3. Apply impedance function to travel times
4. Weight opportunities by accessibility measure
5. Visualize results on maps and charts
Transit Planning Workflow:
1. Import GTFS data and ridership information
2. Analyze service coverage and frequency
3. Calculate accessibility to transit stops
4. Evaluate equity and performance metrics
5. Generate reports and visualizations
Land Use Analysis Workflow:
1. Process parcel and zoning data
2. Calculate development capacity and density
3. Analyze land use mix and compatibility
4. Model growth scenarios and impacts
5. Create policy recommendations

Key Takeaways

πŸš€ Start Small

Begin with simple data analysis tasks and gradually incorporate more complex spatial and network analysis

πŸ“š Keep Learning

The Python ecosystem is constantly evolving. Stay updated with new libraries and techniques

🀝 Community

Join planning and Python communities to share knowledge and get help when needed

Python + Planning = Powerful Analysis

Transform your planning practice with data-driven insights and automated workflows