Python通过matplotlib动态绘图实现中美GDP历年对比趋势动图

news/2024/11/22 20:28:23/

随着中国的各种实力的提高,经常在各种媒体上看到中国与各个国家历年的各种指标数据的对比,为了更清楚的展示历年的发展趋势,有的还做成了动图,看到中国各种指标数据的近年的不断逆袭,心中的自豪感油然而生。今天通过Python来实现matplotlib的动态绘图,将中美两国近年的GDP做个对比,展示中国GPD对美国的追赶态势,相信不久的将来中国的GDP数据将稳超美国。
效果如下:
中美GDP历年对比趋势动图

实现上面的动态绘图效果,综合用到了pandas的数据采集、数据整理、matplotlib绘图、坐标轴及数据的动态定义、定时器等知识。最终通过Python的GUI库PySide进行展示形成一个GUI的可执行程序。

一、数据采集和准备

中美历年的GDP数据通过百度在网上一搜一大把。我是从https://www.kylc.com/stats/global/yearly_per_country/g_gdp/chn-usa.html 找到的数据。将数据整理成EXCEL保存至data\中国VS美国.xlsx。
中国VS美国GDP数据集

有从1966年至2022年的中美GDP的数据。
首先对这些数据进行整理,因为获取的GDP数据是字符串类型如17.96万亿 (17,963,170,521,079),我们需要将GDP的数据从文本中提取出来,也就是取括号中的数据。
这里通过正则表达式将括号中的GDP数据提取出来,并转换为亿元为单位。

import re
import pandas as pd
import locale
import matplotlib.pyplot as pltpattern = re.compile('\((\S*)\)')def getgdpvalue(gdpstr):re_obj=pattern.search(gdpstr)gdp_value=locale.atof(re_obj.group(1))/100000000return gdp_valuedf_data = pd.read_excel('data\中国VS美国.xlsx')
df_data = df_data.loc[1:len(df_data)]
df_data['china_gdp_value'] = df_data['中国'].map(getgdpvalue)
df_data['us_gdp_value'] = df_data['美国'].map(getgdpvalue)
df_data = df_data.sort_values('年份')

有了数据以后就可以通过数据绘图了。

二、matplotlib绘图

先通过matplotlib绘图看看数据的效果。

import matplotlib.pyplot as plt
plt.figure()
plt.plot(df_data['年份'],df_data['china_gdp_value'])
plt.plot(df_data['年份'],df_data['us_gdp_value'])
plt.title('中美GDP对比')
plt.xlabel('年份')
plt.ylim('GDP(亿)')
plt.show()

中美GDP对比趋势

可以看到中国的GDP数据在1960年至1990年都是比较平稳的,到了1990年后中国开始了爆发式的追赶模式。
我们要将这种趋势通过动态的方式展示出来。

三、数据展示与动态更新

首先通过QMainWindw定义QWidget组件,在QWidget中加入FigureCanvasQTAgg组件通过canvas载入matplotlib绘图。

class ApplicationWindow(QMainWindow):def __init__(self, parent=None,org_data=None):QMainWindow.__init__(self, parent)self.axes = Noneself.axis_china=Noneself.axis_us=Noneself.datacount=10self.org_data = org_dataself.auto_offset = 0# Central widgetself._main = QWidget()self.setCentralWidget(self._main)# Figureself.canvas = FigureCanvasQTAgg(figure)if len(self.org_data) > 0:show_data = self.org_data[0:self.datacount]self.axes = self.canvas.figure.subplots()self.axes.set_title('中美GDP对比')self.axis_china = self.axes.plot(show_data['年份'], show_data['china_gdp_value'], label='中国GDP')self.axis_us = self.axes.plot(show_data['年份'], show_data['us_gdp_value'], label='美国GDP')y_max = max(self.org_data['us_gdp_value'].max(), self.org_data['china_gdp_value'].max())self.axes.set_ylabel('GDP(亿元)')self.axes.set_xlabel('年份')self.axes.set_ylim(0, y_max)self.axes.set_xlim(show_data['年份'].min(), show_data['年份'].max())self.axes.legend(loc="upper left")self.axes.yaxis.set_major_locator(mticker.MultipleLocator(20000))self.axes.xaxis.set_major_locator(mticker.MultipleLocator(1))figure.tight_layout()  # 自动调整子图参数,使之填充整个图像区域# 下拉框,选择模式 # ComboBox (combo_type)self.combo_type = QComboBox()self.combo_type.addItems(['自动播放', '手动播放'])# Slidersmin_value = 0self.max_value = len(self.org_data)-cur_data_lenself.slider_update = QSlider(minimum=min_value, maximum=self.max_value, orientation=Qt.Horizontal) # 滑动条layout1 = QHBoxLayout()layout1.addWidget(self.combo_type)# layoutlayout2 = QVBoxLayout()layout2.addWidget(self.canvas, 88)layout2.addWidget(self.slider_update)# Main layoutlayout = QVBoxLayout(self._main)layout.addLayout(layout1)layout.addLayout(layout2, 100)self.canvas.draw()# Signal and Slots connectionsself.combo_type.currentTextChanged.connect(self.selecttype)self.slider_update.valueChanged.connect(self.update_frequency)self.autoslider()

一种方式是通过QSlider组件,通过手动拉slider组件来实现数据的变化,一种通过QTimer组件自动让数据变化。

1、QSlider组件,手动方式实现动态绘图

@Slot()
def update_frequency(self, new_val):# 偏移量每次偏移1f = int(new_val)offset = f + cur_data_len  # 偏移刻度show_data = self.org_data[f: offset]x = show_data['年份']y_china = show_data['china_gdp_value']y_us = show_data['us_gdp_value']self.axes.set_xlim(x.min(), x.max())self.axis_china[0].set_data(x, y_china)self.axis_us[0].set_data(x, y_us)self.canvas.draw()

手动拉slider组件来实现数据的变化效果:
手动数据变化

2、QTimer组件,自动动态绘图

    self.autoslider()def autoslider(self):self.timer = QTimer()self.timer.setInterval(100) # 100毫秒更新一次数据self.timer.timeout.connect(self.autoupdate) #自动更新数据,每次更新偏移量加1,也就是跳一年的数据 self.timer.start()def autoupdate(self):self.update_frequency(self.auto_offset)self.slider_update.setSliderPosition(self.auto_offset)if self.auto_offset < self.max_value:self.auto_offset = self.auto_offset+1else:self.auto_offset = 0

效果如文章最前面所示。

四、完整代码

import re
import sys
import pandas as pd
import locale
import matplotlib.ticker as mticker
from PySide6.QtCore import Qt, Slot, QTimer
from PySide6.QtWidgets import QMainWindow, QApplication, QVBoxLayout, QHBoxLayout, QWidget, QSlider, QComboBox
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg
from matplotlib.figure import Figurefigure = Figure(figsize=(12, 6), dpi=90)global cur_data_len, cur_major_locator
cur_data_len = 10  # 当前显示的数据量(显示10年的数据)
cur_major_locator = 10  # 当前刻度的定位器(主刻度)pattern = re.compile('\((\S*)\)')def getgdpvalue(gdpstr):re_obj=pattern.search(gdpstr)gdp_value=locale.atof(re_obj.group(1))/100000000return gdp_valueclass ApplicationWindow(QMainWindow):def __init__(self, parent=None,org_data=None):QMainWindow.__init__(self, parent)self.axes = Noneself.axis_china=Noneself.axis_us=Noneself.datacount=10self.org_data = org_dataself.auto_offset = 0# Central widgetself._main = QWidget()self.setCentralWidget(self._main)# Figureself.canvas = FigureCanvasQTAgg(figure)if len(self.org_data) > 0:show_data = self.org_data[0:self.datacount]self.axes = self.canvas.figure.subplots()self.axes.set_title('中美GDP对比')self.axis_china = self.axes.plot(show_data['年份'], show_data['china_gdp_value'], label='中国GDP')self.axis_us = self.axes.plot(show_data['年份'], show_data['us_gdp_value'], label='美国GDP')y_max = max(self.org_data['us_gdp_value'].max(), self.org_data['china_gdp_value'].max())self.axes.set_ylabel('GDP(亿元)')self.axes.set_xlabel('年份')self.axes.set_ylim(0, y_max)self.axes.set_xlim(show_data['年份'].min(), show_data['年份'].max())self.axes.legend(loc="upper left")self.axes.yaxis.set_major_locator(mticker.MultipleLocator(20000))self.axes.xaxis.set_major_locator(mticker.MultipleLocator(1))figure.tight_layout()  # 自动调整子图参数,使之填充整个图像区域# 下拉框,选择模式 # ComboBox (combo_type)self.combo_type = QComboBox()self.combo_type.addItems(['自动播放', '手动播放'])# Slidersmin_value = 0self.max_value = len(self.org_data)-cur_data_lenself.slider_update = QSlider(minimum=min_value, maximum=self.max_value, orientation=Qt.Horizontal) # 滑动条layout1 = QHBoxLayout()layout1.addWidget(self.combo_type)# layoutlayout2 = QVBoxLayout()layout2.addWidget(self.canvas, 88)layout2.addWidget(self.slider_update)# Main layoutlayout = QVBoxLayout(self._main)layout.addLayout(layout1)layout.addLayout(layout2, 100)self.canvas.draw()# Signal and Slots connectionsself.combo_type.currentTextChanged.connect(self.selecttype)self.slider_update.valueChanged.connect(self.update_frequency)self.autoslider()def autoslider(self):self.timer = QTimer()self.timer.setInterval(100) # 100毫秒更新一次数据self.timer.timeout.connect(self.autoupdate) #自动更新数据,每次更新偏移量加1,也就是跳一年的数据self.timer.start()def autoupdate(self):self.update_frequency(self.auto_offset)self.slider_update.setSliderPosition(self.auto_offset)if self.auto_offset < self.max_value:self.auto_offset = self.auto_offset+1else:self.auto_offset = 0@Slot()def selecttype(self, text):if '自动播放' == text:self.autoslider()elif '手动播放' == text:self.timer.stop()@Slot()def update_frequency(self, new_val):# 偏移量每次偏移1f = int(new_val)offset = f + cur_data_len  # 偏移刻度show_data = self.org_data[f: offset]x = show_data['年份']y_china = show_data['china_gdp_value']y_us = show_data['us_gdp_value']self.axes.set_xlim(x.min(), x.max())self.axis_china[0].set_data(x, y_china)self.axis_us[0].set_data(x, y_us)self.canvas.draw()if __name__ == "__main__":app = QApplication(sys.argv)locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')df_data = pd.read_excel('data\中国VS美国.xlsx')df_data = df_data.loc[1:len(df_data)]df_data['china_gdp_value'] = df_data['中国'].map(getgdpvalue)df_data['us_gdp_value'] = df_data['美国'].map(getgdpvalue)df_data = df_data.sort_values('年份')w = ApplicationWindow(org_data=df_data)w.setFixedSize(1000, 500)w.show()app.exec()

六、总结

Python实现matplotlib动态绘图,是非常简单和容易的,其实关键还是在数据的组织,也就是要准备好要绘图的坐标轴的x的数据和y的数据,通过set_data(x,y)来动态更新数据,要注意的是变化的数据后X轴或Y轴的显示要变化,这里可以通过轴的set_xlim()或set_ylim()方法来动态设置,刻度也可通过set_major_locator()来指定。

完整代码及数据集见 http://xiejava.ishareread.com/


作者博客:http://xiejava.ishareread.com/


http://www.ppmy.cn/news/1064574.html

相关文章

CSS3渐变及2D转换

CSS3渐变及2D转换 持续更新哦… 1、css3渐变 概念: CSS3渐变(gradient)可以让你在两个或多个指定的颜色之间显示平 稳的过渡。以前&#xff0c;你必须使用图像来实现这些效果&#xff0c;现在通过使用 CSS3的渐变(gradients)即可实现。此外&#xff0c;渐变效果的元素在放大…

Springboot实现ENC加密

Springboot实现ENC加密 1、导入依赖2、配置加密秘钥&#xff08;盐&#xff09;3、获取并配置密文4、重启项目测试5、自定义前缀、后缀6、自定义加密方式 1、导入依赖 关于版本&#xff0c;需要根据spring-boot版本&#xff0c;自行修改 <dependency><groupId>co…

Dataset类实践

Dataset类实践 蚂蚁蜜蜂分类数据集和下载链接https://download.pytorch.org/tutorial/hymenoptera_data.zip Dataset&#xff1a;提供一种方式去获取数据及其lable Q&#xff1a;如何获取每个数据及其lable 重写构造方法和获取标签方法 Q&#xff1a;告诉我们总共有多少数据 …

【LeetCode-面试经典150题-day14】

目录 19.删除链表的倒数第N个结点 82.删除排序链表中的重复元素Ⅱ 61. 旋转链表 86.分隔链表 146.LRU缓存 19.删除链表的倒数第N个结点 题意&#xff1a; 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 【输入样例】head [1,2,3,4,5…

android 下载网络文件

工具类 import android.app.ProgressDialog; import android.content.Context; import android.os.AsyncTask; import android.os.Environment; import android.util.Log;import java.io.BufferedInputStream; import java.io.File; import java.io.FileOutputStream; import …

C++动态库编程 | C++名称改编、标准C接口、extern “C”、函数调用约定以及def文件详解

目录 1、导入导出声明 2、C函数名称改编与extern "C" 3、函数调用约定与跨语言调用 3.1、函数调用约定 3.2、跨语言调用dll库接口 3.3、函数调用约定以哪个为准 4、def文件的使用 5、在C程序中引用ffmpeg库中的头文件链接报错问题 6、最后 VC常用功能开发汇…

[LitCTF 2023]Flag点击就送!

进入环境后是一个输入框&#xff0c;可以提交名字 然后就可以点击获取flag&#xff0c;结果回显提示&#xff0c;需要获取管理员 可以尝试将名字改为admin 触发报错&#xff0c;说明可能存在其他的验证是否为管理员的方式 通过抓包后&#xff0c;在cookie字段发现了 特殊的东西…

C++中的Pimpl和RAII惯用法

一、PImpl 惯用法 PImpl&#xff08;Pointer to implementation&#xff09;是一种比较常见的C编程技巧&#xff0c;采用这种技巧能够减少代码依赖以及编译时间&#xff0c;具体思想是&#xff1a;将类的实现细节&#xff08;如一些非虚的私有成员&#xff09;从对象的表示中移…