《Python从入门到实践》项目 数据可视化

生成数据

安装Matplotlib

python -m pip install matplotlib

绘制简单的折线图

import matplotlib.pyplot as plt


squares = [1, 4, 9, 16, 25]
fig, ax = plt.subplots()
ax.plot(squares)

plt.show()

首先导入pyplot模块,并给他指定别名plt,以免反复输入pyplot,然后调用subplots()函数,这个函数可在一个图形绘制一个或多个图形,变量fig表示由生成的一些列绘图构成的整个图形,变量ax表示图形中的绘图,在大多数情况下,使用这个变量来定义和定制绘图。

接下来调用plot()方法,它将根据给定的数据以浅显易懂的方式绘制绘图。plt.show()函数打开Matplotlib查看器并显示绘图。

修改标签文字和线条粗细

import matplotlib.pyplot as plt


squares = [1, 4, 9, 16, 25]
fig, ax = plt.subplots()
ax.plot(squares, linewidth=3)

# 设置图题并给坐标轴加上标签、
ax.set_title("Square Numbers", fontsize=24)
ax.set_xlabel("Value", fontsize=14)
ax.set_ylabel("Square of Value", fontsize=14)
# 设置刻度标记的样式
ax.tick_params(labelsize=14)

plt.show()

通常,需要尝试不同的值,才能找到最佳参数生成理想的图

矫正绘图

折线图的终点指出4的平方为25,接下来我们修复这个问题。

在想plot()提供一个数值序列时,它假设第一个数据点对应的x坐标值为0,但这里第一个点对应的x坐标应该为1.

import matplotlib.pyplot as plt

input_vale = [1, 2, 3, 4, 5]
squares = [1, 4, 9, 16, 25]
fig, ax = plt.subplots()
ax.plot(input_vale, squares, linewidth=3)

# 设置图题并给坐标轴加上标签、
ax.set_title("Square Numbers", fontsize=24)
ax.set_xlabel("Value", fontsize=14)
ax.set_ylabel("Square of Value", fontsize=14)
# 设置刻度标记的样式
ax.tick_params(labelsize=14)

plt.show()

现在,plot()无需对输出值的生成方式做出假设,因此生成了正确的绘图

使用内置样式

Matplotlib提供了很多已定义好的样式,要看到能在你的系统重使用的所有样式,可在终端会话中执行如下命令:

import matplotlib.pyplot as plt
print(plt.style.available)

要使用这些样式,可在调用subplots()的代码前添加如下代码行:

import matplotlib.pyplot as plt

input_vale = [1, 2, 3, 4, 5]
squares = [1, 4, 9, 16, 25]

plt.style.use('seaborn-v0_8')
fig, ax = plt.subplots()
ax.plot(input_vale, squares, linewidth=3)

# 设置图题并给坐标轴加上标签、
ax.set_title("Square Numbers", fontsize=24)
ax.set_xlabel("Value", fontsize=14)
ax.set_ylabel("Square of Value", fontsize=14)
# 设置刻度标记的样式
ax.tick_params(labelsize=14)

plt.show()

使用scatter()绘制散点图并设置样式

要绘制单个点,可使用scatter()方法,并向他传递该点的x坐标和y坐标

import matplotlib.pyplot as plt

plt.style.use('seaborn-v0_8')
fig, ax = plt.subplots()
# s表示点的尺寸
ax.scatter(2, 4, s=200)

# 设置图题并给坐标轴加上标签、
ax.set_title("Square Numbers", fontsize=24)
ax.set_xlabel("Value", fontsize=14)
ax.set_ylabel("Square of Value", fontsize=14)
# 设置刻度标记的样式
ax.tick_params(labelsize=14)

plt.show()

使用scatter()绘制一系列点

要绘制一系列点,可想scatter()传递两个分别包含x坐标值和y坐标值的列表。

import matplotlib.pyplot as plt

x_value = [1, 2, 3, 4, 5]
y_value = [1, 4, 9, 16, 25]

plt.style.use('seaborn-v0_8')
fig, ax = plt.subplots()
# s表示点的尺寸
ax.scatter(x_value, y_value, s=200)

# 设置图题并给坐标轴加上标签、
ax.set_title("Square Numbers", fontsize=24)
ax.set_xlabel("Value", fontsize=14)
ax.set_ylabel("Square of Value", fontsize=14)
# 设置刻度标记的样式
ax.tick_params(labelsize=14)

plt.show()

自动计算数据

手动指定列表要包含的值效率不高,在需要绘制的点很多时候尤其如此,我们可以不指定值,直接用使用循环来计算:

import matplotlib.pyplot as plt

x_value = range(1,1001)
y_value = [x**2 for x in x_value]

plt.style.use('seaborn-v0_8')
fig, ax = plt.subplots()
# s表示点的尺寸
ax.scatter(x_value, y_value, s=1)

# 设置图题并给坐标轴加上标签、
ax.set_title("Square Numbers", fontsize=24)
ax.set_xlabel("Value", fontsize=14)
ax.set_ylabel("Square of Value", fontsize=14)
# 设置刻度标记的样式
ax.tick_params(labelsize=14)

# 设置每个坐标轴的取值范围
ax.axis([0, 1100, 0, 1_100_000])
plt.show()

定制刻度标记

在刻度标记表示的数足够大时,Matplotlib将默认使用科学计数法。这通常是好事,因为如果使用常规表示法,很大的数据将占据很多内存

几乎每个图形元素都是可定制的,如果你愿意,可让Matplotlib始终使用常规表示法

import matplotlib.pyplot as plt

x_value = range(1,1001)
y_value = [x**2 for x in x_value]

plt.style.use('seaborn-v0_8')
fig, ax = plt.subplots()
# s表示点的尺寸
ax.scatter(x_value, y_value, s=1)

# 设置图题并给坐标轴加上标签、
ax.set_title("Square Numbers", fontsize=24)
ax.set_xlabel("Value", fontsize=14)
ax.set_ylabel("Square of Value", fontsize=14)
# 设置刻度标记的样式
ax.tick_params(labelsize=14)

# 设置每个坐标轴的取值范围
ax.axis([0, 1100, 0, 1_100_000])
ax.ticklabel_format(style='plain')
plt.show()

定制颜色

要修改数据点的颜色,可先scatter()传递参数color并将其设置为要使用的颜色的名称,如下:

ax.scatter(x_value, y_value, color='green', s=1)

还可以使用RGB颜色模式定制颜色,此时传递参数color,并将其设置为一个元组,其中包含三个0~1的浮点数,分别表示红色、绿色、蓝色分量:

ax.scatter(x_value, y_value, color=(0.9, 0.4, 0.2), s=1)

值越接近0,指定的颜色越深;值越接近1,指定的颜色越浅

使用颜色映射

ax.scatter(x_value, y_value, c=y_value, cmap=plt.cm.Reds, s=10)

颜色映射是一个从起始颜色渐变到结束颜色的颜色序列,在可视化中,颜色映射用于突出数据的规律,例如,你可以使用较浅的颜色来显示较小的值,使用较深的颜色来显示较大的值,使用颜色映射时,可根据精心设计的色标准确地设置所有点的颜色。

参数c类似于参数color,但用于将一系列值关联到颜色映射,这里将参数c设置成了一个y坐标值列表,并使用参数cmap告诉pyplot使用哪个颜色映射。

如Reds, Blues, jet

自动保存绘图

如果要将绘图保存到文件中,而不是在Matplotlib查看器中显示它,可将plt.show()替换为plt.savefig():

plt.savefig('square_plot.png', bbox_inches='tight')

第一个实参指定要以什么文件名保存绘图,这个文件将被存储到scatter_squares.py所在的目录中,第二个实参指定将绘图多余的空白区域裁剪掉。如果要保留绘图周围多余的空白区域,只需省略这个实参即可,你还可以再调用savefig()时使用Path对象,将输出文件存储到系统上的任何地方。

plt.savefig('C:/Users/xjj/Pictures/Camera Roll/square_plot.png', bbox_inches='tight')

练习

import matplotlib.pyplot as plt

x_value = range(1, 5001)
y_value = [x**3 for x in x_value]

plt.style.use('seaborn-v0_8')
fig, ax = plt.subplots()

# s表示点的尺寸
ax.scatter(x_value, y_value, c=y_value, cmap=plt.cm.jet, s=10)

# 设置图题并给坐标轴加上标签、
ax.set_title("Square Numbers", fontsize=24)
ax.set_xlabel("Value", fontsize=14)
ax.set_ylabel("Square of Value", fontsize=14)
# 设置刻度标记的样式
ax.tick_params(labelsize=14)

# 设置每个坐标轴的取值范围
plt.savefig('C:/Users/xjj/Pictures/Camera Roll/square_plot.png', bbox_inches='tight')
plt.show()

随机游走

随机游走是由一系列简单的随机决策产生的行走路径

创建RandomWalk类

为了模拟随机游走,我们将创建一个名为RandomWalk的类,用来随机选择前进的方向。

这个类需要三个属性,一个是跟踪随机游走次数的变量,另外两个是列表,分别存储随机游走经过的每个点的x,y坐标值。

from random import choice


class RandomWalk:
    """生成一个随机游走数据的类"""
    
    def __init__(self, num_points=5000):
        """初始化随机游走数据的类"""
        
        self.num_points = num_points
        # 所有随机游走都始于(0,0)
        self.x_values = [0]
        self.y_values = [0]

选择方向

下面使用fill_walk()方法来生成游走包含的点:

放在class RandomWalk类中:

def fill_walk(self):
    """计算随机游走包含的所有点"""
    
    # 不断游走,直到列表达到指定的长度
    while len(self.x_values) < self.num_points:
        
        # 决定前进的方向以及沿这个方向前进的距离
        x_direction = choice([-1, 1])
        x_distance = choice([0, 1, 2, 3, 4])
        x_step = x_direction*x_distance

        y_direction = choice([-1, 1])
        y_distance = choice([0, 1, 2, 3, 4])
        y_step = y_direction * y_distance
        
        # 拒绝原地踏步
        if x_step == 0 and y_step ==0:
            continue
            
        # 计算下一个点的x坐标和y坐标
        x = self.x_values[-1] + x_step
        y = self.y_values[-1] + y_step
        
        self.x_values.append(x)
        self.y_values.append(y)

绘制随机游走图:

模拟多次随机游走

要在不运行程序多次的情况下使用前面的代码模拟多次随机游走,一种办法是将这些代码放在一个while循环中:

import matplotlib.pyplot as plt
from random_walk import RandomWalk

while True:
    # 创建一个RandomWalk实例
    rw = RandomWalk()
    rw.fill_walk()

    # 将所有的点都绘制出来
    plt.style.use('classic')
    fig, ax = plt.subplots()
    ax.scatter(rw.x_values, rw.y_values, s=15)
    ax.set_aspect('equal')
import matplotlib.pyplot as plt
from random_walk import RandomWalk

while True:
    # 创建一个RandomWalk实例
    rw = RandomWalk()
    rw.fill_walk()

    # 将所有的点都绘制出来
    plt.style.use('classic')
    fig, ax = plt.subplots()
    point_numbers = range(rw.num_points)
    ax.scatter(rw.x_values, rw.y_values, c=point_numbers,
               cmap=plt.cm.Blues, s=15)
    ax.set_aspect('equal')

    plt.show()

    keep_running = input("Make another walk?(y/n):\n")
    if keep_running == 'n':
        break
    plt.show()

    keep_running = input("Make another walk?(y/n):\n")
    if keep_running == 'n':
        break

设置随机游走图的样式

给点着色
import matplotlib.pyplot as plt
from random_walk import RandomWalk

while True:
    # 创建一个RandomWalk实例
    rw = RandomWalk()
    rw.fill_walk()

    # 将所有的点都绘制出来
    plt.style.use('classic')
    fig, ax = plt.subplots()
    point_numbers = range(rw.num_points)
    ax.scatter(rw.x_values, rw.y_values, c=point_numbers,
               cmap=plt.cm.Blues, edgecolor='none', s=15)
    ax.set_aspect('equal')

    plt.show()

    keep_running = input("Make another walk?(y/n):\n")
    if keep_running == 'n':
        break

将参数c设置为point_numbers,指定使用颜色映射Blues,并传递实参edgecolor='none',用以删除每个点的轮廓。

重新绘制起点和终点
import matplotlib.pyplot as plt
from random_walk import RandomWalk

while True:
    # 创建一个RandomWalk实例
    rw = RandomWalk()
    rw.fill_walk()

    # 将所有的点都绘制出来
    plt.style.use('classic')
    fig, ax = plt.subplots()
    point_numbers = range(rw.num_points)
    ax.scatter(rw.x_values, rw.y_values, c=point_numbers,
               cmap=plt.cm.Blues, edgecolor='none', s=15)
    ax.set_aspect('equal')

    # 突出起点和终点
    ax.scatter(0, 0, color='green', edgecolor='none', s=100)
    ax.scatter(rw.x_values[-1], rw.y_values[-1], color='red',
               edgecolor='none', s=100)


    plt.show()

    keep_running = input("Make another walk?(y/n):\n")
    if keep_running == 'n':
        break


隐藏坐标轴:
import matplotlib.pyplot as plt
from random_walk import RandomWalk

while True:
    # 创建一个RandomWalk实例
    rw = RandomWalk()
    rw.fill_walk()

    # 将所有的点都绘制出来
    plt.style.use('classic')
    fig, ax = plt.subplots()
    point_numbers = range(rw.num_points)
    ax.scatter(rw.x_values, rw.y_values, c=point_numbers,
               cmap=plt.cm.Blues, edgecolor='none', s=15)
    ax.set_aspect('equal')

    # 突出起点和终点
    ax.scatter(0, 0, color='green', edgecolor='none', s=100)
    ax.scatter(rw.x_values[-1], rw.y_values[-1], color='red',
               edgecolor='none', s=100)

    #隐藏坐标轴
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
    
    plt.show()

    keep_running = input("Make another walk?(y/n):\n")
    if keep_running == 'n':
        break
增加点的个数
rw = RandomWalk(50000)

在创建对象的时候改变默认的点数

调整尺寸以适应屏幕
fig, ax = plt.subplots(figsize=(15, 9))

参数figsize是一个元组,向Matplotlib指出绘图窗口的尺寸,单位为英寸。

如果直到当前系统的分辨率,可通过参数dpi向plt.subplots()传递该分辨率

fig, ax = plt.subplots(figsize=(15, 9), dpi=128)

练习

15.3

import matplotlib.pyplot as plt
from random_walk import RandomWalk

while True:
    # 创建一个RandomWalk实例
    rw = RandomWalk()
    rw.fill_walk()

    # 将所有的点都绘制出来
    plt.style.use('classic')
    fig, ax = plt.subplots(figsize=(15, 9), dpi=128)
    point_numbers = range(rw.num_points)
    ax.set_aspect('equal')
    ax.plot(rw.x_values, rw.y_values, color='blue', linewidth=1)

    plt.show()

    keep_running = input("Make another walk?(y/n):\n")
    if keep_running == 'n':
        break

15.5

from random import choice


class RandomWalk:
    """生成一个随机游走数据的类"""

    def __init__(self, num_points=5000):
        """初始化随机游走数据的类"""

        self.num_points = num_points
        # 所有随机游走都始于(0,0)
        self.x_values = [0]
        self.y_values = [0]

    def get_step(self):
        direction = choice([-1, 1])
        distance = choice([0, 1, 2, 3, 4, 5])
        return direction * distance

    def fill_walk(self):
        """计算随机游走包含的所有点"""

        # 不断游走,直到列表达到指定的长度
        while len(self.x_values) < self.num_points:

            # 决定前进的方向以及沿这个方向前进的距离
            x_step = self.get_step()
            y_step = self.get_step()

            # 拒绝原地踏步
            if x_step == 0 and y_step == 0:
                continue

            # 计算下一个点的x坐标和y坐标
            x = self.x_values[-1] + x_step
            y = self.y_values[-1] + y_step

            self.x_values.append(x)
            self.y_values.append(y)

使用Plotly模拟掷骰子

安装Plotly

使用pip安装Plotly以及pandas

Plotly Express依赖于pandas,因此需要同时安装pandas

创建Die类

from random import randint


class Die:
    """表示一个骰子的类"""
    
    def __init__(self, num_sides=6):
        self.num_sides = num_sides
        
    def roll(self):
        """返回一个介于1和骰子面数之间的随机值"""
        return randint(1, self.num_sides)

掷骰子

from  die import Die


die = Die()
results = []
for roll_num in range(100):
    result = die.roll()
    results.append(result)

print(results)

分析结果

from  die import Die


die = Die()
results = []
for roll_num in range(100):
    result = die.roll()
    results.append(result)

print(results)

# 分析结果
frequencies = []
poss_results = range(1, die.num_sides+1)
for value in poss_results:
    frequency = results.count(value)
    frequencies.append(frequency)

print(frequencies)

绘制直方图

有了所需的数据,就可以使用Plotly Express来创建图形了。

from die import Die
import plotly.express as px


die = Die()
results = []
for roll_num in range(1000):
    result = die.roll()
    results.append(result)

print(results)

# 分析结果
frequencies = []
poss_results = range(1, die.num_sides+1)
for value in poss_results:
    frequency = results.count(value)
    frequencies.append(frequency)

print(frequencies)

# 对结果进行可视化
fig = px.bar(x=poss_results, y=frequencies)
fig.show()

调用fig.show()让Plotly将生成的直方图渲染为HTML文件,并在最后一个新的浏览器选项卡中打开这个文件。

这个直方图非常简单,但是并不完整。然而这正是Plotly Express的用途所在:让你在编写几行代码就能查看生成的图,确定它以你希望的方式呈现了数据。如果你对结果大致满意,可进一步定制图形元素,如标签的样式,如果你想使用其他的图表类型,也可马上做出改变,而不用花额外的时间来定制当前的图形状,如将px.bar()替换为px.scatter(),px.line(),直方图,散点图,折线图。

定制绘图

title = 'Result of Rolling One D6 1000 Times'
labels = {'x': 'Result', 'y': 'Frequency of Result'}
fig = px.bar(x=poss_results, y=frequencies, title=title, labels=labels)
fig.show()

同时投掷两个骰子

from die import Die
import plotly.express as px


die_1 = Die()
die_2 = Die()

results_1 = []
results_2 = []
results = []
for roll_num in range(1000):
    result_1 = die_1.roll()
    result_2 = die_2.roll()
    result = result_1 + result_2
    results_1.append(result_1)
    results_2.append(result_2)
    results.append(result)

# 分析结果
frequencies = []
poss_results = range(2, 2*die_1.num_sides+1)
for value in poss_results:
    frequency = results.count(value)
    frequencies.append(frequency)

print(frequencies)

# 对结果进行可视化
title = 'Result of Rolling One D6 1000 Times'
labels = {'x': 'Result', 'y': 'Frequency of Result'}
fig = px.bar(x=poss_results, y=frequencies, title=title, labels=labels)
fig.show()

进一步定制

Plotly提供了update_layout()方法,可用来对创建的图形做各种修改。下面演示了如何让Plotly给每个条形都加上标签:

fig.update_layout(xaxis_dtick=1)

表示对整张图的fig对象调用update_layout()方法。这里传递了参数xaxis_dtick,它指定x轴上刻度标记的间距,我们将这个间距设置为1,给每个条形都加上了标签。

同时投掷两个面数不同的骰子

from die import Die
import plotly.express as px


die_1 = Die()
die_2 = Die(10)

results_1 = []
results_2 = []
results = []
for roll_num in range(50000):
    result_1 = die_1.roll()
    result_2 = die_2.roll()
    result = result_1 + result_2
    results_1.append(result_1)
    results_2.append(result_2)
    results.append(result)

# 分析结果
frequencies = []
poss_results = range(2, die_1.num_sides+die_2.num_sides+1)
for value in poss_results:
    frequency = results.count(value)
    frequencies.append(frequency)

print(frequencies)

# 对结果进行可视化
title = 'Result of Rolling One D6 1000 Times'
labels = {'x': 'Result', 'y': 'Frequency of Result'}
fig = px.bar(x=poss_results, y=frequencies, title=title, labels=labels)
fig.update_layout(xaxis_dtick=1)
fig.show()

下载数据

CSV文件格式

将数据组织为一系列以逗号分隔的值,这样的文件称为CSV文件

CSV文件阅读起来比较麻烦,但是程序能够快速而准确地提取并处理其中的信息。

解析CSV文件头

from pathlib import Path
import csv


path = Path('D:/python/Lib/site-packages/python_work/36736099999.csv')
lines = path.read_text().splitlines()

reader = csv.reader(lines)
header_row = next(reader)
print(header_row)

创建了reader对象,用于解析文件的各行,为了创建reader对象,调用csv.reader()函数并包含CSV文件中各行的列表传递给他

当以reader对象为参数时,函数next()返回文件中的下一行(从文件开头开始),在上述代码中,只调用了next()一次,而且是首次调用,因此得到的是文件的第一行,其中包含文件头。

打印文件头机器位置

from pathlib import Path
import csv


path = Path('D:/python/Lib/site-packages/python_work/36736099999.csv')
lines = path.read_text().splitlines()

reader = csv.reader(lines)
header_row = next(reader)

for index, column_header in enumerate(header_row):
    print(index, column_header)

对列表调用enumerate()来获取每个元素的索引及其值

提取并读取数据

尝试提取某一列的全部数据

from pathlib import Path
import csv


path = Path('D:/python/Lib/site-packages/python_work/36736099999.csv')
lines = path.read_text().splitlines()

reader = csv.reader(lines)
header_row = next(reader)

# 提取平均温度
temps = []
for row in reader:
    temp = float(row[6])
    temps.append(temp)

print(temps)

先创建一个temps空列表,在便利文件中余下的各行,reader对象从刚才中断的地方继续往下读取CSV文件,每次都自动返回当前所处位置的下一行,由于以及读取了文件头行,这个循环将从第二行开始——从这行开始才是实际数据,每次执行循环时都将索引为4的数据追加到temps的末尾,在文件中,这项数据是以字符串的格式存储的,因此在追加到temps的末尾时,要使用float()函数将其转换为数值格式以便使用。

绘制温度图

为了可视化这些温度数据,首先使用Matplotlib创建一个显示每日最高温度的简单绘图

from pathlib import Path
import csv
import matplotlib.pyplot as plt


path = Path('D:/python/Lib/site-packages/python_work/36736099999.csv')
lines = path.read_text().splitlines()

reader = csv.reader(lines)
header_row = next(reader)
print(header_row)

# 提取平均温度
temps = []
for row in reader:
    temp = float(row[6])
    temps.append(temp)

print(temps)

# 根据温度绘图
plt.style.use('seaborn-v0_8')
fig, ax = plt.subplots()
ax.plot(temps, color='red')

# 设置绘图的格式
ax.set_title('Average Temperatures, 1952', fontsize=24)
ax.set_xlabel('', fontsize=16)
ax.set_ylabel('Temperatures(F)', fontsize=16)
# tick_params用于设置刻度线的样式
ax.tick_params(labelsize=16)

plt.show()

datetime模块

在读取日期时,获得的是一个字符串,因此需要想办法将字符串”2021-7-1“转换为一个相应日期的对象,为了创建一个表示2021年7月1日的对象,可使用datetime模块中的strptime()方法:

from datetime import datetime

first_date = datetime.strptime('2021-07-01', '%Y-%m-%d')
print(first_date)

2021-07-01 00:00:00

首先导入datetime模块中的datetime类,再调用strptime()方法,将包含日期的字符串作为第一个实参,第二各实参告诉Python如何设置日期的格式。

strptime()方法的第二个实参可接受各种以%打头的参数,并根据它们决定如何解读日期

在途中添加日期

现在可对温度图进行改进——提取日期和最高温度,并将日期作为x坐标值:

from pathlib import Path
import csv
import matplotlib.pyplot as plt
from datetime import datetime


path = Path('D:/python/Lib/site-packages/python_work/36736099999.csv')
lines = path.read_text().splitlines()

reader = csv.reader(lines)
header_row = next(reader)
print(header_row)

# 提取平均温度
temps, dates = [], []
for row in reader:
    current_date = datetime.strptime(row[1], '%Y-%m-%d')
    temp = float(row[6])
    temps.append(temp)
    dates.append(current_date)

print(temps)
print(dates)

# 根据温度绘图
plt.style.use('seaborn-v0_8')
fig, ax = plt.subplots()
ax.plot(dates, temps, color='red')

# 设置绘图的格式
ax.set_title('Average Temperatures, 1952', fontsize=24)
ax.set_xlabel('', fontsize=16)
ax.set_ylabel('Temperatures(F)', fontsize=16)
# 绘制倾斜的日期标签,以防止重叠
fig.autofmt_xdate()
# tick_params用于设置刻度线的样式
ax.tick_params(labelsize=16)

plt.show()

datetime模块还能够合理安排日期,让其合理分布

涵盖更长的时间

方法同上

再绘制一个图形

可以再一张图中包含两个图像

ax.plot(dates, highs, color='red')
ax.plot(dates, lows, color='red')

给图中区域着色

fill_between()方法,它接受一组x坐标和两组y坐标,并填充两组y坐标值之间的空间

ax.fill_between(dates, highs, lows, facecolor='blue', alpha=0.1)

实参alpha指定颜色的透明度,0表示完全透明,1表示完全不透明

错误检查

当出现数据缺失,可以合理采用try-except-else

for row in reader:
    current_date = datetime.strptime(row[1], '%Y-%m-%d')
    try:
        high = int(row[3])
        low = int(row[4])
    except:
        print(f"Missing data for {current_date}")
    else:
        dates.append(current_date)
        highs.append(high)
        lows.append(low)

自娱自乐:

from pathlib import Path
import csv
import matplotlib.pyplot as plt
from datetime import datetime


path = Path('D:/python/Lib/site-packages/python_work/3639474.csv')
lines = path.read_text().splitlines()

reader = csv.reader(lines)
header_row = next(reader)
print(header_row)

# 提取最低温和最高温
tmaxs, tmins, dates = [], [], []
for row in reader:
    current_date = datetime.strptime(row[2], '%Y-%m-%d')
    try:
        tmax = int(row[4])
        tmin = int(row[5])
    except:
        print(f"Missing data for {current_date}")
    else:
        dates.append(current_date)
        tmaxs.append(tmax)
        tmins.append(tmin)


print(dates)
print(tmaxs)
print(tmins)

# 根据温度绘图
plt.style.use('seaborn-v0_8')
fig, ax = plt.subplots()
ax.plot(dates, tmaxs, color='red')
ax.plot(dates, tmins, color='red')

# 设置绘图的格式
ax.set_title('Temperatures in 2024', fontsize=24)
ax.set_xlabel('', fontsize=16)
ax.set_ylabel('Temperatures(F)', fontsize=16)
# 绘制倾斜的日期标签,以防止重叠
fig.autofmt_xdate()
# tick_params用于设置刻度线的样式
ax.tick_params(labelsize=16)
ax.fill_between(dates, tmaxs, tmins, facecolor='blue', alpha=0.5)

plt.show()

制作全球地震散点图:GeoJSON格式

GeoJSON格式使用json模块来处理

地震数据

直接打开GeoJSON能够发现数据内容密密麻麻难以阅读,这些数据适合机器读取。

查看GeoJSON数据

json模块提供了探索和处理JSON数据的各种工具,其中有一些有助于重新设置这个文件的格式,让我们能够更清楚地查看原始数据,继而决定如何以编程的方式处理它们。

首先加载这些数据并以易于阅读的方式显示它们,这个数据文件很长,因此我们不打印它,而是将数据写入另一个文件,从而可以打开这个文件并轻松地滚动查看。

from pathlib import Path
import json


# 将数据作为字符串读取并转换为Python对象
path = Path('D:/python/Lib/site-packages/python_work/赣州市.geoJson')
contents = path.read_text('utf-8-sig')
all_eq_data = json.loads(contents)

# 将数据文件转换为更易于阅读的版本
path = Path('D:/python/Lib/site-packages/python_work/readable_赣州市.geoJson')
readable_contents = json.dumps(all_eq_data, indent=4)
path.write_text(readable_contents)

GeoJSON格式遵循(经度,纬度)的格式

创建地震列表

from pathlib import Path
import json


# 将数据作为字符串读取并转换为Python对象
path = Path('D:/python/Lib/site-packages/python_work/赣州市.geoJson')
contents = path.read_text('utf-8-sig')
all_eq_data = json.loads(contents)

# 查看数据集中的所有地震
all_eq_dicts = all_eq_data['features']
print(len(all_eq_dicts))

提取震级

from pathlib import Path
import json


# 将数据作为字符串读取并转换为Python对象
path = Path('D:/python/Lib/site-packages/python_work/赣州市.geoJson')
contents = path.read_text('utf-8-sig')
all_eq_data = json.loads(contents)

# 查看数据集中的所有地震
all_eq_dicts = all_eq_data['features']
print(len(all_eq_dicts))

mags = []
for eq_dict in all_eq_dicts:
    mag =eq_dict['properties']['mage']   # 目录的值还是目录
    mags.append(mag)
    
print(mags[:10])

提取位置数据

from pathlib import Path
import json


# 将数据作为字符串读取并转换为Python对象
path = Path('D:/python/Lib/site-packages/python_work/赣州市.geoJson')
contents = path.read_text('utf-8-sig')
all_eq_data = json.loads(contents)

# 查看数据集中的所有地震
all_eq_dicts = all_eq_data['features']
print(len(all_eq_dicts))

mags, titles, lons, lats = [], [], [], []
for eq_dict in all_eq_dicts:
    mag =eq_dict['properties']['mage'] 
    title = eq_dict['properties']['title']
    lon = eq_dict['geometry']['coordinates'][0] # 目录的值还是目录
    lat = eq_dict['geometry']['coordinates'][1]
    mags.append(mag)
    titles.append(title)
    lons.append(lon)
    lats.append(lat)

有了这些数据,就可以绘制地震散点图了

绘制地震散点图

绘制初始散点图的代码如下:

from pathlib import Path
import json
import  plotly.express as px


# 将数据作为字符串读取并转换为Python对象
path = Path('D:/python/Lib/site-packages/python_work/赣州市.geoJson')
contents = path.read_text('utf-8-sig')
all_eq_data = json.loads(contents)

# 查看数据集中的所有地震
all_eq_dicts = all_eq_data['features']
print(len(all_eq_dicts))

mags, titles, lons, lats = [], [], [], []
for eq_dict in all_eq_dicts:
    mag =eq_dict['properties']['mage']
    title = eq_dict['properties']['title']
    lon = eq_dict['geometry']['coordinates'][0] # 目录的值还是目录
    lat = eq_dict['geometry']['coordinates'][1]
    mags.append(mag)
    titles.append(title)
    lons.append(lon)
    lats.append(lat)

fig = px.scatter(
    x = lons,
    y = lats,
    labels={'x': '经度', 'y':'维度'},
    range_x=[-200,200],
    rang_y=[-90,90],
    width=800,
    height=800,
    title='全球地震散点图',
    )
fig.write_html('global_eqrthquakes.html')
fig.show()

fig.write_html方法可以将图形保存为.html文件。在文件夹中找到global_eqrthquakes.html,用浏览器打开即可,如果使用Jupyter Notebook,可以直接使用fig.show方法在notebook单元格中显示散点图。

指定数据的另一种方式

当前,经度和纬度数据是手动配置的

x=lons,
y=lats,
labels={'x':'经度', 'y':'纬度'}

这是在Plotly Express中给图形指定数据的最简单的方式之一,但在数据处理中并不是最佳的,下面介绍给图形指定数据的一种等效方式,需要使用pandas数据分析工具,首先创建一个DataFrame,将需要的数据封装起来:

import pandas as pd

data = pd.DataFrame(
    data=zip(lons, lats, titles, mags), columns=['经度', '纬度', '位置', '震级']
    )
data.head()

然后将配置参数的方式变更为:

data,
x='经度'
y='纬度'

关于zip:

参考:详细分析Python遇到的各种数据结构Map、Dict、Set、DataFrame、Series、Zip_python map结构-CSDN博客

定制标记的尺寸:

fig = px.scatter(
    data,
    x='经度',
    y='纬度',
    range_x=[-200,200],
    rang_y=[-90,90],
    width=800,
    height=800,
    title='全球地震散点图',
    size='震级',
    size_max=10,
    )
    这里使用size参数来指定散点图中每个标记的尺寸,只需要将前面data中的‘震级’字段提供给size参数即可,另外标记尺寸默认为20像素,还可以通过size_max=10将最大显示尺寸缩小到10像素。

定制标记的颜色

try:
    contents = path.read()
except UnicodeDecodeError:
    contents = path.read('utf-8')

fig = px.scatter(
    data,
    x='经度',
    y='纬度',
    range_x=[-200,200],
    rang_y=[-90,90],
    width=800,
    height=800,
    title='全球地震散点图',
    size='震级',
    size_max=10,
    color='震级',
    )

视觉映射图例的默认渐变色范围是从蓝色到红色再到黄色,数值越小标记越蓝,数值越大则标记越黄。

其他渐变

import plotly.express as px

px.colors.named_colorscales()

可以通过这两行代码知道哪些渐变可供使用

添加悬停文本

为了完成这幅散点图的绘制,我们将添加一些说明性文本,在你将鼠标指向表示地震的标记时显示出来。除了默认显示的经度和纬度以外,这还将显示震级以及地震的大致位置。

fig = px.scatter(
    data,
    x='经度',
    y='纬度',
    range_x=[-200,200],
    rang_y=[-90,90],
    width=800,
    height=800,
    title='全球地震散点图',
    size='震级',
    size_max=10,
    color='震级',
    hover_name='位置'
    )

使用API

使用API

API是网站的一部分,用于与程序进行交互。这些程序使用非常具体的URL请求特定的信息,而这种请求称为API调用,请求的数据将以程序易于处理的格式(如JSON,CSV)返回。使用外部数据源的应用程序大多依赖API调用。

Git和GitHub

GitHub上的项目都存储在仓库中,接下来我们将编写一个程序,自动下载GitHub上星数最多的Python项目的信息,并对这些信息进行可视化。

使用API调用请求数据

在浏览器的地址栏中输入如下地址并按回车键:

https://api.github.com/search/repositories?q=language:python+sort:stars

这个API调用返回GitHub当前托管了多少个Python项目,以及有关最受欢迎的Python仓库的信息

https://api.github.com/是GitHub的API地址,接下来的search/repositories让API搜索GitHub上的所有仓库,repositories后面的问号指出需要传递一个参数,参数q表示查询,而等号(=)让我们能够开始指定查询(q=)。接着,通过language:python指出只想要获取主要语言为Python的仓库的信息。最后的+sort:stars指定将项目按照星数排序。

incomplete_results为ture说明GitHub没有处理完这个查询。为了确保API能够及时相应所有用户,GitHub对每个查询的运行时间进行了限制。

安装Requests

-m pip install --user requests

处理API响应

编写程序,自动执行API调用并处理结果:

import requests

# 执行API调用并查看响应
url = "https://api.github.com/search/repositories"
url += "?q=language:python+sort:stars+stars>10000"

headers = {'Accept': 'application/vnd.github.v3+json'}
r = requests.get(url, headers=headers)
print(f"Status code: {r.status_code}")

# 将响应转换为字典
response_dict = r.json()

# 处理结果
print(response_dict.keys())

通过指定headers显式地要求使用这个版本的API并返回JSON格式的结果。 headers的作用在于使用我们浏览器的信息,将请求伪装成是浏览器发出的。

使用request调用API,发送HTTP GET请求到指定的URL,并获取响应对象。

        响应对象包含一个status_code的属性,状态码200表示成功,我们打印status_code,以合适调用是否成功。前面已经让API放回JSON格式的信息了,因此使用json()方法将这些信息转换为一个Python字典,并将结果赋给变量response_dict,最后,打印出response_dict中的键。

处理响应字典

将API调用返回的信息存储到字典中后,就可以处理其中的数据了。

import requests

# 执行API调用并查看响应
url = "https://api.github.com/search/repositories"
url += "?q=language:python+sort:stars+stars:>10000"

headers = {'Accept': 'application/vnd.github.v3+json'}
r = requests.get(url, headers=headers)
print(f"Status code: {r.status_code}")

# 将响应转换为字典
response_dict = r.json()

# 处理结果
print(response_dict.keys())
print(f"Total repositories: {response_dict['total_count']}")
print(f"Complete results: {not response_dict['incomplete_results']}")

# 探索有关仓库的信息
repo_dicts = response_dict['items']
print(f"Repositories returned: {len(repo_dicts)}")

# 研究第一个仓库
repo_dict = repo_dicts[0]
print(f"\nKeys:{len(repo_dict)}")
for key in sorted(repo_dict.keys()):
    print(key)

如你所见,GitHub有足够的时间处理完这个API调用,在这个响应中,GitHub返回了前30个满足查询条件的仓库的信息。如果要获取更多仓库的信息,可请求额外的数据页码:

下面来提取repo_dict中与一些键相关联的值:

import requests

# 执行API调用并查看响应
url = "https://api.github.com/search/repositories"
url += "?q=language:python+sort:stars+stars:>10000"

headers = {'Accept': 'application/vnd.github.v3+json'}
r = requests.get(url, headers=headers)
print(f"Status code: {r.status_code}")

# 将响应转换为字典
response_dict = r.json()

# 处理结果
print(response_dict.keys())
print(f"Total repositories: {response_dict['total_count']}")
print(f"Complete results: {not response_dict['incomplete_results']}")

# 探索有关仓库的信息
repo_dicts = response_dict['items']
print(f"Repositories returned: {len(repo_dicts)}")

# 研究第一个仓库
repo_dict = repo_dicts[0]
print(f"\nKeys:{len(repo_dict)}")
for key in sorted(repo_dict.keys()):
    print(key)

print("\nSelected information about first repository:")
print(f"Name:{repo_dict['name']}")
print(f"Owner:{repo_dict['owner']['login']}")    #套娃典中典
print(f"Stars:{repo_dict['stargazers_count']}")
print(f"Repository:{repo_dict['html_url']}")
print(f"Created:{repo_dict['created_at']}")
print(f"Updated:{repo_dict['updated_at']}")
print(f"Description:{repo_dict['description']}")

概述最受欢迎的仓库

在对这些数据进行可视化时,我们想涵盖多个仓库,下面就来编写一个循环,打印API调用返回的每个仓库的特定信息,以便能够在图形中包含这些信息:

import requests

# 执行API调用并查看响应
url = "https://api.github.com/search/repositories"
url += "?q=language:python+sort:stars+stars:>10000"

headers = {'Accept': 'application/vnd.github.v3+json'}
r = requests.get(url, headers=headers)
print(f"Status code: {r.status_code}")

# 将响应转换为字典
response_dict = r.json()

# 处理结果
print(response_dict.keys())
print(f"Total repositories: {response_dict['total_count']}")
print(f"Complete results: {not response_dict['incomplete_results']}")

# 探索有关仓库的信息
repo_dicts = response_dict['items']
print(f"Repositories returned: {len(repo_dicts)}")

print("\nSelected information about each repository:")

for repo_dict in repo_dicts:
    print(f"\nName:{repo_dict['name']}")
    print(f"Owner:{repo_dict['owner']['login']}")
    print(f"Stars:{repo_dict['stargazers_count']}")
    print(f"Repository:{repo_dict['html_url']}")
    print(f"Description:{repo_dict['description']}")

 监控API的速率限制

大多数API存在速率限制,即在特定时间内可执行的请求数存在限制,要获悉是否接近了GitHub的限制,请在浏览器中输入:

http://api.github.com/rate_limit

我们将会得到:

resources
core{…}
graphql{…}
integration_manifest{…}
search
limit10
remaining10
reset1711698161
used0
resource"search"
rate{…}

我们可以看到限制为每分钟10个请求,在当前这一分钟内,还可以执行remaining也就是9个请求,与键reset对应的值是配额将被重置的Unix时间或新纪元时间(从1970年1月1日零点开始经过的秒数),在用完配额后,我们将收到一条简单的响应信息,得知已到达API的限值,到达限值后,必须等待配额重置。

注意:很多API要求,在通过注册获得API密钥(访问令牌)后,才能执行API调用,在本书编写期间,GitHub没有这样的要求,但是获得访问令牌后,配额将高得多。

使用Plotly可视化仓库

        下面使用收集到的数据来创建图形,以展示GitHub上Python项目的受欢迎程度,我们将创建一个交互式条形图,其中条形的高度表示项目获得了多少颗星,而单机条形将进入相应项目在GitHub上的主页。

import requests
import plotly.express as px

# 执行API调用并查看响应
url = "https://api.github.com/search/repositories"
url += "?q=language:python+sort:stars+stars:>10000"

headers = {'Accept': 'application/vnd.github.v3+json'}
r = requests.get(url, headers=headers)
print(f"Status code: {r.status_code}")

# 将响应转换为字典
response_dict = r.json()

print(f"Complete results: {not response_dict['incomplete_results']}")

# 探索有关仓库的信息
repo_dicts = response_dict['items']
repo_names, stars = [], []
for repo_dict in repo_dicts:
    repo_names.append(repo_dict['name'])
    stars.append(repo_dict['stargazers_count'])

# 可视化
fig = px.bar(x=repo_names, y=stars)
fig.show()

设置图形的样式

import requests
import plotly.express as px

# 执行API调用并查看响应
url = "https://api.github.com/search/repositories"
url += "?q=language:python+sort:stars+stars:>10000"

headers = {'Accept': 'application/vnd.github.v3+json'}
r = requests.get(url, headers=headers)
print(f"Status code: {r.status_code}")

# 将响应转换为字典
response_dict = r.json()

print(f"Complete results: {not response_dict['incomplete_results']}")

# 探索有关仓库的信息
repo_dicts = response_dict['items']
repo_names, stars = [], []
for repo_dict in repo_dicts:
    repo_names.append(repo_dict['name'])
    stars.append(repo_dict['stargazers_count'])

# 可视化
title = "Most-Starred Python Projects on GitHub"
labels = {'x': 'Repository', 'y': 'Stars'}
fig = px.bar(x=repo_names, y=stars, title=title, labels=labels)
fig.update_layout(title_font_size=28, xaxis_title_font_size=20,
                  yaxis_title_font_size=20)

fig.show()

添加定制工具提示

在Plotly中,将鼠标指向条形将显示它表示的信息,这通常称为工具提示(tooltip)。在这里,当前显示的是项目获得了多少颗星。下面来添加定制工具提示,以显示项目的描述和所有者:

import requests
import plotly.express as px

# 执行API调用并查看响应
url = "https://api.github.com/search/repositories"
url += "?q=language:python+sort:stars+stars:>10000"

headers = {'Accept': 'application/vnd.github.v3+json'}
r = requests.get(url, headers=headers)
print(f"Status code: {r.status_code}")

# 将响应转换为字典
response_dict = r.json()

print(f"Complete results: {not response_dict['incomplete_results']}")

# 探索有关仓库的信息
repo_dicts = response_dict['items']
repo_names, stars, hover_texts = [], [], []
for repo_dict in repo_dicts:
    repo_names.append(repo_dict['name'])
    stars.append(repo_dict['stargazers_count'])

    # 创建悬停文本
    owner = repo_dict['owner']['login']
    description = repo_dict['description']
    hover_text = f"{owner}<br />{description}"
    hover_texts.append(hover_text)

# 可视化
title = "Most-Starred Python Projects on GitHub"
labels = {'x': 'Repository', 'y': 'Stars'}
fig = px.bar(x=repo_names, y=stars, title=title, labels=labels, hover_name=hover_texts)
fig.update_layout(title_font_size=28, xaxis_title_font_size=20,
                  yaxis_title_font_size=20)

fig.show()

<br /> 换行符

添加可单击的链接

import requests
import plotly.express as px

# 执行API调用并查看响应
url = "https://api.github.com/search/repositories"
url += "?q=language:python+sort:stars+stars:>10000"

headers = {'Accept': 'application/vnd.github.v3+json'}
r = requests.get(url, headers=headers)
print(f"Status code: {r.status_code}")

# 将响应转换为字典
response_dict = r.json()

print(f"Complete results: {not response_dict['incomplete_results']}")

# 探索有关仓库的信息
repo_dicts = response_dict['items']
repo_links, stars, hover_texts = [], [], []
for repo_dict in repo_dicts:
    # 将仓库名转换为链接
    repo_name = repo_dict['name']
    repo_url = repo_dict['html_url']
    repo_link = f"<a href='{repo_url}'>{repo_name}</a>"
    repo_links.append(repo_link)
    stars.append(repo_dict['stargazers_count'])

    # 创建悬停文本
    owner = repo_dict['owner']['login']
    description = repo_dict['description']
    hover_text = f"{owner}<br />{description}"
    hover_texts.append(hover_text)

# 可视化
title = "Most-Starred Python Projects on GitHub"
labels = {'x': 'Repository', 'y': 'Stars'}
fig = px.bar(x=repo_links, y=stars, title=title, labels=labels, hover_name=hover_texts)
fig.update_layout(title_font_size=28, xaxis_title_font_size=20,
                  yaxis_title_font_size=20)

fig.show()

这里将reop_names修改成repo_links,更准确指出了其中存放的是哪些信息,然后从repo_dict中提取项目的URL,将其赋给临时变量repo_url。接下来创建一个指向项目的连接,为此使用了HTML标签<a>,其格式为<a herf='URL'>link text</a>,然后将这个链接追加到列表repo_links的末尾。

生成的图形是可交互的,点击图形底端的项目名,可以访问响应项目在GitHub上的主页。

定制标记颜色

        创建图形后,可使用update_打头的方法来定制其各个方面。前面使用了update_layout()方法,而update_traces()则可用来定制图形呈现的数据。

fig.update_traces(marker_color='SteelBlue', marker_opacity=0.6)

我们将条形改为更深的蓝色并且是半透明的

        在Plotly中,trace指的是图形上的一系列数据。update_trace()方法接受大量的参数,其中一marker_打头的参数都会影响图形上的标记。这里将每个标记的颜色都设置成了‘SteelBlue’,你可以将参数marker_color设置为任何有具体名称的CSS颜色。我们还可将每个标记的不透明度都设置成0.6,不透明度1.0表示完全不透明,0表示完全透明。

深入了解Plotly和GitHub API

深入了解Plotly ,可阅读文章 Plotly Express in Python

要深入了解如何定制Plotly图形,可阅读文章Styling Plotly Express Figures in Python。

要深入地了解GitHub API,可参阅其文档。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值