An Analysis of Population and Political Influence
The Infrastructure Investment and Jobs Act (IIJA) represents a historic investment in the infrastructure of the United States, aiming to address transportation, utilities, broadband, and more. With billions of dollars allocated across states and territories, questions of equity and potential political bias in the distribution of funds arise. This analysis seeks to answer two critical questions:
Using population data, IIJA funding allocation figures, and presidential election results, we aim to provide data-driven insights into the fairness and motivations of the funding distribution.
Visualization: The scatter plot below shows the relationship between state populations and their corresponding IIJA funding allocations. States with smaller populations often receive disproportionately higher funding per capita, indicating disparities in the distribution strategy.
Insights: - Larger states like California and Texas receive lower funding per capita despite their significant population sizes. - Smaller states and territories, such as Delaware, benefit from higher funding per capita.
Visualization: The bar plot below examines funding allocation by states’ voting patterns in the 2020 presidential election. States with higher votes for President Biden are compared against those with fewer votes.
Insights: - Funding distribution does not conclusively indicate overt political favoritism. - However, some trends show states with higher votes for the Biden administration receiving relatively favorable allocations.
The analysis reveals disparities in the per-capita allocation of IIJA funding, suggesting inequities in the distribution process. Smaller states and territories often benefit more on a per-capita basis, potentially due to baseline funding guarantees or other strategic priorities. While there is no definitive evidence of political bias, certain trends hint at possible alignments with the Biden administration’s interests.
The full Python code for data cleaning, merging, and visualization is included in the attached notebook.
“The allocation of IIJA funding reveals disparities in per-capita distributions across states, raising questions about equity and potential political bias in the funding strategy.”
This notebook analyzes the equity and political implications of IIJA funding allocations.
import pandas as pd
iija_data = pd.read_excel("IIJA FUNDING AS OF MARCH 2023.xlsx")
iija_data.head()
State, Teritory or Tribal Nation | Total (Billions) | |
---|---|---|
0 | ALABAMA | 3.0000 |
1 | ALASKA | 3.7000 |
2 | AMERICAN SAMOA | 0.0686 |
3 | ARIZONA | 3.5000 |
4 | ARKANSAS | 2.8000 |
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 57 entries, 0 to 56
Data columns (total 2 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 State, Teritory or Tribal Nation 57 non-null object
1 Total (Billions) 57 non-null float64
dtypes: float64(1), object(1)
memory usage: 1.0+ KB
Total (Billions) | |
---|---|
count | 57.000000 |
mean | 3.439165 |
std | 3.293913 |
min | 0.068600 |
25% | 1.300000 |
50% | 2.700000 |
75% | 3.900000 |
max | 18.400000 |
state | county | current_votes | total_votes | percent | |
---|---|---|---|---|---|
0 | Delaware | Kent County | 85415 | 87025 | 100 |
1 | Delaware | New Castle County | 280039 | 287633 | 100 |
2 | Delaware | Sussex County | 127181 | 129352 | 100 |
3 | Indiana | Adams County | 14154 | 14209 | 100 |
4 | Indiana | Allen County | 168312 | 169082 | 100 |
state | Abbreviation | |
---|---|---|
0 | Alabama | AL |
1 | Alaska | AK |
2 | Arizona | AZ |
3 | Arkansas | AR |
4 | California | CA |
print("Columns in iija_data:", iija_data.columns)
print("Columns in governors_data:", governors_data.columns)
print("Columns in state_abbreviations:", state_abbreviations.columns)
print("Columns in population_data:", population_data.columns)
Columns in iija_data: Index(['State, Teritory or Tribal Nation', 'Total (Billions)'], dtype='object')
Columns in governors_data: Index(['state', 'county', 'current_votes', 'total_votes', 'percent'], dtype='object')
Columns in state_abbreviations: Index(['state', 'State_Abbreviation'], dtype='object')
Columns in population_data: Index(['fips', 'state', 'densityMi', 'pop2024', 'pop2023', 'pop2020',
'pop2019', 'pop2010', 'growthRate', 'growth', 'growthSince2010', 'area',
'rank', 'percent'],
dtype='object')
iija_data.rename(columns={'State, Teritory or Tribal Nation': 'State', 'Total (Billions)': 'Funding Allocation'}, inplace=True)
population_data.rename(columns={'state': 'State', 'pop2023': 'Population'}, inplace=True)
print("Unique states in iija_data:", iija_data['State'].unique())
print("Unique states in population_data:", population_data['State'].unique())
Unique states in iija_data: ['ALABAMA' 'ALASKA' 'AMERICAN SAMOA' 'ARIZONA' 'ARKANSAS' 'CALIFORNIA'
'COLORADO' 'CONNECTICUT' 'DELEWARE' 'DISTRICT OF COLUMBIA' 'FLORIDA'
'GEORGIA' 'GUAM' 'HAWAII' 'IDAHO' 'ILLINOIS' 'INDIANA' 'IOWA' 'KANSAS'
'KENTUCKY' 'LOUISIANA' 'MAINE' 'MARYLAND' 'MASSACHUSETTS' 'MICHIGAN'
'MINNESOTA' 'MISSISSIPPI' 'MISSOURI' 'MONTANA' 'NEBRASKA' 'NEVADA'
'NEW HAMPSHIRE' 'NEW JERSEY' 'NEW MEXICO' 'NEW YORK' 'NORTH CAROLINA'
'NORTH DAKOTA' 'NORTHERN MARIANA ISLANDS' 'OHIO' 'OKLAHOMA' 'OREGON'
'PENNSYLVANIA' 'PUERTO RICO' 'RHODE ISLAND' 'SOUTH CAROLINA'
'SOUTH DAKOTA' 'TENNESSEE' 'TEXAS' 'TRIBAL COMMUNITIES'
'US VIRGIN ISLANDS' 'UTAH' 'VERMONT' 'VIRGINIA' 'WASHINGTON'
'WEST VIRGINIA' 'WISCONSIN' 'WYOMING']
Unique states in population_data: ['California' 'Texas' 'Florida' 'New York' 'Pennsylvania' 'Illinois'
'Ohio' 'Georgia' 'North Carolina' 'Michigan' 'New Jersey' 'Virginia'
'Washington' 'Arizona' 'Tennessee' 'Massachusetts' 'Indiana' 'Missouri'
'Maryland' 'Wisconsin' 'Colorado' 'Minnesota' 'South Carolina' 'Alabama'
'Louisiana' 'Kentucky' 'Oregon' 'Oklahoma' 'Connecticut' 'Utah' 'Iowa'
'Nevada' 'Arkansas' 'Kansas' 'Mississippi' 'New Mexico' 'Idaho'
'Nebraska' 'West Virginia' 'Hawaii' 'New Hampshire' 'Maine' 'Montana'
'Rhode Island' 'Delaware' 'South Dakota' 'North Dakota' 'Alaska'
'Vermont' 'Wyoming']
iija_data['State'] = iija_data['State'].str.strip().str.title()
population_data['State'] = population_data['State'].str.strip().str.title()
state_abbreviations['state'] = state_abbreviations['state'].str.strip().str.title()
missing_in_population = set(iija_data['State']) - set(population_data['State'])
missing_in_iija = set(population_data['State']) - set(iija_data['State'])
print("States missing in population data:", missing_in_population)
print("States missing in iija data:", missing_in_iija)
States missing in population data: {'District Of Columbia', 'Deleware', 'Guam', 'American Samoa', 'Puerto Rico', 'Northern Mariana Islands', 'Us Virgin Islands', 'Tribal Communities'}
States missing in iija data: {'Delaware'}
iija_data = iija_data[~iija_data['State'].isin([
'Guam', 'American Samoa', 'Puerto Rico',
'Northern Mariana Islands', 'Us Virgin Islands', 'Tribal Communities'
])]
missing_in_population = set(iija_data['State']) - set(population_data['State'])
missing_in_iija = set(population_data['State']) - set(iija_data['State'])
print("States missing in population data after cleaning:", missing_in_population)
print("States missing in iija data after cleaning:", missing_in_iija)
States missing in population data after cleaning: {'District Of Columbia'}
States missing in iija data after cleaning: set()
merged_data = pd.merge(iija_data, population_data[['State', 'Population']], on='State', how='inner')
merged_data = pd.merge(merged_data, state_abbreviations, left_on='State', right_on='state', how='left')
merged_data['Funding Per Capita'] = merged_data['Funding Allocation'] * 1e9 / merged_data['Population']
print(merged_data.head()) # Confirm that the merge is successful
State Funding Allocation Population state State_Abbreviation \
0 Alabama 3.0 5108468 Alabama AL
1 Alaska 3.7 733406 Alaska AK
2 Arizona 3.5 7431344 Arizona AZ
3 Arkansas 2.8 3067732 Arkansas AR
4 California 18.4 38965193 California CA
Funding Per Capita
0 587.260212
1 5044.954636
2 470.978063
3 912.726405
4 472.216319
import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize=(10, 6))
sns.scatterplot(data=merged_data, x='Population', y='Funding Allocation', hue='Funding Per Capita', size='Funding Per Capita', sizes=(20, 200))
plt.title('IIJA Funding Allocation vs Population by State')
plt.xlabel('Population')
plt.ylabel('Funding Allocation (Billions)')
plt.legend(title='Funding Per Capita')
plt.show()
png
merged_data['State'] = merged_data['State'].str.strip().str.title()
governors_data['State'] = governors_data['State'].str.strip().str.title()
# Check for missing states
missing_states = set(merged_data['State']) - set(governors_data['State'])
print("States in merged_data but not in governors_data:", missing_states)
# Exclude missing states if necessary
merged_data = merged_data[~merged_data['State'].isin(missing_states)]
# Re-run the merge
final_data = pd.merge(merged_data, governors_data[['State', 'current_votes']], on='State', how='inner')
# Debugging final_data
if final_data.empty:
print("final_data is still empty. Check for remaining mismatches or missing data.")
else:
print("final_data preview:\n", final_data.head())
common_states = set(merged_data['State']) & set(governors_data['State'])
filtered_merged_data = merged_data[merged_data['State'].isin(common_states)]
# Re-run the merge
final_data = pd.merge(filtered_merged_data, governors_data[['State', 'current_votes']], on='State', how='inner')
if final_data.empty:
print("final_data is still empty. Check if filtering is too restrictive.")
else:
print("final_data preview:\n", final_data.head())
final_data preview:
State Funding Allocation Population state State_Abbreviation \
0 Delaware 0.792 1031890 Delaware DE
1 Delaware 0.792 1031890 Delaware DE
2 Delaware 0.792 1031890 Delaware DE
3 Indiana 3.400 6862199 Indiana IN
4 Indiana 3.400 6862199 Indiana IN
Funding Per Capita current_votes
0 767.523670 85415
1 767.523670 280039
2 767.523670 127181
3 495.467998 14154
4 495.467998 168312
party_funding = final_data.groupby('current_votes')['Funding Allocation'].sum().reset_index()
party_funding.rename(columns={'current_votes': 'Votes for Biden'}, inplace=True)
# Visualization: Funding by political bias
if not party_funding.empty:
plt.figure(figsize=(8, 6))
sns.barplot(data=party_funding, x='Votes for Biden', y='Funding Allocation', hue='Votes for Biden', palette='coolwarm', dodge=False)
plt.title('Funding Allocation by Presidential Votes')
plt.xlabel('Votes for Biden')
plt.ylabel('Total Funding Allocation (Billions)')
plt.show()
else:
print("No data available for visualization.")
png