NumPy基础(一)


简介

NumPy(Numerical Python)是Python的一种开源的数值计算扩展。这种工具可用来存储和处理大型矩阵,比Python自身的嵌套列表(nested list structure)结构要高效的多(该结构也可以用来表示矩阵(matrix)),支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库。 -- 百度百科

NumPy 目前已经成为Python中用于科学计算的基础包。

NumPy提供了高性能的向量、矩阵以及多维数据结构及计算方法。

NumPy包的核心是ndarray对象。它封装了与Python原生的数据类型相同的n维数组,其中有许多操作以C语言及Fortran语言编写并编译来实现,从而提供了极佳的性能。

由于NumPy封装了大量的复杂计算方法,因此使代码变得极为简洁、高效。例如,对于两个数组a和b的加法操作,如果用C语言实现,则需要通过两层循环求和,代码如下:

而用NumPy实现,只需要写作:

c = a+b

代码非常简洁、易读,且性能高效。当然,计算机进行c = a + b的数组求和操作,本质上还是由上述C语言的循环方式完成的,只是NumPy已经在内部实现了这些复杂的操作,我们只需引用而已。

使用NumPy前,首先需要安装和导入NumPy。

安装 numpy

除了 NumPy 之外,我们还需要安装matplotlib用于简单的数据可视化。

pip install numpy
pip install matplotlib
import numpy 
# 查看numpy版本号
numpy.version.version
'1.18.5'

快速入门

学习目标

  • 理解NumPy中一维、二维和n维数组的区别;

  • 了解如何在不使用 for 循环的情况下将一些线性代数运算应用于 n 维数组;

  • 了解 n 维数组的轴和形状属性。

基础知识

NumPy的主要数据对象是同构多维数组。

它是一个元素表(通常是数字),所有类型都相同,由非负整数元组索引。在 NumPy 中,维度称为轴。

例如,3D 空间中一个点的坐标[1, 2, 1]只有一个轴。该轴有 3 个元素,因此我们说它的长度为 3。

在下图中的示例中,数组有 2 个轴。第一个轴的长度为 2,第二个轴的长度为 3。

# 维度为(2,3)的数组 
[[1., 0., 0.],
 [0., 1., 2.]]
[[1.0, 0.0, 0.0], [0.0, 1.0, 2.0]]

NumPy中的数组称为ndarray,它的别名是array。

ndarray的属性介绍: * ndarray.ndim 数组的轴数(维度) * ndarray.shape 数组的维度。结果是一个元组。(n,m)表示数组有n行m列。数组中元素总数为n*m. * ndarray.dtype 数组中元素的类型. * ndarray.itemsize 数组每个元素的大小(以字节为单位) * ndarray.data 包含实际元组的缓冲区。

import numpy as np
# 生成数组值范围0-14,维度为3行5列的数组
a = np.arange(15).reshape(3,5)
a
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14]])
# 维度
a.shape 
(3, 5)
# 轴数
a.ndim
2
# 元素类型
a.dtype
# a.dtype.name
dtype('int32')
# 每个元素所占字节数
a.itemsize
4
# 数组类型是ndarray
type(a)
numpy.ndarray
# 修改数组维度为5行。3列
a.reshape(5,3)
array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 9, 10, 11],
       [12, 13, 14]])

数组的创建

方法1.使用array函数创建数组。转换Python的列表为ndarrray。

b = np.array([1,2,3])
b
array([1, 2, 3])
c = np.array([1.2,2.4,4.8])
c
array([1.2, 2.4, 4.8])
b.dtype
dtype('int32')
c.dtype
dtype('float64')
#数组也可以指定格式
e = np.array([[1,2],[3,4]],dtype=complex)
e
array([[1.+0.j, 2.+0.j],
       [3.+0.j, 4.+0.j]])
# 全零数组
np.zeros((2,3)) 
array([[0., 0., 0.],
       [0., 0., 0.]])
# 全一数组
np.ones((3,2))
array([[1., 1.],
       [1., 1.],
       [1., 1.]])
# arange() 范围数组
np.arange(10,20,2)
array([10, 12, 14, 16, 18])
np.arange(1,2,0.1)
array([1. , 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9])
# linspace 等分
np.linspace(1,3,7)
array([1.        , 1.33333333, 1.66666667, 2.        , 2.33333333,
       2.66666667, 3.        ])
x = np.linspace(0,2*np.pi,100)
x
array([0.        , 0.06346652, 0.12693304, 0.19039955, 0.25386607,
       0.31733259, 0.38079911, 0.44426563, 0.50773215, 0.57119866,
       0.63466518, 0.6981317 , 0.76159822, 0.82506474, 0.88853126,
       0.95199777, 1.01546429, 1.07893081, 1.14239733, 1.20586385,
       1.26933037, 1.33279688, 1.3962634 , 1.45972992, 1.52319644,
       1.58666296, 1.65012947, 1.71359599, 1.77706251, 1.84052903,
       1.90399555, 1.96746207, 2.03092858, 2.0943951 , 2.15786162,
       2.22132814, 2.28479466, 2.34826118, 2.41172769, 2.47519421,
       2.53866073, 2.60212725, 2.66559377, 2.72906028, 2.7925268 ,
       2.85599332, 2.91945984, 2.98292636, 3.04639288, 3.10985939,
       3.17332591, 3.23679243, 3.30025895, 3.36372547, 3.42719199,
       3.4906585 , 3.55412502, 3.61759154, 3.68105806, 3.74452458,
       3.8079911 , 3.87145761, 3.93492413, 3.99839065, 4.06185717,
       4.12532369, 4.1887902 , 4.25225672, 4.31572324, 4.37918976,
       4.44265628, 4.5061228 , 4.56958931, 4.63305583, 4.69652235,
       4.75998887, 4.82345539, 4.88692191, 4.95038842, 5.01385494,
       5.07732146, 5.14078798, 5.2042545 , 5.26772102, 5.33118753,
       5.39465405, 5.45812057, 5.52158709, 5.58505361, 5.64852012,
       5.71198664, 5.77545316, 5.83891968, 5.9023862 , 5.96585272,
       6.02931923, 6.09278575, 6.15625227, 6.21971879, 6.28318531])
f = np.sin(x)
f
array([ 0.00000000e+00,  6.34239197e-02,  1.26592454e-01,  1.89251244e-01,
        2.51147987e-01,  3.12033446e-01,  3.71662456e-01,  4.29794912e-01,
        4.86196736e-01,  5.40640817e-01,  5.92907929e-01,  6.42787610e-01,
        6.90079011e-01,  7.34591709e-01,  7.76146464e-01,  8.14575952e-01,
        8.49725430e-01,  8.81453363e-01,  9.09631995e-01,  9.34147860e-01,
        9.54902241e-01,  9.71811568e-01,  9.84807753e-01,  9.93838464e-01,
        9.98867339e-01,  9.99874128e-01,  9.96854776e-01,  9.89821442e-01,
        9.78802446e-01,  9.63842159e-01,  9.45000819e-01,  9.22354294e-01,
        8.95993774e-01,  8.66025404e-01,  8.32569855e-01,  7.95761841e-01,
        7.55749574e-01,  7.12694171e-01,  6.66769001e-01,  6.18158986e-01,
        5.67059864e-01,  5.13677392e-01,  4.58226522e-01,  4.00930535e-01,
        3.42020143e-01,  2.81732557e-01,  2.20310533e-01,  1.58001396e-01,
        9.50560433e-02,  3.17279335e-02, -3.17279335e-02, -9.50560433e-02,
       -1.58001396e-01, -2.20310533e-01, -2.81732557e-01, -3.42020143e-01,
       -4.00930535e-01, -4.58226522e-01, -5.13677392e-01, -5.67059864e-01,
       -6.18158986e-01, -6.66769001e-01, -7.12694171e-01, -7.55749574e-01,
       -7.95761841e-01, -8.32569855e-01, -8.66025404e-01, -8.95993774e-01,
       -9.22354294e-01, -9.45000819e-01, -9.63842159e-01, -9.78802446e-01,
       -9.89821442e-01, -9.96854776e-01, -9.99874128e-01, -9.98867339e-01,
       -9.93838464e-01, -9.84807753e-01, -9.71811568e-01, -9.54902241e-01,
       -9.34147860e-01, -9.09631995e-01, -8.81453363e-01, -8.49725430e-01,
       -8.14575952e-01, -7.76146464e-01, -7.34591709e-01, -6.90079011e-01,
       -6.42787610e-01, -5.92907929e-01, -5.40640817e-01, -4.86196736e-01,
       -4.29794912e-01, -3.71662456e-01, -3.12033446e-01, -2.51147987e-01,
       -1.89251244e-01, -1.26592454e-01, -6.34239197e-02, -2.44929360e-16])
import  matplotlib.pyplot as  plt
Matplotlib is building the font cache; this may take a moment.
plt.plot(x,f)
[<matplotlib.lines.Line2D at 0x190209d15c8>]

png

打印数组

显示布局: * 最后一个轴从左到右打印, * 倒数第二个是从上到下打印的, * 其余的也从上到下打印,每个切片与下一个切片用空行分隔。

# 一维数组
print(np.arange(6))  
[0 1 2 3 4 5]
# 二维数组
print(np.arange(12).reshape(4, 3))
[[ 0  1  2]
 [ 3  4  5]
 [ 6  7  8]
 [ 9 10 11]]
# 三维数组
print(np.arange(24).reshape(2,3,4))
[[[ 0  1  2  3]
  [ 4  5  6  7]
  [ 8  9 10 11]]

 [[12 13 14 15]
  [16 17 18 19]
  [20 21 22 23]]]
# 四维数组
print(np.arange(120).reshape(2,3,4,5))
[[[[  0   1   2   3   4]
   [  5   6   7   8   9]
   [ 10  11  12  13  14]
   [ 15  16  17  18  19]]

  [[ 20  21  22  23  24]
   [ 25  26  27  28  29]
   [ 30  31  32  33  34]
   [ 35  36  37  38  39]]

  [[ 40  41  42  43  44]
   [ 45  46  47  48  49]
   [ 50  51  52  53  54]
   [ 55  56  57  58  59]]]


 [[[ 60  61  62  63  64]
   [ 65  66  67  68  69]
   [ 70  71  72  73  74]
   [ 75  76  77  78  79]]

  [[ 80  81  82  83  84]
   [ 85  86  87  88  89]
   [ 90  91  92  93  94]
   [ 95  96  97  98  99]]

  [[100 101 102 103 104]
   [105 106 107 108 109]
   [110 111 112 113 114]
   [115 116 117 118 119]]]]
# 如果数组太大而无法打印,NumPy 会自动跳过数组的中心部分,只打印角落
print(np.arange(10000))
[   0    1    2 ... 9997 9998 9999]
print(np.arange(10000).reshape(100, 100))
[[   0    1    2 ...   97   98   99]
 [ 100  101  102 ...  197  198  199]
 [ 200  201  202 ...  297  298  299]
 ...
 [9700 9701 9702 ... 9797 9798 9799]
 [9800 9801 9802 ... 9897 9898 9899]
 [9900 9901 9902 ... 9997 9998 9999]]
# 要禁用此行为并强制 NumPy 打印整个数组,您可以使用set_printoptions.
# np.set_printoptions(threshold=sys.maxsize) 

数组算术运算

数组上的算术运算符按元素应用。创建一个新数组并填充结果。

a = np.array([1,2,3])
b = np.arange(3)
a + b
array([1, 3, 5])
a - b
array([1, 1, 1])
a * b
array([0, 2, 6])
a / b
D:\work\python3.7\lib\site-packages\ipykernel_launcher.py:1: RuntimeWarning: divide by zero encountered in true_divide
  """Entry point for launching an IPython kernel.





array([inf, 2. , 1.5])
a * 2
array([2, 4, 6])
a ** 3
array([ 1,  8, 27], dtype=int32)
np.sin(a)
array([0.84147098, 0.90929743, 0.14112001])
a > 2
array([False, False,  True])
a == 1
array([ True, False, False])
# @矩阵乘法
a * b 
array([0, 2, 6])
a @ b 
8
# +=and *=,会修改现有数组而不是创建新数组。

rg = np.random.default_rng(1)
rg
Generator(PCG64) at 0x1902109B7C8
a = np.ones((2,3))
b = rg.random((2,3))
a *=3
a
array([[3., 3., 3.],
       [3., 3., 3.]])
b += a
b
array([[3.82770259, 3.40919914, 3.54959369],
       [3.02755911, 3.75351311, 3.53814331]])
a+=b
a
array([[6.82770259, 6.40919914, 6.54959369],
       [6.02755911, 6.75351311, 6.53814331]])
# 当处理不同类型的数组时,结果数组的类型对应于更一般或更精确的类型(一种称为向上转换的行为)。

a = np.ones(3,dtype=np.int32)
b = np.linspace(0,np.pi,3)
a
array([1, 1, 1])
b
array([0.        , 1.57079633, 3.14159265])
b.dtype
dtype('float64')
c = a+b
c.dtype
dtype('float64')
c
array([1.        , 2.57079633, 4.14159265])
d = np.exp(c * 1j)
d
array([ 0.54030231+0.84147098j, -0.84147098+0.54030231j,
       -0.54030231-0.84147098j])
d.dtype
dtype('complex128')
# ndarray常用方法

a = rg.random((2,3))
a
array([[0.32973172, 0.7884287 , 0.30319483],
       [0.45349789, 0.1340417 , 0.40311299]])
a.sum()
2.412007822394087
a.max()
0.7884287034284043
a.min()
0.13404169724716475
# axis 沿数组轴数操作

b = np.arange(12).reshape(3,4)
b
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
b.sum()
66
# 按列求和
b.sum(axis=0)
array([12, 15, 18, 21])
# 按行求和
b.sum(axis=1)
array([ 6, 22, 38])
# exp 高等数学里以自然常数e为底的指数函数
c = np.arange(3)
c
array([0, 1, 2])
np.exp(c)
array([1.        , 2.71828183, 7.3890561 ])
# sqrt 开方
np.sqrt(c)
array([0.        , 1.        , 1.41421356])
# add 加法
d = np.array([3,4,5])
np.add(c,d)
array([3, 5, 7])

索引、切片和迭代

一维数组可以被索引、切片和迭代,就像 列表 和其他 Python 序列一样。

多维数组的每个轴可以有一个索引。这些索引在用逗号分隔的元组中给出。

a = np.arange(10) ** 3
a
array([  0,   1,   8,  27,  64, 125, 216, 343, 512, 729], dtype=int32)
a[2]
8
a[4:7]
array([ 64, 125, 216], dtype=int32)
a[::-1]
array([729, 512, 343, 216, 125,  64,  27,   8,   1,   0], dtype=int32)
a[:6:2] = 1024
a
array([1024,    1, 1024,   27, 1024,  125,  216,  343,  512,  729],
      dtype=int32)
for i in a:
    print(i ** (1/3))
10.079368399158984
1.0
10.079368399158984
3.0
10.079368399158984
5.0
5.999999999999999
6.999999999999999
7.999999999999999
8.999999999999998
b = np.arange(16)
b = b.reshape(4,4)
b
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])
b[2]
array([ 8,  9, 10, 11])
b[2,3]
11
b[0:3]
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
b[0:3,0]
array([0, 4, 8])
b[::2][1,1]
9
b[0,...]
array([0, 1, 2, 3])
b[...,1]
array([ 1,  5,  9, 13])
for  row in b:
    print(row)
[0 1 2 3]
[4 5 6 7]
[ 8  9 10 11]
[12 13 14 15]
for element in b.flat:
    print(element)
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

形状操作

数组的形状由沿每个轴的元素数量决定

a = np.floor(10 * rg.random((3, 4)))
a
array([[5., 1., 6., 7.],
       [6., 9., 0., 5.],
       [4., 0., 6., 8.]])
a.shape
(3, 4)
# 下列方法返回一个修改过的数组,但不改变原来的数组
# 转一维
a.ravel()
array([5., 1., 6., 7., 6., 9., 0., 5., 4., 0., 6., 8.])
# 任意转
a.reshape(6,2)
array([[5., 1.],
       [6., 7.],
       [6., 9.],
       [0., 5.],
       [4., 0.],
       [6., 8.]])
# 变形,行变列,列变行
a.T
array([[5., 6., 4.],
       [1., 9., 0.],
       [6., 0., 6.],
       [7., 5., 8.]])
# 功能同reshape, ndarray.resize方法修改数组本身
a.resize((2,6))
a
array([[5., 1., 6., 7., 6., 9.],
       [0., 5., 4., 0., 6., 8.]])
# reshape参数为如果-1在重塑操作中给出了一个维度,则会自动计算其他维度

a.reshape(4,-1)
array([[5., 1., 6.],
       [7., 6., 9.],
       [0., 5., 4.],
       [0., 6., 8.]])

将不同的数组堆叠在一起

多个数组可以沿不同的轴堆叠在一起

a = np.floor(10 * rg.random((2,2)))
a
array([[5., 2.],
       [8., 5.]])
b = np.floor(10 * rg.random((2, 2)))
b
array([[5., 7.],
       [1., 8.]])
# 上下叠
np.vstack((a,b))
array([[5., 2.],
       [8., 5.],
       [5., 7.],
       [1., 8.]])
# 左右叠
np.hstack((a,b))
array([[5., 2., 5., 7.],
       [8., 5., 1., 8.]])