一文通俗理解Python的numpy库

作者:carzy2026.11.07 01:56浏览量:0

简介:numpy是Python科学计算的基石,核心是n维数组对象ndarray。本文用通俗语言讲清numpy的定位与优势、它与Python列表的区别、安装步骤、数组与“轴”的概念、常用创建方式、索引切片、广播机制与向量化运算,并配可运行的代码示例,帮你快速建立数组思维,为数据分析与机器学习打下扎实基础。

numpy是许多开发者从“写脚本”迈向“做数据”的第一站,但初学者常被“多维数组”“广播”“向量化”这些词劝退。其实这些概念并不神秘,下面用通俗的语言和完整的代码示例,把numpy一次讲透。

一、numpy是什么:一句话说清定位

一言以蔽之:numpy是Python中基于数组对象的科学计算基础库。把它拆开,可以提炼出三个关键词:

  • n维数组对象(ndarray):numpy的灵魂,所有功能都围绕它展开;
  • 广播机制(broadcasting):让形状不同的数组也能直接做运算;
  • 成套的科学计算API:求和、均值、矩阵运算、随机数……开箱即用。

Python能成为数据科学领域的主流语言,numpy居功至伟——大量数据分析和机器学习相关的框架,都直接以numpy数组作为最基础的数据交换格式。先把numpy吃透,后续学习任何数据处理、算法建模的内容都会顺畅得多。

二、为什么需要numpy:Python列表差在哪

Python自带的list灵活有余、效率不足,原因有三:

  1. list里的每个元素本质是一个“指针”,指向散落在内存各处的对象,访问时需要多次跳转;
  2. list允许混装不同类型的数据,底层无法针对单一类型做极致优化;
  3. 对list做数值运算往往要写for循环,每个元素都要经过解释器调度,数据一大,开销急剧上升。

ndarray的设计正好反其道而行之:

  • 同类数据:一个数组只装一种类型的元素,数据整块连续存储,CPU读取效率更高;
  • 向量化运算:一条语句对整个数组生效,底层由高度优化的编译代码批量完成,跳过了逐元素的解释开销。

示意对比:

  1. import numpy as np
  2. data = [1, 2, 3, 4, 5]
  3. # 纯Python:列表推导式逐元素计算
  4. result_list = [x ** 2 for x in data]
  5. # numpy:直接对整块数组运算
  6. arr = np.array(data)
  7. result_arr = arr ** 2
  8. # array([ 1, 4, 9, 16, 25])

两段代码都只有一行,但当数据量达到十万、百万级时,数组写法通常能取得数倍乃至数十倍的性能优势(具体取决于任务规模与硬件环境),内存占用也更低。这就是“能用数组就不写循环”的由来。

三、如何安装numpy

numpy是一个标准的Python库,用Python官方自带的包管理工具pip即可安装。装好Python后,打开系统的命令行终端,执行:

  1. pip install numpy

验证安装是否成功:

  1. python -c "import numpy; print(numpy.__version__)"

能正常打印版本号即说明环境就绪。两点补充:其一,若下载速度较慢,可以配置镜像源加速;其二,如果使用的是某些科学计算发行版环境,也可以用其自带的包管理工具安装,效果等同。此外,本地机器配置有限时,还可以在百度智能云等云平台开通一台云服务器,用同样的方式搭建Python环境,为后续更大规模的计算留出资源余量。

四、核心对象ndarray:先理解“轴”

ndarray是一系列同类数据的集合,可以进行索引、切片和迭代。理解它最关键的概念是轴(axis):

  • 一维数组只有1个轴,可以想象成“一行数据”;
  • 二维数组有2个轴:axis 0沿行方向(纵向)、axis 1沿列方向(横向),类似一张表格;
  • 三维数组有3个轴,可以想象成“一摞表格”。
  1. import numpy as np
  2. arr3d = np.array([
  3. [[ 0, 1, 2], [ 3, 4, 5]],
  4. [[ 6, 7, 8], [ 9, 10, 11]]
  5. ])
  6. print(arr3d.shape) # (2, 2, 3):2张"表格",每张2行3列
  7. print(arr3d.ndim) # 3:共三个轴

上面这个数组可以理解成:一摞共2张表格,每张表格2行、3列。以此类推,n维数组就是n个轴。实际应用里,一张彩色图片本质上就是一个三维数组(高×宽×3个颜色通道),这个类比能帮你迅速建立直觉。

日常调试时,最常用的三个“体检属性”是:

  • shape:各轴的长度,如(2, 2, 3);
  • ndim:轴的个数(维度数);
  • dtype:元素的数据类型,如int、float等。

五、数组的常见创建方式

1. 从Python序列创建

  1. import numpy as np
  2. # 一维数组,并显式指定类型为int
  3. a = np.array([1, 2, 3], dtype=int)
  4. # array([1, 2, 3])
  5. # 二维数组:传入嵌套序列即可
  6. b = np.array(((1, 2), (3, 4)))
  7. '''
  8. array([[1, 2],
  9. [3, 4]])
  10. '''

numpy会自动推断元素类型,但在需要严格控制计算性质时(比如保证整数运算不被转成浮点),显式写明dtype是更稳妥的习惯。

2. 按区间生成

np.arange的用法类似Python内置的range,同样是左闭右开:

  1. np.arange(1, 6)
  2. # array([1, 2, 3, 4, 5])
  3. np.arange(0, 10, 2) # 起点0、终点10(不含)、步长2
  4. # array([0, 2, 4, 6, 8])
  5. np.arange(6).reshape(2, 3) # 生成后重塑为2行3列
  6. '''
  7. array([[0, 1, 2],
  8. [3, 4, 5]])
  9. '''

3. 按形状批量生成

初始化和占位时的高频工具:

  1. np.zeros((2, 3)) # 全0数组
  2. np.ones((2, 3)) # 全1数组
  3. np.full((2, 3), 7) # 用指定值填充
  4. np.linspace(0, 1, 5) # [0,1]之间等间隔取5个点
  5. # array([0. , 0.25, 0.5 , 0.75, 1. ])

4. 创建随机数组

模拟数据、随机初始化都离不开随机数组:

  1. import numpy as np
  2. # 2行2列、取值范围[0, 1)的均匀分布随机数组
  3. np.random.rand(2, 2)
  4. '''
  5. array([[0.99449146, 0.92339551],
  6. [0.1837405 , 0.41719798]])
  7. '''
  8. # 3行2列、服从标准正态分布的随机数组
  9. np.random.randn(3, 2)
  10. '''
  11. array([[-1.27481003, -1.5888111 ],
  12. [ 0.16985203, -2.91526479],
  13. [ 1.75992671, -2.81304831]])
  14. '''
  15. # 在[0, 10)内随机生成2行3列整数
  16. np.random.randint(0, 10, size=(2, 3))
  17. # 固定随机种子,保证结果可复现
  18. np.random.seed(42)

两个新手常踩的坑,特别提醒:其一,rand/randn的形状以独立参数传入,写作rand(2, 2);而zeros/ones等以元组传入,写作zeros((2, 3)),两种风格不同,极易混淆。其二,写实验代码时先固定随机种子,可以让每次运行结果一致,这是保证实验可复现、方便排查问题的基本做法。

六、索引与切片:取用数据的基本姿势

  1. a = np.arange(12).reshape(3, 4)
  2. '''
  3. array([[ 0, 1, 2, 3],
  4. [ 4, 5, 6, 7],
  5. [ 8, 9, 10, 11]])
  6. '''
  7. a[1, 2] # 第2行第3列:6
  8. a[0] # 第1行:array([0, 1, 2, 3])
  9. a[:, 1] # 所有行的第2列:array([1, 5, 9])
  10. a[0:2, 1:3] # 前2行、第2~3列构成的子数组

除位置索引外,还有两种高频用法:

  • 布尔索引:按条件筛选。a[a > 5]会取出所有大于5的元素;data[data < 0] = 0可以把负值批量置零,是数据清洗的常用手段。
  • 花式索引:用整数列表一次性取多行,如a[[0, 2]]取出第1、3行。

一个必须知道的细节:通过基本切片得到的子数组是原数组的视图而非拷贝,修改子数组会同步影响原数组;如果需要独立副本,请显式调用copy()方法。

七、广播机制:形状不同也能运算

按数学惯例,两个矩阵形状相同才能相加。但numpy允许形状不同的数组直接运算,靠的就是广播。规则可概括为三条:

  1. 从两个数组shape的末尾维度开始逐位比较;
  2. 每一对维度要么相等,要么其中一方为1,要么一方缺失,才算兼容;
  3. 长度为1的维度在运算时被“虚拟拉伸”以匹配另一方——注意这只是计算上的便利,并不真的复制数据。

最简单的广播是数组与标量:

  1. a = np.array([1, 2, 3])
  2. a * 2 # array([2, 4, 6])
  3. a + 10 # array([11, 12, 13])

稍复杂一点的例子:

  1. A = np.ones((3, 1)) # 3行1列
  2. B = np.ones((1, 4)) # 1行4列
  3. A + B # 结果形状为(3, 4)

对照规则:(3,1)与(1,4)从末尾比较——末位1对4(1可拉伸为4),首位3对1(1可拉伸为3),于是双方都按(3, 4)参与运算。如果某对维度既不相等、也没有1,运算会直接报错,此时应先用reshape等方法把形状对齐。理解广播后你会发现:numpy代码之所以能写得很短,一半功劳在向量化,另一半就在广播。

八、向量化运算与统计函数

加减乘除、乘方、开方、比较……numpy的运算符和函数都直接作用于整块数组:

  1. a = np.array([1, 2, 3, 4])
  2. a + 1 # array([2, 3, 4, 5])
  3. a * a # array([ 1, 4, 9, 16])
  4. np.sqrt(a) # array([1. , 1.41421356, 1.73205081, 2. ])

统计函数同样一行搞定,并且可通过axis参数指定沿哪个轴计算:

  1. m = np.arange(12).reshape(3, 4)
  2. m.sum() # 全局求和:66
  3. m.sum(axis=0) # 沿axis 0压扁 → 每列的和:array([12, 15, 18, 21])
  4. m.sum(axis=1) # 沿axis 1压扁 → 每行的和:array([ 6, 22, 40])
  5. m.mean() # 均值:5.5
  6. m.max(axis=1) # 每行最大值:array([ 3, 7, 11])
  7. m.std() # 标准差

记忆技巧:axis等于几,就沿第几个轴把数组“压扁”。按行统计还是按列统计,是日常使用中最容易出错、也最值得练熟的点。

九、小实战:三行代码完成数据标准化

把前面所有知识点串起来,做一个数据预处理中最常见的任务——标准化(把每个特征调整为均值0、标准差1):

  1. import numpy as np
  2. # 模拟5个样本、3个特征的数据
  3. data = np.random.randn(5, 3)
  4. mean = data.mean(axis=0) # 每个特征的均值,形状(3,)
  5. std = data.std(axis=0) # 每个特征的标准差,形状(3,)
  6. normalized = (data - mean) / std

这段代码有两个值得细品的地方:一是axis=0让统计沿样本方向进行,得到的是“每个特征”的统计量;二是data形状为(5, 3),而mean形状为(3,),两者并不相同却能直接相减——这正是广播在幕后把(3,)按行匹配到了(5, 3)。若用纯Python实现同样的逻辑,需要两层嵌套循环外加小心维护下标;两相对比,就能体会numpy的表达力所在。

十、总结与学习路线

最后用几句话收束全文:

  • 定位:numpy是Python科学计算的基础库,核心是n维数组对象ndarray;
  • 它快的原因:同类数据 + 连续内存 + 向量化运算 + 广播机制;
  • 上手路径:先掌握数组创建和shape、axis、ndim三个属性,再练索引与切片,然后吃透广播,最后熟练运用统计函数;
  • 练习建议:把日常用for循环处理数字的场景,尝试改写成数组运算,对比代码长度和运行速度,这是建立“数组思维”最快的路径。

numpy是数据分析与机器学习绕不开的地基。把“形状”和“轴”两个概念吃透、再理解广播,你遇到的大部分报错都能自行定位。用好numpy之后,你写出的数值计算代码会更短、更快,也更接近数学表达式本身。