前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >飞速搞定数据分析与处理-day3-一篇入门NumPy

飞速搞定数据分析与处理-day3-一篇入门NumPy

作者头像
用户10002156
发布2023-08-07 13:11:34
2340
发布2023-08-07 13:11:34
举报
文章被收录于专栏:生活处处有BUG

NumPy入门

NumPy数组

如果要对嵌套列表进行数组运算,可以使用循环来完成。例如,要为嵌套列表中的每一个元素都加上 1,可以使用下面的嵌套列表推导式

代码语言:javascript
复制
In [1]: matrix = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
In [2]: [[i + 1 for i in row] for row in matrix]
Out[2]: [[2, 3, 4], [5, 6, 7], [8, 9, 10]]

但是这样的代码可读性很低。更关键的是,在面对更大的数组时,遍历整个数组会非常慢。

如果你的用例和数组大小合适的话,那么使用 NumPy 数组进行运算会比 Python 列表快上几百倍。为了达到如此高的性能,NumPy 利用了用 C 和 Fortran(它们都是编译型语言,比 Python 要快得多)编写的代码。NumPy 数组是保存同构数据(homogenous data)的 N 维数组。“同构”意味着数组中的所有数据都必须是相同类型。

代码语言:javascript
复制
In [3]: # 首先导入NumPy
import numpy as np
In [4]: # 使用列表构造一个一维数组
array1 = np.array([10, 100, 1000.])
In [5]: # 使用嵌套列表构造一个二维数组
array2 = np.array([[1., 2., 3.],
[4., 5., 6.]])

即使 array1 除了最后一个元素(浮点数)之外全是整数,但由于 NumPy 对同构的要求,这个数组的数据类型依然是 float64,这个类型足以容纳所有的元素。要想了解一个数组的数据类型,可以访问它的 dtype 属性

代码语言:javascript
复制
In [6]: array1.dtype
Out[6]: dtype('float64')

dtype 返回的是 float64 而不是第 3 章中介绍过的 float。你可能已经猜到了,NumPy 使用的是它自己的数值数据类型,它们比 Python 的数据类型粒度要细。通常这都不是问题,因为大部分时候 Python 和 NumPy 中的不同数据类型可以自动转换。

向量化和广播

如果你对一个标量和 NumPy 数组求和,那么 NumPy 会执行按元素的操作。也就是说,你不用亲自遍历每一个元素。NumPy 社区称之为向量化(vectorization)。向量化可以让代码更简洁,更接近于数学记法。

代码语言:javascript
复制
In [8]: array2 + 1
Out[8]: array([[2., 3., 4.],
[5., 6., 7.]])

在处理两个数组时也是同样的道理,NumPy 会执行按元素的运算:

代码语言:javascript
复制
In [9]: array2 * array2
Out[9]: array([[ 1., 4., 9.],
[16., 25., 36.]])

如果你在算术运算中使用了两个形状不同的数组,那么 NumPy 在可能的情况下会自动将较小的数组扩展成较大的数组的形状。这就是广播(broadcasting)

代码语言:javascript
复制
In [10]: array2 * array1
Out[10]: array([[ 10., 200., 3000.],
[ 40., 500., 6000.]])

要求矩阵的点积,需要使用 @ 运算符

代码语言:javascript
复制
In [11]: array2 @ array2.T # array2.T是array2.transpose()的缩写形式
Out[11]: array([[14., 32.],
[32., 77.]])

通用函数

通用函数(universal function,简称 ufunc)会对 NumPy 数组中的每个元素执行操作。如果在 NumPy 数组中使用 Python 标准库 math 模块中的开平方函数,那么你会得到一个错误

代码语言:javascript
复制
n [12]: import math
In [13]: math.sqrt(array2) # 这里会发生错误
---------------------------------------------------------------------------
TypeError
Traceback (most recent call last)
<ipython-input-13-5c37e8f41094> in <module>
----> 1 math.sqrt(array2) # 这里会发生错误
TypeError: only size-1 arrays can be converted to Python scalars

当然,你可以写一个嵌套循环来计算每个元素的平方根,然后再把结果构造成一个 NumPy数组

代码语言:javascript
复制
In [14]: np.array([[math.sqrt(i) for i in row] for row in array2])
Out[14]: array([[1.
, 1.41421356, 1.73205081],
[2.
, 2.23606798, 2.44948974]])

如果 NumPy 没有提供对应的 ufunc,并且数组足够小的话,这样写也可以。然而要是NumPy 有这样一个 ufunc,你就该直接用它。除了更容易输入和阅读,在处理大型数组时ufunc 会快得多

代码语言:javascript
复制
In [15]: np.sqrt(array2)
Out[15]: array([[1.
[2.
, 1.41421356, 1.73205081],
, 2.23606798, 2.44948974]])

NumPy 的一些 ufunc 也可以用作数组的方法。以 sum 为例,如果你想求出每一列的总和,那么可以像下面这样做

代码语言:javascript
复制
In [16]: array2.sum(axis=0) # 返回一维数组
Out[16]: array([5., 7., 9.])

参数 axis=0 表示以行为轴,参数 axis=1 表示以列为轴,就像图 4-1 中那样。省略 axis 参数会将整个数组加起来

代码语言:javascript
复制
In [17]: array2.sum()
Out[17]: 21.0

创建和操作数组

存取元素

image-20230704183428516

代码语言:javascript
复制
In [18]: array1[2] # 返回标量
Out[18]: 1000.0
In [19]: array2[0, 0] # 返回标量
Out[19]: 1.0
In [20]: array2[:, 1:] # 返回二维数组
Out[20]: array([[2., 3.],
[5., 6.]])
In [21]: array2[:, 1] # 返回一维数组
Out[21]: array([2., 5.])
In [22]: array2[1, :2] # 返回一维数组
Out[22]: array([4., 5.])

记住,对二维数组的行或列进行切片,得到的是一个一维数组,而不是二维列向量或行向量。

方便的数组构造器

通过 arange 和 reshape,可以快速生成指定维度的数组.

代码语言:javascript
复制
In [23]: np.arange(2 * 5).reshape(2, 5)
Out[23]: array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9]])

以蒙特卡罗模拟为例,一个常见需求是生成服从正态分布的伪随机数数组。NumPy 可以轻松做到

代码语言:javascript
复制
In [24]: np.random.randn(2, 3) # 2行,3列
Out[24]: array([[-0.30047275, -1.19614685, -0.13652283],
[ 1.05769357, 0.03347978, -1.2153504 ]]

视图和副本

在对 NumPy 数组切片时,其返回值是视图(view)。这就意味着你是在操作原数组的一个子集,而没有发生数据的复制。因而设置视图的值也会改变原数组中的值

代码语言:javascript
复制
In [25]: array2
Out[25]: array([[1., 2., 3.],
[4., 5., 6.]])
In [26]: subset = array2[:, :2]
subset
Out[26]: array([[1., 2.],
[4., 5.]])
In [27]: subset[0, 0] = 1000
In [28]: subset
Out[28]: array([[1000.,2.],
[ 4.,5.]])
In [29]: array2
Out[29]: array([[1000.,2.,3.
[4.,5.,6.]])

如果不想要这样的结果,那么可以把 In [26] 的代码改成下面这样,对副本进行操作不会影响原数组。

代码语言:javascript
复制
subset = array2[:, :2].copy()
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-07-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 生活处处有BUG 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • NumPy入门
    • NumPy数组
      • 向量化和广播
        • 通用函数
        • 创建和操作数组
          • 存取元素
            • 方便的数组构造器
              • 视图和副本
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档