在众多 Python 的数据处理库中, Numpy 是一个非常强大的存在. Numpy 为我们提供了高性能的多维数组, 以及这些数组对象上的各种操作. 但是, 作为一个刚入门 Python 的新手, 你可能会问: "为什么我需要 Numpy, 而不是直接使用Python 的内置列表?"在这篇文章的开篇, 我们就来探讨这个问题.
Numpy (Numerical Python) 是 Python 非常重要的一个库, 用于处理数值数组. Numpy 为我们提供了大量数据处理的函数以及数学函数. 与 Python 的内列表相比, Numpy 数组在数据分析, 科学计算, 线性代数, 机器学习等方面都表现出了卓越的性能和效率.
虽然 Python 的内置列表很灵活, 能存储任意类型的数据. 但当我们需要进行大量的数值运算时 (线性代数, 统计), Python 的内置列表效率并不高. Numpy 数组相比之下, 是在连续的内存块上存储的, 这使得访问速度更快, 效率更高. 而且 Numpy 是用 C 语言编写的, 其内部迭代计算比 Python 的内置循环要快很多.
例子:
在现代数据科学领域, 数据处理, 清晰, 统计分析, 特征工厂, 机器学习等各个领域都离不开数值计算. Numpy 为我们提供了一套完整, 高效的工具, 使得我们的任务变得简单. 几乎所有的 Python 数据处理库, 如 Pandas, Scipy 等, 都是基于 Numpy 构建的. 所以我们非常有必要要熟悉掌握 Numpy 库.
并发 vs 并行
举个例子:
举个生活中的例子:
小白吃饭吃到一半, 电话来了, 我一直到吃完了以后才去接, 这就说明你不支持并发也不支持并行.
小白吃饭吃到一半, 电话来了, 你停了下来接了电话, 接完后继续吃饭, 这说明你支持并发.
小白吃饭吃到一半, 电话来了, 你一边听电话一边吃饭, 这说明你支持并行.
应用:
单线程 vs 多线程:
GIL (Global Interpreter Lock) 全局解释器, 来源是 Python设计之初的考虑, 为了数据安全所做的决定.
每个 CPU 在同一时间只能执行一个线程 (在单核 CPU 下的多线程其实都只是并发, 不是并行, 并发和并行从宏观上来讲都是同时处理多路请求的概念. 但并发和并行又有区别, 并行是指两个或者多个事件在同一时刻发生, 而并发是指两个或多个事件在同一时间间隔内发生.
Python 内置列表:
Numpy ndarray:
例子:
我们可以看到存储相同数据的情况下, python 内置列表使用了超过 ndarray 2 倍的内存.
进一步说明, 我们来看一下 ndarray 源代码:
/*
* The main array object structure.
*/
/* This struct will be moved to a private header in a future release */
typedef struct tagPyArrayObject_fields {
PyObject_HEAD
/* Pointer to the raw data buffer */
char *data;
/* The number of dimensions, also called 'ndim' */
int nd;
/* The size in each dimension, also called 'shape' */
npy_intp *dimensions;
/*
* Number of bytes to jump to get to the
* next element in each dimension
*/
npy_intp *strides;
PyObject *base;
/* Pointer to type structure */
PyArray_Descr *descr;
/* Flags describing array -- see below */
int flags;
/* For weak references */
PyObject *weakreflist;
} PyArrayObject_fields;
在上述实验中, 我们发现 10,000,000 个元素的列表 Numpy 占用的内存是 40,000,096 字节, 这是因为我们存储的元素为 int32 类型, 也就是 4 个字节, 加上Numpy 数组存储的一些指针, 维度, PyObject_HEAD, 为 96 字节.
对比 int32 数组和 int64 数组:
安装命令:
pip install numpy
pip3 install numpy
Anaconda 是一个计算科学库, 可以为我们提供便利的 Python 环境.
安装:
Anaconda 官网
导入 Numpy 包:
# 导包
import numpy as np
print(np.__version__)
ndarray 是 Numpy 最重要的一个特点. ndarray 是一个 N 维数组对象.
np.array
可以帮助我们创建一 ndarray.
格式:
numpy.array(object, dtype=None, *, copy=True, order='K', subok=False, ndmin=0, like=None)
参数:
例子:
# 导包
import numpy as np
# 创建ndarray
array1 = np.array([1, 2, 3]) # 通过lsit创建
array2 = np.array([1, 2, 3], dtype=float)
# 调试输出
print(array1, type(array1))
print(array2, type(array2))
输出结果:
[1 2 3] <class 'numpy.ndarray'>
[1. 2. 3.] <class 'numpy.ndarray'>
创建 Numpy 数组后, 我们可以进一步查询 ndarray 的属性, 如形状, 维度, 数据类型等:
例子:
"""
@Module Name: Numpy 数组属性.py
@Author: CSDN@我是小白呀
@Date: October 13, 2023
Description:
Numpy 数组属性
"""
import numpy as np
# 创建 ndarray
arr = np.array([[1, 2, 3], [4, 5, 6]])
print(arr)
# 输出数组属性
print(arr.shape) # 输出 [2, 3] (两行, 三列)
print(arr.dtype) # 输出 int32 (整型)
print(arr.ndim) # 输出 2 (二维数组)
print(arr.size) # 输出 6 (2*3, 6个元素)
np.zeros
可以帮助我们创建指定形状的全 0 数组.
格式:
numpy.zeros(shape, dtype=float, order='C', *, like=None)
参数:
例子:
import numpy as np
# 创建全0的ndarray
array = np.zeros((3, 3), dtype=int)
print(array)
输出结果:
[[0 0 0]
[0 0 0]
[0 0 0]]
np.zeros
可以帮助我们创建指定形状的全 1 数组.
格式:
numpy.ones(shape, dtype=float, order='C', *, like=None)
参数:
例子:
import numpy as np
# 创建全1的ndarray
array = np.ones((3, 3), dtype=int)
print(array)
print(type(array))
输出结果:
[[1 1 1]
[1 1 1]
[1 1 1]]
<class 'numpy.ndarray'>
Numpy 数组支持 Python 的索引和切片操作, 并提供了更为丰富的功能.
格式 1:
数组[起始索引:结束索引]
格式 2:
数组[起始索引:结束索引:间隔]
import numpy as np
# 创建 ndarray
arr = np.array([1, 2 ,3 ,4 ,5])
# 切片, 取索引 0 对应的元素
print("输出第一个元素:", arr[0])
输出结果:
输出第一个元素: 1
例子:
import numpy as np
# 创建 ndarray
arr = np.array([1, 2 ,3 ,4 ,5])
# 切片数组前三个元素
print("前三个素:", arr[:3])
# 切片数组 2-3
print("2-3 元素:", arr[1:3])
# 切片最后一个元素
print("最后一个元素:", arr[-1])
# 切片奇数索引
print("奇数元素:", arr[::2])
# 切片反转
print("反转数组:", arr[::-1])
输出结果:
前三个素: [1 2 3]
2-3 元素: [2 3]
最后一个元素: 5
奇数元素: [1 3 5]
反转数组: [5 4 3 2 1]
与 Python 的内置列表不同, Numpy 数组支持元素级别的运算. 我们可以对 ndarray 进行加, 减, 乘, 除等操作.
例子:
通过reshape()
我们可以改变数组形状.
格式:
numpy.reshape(arr, newshape, order='C')
参数:
例子:
import numpy as np
# 创建ndarray
array = np.zeros(9)
print(array)
# reshape
array = array.reshape((3,3))
print(array)
print(array.shape) # 调试输出数组形状
输出结果:
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[[0. 0. 0.]
[0. 0. 0.]
[0. 0. 0.]]
(3, 3)
通过flatten()
我们可以将多维数组摊平成1 维数组.
例子:
import numpy as np
# 创建多维数组
array = np.zeros((3, 3))
print(array)
# flatten转变为一维数组
array = array.flatten()
print(array)
输出结果:
[[0. 0. 0.]
[0. 0. 0.]
[0. 0. 0.]]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
常见的聚合函数:
例子:
import numpy as np
# 创建 ndarray
arr = np.array([1, 2, 3, 4, 5])
# 调用常用聚合函数
print(np.sum(arr))
print(np.min(arr))
print(np.max(arr))
print(np.mean(arr))
print(np.median(arr))
输出结果:
15
1
5
3.0
3.0
下面我们来讲一下 Numpy 的高级功能. Numpy 的高级功能可以帮助我们有效的处理数据, 进行科学计算, 以便帮我们更好地处理数据.
广播 (Broadcasting) 是 Numpy 的一个强大功能, 可以帮助我们进行不同形状数组的的运算. Numpy 中广播的规则是从尾部的维度开始对比.
例子:
import numpy as np
# 广播
a = np.array([1, 2, 3])
b = np.array([[10], [20], [30]])
print(a + b)
输出结果:
[[11 12 13]
[21 22 23]
[31 32 33]]
例子:
import numpy as np
# 定义矩阵
mat1 = np.array([[1, 2], [3, 4]])
mat2 = np.array([[2, 0], [1, 3]])
# 矩阵乘法
# 1*2 + 2*1 = 2
# 1*1 + 2*3 = 6
# 3*2 + 4*1 = 10
# 3*0 + 4*3 = 12
result = np.dot(mat1, mat2)
print(result)
输出结果:
[[ 4 6]
[10 12]]
当我们已经掌握了 Numpy 的基础用法和高级功能后, 小白我来带大家了解一下 Numpy 的实际应用.
求数组平均数和标准差:
import numpy as np
# 定义数组
data = np.array([23, 45, 56, 78, 12, 9])
# 计算平均值和标准差
print("平均值:", np.mean(data))
print("标准差:", np.std(data))
输出结果:
3.14
利用 Numpy, 我们可以将图像转化为数组进行处理.
例子:
import numpy as np
from PIL import Image
# 将图像转化为数据
image = Image.open('path_to_image.jpg')
image_array = np.array(image)
print(image_array.shape)
输出结果:
(1707, 2560, 3)
例子:
import numpy as np
from numpy.linalg import solve
# 创建 ndarray
a = np.array([[3, 1], [1, 2]]) # 3x + y = 9
b = np.array([9, 8]) # x + 2y = 8
# 解方程
x = solve(a, b) # x = 2, y = 3
print(x)
输出结果:
[2. 3.]
在本篇文章中, 我们深入地探讨了 Numpy, 这是 Python 中用于数值计算和数据分析的核心库. 从数组的基本操作, 数组的形状和维度, 高级数组操作, 到 Numpy 的最佳实践和常见误区, 我们尝试为读者提供了一个全面且深入的视角.
Numpy 的真正威力在于其高效性和灵活性. 它为我们提供了大量的功能, 能帮助我们轻松处理大规模的数值数据. 但与此同时, 也需要注意其特定的工作原理, 避免常见的陷阱.
对于初学者来说, 可能需要一些时间来适应 Numpy 的思维方式, 特别是它的广播机制和向量化操作. 但一旦你习惯了这种方式, 你会发现自己的数据处理能力大大增强.
无论你是数据分析师, 科学家还是工程师, 掌握 Numpy 都将是你数据处理技能的重要组成部分. 希望这篇文章能为你在 Python 数据处理之路上提供一些有用的指导.
数组创建与基础操作:
数组索引与切片:
数组操作与数学运算:
import numpy as np
array = np.ones([5,5], dtype=int)
print(array)
array = np.random.randint(1, 101, size=20)
print(array)
array = array.reshape((5, 4))
print(array)
输出结果:
[[1 1 1 1 1]
[1 1 1 1 1]
[1 1 1 1 1]
[1 1 1 1 1]
[1 1 1 1 1]]
[22 13 20 67 5 91 26 64 84 85 59 66 44 83 41 63 44 23 76 35]
[[22 13 20 67]
[ 5 91 26 64]
[84 85 59 66]
[44 83 41 63]
[44 23 76 35]]
import numpy as np
array = np.random.randint(1, 101, size=(10, 10)).reshape((10,10))
print(array)
array = array[2:8, 3:9]
print(array)
array = array[array % 2 == 0]
print(array)
输出结果:
[[1 1 1 1 1]
[1 1 1 1 1]
[1 1 1 1 1]
[1 1 1 1 1]
[1 1 1 1 1]]
[ 32 6 91 48 63 81 87 28 19 25 20 93 97 100 70 77 3 46
100 7]
[[ 32 6 91 48]
[ 63 81 87 28]
[ 19 25 20 93]
[ 97 100 70 77]
[ 3 46 100 7]]
[[1 1 1 1 1]
[1 1 1 1 1]
[1 1 1 1 1]
[1 1 1 1 1]
[1 1 1 1 1]]
[71 63 6 50 59 69 14 18 80 88 68 54 35 97 51 82 86 50 61 9]
[[71 63 6 50]
[59 69 14 18]
[80 88 68 54]
[35 97 51 82]
[86 50 61 9]]
import numpy as np
a = np.random.randint(1, 11, size=(3, 3))
b = np.random.randint(1, 11, size=(3, 3))
print(a)
print(b)
result = np.dot(a, b)
print(result)
det_a = np.linalg.det(a)
if det_a == 0:
print("矩阵 A 不可逆")
else:
inverse_a = np.linalg.inv(a)
print("A 的逆矩阵为: \n", inverse_a)
输出结果:
[[ 8 6 4]
[10 5 5]
[ 7 7 9]]
[[ 7 2 9]
[10 9 6]
[ 5 7 1]]
[[136 98 112]
[145 100 125]
[164 140 114]]
A 的逆矩阵为:
[[-9.09090909e-02 2.36363636e-01 -9.09090909e-02]
[ 5.00000000e-01 -4.00000000e-01 -7.93016446e-18]
[-3.18181818e-01 1.27272727e-01 1.81818182e-01]]