Python
在 Python 中轻松绘制直方图: 初学者指南

在 Python 中轻松绘制直方图: 初学者指南

MoeNagy Dev

什么是直方图?

直方图是数据集分布的图形表示。它是数据分析和可视化的基本工具,因为它提供了一种清晰直观的方式来理解数据集的潜在模式和特征。

直方图是通过将数据集的值范围划分为一组箱子(或区间),然后计算落入每个箱子的数据点的数量来创建的。结果图显示了每个箱子内的频率或数据点计数,提供了数据分布的视觉表示。

直方图特别有助于理解数据集的形状、中心趋势和分散程度。它们可以帮助识别模式,如多个峰值(表示多峰分布)、偏斜(分布的不对称性)和异常值(位于主分布之外的数据点)。

准备数据

要在 Python 中创建直方图,我们需要导入必要的库并生成一些样本数据来使用。

import numpy as np
import matplotlib.pyplot as plt
 
# 生成样本数据
data = np.random.normal(0, 1, 1000)

在这个例子中,我们使用 numpy.random.normal() 函数生成 1,000 个来自标准正态分布(平均值 = 0,标准差 = 1)的数据点。您可以用自己的数据集替换它,或使用不同的分布来探索直方图可视化。

基本直方图绘制

在 Python 中创建直方图的基本方法是使用 plt.hist() 函数。这是一个使用 Matplotlib 库创建直方图的示例。

# 创建直方图
plt.hist(data, bins=30, color='blue', alpha=0.5)
 
# 添加标签和标题
plt.xlabel('值')
plt.ylabel('频率')
plt.title('样本数据的直方图')
 
# 显示图表
plt.show()

在这个例子中,我们创建了一个有 30 个柱子的直方图,使用蓝色并设置透明度(alpha)为 0.5。您可以通过调整柱子的数量、宽度、颜色和透明度来自定义直方图。

自定义直方图

设置柱子数量

直方图中的柱子数量是一个重要的参数,它可以显著影响图表的外观和解释。您可以使用 plt.hist() 函数中的 bins 参数来调整柱子的数量。

# 10 个柱子的直方图
plt.hist(data, bins=10, color='green', alpha=0.7)
plt.show()
 
# 50 个柱子的直方图
plt.hist(data, bins=50, color='red', alpha=0.7)
plt.show()

增加柱子的数量可以提供更多关于数据分布的细节,但也可能导致图表看起来更加嘈杂或"崎岖"。减少柱子的数量可以使直方图更加平滑,但可能会隐藏一些细节。

调整柱子宽度

除了柱子的数量,您还可以调整柱子的宽度来控制直方图中的细节级别。

# 宽度为 0.2 的直方图
plt.hist(data, bins=np.arange(-3, 3, 0.2), color='orange', alpha=0.7)
plt.show()
 
# 宽度为 0.5 的直方图
plt.hist(data, bins=np.arange(-3, 3, 0.5), color='purple', alpha=0.7)
plt.show()

在这个例子中,我们使用 np.arange() 函数来创建柱子的边缘,指定起始值、结束值和步长。

更改直方图颜色和透明度

您还可以通过调整柱子的颜色和透明度(alpha)来进一步自定义直方图的外观。

# 使用不同颜色和透明度的直方图
plt.hist(data, bins=30, col.
```python
plt.hist(data, bins=30, color='blue', alpha=0.5)
plt.show()

尝试不同的颜色和透明度设置可以帮助您创建视觉上吸引人并有效传达数据分布的直方图。

高级直方图自定义

除了基本的直方图绘制,您还可以进一步定制可视化效果,使其更加信息丰富和视觉上吸引人。

添加标签和标题

添加清晰的标签和描述性标题可以帮助读者了解直方图的背景和目的。

# 添加标签和标题
plt.hist(data, bins=30, color='blue', alpha=0.5)
plt.xlabel('值')
plt.ylabel('频率')
plt.title('样本数据直方图')
plt.show()

调整坐标轴比例

根据数据的范围和分布,您可能需要调整 x 轴和 y 轴的比例,以更好地适应数据。

# 调整 x 轴和 y 轴比例
plt.hist(data, bins=30, color='blue', alpha=0.5)
plt.xlim(-3, 3)
plt.ylim(0, 150)
plt.xlabel('值')
plt.ylabel('频率')
plt.title('样本数据直方图')
plt.show()

在这个例子中,我们将 x 轴范围设置为 -3 到 3,将 y 轴范围设置为 0 到 150,以更好地适应数据分布。

显示网格线

添加网格线可以帮助读者更好地解释直方图,并识别特定的数据点或频率。

# 添加网格线
plt.hist(data, bins=30, color='blue', alpha=0.5)
plt.grid(True)
plt.xlabel('值')
plt.ylabel('频率')
plt.title('样本数据直方图')
plt.show()

将直方图保存为图像文件

一旦您对直方图感到满意,就可以将其保存为图像文件,以便在报告、演示文稿或其他应用程序中使用。

# 将直方图保存为图像文件
plt.hist(data, bins=30, color='blue', alpha=0.5)
plt.xlabel('值')
plt.ylabel('频率')
plt.title('样本数据直方图')
plt.savefig('histogram.png', dpi=300)

在这个例子中,我们将直方图保存为 histogram.png 文件,分辨率为 300 dpi。这是一个 PNG 文件,分辨率为 300 点每英寸 (dpi)。

直方图归一化

直方图也可以被归一化,以表示数据的相对频率或概率密度,而不是绝对频率。

# 创建一个归一化的直方图
plt.hist(data, bins=30, density=True, color='blue', alpha=0.5)
plt.xlabel('值')
plt.ylabel('概率密度')
plt.title('样本数据的归一化直方图')
plt.show()

通过在 plt.hist() 函数中设置 density=True 参数,直方图的 y 轴将表示概率密度而不是频率。当比较具有不同尺度的数据集的直方图或将直方图与概率分布曲线叠加时,这可能很有用。

在同一图上绘制多个直方图

您可以在同一个图形上绘制多个直方图,以比较不同数据集或变量的分布。

# 生成两个样本数据集
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(1, 0.5, 1000)
 
# 创建一个带有两个子图的图形
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
 
# 绘制第一个直方图
ax1.hist(data1, bins=30, color='blue', alpha=0.5)
ax1.set_xlabel('值')
ax1.set_ylabel('频率')
ax1.set_title('数据集 1 的直方图')
 
# 绘制第二个直方图
ax2.hist(data2, bins=30, color='red', alpha=0.5)
ax2.set_xlabel('值')
ax2.set_ylabel('频率')
ax2.set_title('数据集 2 的直方图')
 
# 调整子图之间的间距
plt.subplots_adjust(wspace=0.4)
plt.show()

在这个例子中,我们创建了一个带有两个子图的图形,每个子图包含一个不同数据集的直方图。我们还使用 plt.subplots_adjust() 函数调整了子图之间的间距。

带有分类数据的直方图

直方图也可用于可视化分类数据的分布,尽管解释略有不同。

# 生成样本分类数据
categories = .
```以下是中文翻译版本:
 
['A', 'B', 'C', 'D', 'E']
data = np.random.choice(categories, 1000)
 
# 为分类数据创建直方图
plt.hist(data, bins=len(categories), edgecolor='black')
plt.xticks(range(len(categories)), categories)
plt.xlabel('类别')
plt.ylabel('频率')
plt.title('分类数据直方图')
plt.show()

在这个例子中,我们生成了1,000个随机的分类数据点,并创建了一个直方图来可视化它们的分布。bins参数被设置为唯一类别的数量,我们使用plt.xticks()来标记x轴上的类别名称。

连续数据的直方图

当处理连续数据时,选择bin的数量变得更加关键,因为它可以显著影响直方图的外观和解释。

# 生成连续数据样本
data = np.random.normal(0, 1, 1000)
 
# 使用不同的bin数量创建直方图
plt.figure(figsize=(12, 4))
 
plt.subplot(1, 2, 1)
plt.hist(data, bins=10, color='blue', alpha=0.5)
plt.xlabel('值')
plt.ylabel('频率')
plt.title('10个bin的直方图')
 
plt.subplot(1, 2, 2)
plt.hist(data, bins=50, color='red', alpha=0.5)
plt.xlabel('值')
plt.ylabel('频率')
plt.title('50个bin的直方图')
 
plt.subplots_adjust(wspace=0.4)
plt.show()

在这个例子中,我们创建了两个并排的直方图,bin的数量分别为10和50,以说明bin大小对连续数据可视化的影响。

函数

函数是可重复使用的代码块,执行特定的任务。它们允许您封装逻辑,使您的代码更加模块化和可维护。

以下是一个计算矩形面积的函数示例:

def calculate_area(length, width):
    area = length * width
    return area
 
# 调用函数
rect_area = calculate_area(5, 10)
print(rect_area)  # 输出: 50

在这个例子中,calculate_area()函数接受两个参数lengthwidth,并返回矩形的面积。

函数

Python 中的函数是一种可重复使用的代码块,可以接受参数并返回结果。下面是一个计算矩形面积的函数示例:

def calculate_rectangle_area(width, height):
    """
    计算矩形的面积。
    
    参数:
    width (float): 矩形的宽度。
    height (float): 矩形的高度。
    
    返回:
    float: 计算得到的矩形面积。
    """
    area = width * height
    return area
 
rect_width = 5
rect_height = 10
rect_area = calculate_rectangle_area(rect_width, rect_height)
print(rect_area)  # 输出: 50.0

这个 calculate_rectangle_area() 函数接受宽度和高度作为参数,计算并返回矩形的面积。你可以调用这个函数,并将结果存储在 rect_area 变量中。

函数还可以有默认参数值,并接受可变数量的参数:

def print_greeting(name, message="你好"):
    """
    打印问候语。
    
    参数:
    name (str): 要问候的人的名字。
    message (str, 可选): 问候语,默认为"你好"。
    """
    print(f"{message}, {name}!")
 
print_greeting("Alice")  # 输出: 你好, Alice!
print_greeting("Bob", "嗨")  # 输出: 嗨, Bob!
 
def calculate_sum(*numbers):
    """
    计算任意数量数字的和。
    
    参数:
    *numbers (float): 要求和的数字。
    
    返回:
    float: 所有数字的和。
    """
    total = 0
    for num in numbers:
        total += num
    return total
 
print(calculate_sum(1, 2, 3))  # 输出: 6
print(calculate_sum(4, 5, 6, 7, 8))  # 输出: 30

在第一个示例中,print_greeting() 函数有一个 message 参数的默认值,如果不提供值,就会使用这个默认值。在第二个示例中,calculate_sum() 函数可以接受任意数量的参数,这些参数会被收集到一个名为 numbers 的元组中。

模块和包

Python 的标准库提供了大量内置模块,你可以在程序中使用。你也可以创建自己的模块和包来组织代码。

下面是一个使用 math 模块的示例:

import math
 
radius = 5
circle_area = math.pi * radius ** 2
print(circle_area)  # 输出: 78.53981633974483

在这个示例中,我们导入了 math 模块,然后使用 pi 常量和 ** 运算符计算了一个圆的面积。

你也可以只导入模块中的特定函数或属性:

from math import pi, sqrt
 
radius = 5
circle_area = pi * radius ** 2
diagonal = sqrt(radius ** 2 + radius ** 2)
print(circle_area)  # 输出: 78.53981633974483
print(diagonal)  # 输出: 7.0710678118654755

在这里,我们直接从 math 模块中导入了 pisqrt 函数,这样就可以直接使用它们,而不需要加上 math. 前缀。

要创建自己的模块,只需将 Python 代码保存在一个以 .py 扩展名结尾的文件中即可。例如,你可以创建一个 my_module.py 文件,内容如下:

def my_function(arg1, arg2):
    """
    执行某些操作并返回结果。
    
    参数:
    arg1 (类型): 描述参数1的作用。
    arg2 (类型): 描述参数2的作用。
    
    返回:
    类型: 描述返回值的含义。
    """
    # 在这里编写函数的实现
    return result
```以下是中文翻译:
 
def greet(name):
    print(f"你好, {name}!")
 
def calculate_area(length, width):
    return length * width

你可以从你的模块中导入并使用这些函数:

import my_module
 
my_module.greet("Alice")  # 输出: 你好, Alice!
area = my_module.calculate_area(5, 10)
print(area)  # 输出: 50

包是一种将你的模块组织成层次结构的方式。要创建一个包, 你需要创建一个带有 __init__.py 文件的目录。这个文件可以是空的, 但是 Python 需要它来识别这个目录为一个包。

例如, 你可以创建一个 my_package 目录, 并在其中添加一个 my_module.py 文件:

my_package/
    __init__.py
    my_module.py

然后你可以从包中导入并使用模块中的函数:

import my_package.my_module
 
my_package.my_module.greet("Alice")  # 输出: 你好, Alice!
area = my_package.my_module.calculate_area(5, 10)
print(area)  # 输出: 50

或者, 你可以使用 from 语句直接从模块中导入函数:

from my_package.my_module import greet, calculate_area
 
greet("Alice")  # 输出: 你好, Alice!
area = calculate_area(5, 10)
print(area)  # 输出: 50

文件 I/O

Python 提供了内置函数来读取和写入文件。最常用的函数是 open(), read(), write()close().

下面是一个读取文件内容的例子:

# 以读取模式打开文件
with open("example.txt", "r") as file:
    contents = file.read()
    print(contents)

在这个例子中, open() 函数用于以读取模式 ("r") 打开 example.txt 文件。with 语句确保在代码块执行完毕后, 文件会被正确关闭, 即使发生异常。

你也可以逐行读取文件:

with open("example.txt", "r") as file:
    for line in file:
       .

print(line.strip())


这将打印文件的每一行,并使用 `strip()` 方法删除任何前导或尾随空白。

要写入文件,可以使用 `write()` 函数:

```python
with open("output.txt", "w") as file:
    file.write("Hello, World!\n")
    file.write("This is a new line.\n")

在这个例子中,我们以写入模式 ("w") 打开 output.txt 文件,然后使用 write() 函数向文件添加两行文本。

您也可以通过以追加模式 ("a") 打开文件来追加数据到现有文件:

with open("output.txt", "a") as file:
    file.write("This is an additional line.\n")

这将在 output.txt 文件的末尾添加一个新行。

异常处理

Python 的异常处理机制允许您处理代码中的错误和意外情况。使用 try-except 块来捕获和处理异常。

以下是如何处理 ZeroDivisionError 的示例:

try:
    result = 10 / 0
except ZeroDivisionError:
    print("Error: Division by zero")

在这个例子中,try 块尝试将 10 除以 0,这将引发 ZeroDivisionErrorexcept 块捕获该异常并打印错误消息。

您也可以在单个 except 块中处理多个异常:

try:
    num = int(input("Enter a number: "))
    result = 10 / num
except (ValueError, ZeroDivisionError):
    print("Error: Invalid input or division by zero")

在这个例子中,try 块尝试将用户输入转换为整数,然后将 10 除以结果。如果用户输入非数字值,将引发 ValueError;如果用户输入 0,将引发 ZeroDivisionErrorexcept 块捕获这两种异常并打印错误消息。

您还可以将 elsefinally 子句与 try-except 块一起使用:

try:
    num = int(input("Enter a number: "))
    result = 10 / num
except ValueErr.
```python
try:
    # 在这里执行可能引发异常的代码
    result = 10 / 0
except ValueError:
    # 如果发生 ValueError 异常,执行这里的代码
    print("错误: 无效的输入")
except ZeroDivisionError:
    # 如果发生 ZeroDivisionError 异常,执行这里的代码
    print("错误: 除以零")
else:
    # 如果没有引发任何异常,执行这里的代码
    print(f"结果是: {result}")
finally:
    # 无论是否引发异常,都会执行这里的代码
    print("'try-except' 块已完成.")

在这个例子中, else 子句在 try 块中没有引发异常时执行,而 finally 子句无论是否引发异常都会执行。

结论

在本教程中,你学习了 Python 的各种概念,包括函数、模块和包、文件 I/O 和异常处理。这些是任何 Python 程序员都必须掌握的基本技能,它们将帮助你编写更有组织、更易维护和更健壮的代码。

记住,提高 Python 技能的最佳方式是实践。尝试将你学到的概念应用到自己的项目中,不要害怕探索 Python 丰富的库和工具生态系统。祝你 Python 编程之路顺利!

MoeNagy Dev.