2 NumPy基础
文章目录
NumPy(Numerical Python的简称)是Python数值计算最重要的基础包。大多数提供科学计算的包都是用NumPy的数组作为构建基础。
NumPy的部分功能如下:
- ndarray,一个具有矢量算术运算和复杂广播能力的快速且节省空间的多维数组。
- 用于对整组数据进行快速运算的标准数学函数(无需编写循环)。
- 用于读写磁盘数据的工具以及用于操作内存映射文件的工具。
- 线性代数、随机数生成以及傅里叶变换功能。
- 用于集成由C、C++、Fortran等语言编写的代码的API。
NumPy对于数值计算特别重要的原因之一,是因为它可以高效处理大数组的数据。这是因为:
- NumPy是在一个连续的内存块中存储数据,独立于其他Python内置对象。NumPy的C语言编写的算法库可以操作内存,而不必进行类型检查或其它前期工作。比起Python的内置序列,NumPy数组使用的内存更少。
- NumPy可以在整个数组上执行复杂的计算,而不需要Python的for循环。
# 考察一个包含一百万整数的数组,和一个等价的Python列表:
import numpy as np
my_arr = np.arange(1000000) #np.arange(5)=array([0, 1, 2, 3, 4])
my_list = list(range(1000000))
# 各个序列分别乘以2:
%time for _ in range(10): my_arr2 = my_arr * 2
%time for _ in range(10): my_list2 = [x * 2 for x in my_list]
------------------------------------------------------------------------
CPU times: user 12.4 ms, sys: 5.28 ms, total: 17.7 ms
Wall time: 17.8 ms
CPU times: user 683 ms, sys: 155 ms, total: 839 ms
Wall time: 838 ms
1. NumPy的ndarray:一种多维数组对象
NumPy最重要的一个特点就是其N维数组对象(即ndarray),该对象是一个快速而灵活的大数据集容器。你可以利用这种数组对整块数据执行一些数学运算,其语法跟标量元素之间的运算一样。
import numpy as np
data = np.random.randn(2,3)
data
--------------------------------------------------------------------
array([[-1.17091437, 0.42090076, 0.4228218 ],
[ 1.3633156 , 0.44249963, -0.04324118]])
data * 10
data + data
--------------------------------------------------------------------
array([[-11.70914371, 4.20900759, 4.22821805],
[ 13.63315597, 4.42499626, -0.43241184]])
array([[-2.34182874, 0.84180152, 0.84564361],
[ 2.72663119, 0.88499925, -0.08648237]])
对于Python原生的列表,这些乘法和加法的规则有点不一样
#对于Python原生的列表,这些乘法和加法的规则有点不一样
pythonList = [1,2,3]
pythonList * 3
pythonList + pythonList
--------------------------------------------------------------------
[1, 2, 3, 1, 2, 3, 1, 2, 3]
[1, 2, 3, 1, 2, 3]
ndarray是一个通用的同构数据多维容器,也就是说,其中的所有元素必须是相同类型的。每个数组都有一个shape(一个表示各维度大小的元组)和一个dtype(一个用于说明数组数据类型的对象):
.shape .dtype
print(data.shape)
print(data.dtype)
------------------------------------------------------------------------
(2, 3)
float64
(1)创建ndarray
创建数组最简单的办法就是使用array函数。它接受一切序列型的对象(包括其他数组),然后产生一个新的含有传入数据的NumPy数组。以一个列表的转换为例:
data1 = [1,2,3,5.0,6]
arr1 = np.array(data1)
arr1
------------------------------------------------------------------------
array([1., 2., 3., 5., 6.])
嵌套序列(如由一组等长列表组成的列表)将会被转换为一个多维数组:
data2 = [[1, 2, 3, 4], [5, 6.1, 7, 8]]
arr2 = np.array(data2)
arr2
------------------------------------------------------------------------
array([[1. , 2. , 3. , 4. ],
[5. , 6.1, 7. , 8. ]])
print(arr1.ndim) #ndim返回的是数组的维度
print(arr1.shape)
print(arr2.ndim)
print(arr2.shape)
print(arr1.dtype)
print(arr2.dtype)
------------------------------------------------------------------------
1
(5,)
2
(2, 4)
float64
float64
除np.array之外,还有一些函数也可以新建数组。比如,zeros和ones分别可以创建指定长度或形状的全0或全1数组。empty可以创建一个没有任何具体值的数组。要用这些方法创建多维数组,只需传入一个表示形状的元组即可:
一些数组创建函数
下表列出了一些数组创建函数。由于NumPy关注的是数值计算,因此,如果没有特别指定,数据类型基本都是float64(浮点数)。
后面的函数参数基本规则为:函数名(shape, dtype, order=‘C’)
- shape:int 或者 int元组
- dtype : 数组中元素的格式
- order : ‘C’ 或 ‘F’, 分别代表,行优先row-major (C-style)和列优先column-major (Fortran-style),表示在计算机内存中的存储元素的顺序。
np.zeros(10)
np.zeros((3,6))
np.empty((2,3,2))
------------------------------------------------------------------------
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
array([[0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0.]])
array([[[0., 0.],
[0., 0.],
[0., 0.]],
[[0., 0.],
[0., 0.],
[0., 0.]]])
full
full(shape, fill_value, dtype=None, order=‘C’)
- shape:int 或者 int元组
- dtype : 数组中元素的格式
- fill_value:填充到数组中的值
- order : ‘C’ 或 ‘F’, 分别代表,行优先row-major (C-style)和列优先column-major (Fortran-style),表示在计算机内存中的存储元素的顺序。
# 构造一个2x3的数组,其中元素全部都为 7
ndarray = np.full((2, 3), 7)
print(ndarray)
------------------------------------------------------------------------
[[7 7 7]
[7 7 7]]
arange是Python内置函数range的数组版:
np.arange(10)
------------------------------------------------------------------------
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
dataArange = np.arange(15)
print(type(dataArange))
print(dataArange)
------------------------------------------------------------------------
<class 'numpy.ndarray'>
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14]
(2) ndarray的数据类型(dtype、astype)
参考 Numpy中ndim、shape、dtype、astype的用法
dtype(数据类型)是一个特殊的对象,它含有ndarray将一块内存解释为特定数据类型所需的信息:
arr1 = np.array([1,2,3], dtype=np.float64)
arr2 = np.array([1,2,3], dtype=np.int32)
print(arr1.dtype)
print(arr1)
print(arr2.dtype)
print(arr2)
------------------------------------------------------------------------
float64
[1. 2. 3.]
int32
[1 2 3]
- 数值型dtype的命名方式相同:一个类型名(如float或int),后面跟一个用于表示各元素位长的数字。
标准的双精度浮点值(即Python中的float对象)需要占用8字节(即64位)。因此,该类型在NumPy中就记作float64。表4-2列出了NumPy所支持的全部数据类型。
- 可以通过ndarray的astype方法明确地将一个数组从一个dtype转换成另一个dtype:
arr = np.array([1,2,3,4,5])
print(arr.dtype)
print(arr)
float_arr = arr.astype(np.float64)
print(float_arr.dtype)
print(float_arr)
------------------------------------------------------------------------
int32
[1 2 3 4 5]
float64
[1. 2. 3. 4. 5.]
- 如果将浮点数转换成整数,则小数部分将会被截取删除:
arr = np.array([3.7, -1.2, -2.6, 0.5, 12.9, 10.1])
print(arr)
print(arr.astype(np.int32))
------------------------------------------------------------------------
[ 3.7 -1.2 -2.6 0.5 12.9 10.1]
[ 3 -1 -2 0 12 10]
- 如果某字符串数组表示的全是数字,也可以用astype将其转换为数值形式:
numeric_strings = np.array(['1.25', '-9.6', '42'], dtype=np.string_)
print(numeric_strings)
print(numeric_strings.astype(np.float64))
print(numeric_strings.astype(np.float64).astype(np.int32))
------------------------------------------------------------------------
[b'1.25' b'-9.6' b'42']
[ 1.25 -9.6 42. ]
[ 1 -9 42]
(3) NumPy数组的运算(元素级)
数组很重要,因为它使你不用编写循环即可对数据执行批量运算。NumPy用户称其为矢量化(vectorization)。
- 大小相等的数组之间的任何算术运算都会将运算应用到元素级:
arr = np.array([[1.,2,3],[4,5,6]])
arr
arr * arr
arr - arr
------------------------------------------------------------------------
array([[1., 2., 3.],
[4., 5., 6.]])
array([[ 1., 4., 9.],
[16., 25., 36.]])
array([[0., 0., 0.],
[0., 0., 0.]])
- 数组与标量的算术运算会将标量值传播到各个元素
1 / arr
arr * 0.5 #数乘0.5
arr ** 0.5 #0.5幂次方
------------------------------------------------------------------------
array([[1. , 0.5 , 0.33333333],
[0.25 , 0.2 , 0.16666667]])
array([[0.5, 1. , 1.5],
[2. , 2.5, 3. ]])
array([[1. , 1.41421356, 1.73205081],
[2. , 2.23606798, 2.44948974]])
- 大小相同的数组之间的比较会生成布尔值数组:
arr = np.array([[1.,2,3],[4,5,6]])
arr2 = np.array([[0., 4,1],[7,2,12]])
arr > arr2
------------------------------------------------------------------------
array([[ True, False, True],
[False, True, False]])
(4)基本的索引和切片
NumPy数组的索引是一个内容丰富的主题,因为选取数据子集或单个元素的方式有很多。一维数组很简单。从表面上看,它们跟Python列表的功能差不多:
arr = np.arange(10) * 2
arr
arr[5]
arr_tmp = arr[5:8]
arr_tmp
arr_tmp[0] = 100
arr_tmp
arr
arr[5:8] = 12
arr
------------------------------------------------------------------------
array([ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18])
10
array([10, 12, 14])
array([100, 12, 14])
array([ 0, 2, 4, 6, 8, 100, 12, 14, 16, 18])
array([ 0, 2, 4, 6, 8, 12, 12, 12, 16, 18])
- 跟列表最重要的区别在于,数组切片是原始数组的视图。这意味着数据不会被复制,视图上的任何修改都会直接反映到源数组上。
# 先创建一个arr的切片:
arr = np.arange(10) * 2
arr_slice = arr[5:8]
arr_slice
# 现在,当修改arr_slice中的值,变动也会体现在原始数组arr中:
arr_slice[1] = 12345
arr
# 切片[ : ]会给数组中的所有值赋值:
arr_slice[:] = 64
arr
------------------------------------------------------------------------
array([10, 12, 14])
array([ 0, 2, 4, 6, 8, 10, 12345, 14, 16,
18])
array([ 0, 2, 4, 6, 8, 64, 64, 64, 16, 18])
# 切片[ : ]会给数组中的所有值赋值:
arr_slice[:] = 64
lst_slice = lst[5:8]
lst_slice[1] = 12345
lst_slice
lst
------------------------------------------------------------------------
[5, 12345, 7]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
如果你想要得到的是ndarray切片的一份副本而非视图,就需要明确地进行复制操作,例如:
arr = np.arange(10) * 2
arr_slice = arr[5:8].copy()
arr_slice[:] = 64
arr
------------------------------------------------------------------------
array([ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18])
对于高维度数组,能做的事情更多。在一个二维数组中,各索引位置上的元素不再是标量而是一维数组
arr2d = np.array([[1,2,3],[4,5,6],[7,8,9]])
arr2d[2]
--------------------------------