费曼学习法6 - 你好,NumPy!数据分析的 “瑞士军刀” (入门篇)
第一篇:你好,NumPy!数据分析的 “瑞士军刀” (入门篇)
开篇提问:
想象一下,你是一位厨艺高手,想要做出各种美味佳肴。 你手里已经有了各种食材,但是,如果想要 高效、快速、精确 地处理这些食材,做出色香味俱全的菜肴,你最需要的是什么呢? 没错,是一套 得心应手的厨具! 比如锋利的菜刀、精准的量具、高效的搅拌机等等。
在 Python 的数据分析世界里,NumPy 就扮演着 “厨具” 这样的角色。 它是一个功能强大的 工具库,专门用来 高效处理各种数据,就像瑞士军刀一样,一把在手,数据分析无忧! 那么,这个神奇的 NumPy 到底是什么? 它为什么如此重要? 今天,就让我们一起揭开 NumPy 的神秘面纱,开启数据分析的奇妙旅程!
核心概念讲解 (费曼式解释):
-
NumPy 是什么?数据分析为什么要用它? (瑞士军刀的比喻)
NumPy,全称 Numerical Python,顾名思义,就是 “用于数值计算的 Python 库”。 但如果仅仅理解为 “数值计算”,就太小看它了。 实际上,NumPy 是 Python 数据科学 和 分析 领域中 最核心、最基础 的库,几乎所有其他数据分析库 (比如后面我们会学到的 Pandas, Matplotlib, Scikit-learn 等) 都离不开 NumPy 的支持。
你可以把 NumPy 想象成一把 瑞士军刀,它集成了各种 数据处理的 “刀具”,每一种 “刀具” 都能帮你解决特定的数据处理难题。 例如:
-
高效的 “数据容器” - NumPy 数组 (ndarray): 就像瑞士军刀的主刀,NumPy 最核心的 “刀具” 就是 NumPy 数组 (ndarray)。 它可以 高效地存储和处理大量的数值数据,无论是 一维的数字序列 (比如商品价格列表),还是 多维的数据表格 (比如学生成绩单),NumPy 数组都能轻松胜任。 而且,NumPy 数组在 运算速度上 远超 Python 自带的列表 (list),就像用锋利的菜刀切菜比用普通水果刀快得多一样!
-
强大的 “数学工具箱” - 各种数学函数: 瑞士军刀里有各种工具,NumPy 也内置了 丰富的数学函数,可以进行各种 数学运算、统计分析、线性代数、傅里叶变换 等等。 就像瑞士军刀里的螺丝刀、锯子、尺子等工具,NumPy 的数学函数可以帮你快速完成各种复杂的数据计算和分析任务。
-
灵活的 “数据变形” 能力 - 形状变换、索引切片: 瑞士军刀可以根据需要变形展开,NumPy 数组也具有 灵活的 “变形” 能力。 你可以 随意改变数组的形状 (比如把一维数组变成二维数组), 自由地访问和操作数组中的数据 (通过索引和切片),就像瑞士军刀可以灵活地调整刀片角度、切换不同工具一样,NumPy 数组可以灵活地适应各种数据处理需求。
为什么要用 NumPy? 简单来说,就是为了 更高效、更方便、更强大 地进行数据分析和数值计算! 如果你想在数据科学领域有所作为,NumPy 绝对是你 必须掌握 的 “瑞士军刀”!
-
-
安装 NumPy: 准备好你的 “瑞士军刀”
想要使用 NumPy 这把 “瑞士军刀”,首先要把它 安装 到你的 Python 环境中。 安装 NumPy 非常简单,只需要一行命令:
pip install numpy
和安装 PIL 库 (Pillow) 一样,打开你的电脑终端 (Windows 用户是命令提示符或 PowerShell,Mac/Linux 用户是终端),复制粘贴这行代码,按下回车键,等待安装完成就可以了。 就像网购了一把瑞士军刀,收到货拆开包装就OK了!
安装完成后,你就可以在 Python 代码中 导入 NumPy 库,开始使用它的各种功能了。 通常我们 约定俗成 地使用
np
作为 NumPy 的 别名 (alias),这样可以更方便地调用 NumPy 的函数和对象。import numpy as np # 导入 NumPy 库,并使用 np 作为别名 # 现在你就可以使用 np.xxx 来调用 NumPy 的功能了,例如: # np.array(), np.zeros(), np.sum(), np.mean() ...
-
NumPy 数组 (ndarray): 数据分析的 “积木”
NumPy 最核心的概念就是 NumPy 数组 (ndarray),全称 N-dimensional array,也就是 “N 维数组”。 你可以把 NumPy 数组想象成 数据分析的 “积木”,我们可以用它来 构建各种复杂的数据结构,进行各种数据处理和计算。
-
什么是数组? (有序的 “数据集合”)
简单来说,数组 就是 一组有序排列的 “数据” 的集合。 这组数据可以是数字、文字、图像、音频等等。 在 NumPy 中,我们主要处理的是 数值数据。 NumPy 数组和 Python 自带的列表 (list) 有些类似,但它们之间有着本质的区别。 NumPy 数组更像是一个 “数学上的向量或矩阵”,它对 数值运算进行了专门的优化,因此在处理数值数据时,NumPy 数组的效率远高于 Python 列表。 就像用专业的搅拌机打果汁比用手动搅拌器快得多一样!
-
数组的维度 (ndim): “积木” 的层数
维度 (ndim) 描述了数组是 “几维的”。 你可以把数组的维度想象成 “积木的层数”。
-
一维数组 (1D array): 就像 一条直线,或者 一列数字,只有一个维度 (层)。 例如,
[1, 2, 3, 4, 5]
就是一个一维数组,它只有一个轴 (axis),通常称为 行 (row) 或 列 (column) (取决于方向)。 -
二维数组 (2D array): 就像 一个表格,或者 一个矩阵,有两个维度 (层)。 例如,
[[1, 2, 3], [4, 5, 6]]
就是一个二维数组,它有两个轴,通常称为 行 (row) 和 列 (column)。 -
三维数组 (3D array): 就像 一个立方体,或者 多张表格堆叠在一起,有三个维度 (层)。 例如,在图像处理中,彩色图像通常用三维数组表示,三个维度分别代表 高度 (height)、宽度 (width) 和颜色通道 (channels) (比如 RGB 三个颜色通道)。
-
更高维度的数组 (ND array): NumPy 数组可以扩展到 任意维度,维度数由
ndim
属性表示。 维度越多,数据结构就越复杂,但 NumPy 都能高效地处理。
-
-
数组的形状 (shape): “积木” 的尺寸
形状 (shape) 描述了数组在 每个维度上的 “长度” (或者说 “尺寸”)。 你可以把数组的形状想象成 “积木的尺寸”。 形状通常用一个 元组 (tuple) 来表示。
-
一维数组的形状: 形状元组只有一个元素,表示数组的 长度。 例如,一维数组
[1, 2, 3, 4, 5]
的形状是(5,)
,表示它有 5 个元素。 -
二维数组的形状: 形状元组有两个元素,分别表示数组的 行数和列数。 例如,二维数组
[[1, 2, 3], [4, 5, 6]]
的形状是(2, 3)
,表示它有 2 行 3 列。 -
三维数组的形状: 形状元组有三个元素,分别表示数组在 高度、宽度、深度 (或其他维度) 上的长度。 例如,一个表示 RGB 图像的三维数组,形状可能是
(256, 256, 3)
,表示图像高度 256 像素,宽度 256 像素,颜色通道数为 3 (RGB)。 -
可以通过
数组名.shape
来查看数组的形状。
-
-
数组的数据类型 (dtype): “积木” 的材质
数据类型 (dtype) 描述了数组中 元素的数据类型。 你可以把数组的数据类型想象成 “积木的材质”。 NumPy 数组要求 所有元素的类型必须相同,这是 NumPy 数组高效运算的关键。 常见的数据类型包括:
-
int
: 整数类型 (例如int8
,int16
,int32
,int64
等,表示不同位数的整数) -
float
: 浮点数类型 (例如float16
,float32
,float64
等,表示不同精度的浮点数) -
bool
: 布尔类型 (True 或 False) -
string_
或U
: 字符串类型 (Unicode 字符串) -
object
: Python 对象类型 (可以存储任意 Python 对象,但效率较低,一般不常用) -
可以通过
数组名.dtype
来查看数组的数据类型。 在创建数组时,NumPy 会 自动推断数据类型,也可以 手动指定数据类型。
-
-
-
创建 NumPy 数组: “搭积木” 的第一步
掌握了 NumPy 数组的基本概念,我们就可以开始 创建 NumPy 数组 了,就像 “搭积木” 的第一步! NumPy 提供了多种创建数组的方法,我们可以根据不同的需求选择合适的方法。
-
从 Python 列表或元组创建:
np.array()
np.array()
函数是最常用的创建数组的方法之一,它可以 将 Python 列表 (list) 或 元组 (tuple) 转换为 NumPy 数组。import numpy as np # 从 Python 列表创建一维数组 list1 = [1, 2, 3, 4, 5] array1 = np.array(list1) print("从列表创建的一维数组:\n", array1) print("数组的形状:", array1.shape) print("数组的维度:", array1.ndim) print("数组的数据类型:", array1.dtype) # 从 Python 列表创建二维数组 list2 = [[1, 2, 3], [4, 5, 6]] array2 = np.array(list2) print("\n从列表创建的二维数组:\n", array2) print("数组的形状:", array2.shape) print("数组的维度:", array2.ndim) print("数组的数据类型:", array2.dtype) # 从 Python 元组创建数组 (方法类似,只需要把列表换成元组) tuple1 = (1, 2, 3) array3 = np.array(tuple1) print("\n从元组创建的一维数组:\n", array3)
代码解释:
np.array(object, dtype=None, ...)
:np.array()
函数的主要参数是object
,表示要转换为数组的 Python 对象 (例如列表、元组、或其他数组)。dtype
参数可以 手动指定数组的数据类型,如果不指定,NumPy 会 自动推断。
-
创建全零数组:
np.zeros()
np.zeros(shape, dtype=float, ...)
函数可以 创建指定形状和数据类型的 “全零” 数组。 就像准备一块 “空白画布”,可以用来 初始化数组,或者作为 占位符。import numpy as np # 创建一个形状为 (3, 4) 的全零二维数组 (默认数据类型为 float64) zeros_array1 = np.zeros((3, 4)) # 注意形状参数要用元组表示 print("全零二维数组:\n", zeros_array1) print("数组的形状:", zeros_array1.shape) print("数组的数据类型:", zeros_array1.dtype) # 创建一个形状为 (2, 2, 2) 的全零三维数组,并指定数据类型为 int32 zeros_array2 = np.zeros((2, 2, 2), dtype=np.int32) # 或者 dtype='int32' print("\n全零三维数组 (int32 类型):\n", zeros_array2) print("数组的数据类型:", zeros_array2.dtype)
代码解释:
np.zeros(shape, dtype=float, ...)
:shape
参数是 数组的形状,需要用 元组 表示。dtype
参数可以 指定数组的数据类型,默认为float64
(双精度浮点数)。 可以使用np.int32
,np.float32
,np.bool_
,np.string_
等 NumPy 内置的数据类型,也可以使用 Python 内置的数据类型字符串,例如'int32'
,'float64'
,'bool'
,'U10'
(Unicode 字符串,长度为 10) 等。
-
创建全一数组:
np.ones()
np.ones(shape, dtype=float, ...)
函数可以 创建指定形状和数据类型的 “全一” 数组。 和np.zeros()
类似,但数组元素都是 1。 可以用来 初始化数组,或者作为 “单位矩阵” (在线性代数中)。import numpy as np # 创建一个形状为 (2, 5) 的全一二维数组 (默认数据类型为 float64) ones_array1 = np.ones((2, 5)) print("全一二维数组:\n", ones_array1) # 创建一个形状为 (3,) 的全一维数组,并指定数据类型为 int8 ones_array2 = np.ones(3, dtype=np.int8) # 注意:一维数组的形状可以直接写整数,不用元组 print("\n全一维数组 (int8 类型):\n", ones_array2) print("数组的数据类型:", ones_array2.dtype)
代码解释:
np.ones(shape, dtype=float, ...)
: 参数与np.zeros()
类似,shape
是形状,dtype
是数据类型,默认为float64
。 注意:当创建一维数组时,shape
参数可以直接写一个整数,表示数组的长度,不用写成元组(length,)
。 但为了代码风格统一,建议都写成元组形式,例如(3,)
。
-
创建未初始化数组:
np.empty()
np.empty(shape, dtype=float, ...)
函数可以 创建指定形状和数据类型的 “未初始化” 数组。 注意:“未初始化” 并不意味着数组是空的,而是数组的元素值是随机的,取决于内存中的现有数据,而不是被设置为 0 或 1。np.empty()
的 创建速度通常比np.zeros()
和np.ones()
更快,因为它不需要初始化数组元素。 但使用np.empty()
创建的数组,在使用前必须手动赋值,否则可能会得到意想不到的结果。np.empty()
通常用于 对性能要求较高,并且后续会覆盖数组元素值的场景。import numpy as np # 创建一个形状为 (2, 3) 的未初始化二维数组 (数据类型为 float64) empty_array = np.empty((2, 3)) print("未初始化二维数组 (元素值随机):\n", empty_array) # 每次运行结果可能不一样 print("数组的数据类型:", empty_array.dtype)
代码解释:
np.empty(shape, dtype=float, ...)
: 参数与np.zeros()
和np.ones()
类似。 请务必注意np.empty()
创建的数组是未初始化的,元素值是随机的,使用前需要手动赋值!
-
创建连续整数序列数组:
np.arange()
np.arange([start, ]stop, [step, ]dtype=None, ...)
函数可以 创建等差的整数序列数组,类似于 Python 内置的range()
函数,但np.arange()
返回的是 NumPy 数组,而不是列表。import numpy as np # 创建从 0 到 9 的整数序列数组 (不包含 10,步长为 1,默认数据类型为 int64) arange_array1 = np.arange(10) # 相当于 range(0, 10) print("arange 整数序列数组 (0-9):\n", arange_array1) # 创建从 5 到 15 的整数序列数组 (不包含 16,步长为 2) arange_array2 = np.arange(5, 16, 2) # 相当于 range(5, 16, 2) print("\narange 整数序列数组 (5-15, 步长 2):\n", arange_array2) print("数组的数据类型:", arange_array2.dtype) # 创建从 0 到 1 的浮点数序列数组 (不包含 1,步长为 0.1) arange_array3 = np.arange(0, 1, 0.1) # 步长可以是浮点数,但结果可能 неточное из-за проблем с плавающей точкой print("\narange 浮点数序列数组 (0-1, 步长 0.1):\n", arange_array3) print("数组的数据类型:", arange_array3.dtype) # 注意数据类型变成了 float64
代码解释:
np.arange([start, ]stop, [step, ]dtype=None, ...)
: 参数类似于range()
函数:start
: 序列的 起始值 (可选,默认为 0)。stop
: 序列的 结束值 (必须, 不包含 结束值)。step
: 序列的 步长 (可选,默认为 1)。dtype
: 可以 指定数组的数据类型,如果不指定,NumPy 会 自动推断。
- 注意:
np.arange()
的stop
参数是不包含在序列内的,就像 Python 的range()
函数一样。 另外,使用浮点数作为步长时,由于浮点数精度问题,结果可能会 不完全精确,建议尽量使用整数步长,或者使用np.linspace()
函数创建更精确的等差浮点数序列。
-
创建等间隔浮点数序列数组:
np.linspace()
np.linspace(start, stop, num=50, endpoint=True, ...)
函数可以 创建指定数量的、等间隔的浮点数序列数组。 与np.arange()
不同,np.linspace()
更精确地控制序列的元素数量和间隔,并且 默认包含结束值。import numpy as np # 创建从 0 到 1 的 5 个等间隔浮点数序列数组 (包含 1,默认包含结束值) linspace_array1 = np.linspace(0, 1, num=5) # num 参数指定元素数量 print("linspace 等间隔浮点数序列数组 (0-1, 5 个元素):\n", linspace_array1) print("数组的数据类型:", linspace_array1.dtype) # 数据类型为 float64 # 创建从 0 到 1 的 10 个等间隔浮点数序列数组 (不包含 1,endpoint=False) linspace_array2 = np.linspace(0, 1, num=10, endpoint=False) # endpoint=False 不包含结束值 print("\nlinspace 等间隔浮点数序列数组 (0-1, 10 个元素, 不含结尾):\n", linspace_array2)
代码解释:
np.linspace(start, stop, num=50, endpoint=True, ...)
:start
: 序列的 起始值。stop
: 序列的 结束值 ( 默认包含 结束值,可以通过endpoint=False
设置为不包含)。num
: 要生成的 元素数量 (默认为 50)。endpoint
: 是否 包含结束值,默认为True
(包含),设置为False
则不包含结束值。
-
创建 0-1 之间均匀分布的随机数数组:
np.random.rand()
np.random.rand(d0, d1, ..., dn)
函数可以 创建指定形状的、元素值在 0-1 之间均匀分布的随机数数组。np.random
是 NumPy 的 随机数模块,提供了各种随机数生成函数。import numpy as np # 创建一个形状为 (2, 3) 的 0-1 均匀分布随机数二维数组 rand_array1 = np.random.rand(2, 3) # 注意形状参数直接写整数,不用元组 print("0-1 均匀分布随机数二维数组:\n", rand_array1) print("数组的形状:", rand_array1.shape) print("数组的数据类型:", rand_array1.dtype) # 数据类型为 float64 # 创建一个形状为 (5,) 的 0-1 均匀分布随机数一维数组 rand_array2 = np.random.rand(5) # 一维数组的形状可以直接写整数 print("\n0-1 均匀分布随机数一维数组:\n", rand_array2)
代码解释:
np.random.rand(d0, d1, ..., dn)
: 参数d0, d1, ..., dn
表示数组的 各个维度的长度。 注意:np.random.rand()
的形状参数是直接写整数,不用写成元组形式。 例如np.random.rand(2, 3)
表示创建形状为(2, 3)
的二维数组,np.random.rand(5)
表示创建形状为(5,)
的一维数组。
-
-
案例应用: 用 NumPy 数组表示班级学生的成绩单
现在,我们来做一个简单的 案例应用,展示 NumPy 数组的 实用性。 假设我们要用 NumPy 数组来 表示一个班级学生的成绩单,并进行一些简单的 数据分析。
import numpy as np # 假设有 5 个学生,3 门科目 (语文、数学、英语) 的成绩,用二维数组表示成绩单 # 每行代表一个学生,每列代表一门科目 grades_array = np.array([ [85, 92, 78], # 学生 1 的成绩 (语文, 数学, 英语) [90, 88, 95], # 学生 2 的成绩 [75, 80, 82], # 学生 3 的成绩 [95, 98, 92], # 学生 4 的成绩 [80, 85, 88] # 学生 5 的成绩 ]) print("学生成绩单 (NumPy 数组):\n", grades_array) print("成绩单的形状:", grades_array.shape) # (5, 3),5 行 3 列 print("成绩单的维度:", grades_array.ndim) # 2 维 # 1. 计算每门科目的平均分 (按列计算平均值,axis=0 表示按列) average_scores = np.mean(grades_array, axis=0) print("\n每门科目的平均分:", average_scores) # 语文平均分, 数学平均分, 英语平均分 # 2. 计算每个学生的总分 (按行计算总和,axis=1 表示按行) total_scores = np.sum(grades_array, axis=1) print("\n每个学生的总分:", total_scores) # 学生 1 总分, 学生 2 总分, ... # 3. 找出数学科目的最高分 (第二列,索引为 1) max_math_score = np.max(grades_array[:, 1]) # 冒号 : 表示所有行,1 表示第二列 print("\n数学科目的最高分:", max_math_score) # 4. 找出英语科目的最低分 (第三列,索引为 2) min_english_score = np.min(grades_array[:, 2]) # 冒号 : 表示所有行,2 表示第三列 print("\n英语科目的最低分:", min_english_score)
代码解释:
- 用二维数组表示成绩单: 我们用一个 5 行 3 列的二维数组
grades_array
来表示 5 个学生的 3 门科目成绩。 每一行代表一个学生,每一列代表一门科目。 这就像一个 电子表格 一样,非常直观。 np.mean(grades_array, axis=0)
:np.mean()
函数可以计算数组的 平均值。axis=0
参数表示 沿着第 0 轴 (列) 计算平均值,也就是 按列计算平均值,得到每门科目的平均分。np.sum(grades_array, axis=1)
:np.sum()
函数可以计算数组的 总和。axis=1
参数表示 沿着第 1 轴 (行) 计算总和,也就是 按行计算总和,得到每个学生的总分。grades_array[:, 1]
: 数组索引和切片 操作,[:, 1]
表示 选取所有行 (冒号 : 表示所有行),第二列 (索引为 1),也就是 数学科目的所有成绩。np.max(grades_array[:, 1])
可以找出数学科目的最高分。[:, 2]
同理,表示 英语科目的所有成绩,np.min(grades_array[:, 2])
可以找出英语科目的最低分。- 这个简单的案例展示了 NumPy 数组在数据表示和简单数据分析方面的便捷性和高效性。 在后续的文章中,我们会学习更多 NumPy 数组的强大功能,进行更复杂的数据处理和分析。
- 用二维数组表示成绩单: 我们用一个 5 行 3 列的二维数组
费曼回顾 (知识巩固):
现在,请你用自己的话,像给一个完全不懂编程的朋友解释一下,今天我们都学习了哪些关于 NumPy 的知识? 例如:
- NumPy 是什么? 为什么说它是数据分析的 “瑞士军刀”? 它最重要的 “刀具” 是什么?
- 什么是 NumPy 数组 (ndarray)? 它和 Python 列表有什么区别? 它有哪些重要的属性 (维度、形状、数据类型)? 它们分别是什么意思?
- 我们学习了哪些创建 NumPy 数组的方法?
np.array()
,np.zeros()
,np.ones()
,np.empty()
,np.arange()
,np.linspace()
,np.random.rand()
分别有什么用途? 你能用生活中的例子来比喻它们吗? - 在学生成绩单案例中,我们是如何使用 NumPy 数组来表示数据,并进行简单的数据分析的?
尝试用最简洁、最形象的语言来解释,就像你是一位老师,正在给你的学生讲解一样。 如果你能清晰地解释出来,就说明你已经掌握了今天学习的内容!
课后思考 (拓展延伸):
- 尝试修改学生成绩单案例的代码,例如:
- 增加学生人数或科目数量,看看代码是否仍然有效?
- 计算每门科目的标准差、最高分、最低分,或者其他统计指标?
- 找出总分最高的学生?
- 将成绩单保存到文件 (例如 CSV 文件),再从文件中读取数据进行分析? (提示:可以使用
np.savetxt()
和np.loadtxt()
函数,我们会在后续文章中学习)
- 思考一下,除了学生成绩单,NumPy 数组还可以用来表示哪些数据? 例如,图像、音频、股票价格、地理位置信息等等。 你有什么有趣的应用想法吗?
- 尝试查阅 NumPy 官方文档或其他 NumPy 教程,了解更多 NumPy 数组的创建方法和属性,例如
np.full()
,np.eye()
,np.identity()
,np.asarray()
,array.size
,array.itemsize
等。
恭喜你!完成了 NumPy 费曼学习法的第一篇文章学习! 你已经迈出了掌握数据分析 “瑞士军刀” 的第一步! 在下一篇文章中,我们将继续探索 NumPy 数组的 “变形术”,学习如何灵活地改变数组的形状,以及如何使用索引和切片来访问和操作数组中的数据,让你的数据处理技能更上一层楼! 敬请期待!