Python uses matplotlib dynamic drawing to realize the comparative trend of GDP between China and the United States over the years.

As China’s various strengths improve, we often see comparisons of various indicator data between China and various countries over the years in various media. In order to more clearly display the development trends over the years, some even make animated graphics. Seeing China’s continuous counterattacks in various indicator data in recent years, I feel proud in my heart. Today, we use Python to implement matplotlib’s dynamic drawing, compare the GDP of China and the United States in recent years, and show China’s GPD catching up with the United States. I believe that China’s GDP data will steadily surpass the United States in the near future.
The effect is as follows:
China-US GDP comparison trend animation over the years

To achieve the above dynamic drawing effect, pandas data collection, data sorting, matplotlib drawing, dynamic definition of coordinate axes and data, timers and other knowledge are comprehensively used. Finally, it is displayed through Python’s GUI library PySide to form a GUI executable program.

1. Data collection and preparation

A lot of GDP data from China and the United States over the years can be searched online through Baidu. I found the data from https://www.kylc.com/stats/global/yearly_per_country/g_gdp/chn-usa.html. Organize the data into EXCEL and save it to data\China VS United States.xlsx.
China VS US GDP data set

There is data on the GDP of China and the United States from 1966 to 2022.
First, sort out the data. Because the GDP data obtained is of string type such as 17.96 trillion (17,963,170,521,079), we need to extract the GDP data from the text, that is, take the data in brackets.
Here, the GDP data in brackets are extracted through regular expressions and converted into units of 100 million yuan.

import re
import pandas as pd
import locale
import matplotlib.pyplot as plt

pattern = re.compile('\((\S*)\)')

def getgdpvalue(gdpstr):
    re_obj=pattern.search(gdpstr)
    gdp_value=locale.atof(re_obj.group(1))/100000000
    return gdp_value
    
df_data = pd.read_excel('data\China VS United States.xlsx')
df_data = df_data.loc[1:len(df_data)]
df_data['china_gdp_value'] = df_data['China'].map(getgdpvalue)
df_data['us_gdp_value'] = df_data['United States'].map(getgdpvalue)
df_data = df_data.sort_values('Year')

Once you have the data, you can draw data.

2. matplotlib drawing

First, let’s plot the data with matplotlib to see the effect of the data.

import matplotlib.pyplot as plt
plt.figure()
plt.plot(df_data['year'],df_data['china_gdp_value'])
plt.plot(df_data['year'],df_data['us_gdp_value'])
plt.title('Comparison of GDP between China and the United States')
plt.xlabel('Year')
plt.ylim('GDP (billion)')
plt.show()

Comparative Trend of GDP between China and the United States

It can be seen that China’s GDP data was relatively stable from 1960 to 1990. After 1990, China began an explosive catch-up mode.
We want to display this trend in a dynamic way.

3. Data display and dynamic update

First define the QWidget component through QMainWindw, and add the FigureCanvasQTAgg component to QWidget to load matplotlib drawing through canvas.

class ApplicationWindow(QMainWindow):

    def __init__(self, parent=None,org_data=None):
        QMainWindow.__init__(self, parent)
        self.axes = None
        self.axis_china=None
        self.axis_us=None
        self.datacount=10
        self.org_data = org_data
        self.auto_offset = 0
        # Central widget
        self._main = QWidget()
        self.setCentralWidget(self._main)
        # Figure
        self.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('Comparison of GDP between China and the United States')
            self.axis_china = self.axes.plot(show_data['Year'], show_data['china_gdp_value'], label='China GDP')
            self.axis_us = self.axes.plot(show_data['Year'], show_data['us_gdp_value'], label='US GDP')
            y_max = max(self.org_data['us_gdp_value'].max(), self.org_data['china_gdp_value'].max())
            self.axes.set_ylabel('GDP(100 million yuan)')
            self.axes.set_xlabel('Year')
            self.axes.set_ylim(0, y_max)
            self.axes.set_xlim(show_data['Year'].min(), show_data['Year'].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() # Automatically adjust the subfigure parameters to fill the entire image area
        # Drop-down box, select mode # ComboBox (combo_type)
        self.combo_type = QComboBox()
        self.combo_type.addItems(['Automatic play', 'Manual play'])
        #Sliders
        min_value = 0
        self.max_value = len(self.org_data)-cur_data_len
        self.slider_update = QSlider(minimum=min_value, maximum=self.max_value, orientation=Qt.Horizontal) # Slider
        layout1 = QHBoxLayout()
        layout1.addWidget(self.combo_type)
        # layout
        layout2 = QVBoxLayout()
        layout2.addWidget(self.canvas, 88)
        layout2.addWidget(self.slider_update)
        # Main layout
        layout = QVBoxLayout(self._main)
        layout.addLayout(layout1)
        layout.addLayout(layout2, 100)
        self.canvas.draw()
        # Signal and Slots connections
        self.combo_type.currentTextChanged.connect(self.selecttype)
        self.slider_update.valueChanged.connect(self.update_frequency)
        self.autoslider()

One way is to use the QSlider component to manually pull the slider component to change the data, and the other way is to use the QTimer component to automatically change the data.

1. QSlider component, manually implement dynamic drawing

@Slot()
def update_frequency(self, new_val):
    # The offset is offset by 1 each time
    f = int(new_val)
    offset = f + cur_data_len # Offset scale
    show_data = self.org_data[f: offset]
    x = show_data['Year']
    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()

Manually pull the slider component to achieve the data change effect:
Manual data changes

2. QTimer component, automatic dynamic drawing

 self.autoslider()

def autoslider(self):
    self.timer = QTimer()
    self.timer.setInterval(100) #Update data every 100 milliseconds
    self.timer.timeout.connect(self.autoupdate) #Automatically update data, each time the offset is updated, add 1, that is, jump one year of data
    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 + 1
    else:
        self.auto_offset = 0

The effect is shown at the top of the article.

4. Complete code

import re
importsys
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 Figure

figure = Figure(figsize=(12, 6), dpi=90)

global cur_data_len, cur_major_locator
cur_data_len = 10 # The amount of data currently displayed (displays 10 years of data)
cur_major_locator = 10 # Locator of the current scale (major scale)

pattern = re.compile('\((\S*)\)')

def getgdpvalue(gdpstr):
    re_obj=pattern.search(gdpstr)
    gdp_value=locale.atof(re_obj.group(1))/100000000
    return gdp_value

class ApplicationWindow(QMainWindow):

    def __init__(self, parent=None,org_data=None):
        QMainWindow.__init__(self, parent)
        self.axes = None
        self.axis_china=None
        self.axis_us=None
        self.datacount=10
        self.org_data = org_data
        self.auto_offset = 0
        # Central widget
        self._main = QWidget()
        self.setCentralWidget(self._main)
        # Figure
        self.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('Comparison of GDP between China and the United States')
            self.axis_china = self.axes.plot(show_data['Year'], show_data['china_gdp_value'], label='China GDP')
            self.axis_us = self.axes.plot(show_data['Year'], show_data['us_gdp_value'], label='US GDP')
            y_max = max(self.org_data['us_gdp_value'].max(), self.org_data['china_gdp_value'].max())
            self.axes.set_ylabel('GDP(100 million yuan)')
            self.axes.set_xlabel('Year')
            self.axes.set_ylim(0, y_max)
            self.axes.set_xlim(show_data['Year'].min(), show_data['Year'].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() # Automatically adjust the subfigure parameters to fill the entire image area
        # Drop-down box, select mode # ComboBox (combo_type)
        self.combo_type = QComboBox()
        self.combo_type.addItems(['Automatic play', 'Manual play'])
        #Sliders
        min_value = 0
        self.max_value = len(self.org_data)-cur_data_len
        self.slider_update = QSlider(minimum=min_value, maximum=self.max_value, orientation=Qt.Horizontal) # Slider
        layout1 = QHBoxLayout()
        layout1.addWidget(self.combo_type)
        # layout
        layout2 = QVBoxLayout()
        layout2.addWidget(self.canvas, 88)
        layout2.addWidget(self.slider_update)
        # Main layout
        layout = QVBoxLayout(self._main)
        layout.addLayout(layout1)
        layout.addLayout(layout2, 100)
        self.canvas.draw()
        # Signal and Slots connections
        self.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) #Update data every 100 milliseconds
        self.timer.timeout.connect(self.autoupdate) #Automatically update data, each time the offset is updated, add 1, that is, jump one year of data
        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 + 1
        else:
            self.auto_offset = 0

    @Slot()
    def selecttype(self, text):
        if 'Autoplay' == text:
            self.autoslider()
        elif 'Manual play' == text:
            self.timer.stop()

    @Slot()
    def update_frequency(self, new_val):
        # The offset is offset by 1 each time
        f = int(new_val)
        offset = f + cur_data_len # Offset scale
        show_data = self.org_data[f: offset]
        x = show_data['Year']
        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\China VS United States.xlsx')
    df_data = df_data.loc[1:len(df_data)]
    df_data['china_gdp_value'] = df_data['China'].map(getgdpvalue)
    df_data['us_gdp_value'] = df_data['United States'].map(getgdpvalue)
    df_data = df_data.sort_values('Year')
    w = ApplicationWindow(org_data=df_data)
    w.setFixedSize(1000, 500)
    w.show()
    app.exec()

6. Summary

It is very simple and easy to implement matplotlib dynamic drawing in Python. In fact, the key lies in the organization of the data, that is, to prepare the x data and y data of the coordinate axes to be drawn, and dynamically update them through set_data(x, y) Data, it should be noted that the display of the X-axis or Y-axis will change after changing the data. This can be dynamically set through the set_xlim() or set_ylim() method of the axis. The scale can also be specified through set_major_locator().

The complete code and data set can be found at http://xiejava.ishareread.com/

Author's blog: http://xiejava.ishareread.com/