14 KiB
numpy:Python 数据科学的基础
Python 的构想只有一个目标,简单。这种简单性使得 Python 成为当今最流行的语言之一。然而,Python 开发人员不得不牺牲性能来让他们的生活更轻松。不过,这种性能损失会对数值和科学计算产生相当大的影响!幸运的是,NumPy 在那里拯救了世界!
本文是对 NumPy 的介绍。读完之后,您将知道如何安装和导入 NumPy,以及如何用一维 NumPy 数组处理数值数据。在 Python 的土地上,很快就会有一个关于 NumPy 的完整课程,涵盖更多的主题和多维数组。如果你想得到它发布的通知,请订阅低容量的 Python Land 简讯。
目录
为什么是 NumPy?
由于 Python 的易用性和可扩展性,科学家们从早期就对它感兴趣。NumPy 源于科学 Python 社区为解决 Python 中数值计算的弱点所做的努力。需要解决的主要问题是:
- 高效的数组创建和操作
- 对这些数组进行运算的数学函数和算法
NumPy 是在 2005 年通过合并当时可用的两个数值包创建的:Numeric 和 Numarray。由于 Python 没有针对速度进行优化,NumPy 的大部分繁重代码都是用 C 编写的,一些 Fortran 代码在边缘处乱涂乱画。这就是为什么 NumPy 速度如此之快!
自创建以来,NumPy 已经成为 Python 可用的最著名的第三方模块之一。关于是否将其作为 Python 的标准模块之一,甚至有过激烈的争论。只有最好的项目才能获得这样的荣誉。
快进到现在,我们已经看到了利用 Python 的数据科学和机器学习工作的爆炸。NumPy 是一个基础包,它提供了许多其他项目所需的坚实基础。许多数据科学和机器学习包在幕后使用 NumPy,特别是:
- 熊猫
- scikit-learn
- Tensorflow
所以掌握 NumPy 至关重要。
安装 NumPy
NumPy 不是默认 Python 发行版的一部分,所以你需要用 pip install (或者poems/Pipenv)来安装它:
pip install numpy
如果您是 Conda 用户,您可以使用:
conda install numpy
导入数字
像所有包一样,您可以导入 NumPy 的一部分,也可以导入整个包。有一个惯例是导入整个包,然后将其重命名为 np。强烈建议也使用这个约定。原因很简单,因为大多数 NumPy 用户以交互方式使用软件包,所以他们只需输入更少的内容:
import numpy as np
在本文中,我们将遵守约定,并假设您已经将 NumPy 作为np导入。
NumPy 数组
NumPy 的核心是数组。让我们正式定义什么是数组:
array
A data structure with elements of the same type whose position can be accessed by one or more indices is an array. Hence, a programming language implementation of a vector, a matrix, or a tensor is an array.
鉴于数组的广泛定义,Python 有两种类似于数组的内置类型:list 和 tuple。我们可以使用列表的列表或列表的字典来存储多个列表,有效地创建多维数组。然而,无论是列表、元组还是字典都没有针对数值目的进行优化。它们甚至不太符合我们的定义,因为它们可以存储不同类型的值。例如,列表可以包含数字、字符串和任何其他对象类型的混合集合。
NumPy 解决了 Python 在通过数组进行数值计算方面的许多缺点。尤其是 NumPy 中的数组创建和操作非常快,并且得到了很好的优化。
创建一维数组
创建数组最简单的方法是将一个列表传递给 NumPy 的主实用程序来创建数组,np.array:
a = np.array([1, 2, 3])
该方法接受几个可选的关键字参数,我们将讨论其中的三个:copy。
复制参数
copy参数说明是否复制输入对象。当 copy 为True时,结果数组中的任何变化都不会改变输入对象。但是,如果是False,数组的变化可以改变输入对象。
当使用列表制作数组时,NumPy 将总是复制对象,而不管参数的值;例如:
lst = [1, 2, 3]
a = np.array(lst, copy=False)
print(a)
# array([1, 2, 3])
如果我们改变数组,列表将保持不变,因为 NumPy 复制了它:
a[0] = 0
print(lst)
# [1, 2, 3]
如果我们创建相同的列表,但是使用另一个 NumPy 数组作为输入:
a_in = np.array([1, 2, 3])
a = np.array(a_in, copy=False)
a
让我们看看如果我们改变结果数组会发生什么:
a[0] = 0
print(a)
# array([0,2,3])
print(a_in)
# array([0,2,3])
两个数组都发生了变化,因为我们将copy选项设置为False。
Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!
NumPy 数据类型(dttypes)
函数np.array的另一个关键字参数是dtype。此参数指定数组中的数据类型。请记住,数组的一个关键属性是所有元素都具有相同的类型。
NumPy 实现了自己的数据类型,这些数据类型针对高效存储和处理进行了优化。为此,它使用了名为dtype的基类。我们来看看最常见的dtypes:
- np.int16
- np.int32
- np.int64
- np.float32
- np.float64
- 浮动 128
- np.bool_
- np.str_
- np 字节 _
- np .对象 _
在本文中,我们将只关注数值类型。
整数
整数数据类型、np.int16、np.int32和np.int64的区别仅在于它们能存储的数的大小:
np.int16->32762np.int32->2147483647np.int64->9223372036854775807
在正常情况下,使用np.int64是可行的,因为它允许我们存储最大的数字。Int64 是 NumPy 默认使用的dtype。然而,使用较小的整数也有好处:
- 减少内存使用
- 更快的计算
对于相对较小的阵列,内存使用通常不是问题。如果您认为会,请尝试较小的类型,但要确保所有元素以及对这些元素的未来操作的结果不会超过所选类型的最大大小。
漂浮物
浮点类型也指内存中数字的大小。尺寸越大,数组元素的精度就越高。然而,这种精度是以牺牲内存和性能为代价的。经验法则是默认使用np.float64。如果您可以节省一些精度,并且性能和内存使用是最重要的,请使用更小的。
让我们探索一下浮动大小如何影响 REPL 的精度:
>>> np.array([1.3738729019013636723763], dtype=np.float16)[0]
1.374
>>> np.array([1.3738729019013636723763], dtype=np.float32)[0]
1.3738729
>>> np.array([1.3738729019013636723763], dtype=np.float64)[0]
1.3738729019013636
>>> np.array([1.3738729019013636723763], dtype=np.float128)[0]
1.3738729019013635746
在一些系统上,甚至有一个float128类型,如上一个例子所示。如果你在 Windows 上,这可能会出错,但是 Linux 和 MacOS 应该支持它。
使用 NumPy 数组
我们现在将仔细研究如何使用 NumPy 数组,从使用数组索引访问元素开始。
获取单个元素
我们可以访问和修改单个元素:
a = np.array([0.0, 2.0, 3.0, 4.0, 5.0])
print(a[0])
# 0.0
a[0] = 1.0
print(a)
# [1., 2., 3., 4., 5.]
访问多个元素
我们可以一次访问和修改 NumPy 数组中的多个特定元素。注意 Python 列表没有这个特性:
a = np.array([0.0, 2.0, 3.0, 4.0, 5.0])
# Get elements at position 0 and 2
print(a[[0, 2]])
# [0., 3.]
# Change the first two elements
a[[0, 1]] = [0, 3.0]
print(a)
# [0., 3., 3., 4., 5.]
负索引
负索引的工作原理与列表相同;他们倒计数指数。例如,要获取数组末尾的元素,可以使用:
a = np.array([0.0, 2.0, 3.0, 4.0, 5.0])
print(a[-1])
# 5.0
print(a[-2])
4.0
限幅
切片也可以工作,它的行为与列表的常规切片完全一样,例如,格式是a[start: stop: step]。作为一个例子,让我们得到一个数组的前三个元素:
a = np.array([0.0, 2.0, 3.0, 4.0, 5.0])
print(a[0: 3])
或者除最后一个元素之外的所有元素:
print(a[0: -1])
# [0., 2., 3., 4.]
和列表一样,我们也可以这样反转数组:
print(a[:: -1])
# [5., 4., 3., 2., 0.]
追加、插入、删除和排序
NumPy 数组与列表有更多的共同点。许多常规操作的行为类似于 Python 列表,比如排序、删除、插入和追加数据。注意,这些方法都返回一个新的数组,而不是修改给定的数组。
追加到 NumPy 数组
追加意味着在末尾添加元素。我们可以像处理列表一样将单个元素追加到 NumPy 数组中:
a = np.array([1.0, 2.0])
a = np.append(a, 3.0)
print(a)
# [1., 2., 3.]
我们习惯于使用extend方法将多个元素添加到一个列表中。但是,NumPy 数组重用相同的 append 函数来添加多个元素:
a = np.array([1.0, 2.0])
np.append(a, [4.0, 5.0])
print(a)
[1., 2., 4., 5.]
插入 NumPy 数组
我们可以使用 insert 在特定的索引位置插入一个或多个元素:
a = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
# Insert one element at position 3
a = np.insert(a, 3, values=3.5)
# a is now [1\. , 2\. , 3\. , 3.5, 4\. , 5\. ]
# Insert a list of elements at position 3
a = np.insert(a, 3, values=[100, 200])
# a is now [1\. , 2\. , 3\. , 3.5, 100, 200, 4\. , 5\. ]
# Insert multiple elements at multiple positions
a = np.insert(a, [3, 5], values=[4.5, 5.5])
# a is nop [1\. , 2\. , 3\. , 4.5, 4\. , 5\. , 5.5]
从 NumPy 数组中删除元素
我们也可以一次删除一个或多个元素:
a = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
np.delete(a, -1)
# a is now [1., 2., 3., 4.]
np.delete(a, [0, 1])
# a is now [3., 4.]
排序 NumPy 数组
有两种方法可以对 NumPy 数组进行排序:就地排序和创建一个新的排序后的数组。从最后一个开始:
a = np.array([1.0, 3.0, 2.0, 4.0, 5.0])
b = np.sort(a)
# b is now [1., 2., 3., 4., 5.]
要进行就地排序,请执行以下操作:
a = np.array([1.0, 3.0, 2.0, 4.0, 5.0])
a.sort()
# a is now [1., 2., 3., 4., 5.]
重申一下:请注意,除了 sort 方法之外,大多数方法都不属于 array 类。因此,我们必须调用接受数组作为参数的np对象上的方法。因此,这些转换不会就地发生,而是返回一个新数组。
在 NumPy 课程中(即将推出!),我们将学习更多的函数和数组方法,使我们能够用数组做更多的事情。
数学阵列运算
我们将用最常见的数学运算来结束这篇文章,这些运算是人们可能想对数组执行的:和、减、乘、除。
数组像标量一样处理;操作是按元素执行的。因此,数组只能被另一个相同大小的数组或标量加、减、乘或除。
让我们先定义一些数组,注意a和b有相同的大小 4,b_wrong_size有不同大小的 3 个元素:
a = np.array([1.0, 2.0, 3.0, 4.0])
b = np.array([2.0, 2.0, 2.0, 2.0])
b_wrong_size = np.array([2.0, 2.0, 2.0])
如果我们试图操作不同大小的数组,将会出现一个ValueError异常:
a = np.array([1.0, 2.0, 3.0, 4.0])
b_wrong_size = np.array([2.0, 2.0, 2.0])
# raises ValueError exception
a + b_wrong_size
ValueError: operands could not be broadcast together with shapes (4,) (3,)
加法和减法
我们可以将数组相加,也可以为数组的每个元素添加一个值:
a = np.array([1.0, 2.0, 3.0, 4.0])
b = np.array([2.0, 2.0, 2.0, 2.0])
print(a + b)
[3., 4., 5., 6.]
print(a + 2)
[3., 4., 5., 6.]
print(a - b)
[-1., 0., 1., 2.]
print(a - 2)
[-1., 0., 1., 2.]
乘法和除法
乘法和除法也是如此:我们可以使用单个值或两个数组:
a = np.array([1.0, 2.0, 3.0, 4.0])
b = np.array([2.0, 2.0, 2.0, 2.0])
print(a * b)
[2., 4., 6., 8.]
print(a * 2)
[2., 4., 6., 8.]
print(a / b)
[0.5, 1\. , 1.5, 2\. ]
print(a / 2)
[0.5, 1\. , 1.5, 2\. ]
结论
我们已经了解了一维数组的创建、访问元素、数组操作以及数组上最重要的数学运算。关于 NumPy 还有很多要学的。Python Land 将很快在 NumPy 上发布一个完整的课程,涵盖你想知道的一切。在此之前,我推荐以下资源来了解更多信息:
- 官方 NumPy 手册有一个部分是给绝对初学者的
- 如果你懂 MATLAB,你会喜欢 NumPy for MATLAB 用户