Numpy基础

NumPy基础

NumPy的主要对象是同类型的多维数组,是一张表,所有元素(通常是数字)的类型都相同。
维度称为axes,axes的数目为rank。如下rank为2(2维的),第一维度axes长度为2,第二维度axes长度为3.
[[ 1., 0., 0.],
[ 0., 1., 2.]]
NumPy的数组的类称为ndarray。Python的数组提供较少的功能,而ndarray一下重要的属性。

属性

  • ndarray.ndim:数组的axes(维度)的个数,维度的数量称为rank。
  • ndarray.shape:数组的维度。这是一个整数元组,(n, m)表示一个n行,m列的矩阵。
  • ndarray.size:为shape元素的乘积,其实就是矩阵元素个数。
  • ndarray.dtype:描述矩阵中元素的类型的对象。可以使用Python标准的类型,也提供其他类型,例如:numpy.int32、numpy.int16和numpy.float64。
  • ndarray.itemsize:数组中每个元素的字节大小。例如float64字节数为8。
  • ndarray.data:该缓冲区包含数组的实际元素。通常不适用这个,我们一般使用索引进行访问(前面矢量化又说不要遍历,后续看看是怎么回事)。

打开中文API查看示例:属性示例

数组创建

有几种方法来创建数组

  • 可以使用array函数从常规的Python列表或元组中创建数组,得到的数组类型从序列中元素的类型推到而出。
    >>> numpy.array([3, 4, 5])
  • array函数传入列表的列表产生2维阵列,同理3维。
    >>> numpy.array([1, 2, 3], [4, 5, 6])
    array([[1, 2, 3]
    [4, 5, 6]
    ])
    • 数组的类型可以在创建的时候指定
      >>> numpy.array([1, 2, 3], dtype=complex) #元素为复数形式 1. + 0.j
  • 通常数组的元素是位置的,所有NumPy提供创建具体初始占位符的数组,减少数组增长的必要。
    • 函数zeros创建一个由0组成的数组;
    • 函数ones创建一个由1数组的数组;
    • 函数empty内容是随机的并且取决于存储器的状态,元素默认类型是float64。
  • 为了创建数组序列,NumPy提供了类似于range的函数,返回数组而不是列表。

    >>> numpy.arange(1, 10, 2) #使用如上的reshape函数可以改变形状。起始为1步长为2。

  • 当arange与浮点参数一起使用时,由于浮点数的精度是有限的,通常不可能预测获得的元素数量。出于这个原因,通常最好使用函数linspace,它接收我们想要的元素数量而不是步长作为参数:
    >>> numpy.linspace(1, 2, 9) # 1到二之间的9个数,感觉是平均分配的样子。步长0.125
    ‘>>> numpy.sin(numpy.linspace(0, 2*pi, 100))’ #求sin。
    其他函数:
    array, zeros, zeros_like, ones, ones_like, empty, empty_like, arange,
    linspace, numpy.random.rand, numpy.random.randn, fromfunction, fromfile

打印数组

当打印数组时,NumPy以类似于嵌套列表的方式显示它,但是使用以下布局:

  • 最后一个axes从左到右打印,
  • 第二个到最后一个从上到下打印,
  • 其余的也从上到下打印,每个切片与下一个用空行分开
  • 如果数组太大,会跳过中间的部分,只打印边角部分。
    • 如果需要强制打印整个数组,可以使用set_printoptions来更改打印选项
      numpy.set_printoptions(threshold='nan') #官方api说传入的是int值,nan目前不知道什么意思:后面查看可以传入numpy.nan,而非str类型

reshape改变数组形状

  • a.ravel() #平坦化序列,比如3 * 4的矩阵,变成一维矩阵。
  • a.reshape(x, y) #转变形状到x*y 元素需要支持转换该形状才行,如果y=-1则会自动计算维度。
  • a.T #转置 transposed
  • numpy.resize方法直接改变数组本身

基本操作

数组上的算术运算符使用元素基本,将创建一个新数组并用结果填充,差不多就是对应到每个元素,然后返回计算结果。

>>> a = numpy.array([20, 30, 40, 50])
>>> b = numpy.arange(4)

  • >>> c = a - b #对应求其差值
  • >>> b**2 #b元素都乘2
  • >>> 10*np.sin(a) #对a每个元素求sin,并乘10倍值。
  • >>> a < 35 #对a元素做判断,返回boolean矩阵。
  • dot函数做矩阵的乘法:a.dot(b) np.dot(a, b)
  • a * b 是对a和b对应位置的元素做乘积

+= *= 类似的操作会修改当前数组,而不是创建新的数组:

  • ‘>>> a = np.ones((2,3), dtype=int)’
  • >>> b = np.random.random((2, 3))
  • >>> a *= 3
  • >>> b += a
  • >>> a += b #报错, float64 to int32

不同类型的数组操作,结果数组的类型对应更精确的数组,向上转型。

  • 比如int32 和 float64计算 结果类型为float64

许多一元操作,求和sum,可以使用ndarray的方法

  • sum(), max(), min()

默认的这些操作适用于数组,就像一个数字列表求最大值,不管其形状(shape = (2*3))

通过axis参数,可以指定沿数组的指定轴应用操作

  • b.sum(axis=0) # 每列求和
  • b.sum(axis=1) # 每行求和
  • b.cumsum(axis=1) # 沿着行累积和,相反沿着列累积和

另见

all, any, apply_along_axis, argmax, argmin, argsort, average, bincount, ceil, clip, conj, corrcoef, cov, cross, cumprod, cumsum, diff, dot, floor, inner, inv, lexsort, max, maximum, mean, median, min, minimum, nonzero, outer, prod, re, round, sort, std, sum, trace, transpose, var, vdot, vectorize, where

  • all:是判断所以元素是否都为True,可以定义axis来沿着某个轴判断。
  • any:和all相反,判断是否有一个为Ture,是返回True,否返回False,可以传入axis 例如:b.any(0),纵轴是否有为真的。
  • apply_along_axis(func, axis, arr):将arr按照axis定义,行或者列取出作用在func上,并返回一个列表。

    如果上述返回标量,则返回于源arr相同形状(shape)。比如传输sorted进行排序,返回还是列表。

  • argmax:查找最大值,返回索引,支持axis轴选择。如果多个大值,就返回第一次遇到的。

  • argmin:和max相反。
  • argsort:排序,返回排序完的索引值。可以选择算法。选择键排序。
  • mean:平均值,可以按轴进行。axis
  • diff:计算差分,out[n] = a[n+1] - a[n],可以按轴进行。
  • vdot:计算点积。
  • 还有很多函数,先不看了。

索引、切片和迭代

一维数组支持索引,切片和迭代,非常类似于列表和其他序列。

  • a[1:2],其中**是次方,**(1/3) 求根号3

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

比如b[2,3]表示3行4列的元素,b[0:3, 1]表示行数是0,1,2的第2列,如果1数字缺失,则认为是全部列。
如果维度大于3,比如维度(轴,rank)为5的情况,x[1,2,:,:,:]可以等效为x[1,2,…]

  • x[1,2,…]等效于x[1,2,:,:,:]
  • x[…,3]到x[:,:,:,:,3]
  • x[4,…,5,:]到x[4,:,:,5,:]
    例子:y[1:5:2,::3]指的是1-4行间隔为2行,列数间隔为3进行抽取。
    避免迭代使用索引提高性能。
    可以使用一个数组来索引数组,提高的索引必须是索引值,比如a[np.array([1,2,3])] 索引a中的索引为1,2,3的元素。
    并且索引数组可以是多维的生成的矩阵维度和索引一致,a[np.array([[1,2], [3,4]])],二维矩阵,1,2,3,4对应为a一维矩阵的索引。
  • Boolean型索引,比如
    >>> y = np.arange(35).reshape(5,7)
    >>> b = y > 22
    >>> y[b] # 这样就将y里边的大于22的数索引出来了,但是维度只有一维,并且如果索引的布尔数组和y具有相同的形状
    通常,当布尔数组具有比被索引的数组更少的维度时,这等同于y [b,…],这意味着y被索引为b,然后是多个:如同填充y。因此,结果的形状是包含布尔数组的True元素的数目的一个维度,后面是被索引的数组的剩余维度。
    组合索引和切片,感觉就是行和列的索引,三维的话相当于两个数组,就分别对两个进行索引
  • y[1:3, 2:3] #类似的就是1-2行并且列是2列
    结构化索引工具
    >>> y.shape
    (5, 7)
    >>> y[:,np.newaxis,:].shape # np.newaxis 对象来新建一个维度'(5, 1, 7)`
  • 可以使用索引来改变相应位置的值
    y[2:7] = 1 #将2-6位置的数更改为1
    y[2:7] = np.arange(5) #或者直接使用数组的形式
    如果高类型分配给低类型的,会转型,损失精度,或者抛出错误
    下面的一个例子比较特殊:
    >>> x = np.arange(0, 50, 10)
    >>> x
    array([ 0, 10, 20, 30, 40])
    >>> x[np.array([1, 1, 3, 1])] += 1
    >>> x
    array([ 0, 11, 20, 31, 40])
    实际上我们认为1位置的数应该增加3才对,但是最终只增加1,其实是我们每次计算都采用了临时数组的方式,导致最终只增加1.
  • Ellipsis 等同于 ‘…’
  • 元组不像列表索引那样会自动处理,如下例子:
    >>> z[[1,1,1,1]] # produces a large array
    array([[[[27, 28, 29],
    [30, 31, 32], ...
    >>> z[(1,1,1,1)] # returns a single value
    40

多维数组迭代是相对于第一个轴进行的。

可以使用flat,是一个迭代器。
如果使用x[0][2]会降低性能。因为2索引需要在前面0索引创建的数组上面继续操作。
另见indexing。

形状操作

以下的命令不会修改数组,只会返回新的数组

  • ravel 返回连续的平坦的数组,降成一维
  • reshape 修改形状
  • T 转置
    修改数组本身
  • resize:对应的reshape是返回修改的数组
    将不同的数组堆叠
  • vstack
  • hstack h v 分别对应水平方向和垂直方向。
  • column_stack
  • row_stack column 和 row 分别对应,并且这两个方法允许1D的数组堆叠到2D数组中
    将数组分隔成几个小的数组
  • np.hsplit(a, 3) #将a划分为3个 h是水平
  • np.hsplit(a, (3, 4)) #将列为3-4但是不等于4的索引分隔开来
  • 同理vsplit是垂直划分
  • array_split 可以指定某个轴进行划分

复制和视图

当计算和操作数组时,它们的数据有时被复制到新的数组中,有时不复制。这通常是初学者的混乱的来源。有三种情况:

  • 完全不复制
    简单赋值不会创建数组对象或其数据的拷贝:如果将b = a 那么b和a指向的是同一个数组,改变b,a也跟着改变。
    可变对象传递到函数引用,函数不会复制。
  • 视图或浅复制
    不同的数组对象可以共享相同的数据。view方法创建一个新数组对象,该对象看到相同的数据。
    c=a.view(), 这样改变c.shape = (2,6),a的形状不会改变,但是如果c[0,4] = 1234,a的相应位置就会赋值为1234
    c.base is a = True
    对数组切片返回的也是视图
  • 深复制
    d = a.copy()
    这样d就是一个新的数组

函数和方法概述

这里是一些有用的NumPy函数和方法名称按类别排序的列表。有关完整列表,请参见Routines。

  • 数组创建
    arange, array, copy, empty, empty_like, eye, fromfile, fromfunction, identity, linspace, logspace, mgrid, ogrid, ones, ones_like, r, zeros, zeros_like
  • 转换
    ndarray.astype,atleast_1d,atleast_2d,atleast_3d,mat
  • 操纵
    array_split, column_stack, concatenate, diagonal, dsplit, dstack, hsplit, hstack, ndarray.item, newaxis, ravel, repeat, reshape, resize, squeeze, swapaxes, take, transpose, vsplit, vstack
  • 问题
    all,any,nonzero,where
  • 顺序
    argmax, argmin, argsort, max, min, ptp, searchsorted, sort
  • 操作
    choose, compress, cumprod, cumsum, inner, ndarray.fill, imag, prod, put, putmask, real, sum
  • 基本统计
    cov,mean,std,var
    基本线性代数
    cross,dot,outer,linalg.svd,vdot

其他基础

广播规则

  • Broadcasting允许通用函数以有意义的方式处理具有不完全相同形状的输入。
  • Broadcasting的第一个规则是,如果所有输入数组不具有相同数量的维度,则“1”将被重复地添加到较小数组的形状,直到所有数组具有相同数量的维度。
  • Broadcasting的第二个规则确保沿着特定维度具有大小为1的数组表现得好像它们具有沿着该维度具有最大形状的数组的大小。假定数组元素的值沿“Broadcasting”数组的该维度相同。

花式索引技巧

NumPy提供了比常规Python序列更多的索引能力。除了通过整数和切片索引之外,如前所述,数组可以由整数数组和布尔数组索引。

  • 使用索引数组索引
    a[j] j = np.array([1,1,3]) #得出索引a是1,1,3组成的数组
    如果索引的数组是多维的,结果与之对应,j = np.array([[1,2,3], [4, 5, 6]]),结果是形状是(2, 3)
  • 可以对每个维度进行单独索引
    i = np.array([[0,1], [1, 2]])
    j = np.array([[2,1], [1, 3]])
    a[i, j]
    同样的 l=[i,j] a[l]=a[i,j]

函数

np.ix_[a,b,c] ???雨里雾里
np.ufunc.reduce ???云里雾里

线性代数 基础

简单的数组操作

  • a.transpose() # 矩阵转置
  • np.linalg.inv(a) # 逆矩阵
  • np.eye(2) # 2*2的矩阵,返回对角线是1其他都是0的矩阵
  • np.dot(i, j) # 求矩阵乘积
  • np.trace(a) # 对a矩阵的对角线值求和
  • np.linalg.solve(a, y) # 解线性方程组 a矩阵对应参数,y对应等于后边的数
  • np.linalg.eig(j) # 特征向量,线性代数当时没有学好