python科研绘图教程2:从熟悉cbook模块和Matplotlib示例数据集开始练气第一层

文摘   2024-10-19 10:10   新加坡  
点击订阅公众号 | 前沿学术成果每日更新               

目录:

  • 1. 什么是cbook模块

  • 2. cbook的重要功能及解释

    • 2.1 数据处理工具

    • 2.2 Matplotlib的示例数据集和`get_sample_data`函数详解

    • 2.3 回调机制和`CallbackRegistry`

    • 2.3.2. `CallbackRegistry`的使用细节

    • 2.4 类型检查工具

    • 2.5 其他实用工具

  • 3. 使用案例

    • 案例一:加载CSV文件并进行数据拟合

    • 案例二:使用`flatten`展平嵌套列表并绘制图表

    • 案例三:使用`CallbackRegistry`实现自定义事件处理

1. 什么是cbook模块

在数据可视化领域,Matplotlib是Python中最流行的绘图库之一。matplotlib.cbook(简称cbook)模块是Matplotlib的一个实用工具集合,提供了许多便捷的函数和类,帮助开发者更高效地处理数据、事件和文件等操作。cbook模块并非Matplotlib的新特性,它自早期版本就已经存在。然而,随着Matplotlib的不断更新和迭代,cbook模块也在持续优化和丰富,为用户提供了更加全面和强大的功能支持。

2. cbook的重要功能及解释

cbook模块包含丰富实用功能,以下是对一些常用的重要功能的解释:

2.1 数据处理工具

  • **flatten**:用于将嵌套的序列(如列表或元组)展平成一个一维的迭代器,方便遍历所有元素。

    from matplotlib.cbook import flatten

    nested_list = [[12], [34], [56]]
    flat_list = list(flatten(nested_list))
    print(flat_list)  # 输出:[1, 2, 3, 4, 5, 6]
  • **delete_masked_points**:用于删除被掩码(mask)的数据点,常用于处理包含缺失值或无效值的数据集。这在处理可能含有缺失值或无效数据的数值数组时特别有用,例如在绘制图形之前清理数据。

    import numpy as np
    import matplotlib.cbook as cbook

    # 创建含有掩码(np.nan)的数据
    x = np.array([123, np.nan, 567, np.nan, 9])
    y = np.array([9, np.nan, 7654321])

    # 使用 numpy 的 mask_invalid 函数创建掩码数组
    x_masked = np.ma.masked_invalid(x)
    y_masked = np.ma.masked_invalid(y)

    # 使用 delete_masked_points 清理掩码数据
    x_clean, y_clean = cbook.delete_masked_points(x_masked, y_masked)

    # 输出清理后的数据
    print("Cleaned x:", x_clean)
    print("Cleaned y:", y_clean)
    # 输出是
    #Cleaned x: [1. 3. 5. 6. 7. 9.]
    #Cleaned y: [9. 7. 5. 4. 3. 1.]

2.2 Matplotlib的示例数据集和get_sample_data函数详解

2.2.1 Matplotlib的示例数据集

在数据可视化过程中,使用真实的数据集进行演示和测试是非常重要的。Matplotlib自带了一些示例数据集,方便用户在绘图时使用。这些数据集涵盖了图像、CSV文件、NumPy数组等多种格式,存储在Matplotlib安装目录的mpl-data/sample_data文件夹中。

image-20241019002733864

一些示例数据集说明:

  • **goog.npz**:Google公司股票价格的历史数据,存储为NumPy压缩格式文件(.npz)。
  • **data_x_x2_x3.csv**:包含线性和非线性数据的CSV文件,可用于线性回归和多项式拟合的演示。
  • **grace_hopper.png**:计算机科学家格蕾丝·霍珀的肖像图片,PNG格式。
  • **s1045.ima**:医学图像文件,DICOM格式。
  • **logo2.png**:Matplotlib的Logo图片,PNG格式。

强烈建议学会调用Matplotlib的示例数据集,各种更容易掌握matplotlib

安装路径一般为...\lib\site-packages\matplotlib\mpl-data\sample_data\aapl.npz'

想要快速的获得这些数据集的信息,也可以访问:Matplotlib源码仓库:在GitHub上的Matplotlib源码仓库中,可以查看每个数据集的源文件和相关说明。

image-20241019002634885

2.2.2 get_sample_data函数

get_sample_data函数是matplotlib.cbook模块中的一个实用函数,用于方便地加载Matplotlib自带的示例数据集。

函数原型

matplotlib.cbook.get_sample_data(fname, asfileobj=True)

参数说明

  • **fname**:str类型,必选参数。指定要加载的示例数据文件的文件名。例如,'grace_hopper.png'
  • **asfileobj**:bool类型,可选参数,默认值为True。如果为True,则返回一个类文件对象(类似于已打开的文件);如果为False,则返回数据文件的绝对路径。

返回值

  • 如果asfileobj=True,返回一个可读取的文件对象,通常用于直接读取数据内容。
  • 如果asfileobj=False,返回数据文件的完整路径,便于使用其他库或自定义方法加载数据。

支持的数据格式

get_sample_data函数支持加载多种数据格式,具体取决于所请求的数据文件类型。常见的数据格式包括:

  • 图像文件:如PNG、JPG等格式的图片,可使用matplotlib.image.imreadPillow库加载。
  • NumPy数组文件.npz.npy格式的文件,可使用numpy.load加载。
  • CSV文件:逗号分隔值的文本文件,可使用numpy.loadtxtpandas.read_csv等方法加载。
  • 原始数据文件:需要根据具体格式和需求,自定义读取方式。

2.3.1 使用示例:加载并显示一张示例图片

import matplotlib.pyplot as plt
from matplotlib.cbook import get_sample_data
import matplotlib.image as mpimg

# 获取示例图片的文件对象
image_file = get_sample_data('grace_hopper.jpg')

# 读取图片数据
image = mpimg.imread(image_file)

# 显示图片
plt.imshow(image)
plt.axis('off')  # 隐藏坐标轴
plt.title('Grace Hopper')
plt.show()

输出结果:

image-20241019003230632
注意事项
  • 关于asfileobj参数:如果你需要将数据文件传递给需要文件路径的函数(如pandas.read_csv),请将asfileobj设置为False,以获取文件的绝对路径。
  • 文件对象的使用:当asfileobj=True时,返回的文件对象在使用完毕后无需手动关闭,因为Matplotlib会自动管理其生命周期。
  • 数据文件的可用性get_sample_data函数只能访问Matplotlib自带的示例数据集,无法加载自定义路径下的文件。

2.3 回调机制和CallbackRegistry

2.3.1 什么是CallbackRegistry

CallbackRegistrymatplotlib.cbook模块中的一个核心类,用于管理事件与回调函数之间的关联。它提供了一种机制,可以在特定事件发生时,自动调用预先注册的回调函数。这种设计模式在编程中被称为观察者模式(Observer Pattern),广泛应用于事件驱动的编程范式中。

CallbackRegistry可实现的功能:

  • 事件管理:通过CallbackRegistry,开发者可以定义自定义事件,并在事件触发时通知所有相关的回调函数。
  • 回调函数注册与注销:支持动态添加和移除回调函数,提供了灵活的事件响应机制。
  • 参数传递:在事件触发时,可以向回调函数传递必要的参数,方便处理复杂的逻辑。
  • 解耦合:事件的触发方和处理方可以独立开发,降低了代码的耦合性,提高了可维护性。

CallbackRegistry的优势

  • 统一的事件管理:提供了一个统一的接口来管理各种事件,简化了事件处理流程。
  • 灵活性:可以根据需要随时添加或移除回调函数,满足不同场景的需求。
  • 可扩展性:支持自定义事件类型,方便在大型项目中应用。

CallbackRegistry的应用场景

  • GUI编程:在图形用户界面中,响应用户的点击、拖拽等事件。
  • 数据流处理:在数据处理的各个阶段触发事件,进行监控、日志记录或动态调整。
  • 插件机制:允许第三方插件注册回调函数,实现功能的扩展。

在使用CallbackRegistry时,需要注意:

  • 合理命名事件,保持一致性和可读性。
  • 回调函数的设计应尽可能通用,避免过度耦合。
  • 及时注销不再需要的回调函数,防止内存泄漏或意外的函数调用。

2.3.2. CallbackRegistry的使用细节

创建CallbackRegistry实例

from matplotlib.cbook import CallbackRegistry

callbacks = CallbackRegistry()

注册回调函数

# 语法:callbacks.connect(event_name, callback_function)
cid = callbacks.connect('event_name', callback_function)
  • **event_name**:字符串,表示事件的名称。
  • **callback_function**:回调函数,接收事件触发时传递的参数。

触发事件

# 语法:callbacks.process(event_name, *args, **kwargs)
callbacks.process('event_name', arg1, arg2, key=value)
  • **event_name**:要触发的事件名称。
  • ***args**kwargs**:传递给回调函数的参数。

注销回调函数

# 使用connect方法返回的cid进行注销
callbacks.disconnect(cid)

回调函数的签名

回调函数需要能够接受*args**kwargs,以匹配process方法传递的参数。

def callback_function(*args, **kwargs):
    # 处理逻辑
    pass

4. 使用示例

使用案例3

2.4 类型检查工具

  • **is_scalar**:检查对象是否为标量类型。
  • **is_string_like**:检查对象是否类似于字符串。
  • **is_numlike**:检查对象是否类似于数值类型。

2.5 其他实用工具

  • **Bunch**:类似于字典的简单类,允许通过属性访问方式获取键值。

    from matplotlib.cbook import Bunch

    point = Bunch(x=1, y=2)
    print(point.x, point.y)  # 输出:1 2
  • **Saver**:用于在对象被垃圾回收前执行特定的清理操作。

3. 使用案例

案例一:加载CSV文件并进行数据拟合

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.cbook import get_sample_data

# 获取CSV文件的路径
datafile = get_sample_data('data_x_x2_x3.csv', asfileobj=False)

# 加载数据
data = np.loadtxt(datafile, delimiter=' ', skiprows=1)
x = data[:, 0]
y = data[:, 1]

# 进行多项式拟合
coefficients = np.polyfit(x, y, deg=3)
poly_func = np.poly1d(coefficients)

# 绘制原始数据和拟合曲线
x_fit = np.linspace(min(x), max(x), 100)
y_fit = poly_func(x_fit)

plt.scatter(x, y, label='Data Points')
plt.plot(x_fit, y_fit, color='red', label='Polynomial Fit')
plt.title('Data Fitting Example')
plt.xlabel('X')
plt.ylabel('Y')
plt.legend()
plt.show()
image-20241019003612907

案例二:使用flatten展平嵌套列表并绘制图表

在数据处理中,我们经常会遇到嵌套的列表结构。使用flatten函数,可以方便地将其展开并用于绘图。

import matplotlib.pyplot as plt
from matplotlib.cbook import flatten

# 原始嵌套数据
data = [[135], [246], [789]]

# 使用flatten展平数据
flat_data = list(flatten(data))

# 准备绘图数据
x_values = range(len(flat_data))

# 绘制折线图
plt.plot(x_values, flat_data, marker='o')
plt.title('使用flatten展平嵌套列表')
plt.xlabel('索引')
plt.ylabel('值')
plt.grid(True)
plt.show()

输出结果:

image-20241019004301029

案例三:使用CallbackRegistry实现自定义事件处理

为了更好地展示CallbackRegistry的优势,下面我们通过一个更贴近实际应用的例子,演示如何使用CallbackRegistry来管理数据处理流程中的事件:假设我们有一个数据处理流水线,包括数据加载、预处理、分析和保存等步骤。我们希望在每个步骤完成时,自动触发相应的事件,以便进行日志记录、进度更新或其他操作。

import time
from matplotlib.cbook import CallbackRegistry

# 创建回调注册器
callbacks = CallbackRegistry()

# 定义事件类型
EVENTS = ['on_data_loaded''on_preprocessing_done''on_analysis_complete''on_data_saved']

# 定义各个步骤的函数,并在适当的时候触发事件
def load_data():
    time.sleep(1)  # 模拟耗时操作
    data = {'raw_data': [12345]}
    print("数据加载完成")
    # 触发事件
    callbacks.process('on_data_loaded', data)
    return data

def preprocess_data(data):
    time.sleep(1)
    data['preprocessed_data'] = [x * 2 for x in data['raw_data']]
    print("数据预处理完成")
    callbacks.process('on_preprocessing_done', data)
    return data

def analyze_data(data):
    time.sleep(1)
    data['analysis_result'] = sum(data['preprocessed_data'])
    print("数据分析完成")
    callbacks.process('on_analysis_complete', data)
    return data

def save_data(data):
    time.sleep(1)
    print("数据保存完成")
    callbacks.process('on_data_saved', data)

# 定义回调函数
def log_event(name, data):
    print(f"日志记录:{name}事件已触发,数据状态:{data}")

def update_progress(name, data):
    progress = {
        'on_data_loaded'25,
        'on_preprocessing_done'50,
        'on_analysis_complete'75,
        'on_data_saved'100
    }
    print(f"进度更新:已完成{progress[name]}%")

# 注册回调函数到各个事件
for event in EVENTS:
    callbacks.connect(event, lambda name=event, data=None: log_event(name, data))
    callbacks.connect(event, lambda name=event, data=None: update_progress(name, data))

# 执行数据处理流水线
data = load_data()
data = preprocess_data(data)
data = analyze_data(data)
save_data(data)

运行结果:

数据加载完成
日志记录:on_data_loaded事件已触发,数据状态:{'raw_data': [1, 2, 3, 4, 5]}
进度更新:已完成25%
数据预处理完成
日志记录:on_preprocessing_done事件已触发,数据状态:{'raw_data': [1, 2, 3, 4, 5], 'preprocessed_data': [2, 4, 6, 8, 10]}
进度更新:已完成50%
数据分析完成
日志记录:on_analysis_complete事件已触发,数据状态:{'raw_data': [1, 2, 3, 4, 5], 'preprocessed_data': [2, 4, 6, 8, 10], 'analysis_result': 30}
进度更新:已完成75%
数据保存完成
日志记录:on_data_saved事件已触发,数据状态:{'raw_data': [1, 2, 3, 4, 5], 'preprocessed_data': [2, 4, 6, 8, 10], 'analysis_result': 30}
进度更新:已完成100%

示例解读

  • 事件定义:我们定义了四个事件,分别对应数据处理流程中的关键步骤。
  • 回调函数:定义了两个回调函数log_eventupdate_progress,分别用于日志记录和进度更新。
  • 回调注册:将回调函数注册到对应的事件上。注意,这里使用了lambda函数来捕获事件名称。
  • 事件触发:在每个步骤的函数中,当操作完成后,使用callbacks.process(event_name, data)来触发事件,通知所有注册的回调函数。
  • 优势体现
    • 解耦合:数据处理逻辑与日志、进度更新等功能解耦,方便维护和扩展。
    • 灵活性:可以随时添加或移除回调函数,例如添加新的监控或通知机制,而无需修改数据处理代码。
    • 可读性:代码结构清晰,事件的定义和处理逻辑分离,增强了代码的可读性。
               

               

声明:本公众号分享的前沿学术成果来源于各学术网站,不依法享有其所有权。若原作者发现本次分享中的文字及图片涉及侵权,请立刻联系公众号后台或发送邮件,我们将及时修改或删除!         

邮箱:environmodel@sina.com         

若您认为有用,欢迎

Environmodel设为星标,或

点击“在看”或“分享”给他人


Environmodel
Environmodel(环境模型)专注于环境科学与工程领域的建模及模型研究进展,并分享涵盖机器学习、深度学习以及人工智能等相关领域的理论知识、主流工具和Python编程技巧。
 最新文章