使用 Python 绘制美国选举分级统计图

news2024/9/17 6:57:07

「AI秘籍」系列课程:

  • 人工智能应用数学基础

  • 人工智能Python基础

  • 人工智能基础核心知识

  • 人工智能BI核心知识

  • 人工智能CV核心知识

如何创建美国选举结果的时间序列分级统计图

数据地址为源地址,如果失效请与我联系。

2024 年美国大选将至,关于此次选举据说非常戏剧,两位最大可能的提名候选人跟 2020 年如出一辙。不过,本次大选数据咱们是暂时没办法获得了,就拿 2020 年的那次数据来看看。

2020 年美国大选带来了高度紧张的气氛、毫无根据的欺诈指控,最重要的是,带来了一些很棒的可视化效果。至少对数据科学家来说,这很重要。似乎你无论在哪里都看不到一些新颖的选举结果呈现方式。那么为什么不再添加一些呢?在本教程中,你将学习如何使用 Python 创建一些自己的可视化效果。

img

你将学习如何创建两张 1976 年至 2016 年美国总统选举结果的交互式分级统计图。第一张地图有一个时间滑块。当你移动滑块时,地图将发生变化,以显示给定年份每个州的结果。对于第二张地图,每个州都变成了一个按钮。你可以单击该州以查看随时间变化的投票趋势。我将介绍代码,你可以在GitHub上找到完整的项目1。你也可以下载地图2,你应该能够在浏览器中打开和浏览它们。

img

Python 包

我们将使用folium3构建地图。这是一个非常有用的包,可用于创建简单的地理空间数据可视化。除了 folium 之外,我们还将使用一些其他 Python 包。你可以使用以下代码导入它们。请确保你已先安装所有包。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt, mpld3
%matplotlib inline
import json
import datetime

from shapely.geometry import Polygon, mapping
import geopandas as gpd
import folium
from folium.plugins import TimeSliderChoropleth

数据源

美国形状文件

我们需要的第一个数据集是美国的 shapefile4。shapefile是一种用于存储地理空间矢量数据的文件格式。在我们的例子中,我们有一组坐标,它们定义了美国每个州的边界。我们将数据读入为GeoPandas 数据框。数据框的51行中的每一行都给出了州的名称和坐标(即几何图形)。

# Get US states shapefile
us_shape = gpd.read_file(data_path + '/States_shapefile/States_shapefile.shp')
us_shape = us_shape[['State_Name','geometry']]
us_shape.head()

---
	State_Name	geometry
0	ALABAMA	POLYGON ((-85.07007 31.9807, -85.11515 31.9074...
1	ALASKA	MULTIPOLYGON (((-161.33379 58.73325, -161.3824...
2	ARIZONA	POLYGON ((-114.52063 33.02771, -114.55909 33.0...
3	ARKANSAS	POLYGON ((-94.46169 34.19677, -94.45262 34.508...
4	CALIFORNIA	MULTIPOLYGON (((-121.66522 38.16929, -121.7823...

让我们使用此 Shapefile 创建第一张 folium 地图。在下面的代码中,我们初始化地图。通过设置 location=[50.77500, -100],地图在打开时将聚焦于美国。然后,我们使用美国 Shapefile 和 GeoJson 函数向地图添加分级统计图。在图 1 中,你可以看到此代码创建的地图。Shapefile 为我们提供了工作基础,但我们需要另一个用于选举结果的数据集。

# plot the shape file with folium 
m = folium.Map(location=[50.77500, -100],zoom_start=3) 
choropleth =folium.GeoJson(data= us_shape.to_json())
m.add_child(choropleth)

图 1:美国 Shapefile 分级统计图

选举结果数据集

对于选举结果,我们使用麻省理工学院选举数据和科学实验室提供的数据集5。它包含 1976 年至 2020 年美国总统选举结果。该数据集包含该年每个州、年份和参选候选人的行。为了让事情变得简单一点,我们应该先将此数据集转换为不同的格式。

# Get election data
election = pd.read_csv(data_path +  "/U.S. President 1976–2020/1976-2020-president.csv" )
election.replace('democratic-farmer-labor','democrat',inplace=True)
election.head()

---
	year	state	state_po	state_fips	state_cen	state_ic	office	candidate	party_detailed	writein	candidatevotes	totalvotes	version	notes	party_simplified
0	1976	ALABAMA	AL	1	63	41	US PRESIDENT	CARTER, JIMMY	DEMOCRAT	False	659170	1182850	20210113	NaN	DEMOCRAT
1	1976	ALABAMA	AL	1	63	41	US PRESIDENT	FORD, GERALD	REPUBLICAN	False	504070	1182850	20210113	NaN	REPUBLICAN
2	1976	ALABAMA	AL	1	63	41	US PRESIDENT	MADDOX, LESTER	AMERICAN INDEPENDENT PARTY	False	9198	1182850	20210113	NaN	OTHER
3	1976	ALABAMA	AL	1	63	41	US PRESIDENT	BUBAR, BENJAMIN ""BEN""	PROHIBITION	False	6669	1182850	20210113	NaN	OTHER
4	1976	ALABAMA	AL	1	63	41	US PRESIDENT	HALL, GUS	COMMUNIST PARTY USE	False	1954	1182850	20210113	NaN	OTHER

使用下面的代码,我们将数据集转换为嵌套字典。对于每一年,我们都有一个以州名作为键的字典。对于每个州,都有一个字典给出民主党和共和党候选人的投票数。字典的形式如下:

{
  <year>: { 
    <state> : {'dem':<#votes>, 'rep':<#votes>},
    <state> : {'dem':<#votes>, 'rep':<#votes>},
    ...},
  ...
}
# Transform election data 
states = set(election['state'])

results = {}
for year in range(1976,2024,4):
    
    result = {}
    for state in states:
        
        state_year = election[(election.year == year) 
                              & (election.state == state)]
        dem = max(state_year[state_year.party_simplified == 'DEMOCRAT']['candidatevotes'])
        rep = max(state_year[state_year.party_simplified == 'REPUBLICAN']['candidatevotes'])
        
        result[state] = {'dem':dem, 'rep':rep}
        
    results[year] = result
    
results

---
{1976: {'TEXAS': {'dem': 2082319, 'rep': 1953300},
    ...
  'MISSOURI': {'dem': 998387, 'rep': 927443}},
 1980: {'TEXAS': {'dem': 1881147, 'rep': 2510705},
    ...
 2020: {'TEXAS': {'dem': 5259126, 'rep': 5890347},
    ...
  'MISSOURI': {'dem': 1253014, 'rep': 1718736}}}

数据可视化

在开始介绍交互式地图之前,让我们先使用这些数据集来创建一个简单的等值线图。我们首先需要定义两个函数。state_style 函数返回一个用于定义州的颜色和边界的字典。如果某个州在某一年投票给民主党,则该州将变为蓝色;如果投票给共和党,则该州将变为红色。该函数返回的字典略有不同,具体取决于它是被 style_dictionary 还是 style_function 使用。

def state_style(state,year,function=False):
    """
    Returns the style for a state in a given year
    """
    
    state_results = results[year][state]
    
    #Set state colour
    if state_results['dem'] >= state_results['rep']:
        color = '#4f7bff' #blue
    else:
        color = '#ff5b4f' #red
    
    #Set state style
    if function == False:
        # Format for style_dictionary
        state_style = {
            'opacity': 1,
            'color': color,
        } 
    else:
        # Format for style_fucntion
        state_style = {
             'fillOpacity': 1,
             'weight': 1,
             'fillColor': color,
             'color': '#000000'}    
  
    return state_style

对于此等值线图,我们将使用 style_function。 GeoJson 包使用此函数将 GeoJson 特征映射到样式。在我们的例子中,GeoJson 特征将包含有关州的信息(即名称和几何形状)。这些特征由 GeoJson 包传递给 style_function。通过设置 year=2020,我们使用 2020 年选举的结果来定义每个州的样式。

def style_function(feature):
    """
    style_function used by the GeoJson folium function
    """

    state = feature['properties']['State_Name']
    style = state_style(state,year=2020,function=True)
    
    return style

现在,我们可以使用这些函数来创建我们的第一个等值线图。代码与我们用于创建第一张地图的代码非常相似。唯一的区别是我们现在将 style_function 传递给 GeoJson 函数。如上所述,这会根据选举结果为每个州赋予一种颜色。生成的地图如图 2 所示。现在,让我们看看如何改进这张地图并使其更具交互性。

# plot the choropleth 
m = folium.Map(location=[50.77500, -100],zoom_start=3)
choropleth =folium.GeoJson(data= us_shape.to_json(),style_function=style_function)
m.add_child(choropleth)

图 2:2020 年选举结果

地图 1:分级统计图滑块

我们首先创建一个带有时间滑块的等值线地图。这是使用 TimeSliderChoropleth 函数完成的。此函数假定所有日期都采用 Unix 时间格式(即时间戳)。因此,我们使用year_to_ts函数将选举年份转换为时间戳。例如,2020 年将转换为 “1577808000”。

def year_to_ts(year):
    """
    Convert year to timestamp
    """
    time = datetime.datetime(year, 1, 1, 0, 0).strftime('%s')
    if len(time)==9: time ='0{}'.format(time)
    return time

我们需要定义的第二个函数style_dictionary返回一个样式字典。这与style_function类似,只是我们现在处理的是时间序列数据。因此,对于每个州,我们需要定义其从 1976 年到 2016 年每年的样式。style_dictionary函数返回一个嵌套字典,形式如下:

{
  <ID>: { 
    <timestamp> : {'opacity':1, 'color':<hex_color>},
    <timestamp> : {'opacity':1, 'color':<hex_color>},
    ...},
  ...
}

上面提到的 ID 是分配给每个州的唯一 ID。它由.to_json()函数自动分配。TimeSliderChoropleth 使用这些 ID 将州映射到其样式。因此,为了确保我们有正确的映射,我们首先创建从 ID 到州名的映射。这在下面的第 7 行到第 13 行中完成。该函数的其余部分使用上面看到的形式创建字典。

def style_dictionary():
    """
    style_dictionary used by the TimeSliderChoropleth folium function
    """
    
    # get ids used by TimeSliderChoropleth
    ID = {}
    state_json = json.loads(us_shape.to_json())

    for state in state_json['features']:
        state_id = state['id']
        state_name = state['properties']['State_Name']
        ID[state_name] = state_id
    
    
    #create style dictionary
    style_dic= {}
    for state in states:
        
        state_dic = {}
        for year in range(1976,2024,4):
            
            time = year_to_ts(year)
            state_dic[time] =  state_style(state,year)

        style_dic[ID[state]] = state_dic  
        
    return style_dic

现在我们准备创建地图了。同样,代码与之前类似,只是我们使用了 TimeSliderChoropleth 函数并传入了样式字典。代码的结果可以在图 3 中看到。你将能够滑动地图顶部的条形图来查看随时间变化的选举结果。例如,从 2012 年到 2020 年,我们可以看到几个州变成红色。这导致共和党候选人获胜。

# Create time slider map
m = folium.Map(location=[50.77500, -100],zoom_start=3) 
ts = TimeSliderChoropleth(us_shape.to_json(), style_dictionary())
m.add_child(ts)

m.save("../figures/us_election_map1.html")

图 3:分级统计图滑块

我们应该在上面的第 6 行提到保存地图的代码。此行将地图保存为 HTML 文件。你可以在任何浏览器中打开并浏览它。如果你使用的是 jupyter 笔记本,地图也会显示在代码块下方。如果地图太复杂,笔记本可能无法呈现它。在这种情况下,你必须保存地图并在浏览器中打开它,然后才能看到它。

地图 2:分级统计图按钮

对于下一张地图,我们将把每个州变成一个按钮。你可以单击该州以查看随时间变化的投票趋势。首先,要创建这些趋势图,我们使用以下代码。getFigure 函数为给定的州创建标准 matplotlib 图表。在最后几行中,我们将图表转换为 HTML 并将其添加到 IFrame。这样它就可以嵌入到我们的 folium 地图中。你可以在图 5 中看到为加利福尼亚州制作的图表示例。

def getFigure(state):
    """
    Plot voting trends from a given state
    """

    #Get number of votes
    years = range(1976,2024,4)
    dems = []
    reps =[]
    for year in years:

        result = results[year][state]
        dems.append(result['dem']/1000000)  
        reps.append(result['rep']/1000000) 

    #Plot number of votes    
    fig = plt.figure(figsize=(8,4))
    plt.plot(years,dems,label='Democrat',color='#4f7bff')
    plt.plot(years,reps,label='Republican',color='#ff5b4f')

    plt.title(state,size = 18)
    plt.ticklabel_format(style='plain')
    plt.xlabel('Year',size =14)
    plt.xticks(years)
    plt.ylabel('Votes (millions)',size =14)
    plt.legend(loc =0)

    #Add figure to iframe
    html = mpld3.fig_to_html(fig)
    iframe = folium.IFrame(html=html,width = 600, height = 300)

    return iframe

图5:加州投票趋势

在创建按钮等值线图之前,我们必须定义最后一个函数。highlight_style 函数用于定义鼠标悬停在某个状态上时的样式。发生这种情况时,该状态将变得略微阴影化。这使我们能够在单击鼠标之前看到鼠标处于什么状态。

def highlight_style(feature): 
    """
    style_function used when choropleth button
    is highighted
    """
    return {'fillOpacity': 0.2,
         'weight': 1,
         'fillColor': '#000000',
         'color': '#000000'}   

最后,为了创建地图,我们首先使用 2020 年的结果创建一个分级统计图。我们使用与图 2 中的地图完全相同的代码来执行此操作。然后,使用每个州的几何图形,我们创建一个州标记并向每个标记添加一个弹出窗口。每个弹出窗口都包含上面讨论的嵌入式图表之一。单击标记时,将显示弹出窗口,我们将能够看到投票趋势。

# plot the shape file with folium 
m = folium.Map(location=[50.77500, -100],zoom_start=5,max_zoom=5) #Initialize map
choropleth =folium.GeoJson(data= us_shape.to_json(),
                           style_function=style_function)
m.add_child(choropleth)

# Create popup button for each state
for i in range(len(us_shape)):
    
    geometry = us_shape.loc[i]['geometry']
    state_name = us_shape.loc[i]['State_Name']
    popup = folium.Popup(getFigure(state_name),max_width=1000)
    
    state_marker = folium.GeoJson(data=mapping(geometry),
                                  highlight_function = highlight_style)
    state_marker.add_child(popup)
    m.add_child(state_marker)

m.save("../figures/us_election_map2.html")

你可以在图 4 中看到此代码的结果。你可以看到将鼠标悬停在某个州上方会如何突出显示该州。还可以单击德克萨斯州和加利福尼亚州以显示其趋势。在笔记本中查看此地图可能会有些困难。在这种情况下,请将其保存为 HTML 文件并在浏览器中打开。

在这里插入图片描述

本文到这里就要结束了,与本文不同,2024 年美国大选尚未开始。届时,会有很多新的数据可供使用,我们将能够使用 20204 年的数据更新可视化。我们会看到各州的颜色发生变化,趋势是否发生变化。这些变化的原因很复杂。像这样的可视化是帮助我们理解它们的一个很好的步骤。



  1. 茶桁的公开文章代码仓库, https://github.com/hivandu/public_articles ↩︎

  2. 地图 HTML 文件: https://github.com/hivandu/public_articles/tree/main/maps ↩︎

  3. Folium, https://python-visualization.github.io/folium/ ↩︎

  4. shapefile, https://alicia.data.socrata.com/Government/States-21basic/jhnu-yfrj/data ↩︎

  5. 选举数据, https://dataverse.harvard.edu/dataset.xhtml?persistentId=doi:10.7910/DVN/42MVDX ↩︎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1916895.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法通关:004_1选择排序

代码一定要自己手敲理解 public class _004 {//选择排序&#xff0c;冒泡排序&#xff0c;插入排序//交换public static void swap(int[] arr,int i ,int j){int temp arr[i];arr[i] arr[j];arr[j] temp;}//选择排序public static void selectSort(int[] arr){if(arr null…

C++ | Leetcode C++题解之第225题用队列实现栈

题目&#xff1a; 题解&#xff1a; class MyStack { public:queue<int> q;/** Initialize your data structure here. */MyStack() {}/** Push element x onto stack. */void push(int x) {int n q.size();q.push(x);for (int i 0; i < n; i) {q.push(q.front());…

LabVIEW实现LED显示屏视觉检测

为了满足LED显示屏在生产过程中的严格质量检测需求&#xff0c;引入自动化检测系统是十分必要的。传统人工检测方式存在检测强度高、效率低、准确性差等问题&#xff0c;自动化检测系统则能显著提高检测效率和准确性。视觉检测系统的构建主要包含硬件和软件两个部分。 视觉系统…

JDK中不能继承的类:final类的作用与意义

JDK中不能继承的类&#xff1a;final类的作用与意义 1、 为什么要用final类&#xff1f;2、JDK中有哪些final类&#xff1f;3、总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Java编程中&#xff0c;有些类被标记为final&#xff0c…

前端面试题47(在动态控制路由时,如何防止未授权用户访问受保护的页面?)

在Vue中&#xff0c;防止未授权用户访问受保护页面通常涉及到使用路由守卫&#xff08;Route Guards&#xff09;。路由守卫允许你在路由发生改变前或后执行一些逻辑&#xff0c;比如检查用户是否已登录或者有访问某个页面的权限。下面是一些常见的路由守卫类型及其使用方式&am…

MapReduce底层原理详解:大案例解析(第32天)

系列文章目录 一、MapReduce概述 二、MapReduce工作机制 三、Map&#xff0c;Shuffle&#xff0c;reduce阶段详解 四、大案例解析 文章目录 系列文章目录前言一、MapReduce概述二、MapReduce工作机制1. 角色与组件2. 作业提交与执行流程1. 作业提交&#xff1a;2. Map阶段&…

IntelliJ IDEA社区版在Windows电脑中的下载、安装方法

本文介绍IntelliJ IDEA软件Community&#xff08;社区版&#xff09;在Windows操作系统中的下载、安装、运行与使用方法。 IntelliJ IDEA软件是一款由JetBrains公司开发的集成开发环境&#xff08;IDE&#xff09;&#xff0c;主要用于Java语言的开发&#xff0c;但同时也支持其…

面试经验之谈

优质博文&#xff1a;IT-BLOG-CN ​通常面试官会把每一轮面试分为三个环节&#xff1a;① 行为面试 ② 技术面试 ③ 应聘者提问 行为面试环节 面试开始的5~10分钟通常是行为面试的时间&#xff0c;面试官会参照简历和你的自我介绍了解应聘者的过往经验和项目经历。由于面试官…

读书笔记-Java并发编程的艺术-第4章(Java并发编程基础)-第4节(线程应用实例)

文章目录 4.4 线程应用实例4.4.1 等待超时模式4.4.2 一个简单的数据库连接池示例4.4.3 线程池技术及其示例4.4.4 一个基于线程池技术的简单 Web 服务器 4.4 线程应用实例 4.4.1 等待超时模式 开发人员经常会遇到这样的方法调用场景&#xff1a;调用一个方法时等待一段时间(一…

postgres 的dblink使用,远程连接数据库

一.安装下载 dblink create extension if not exists dblink 查看是否已经安装 select * from pg_extension;二.运行&#xff0c;查询数据 其中&#xff0c;第一个参数是dblink名字&#xff0c;也可以是连接字符串。 第二个参数是要执行的SQL查询语句。AS子句用于指定返回结…

网桥与以太网交换机:功能与区别解析

在传统的共享式局域网中&#xff0c;所有站点共享一个公共的传输媒体。随着局域网规模的扩大、网络中站点数目的不断增加&#xff0c;这样的网络通信负载加重&#xff0c;网络效率急剧下降。随着技术的发展、交换技术的成熟和成本的降低&#xff0c;具有更高性能的交换式局域网…

kali安装vulhub遇到的问题及解决方法(docker及docker镜像源更换)

kali安装vulhub&#xff1a; 提示&#xff1a;项目地址 https://github.com/vulhub/vulhub 项目安装&#xff1a; git clone https://github.com/vulhub/vulhub.git 安装docker 提示&#xff1a;普通用户请使用sudo&#xff1a; 首先安装 https 协议、CA 证书 apt-get in…

Apache Flink 运行时架构

Flink 运行时架构 Flink整个系统由两个主要部分组成JobManager和TaskManager&#xff0c;Flink架构也遵循Master-Slave架构设计原则&#xff0c;JobManager为Master节点&#xff0c;TaskManager为worker&#xff08;Slave&#xff09;节点&#xff0c;所有组件之间通讯都是借助…

内容协商源码解析与自定义 MessageConverter

目录 内容协商 1、引入xml依赖 2、postman分别测试返回json和xml 3、开启浏览器参数方式内容协商功能 4、内容协商原理 5、自定义 MessageConverter 综上 内容协商 根据客户端接收能力不同&#xff0c;返回不同媒体类型的数据。 若客户端无法解析服务端返回的内容&#…

CinemachineBrain的属性简介

CinemachineBrain的属性简介 CinemachineBrain是Unity Cinemachine的核心组件&#xff0c;它和Camera组件挂载在一起&#xff0c;监控场景中所有的virtual camera。CinemachineBrain在inspector中暴露的属性如下&#xff1a; Live Camera和Live Blend分别表示当前active的virtu…

深度学习和NLP中的注意力和记忆

深度学习和NLP中的注意力和记忆 文章目录 一、说明二、注意力解决了什么问题&#xff1f;#三、关注的代价#四、机器翻译之外的关注#五、注意力&#xff08;模糊&#xff09;记忆&#xff1f;# 一、说明 深度学习的最新趋势是注意力机制。在一次采访中&#xff0c;现任 OpenAI 研…

矩阵分解及其在机器学习中的应用

阵分解是一种广泛应用于数据挖掘和机器学习领域的技术&#xff0c;它通过将一个高维数据集分解为多个低维的数据集&#xff0c;以降低数据的复杂性、提高计算效率&#xff0c;并发现数据中的隐含结构。本文将详细介绍矩阵分解的基本概念、主要方法及其在机器学习中的应用。 一、…

Spark项目通用开发框架

文章目录 1. 大数据项目结构2. 类说明2.1 公共接口类2.2 TaskNameEnum指定每个任务的名称2.3 TaskRunner中编写任务的业务逻辑 3. 任务执行脚本 每个公司内部都有一套自己的架子&#xff0c;一般新人来了就直接在已有的架子上开发业务。 以下仅仅作为记录下自己使用的架子&…

低代码平台赋能企业全面数字化转型

引言&#xff1a;在当今这个日新月异的数字化时代&#xff0c;企业正面临着前所未有的机遇与挑战。为了保持竞争力并实现可持续发展&#xff0c;企业亟需进行全面的数字化转型。而低代码平台作为数字化转型的重要工具&#xff0c;正以其独特的优势赋能企业&#xff0c;推动其向…