Numpy
NumPy(Numerical Python的简称),是科学计算基础的一个库,提供了大量关于科学计算的相关功能,例如,线性变换,数据统计,随机数生成等。其提供的最核心的类型为多维数组类型(ndarray)。
使用方式
可以使用如下的方式来安装numpy库:pip install numpy
根据惯例,使用numpy库的导入方式为:import numpy as np
在导入之后,我们可以通过:np.__version__
来查看Numpyu库的版本信息。
import numpy as np
np.__version__
数组的创建
Numpy提供了很多方式(函数)来创建数组对象,常用的方式如下:
- array
- arange
- ones / ones_like
- zeros / zeros_like
- empty / empty_like
- full / full_like
- eye / identity
- linspace
- logspace
说明:
- 注意arange函数,不是arrange。
- arange与linspace的区别。
# 创建数组最基本的方式。传递一个列表类型,返回数组类型。
a = np.array([1, 2, 3])
# ndarray数组类型。
# print(type(a))
# print(a)
# 创建多维数组。只需传递一个多维列表即可。
a = np.array([[1, 3], [2, 4]])
# print(a)
# arange 全称为array-range,注意不是英文单词arrange。
# 创建一个区间的数组。
a = np.arange(1, 10, 2)
# print(a)
# Python中的range函数只能产生整数区间。
# print(list(range(1, 2.5)))
# numpy中的arange方法功能更加灵活,其可以产生浮点类型的区间。
# a = np.arange(1, 10.2, 0.5)
# print(a)
# 创建一个值全为1的数组。shape参数来指定数组的形状(每个维度的长度)。
# a = np.ones(shape=(2, 3, 4))
# print(a)
# 创建一个值全为1的数组。形状与参数数组的形状相同。
# a = np.array([1, 2, 3])
# b = np.ones_like(a)
# print(b)
# 创建一个值全为0的数组,shape指定数组的形状。
# a = np.zeros(shape=5)
# print(a)
# 创建一个值全为0的数组,数组的形状与参数数组的形状相同。
# b = np.zeros_like(a)
# print(b)
# 创建一个数组,shape指定数组的形状,数组元素的值是未经初始化的。(数组元素的值是程序
# 上一次遗留下来的值。)
# a = np.empty(shape=(4, 2))
# b = np.empty_like(a)
# print(b)
# 创建一个使用指定值填充的数组。
# a = np.full((3, 3), 100)
# print(a)
# 创建数组,形状由参数数组(第1个参数)指定,填充值由第2个参数指定。
# b = np.full_like(a, 200)
# print(b)
# 创建一个单位矩阵。参数来指定行(列)数。
# np.eye(5)
# np.identity(3)
# linspace = linear space
# 创建一个数组,数组的元素是等差数列。可以通过num指定数列元素的个数。
# 默认为50,也可以通过endpoint来指定是否包含终止点,默认为True。
# np.linspace(1, 10, num= 10, endpoint=False)
# 创建一个数组,数组的元素在对数级别(log)上是等差的数列。【在对数上是等差数列,则原数组的元素就是等比的数列】。
# 产生10 ^ 1 ~ 10 ^ 10区间的元素(10个),元素是等比序列。base指定底数,默认为10。
# np.logspace(1, 10, num=10, endpoint=True, base=10)
# arange与linspace二者之间的对比:
# 相同点:
# 二者都能够产生指定区间的等差数列。
# 不同点:
# 1 arange的终止点一定是不包含的。linspace可以设置是否包含终止点(默认包含)。
# 2 二者的侧重点不同。arange是以固定的增量产生元素,而不太在意在此增量下,能够产生多少个元素(元素的个数)。
# linspace是以固定的元素总数来产生数列,而不太在意元素之间的增量是多少。
li = [1, 2.5]
type(li[0]), type(li[1])
数组(ndarray)与列表(List)
数组与列表类似,是具有相同类型的多个元素构成的整体。
局限:
- 数组元素要求是相同类型,而列表的元素可以是不同类型。
优势:
- 数组可以与标量进行运算,数组之间也可以进行矢量化运算。【对应位置的元素进行运算,无需进行循环操作。这样就可以充分利用现代处理器SIMD Single Instruction,Multiple Data的方式进行并行计算】。
- 数组在运算时,具有广播能力。【可根据需要进行元素的扩展,完成运算。】
- 数组底层使用C程序编写,运算速度快。
- 数组底层使用C中数组的存储方式(紧凑存储),节省内存空间。
应用对比
- 将两个等长的列表(数组)分别进行数学运算(例如,+, -等)。
- 将一个列表(数组)中的所有元素进行相同的改变。
- 对步骤2进行计时,衡量时间消耗。(练习)
- 创建相同大小的列表(数组),衡量内存消耗。(练习)
# 与标量进行运算。Python中列表的操作
li = [1, 2, 3]
value = 100
for i in range(len(li)):
li[i] += value
print(li)
# 与标量进行运算。Numpy中ndarray数组的操作
a = np.array([1, 2, 3])
# 数组与标量进行计算,会使用数组中每个元素与该变量进行运算。
a += value
print(a)
# 矢量化的计算,Python中列表的操作
li = [1, 2, 3]
li2 = [4, 5, 6]
# li3 = [a + b for a, b zip(li, li2)]
li3 = []
for a, b in zip(li, li2):
li3.append(a + b)
print(li3)
# 矢量化的计算,ndarray的操作。
a1 = np.array([1, 2, 3])
a2 = np.array([4, 5, 6])
# ndarray的矢量化计算。此时,就是使用数组中每个元素进行对位计算。
a3 = a1 + a2
print(a3)
# ndarray具有广播的能力。
# 当使用ndarray参与计算时,如果两个操作数的形状不一致,则会尽可能将两个操作数扩展成形状
# 相同,进而可以对位执行计算。【ndarray的计算,就是元素对位执行计算。】这种计算方式成为是广播。
# 广播发生在矢量与标量之间。
a = np.array([1, 2, 3])
print(a + 10)
# 广播也可以发生在矢量与矢量之间。
b = np.array([[2, 3, 4],[5, 6, 7]])
print(a + b)
c = np.array([[10], [20]])
print(a + c)
# 注意:尽管广播非常广泛,但是,也不是任意数组的计算,都能够进行广播。
d = [1, 2]
# 错误,无法进行广播运算。
# print(d + b)
# 练习:
# 1 创建相同长度的数组与列表,衡量时间消耗。
# 2 对相同长度的数组与列表,所有元素进行同样的操作(例如,所有元素增1),衡量时间消耗。
# 3 创建相同长度的数组与列表,衡量空间消耗。
%%timeit
li = []
for i in range(100000):
li.append(i)
%%timeit
li = list(range(100000))
%%timeit
li = [i for i in range(100000)]
# 注意:使用不当,速度更慢!
%%timeit
a = np.array(range(100000))
%%timeit
a = np.arange(100000)
%%timeit li = list(range(100000))
for i in range(len(li)):
li[i] += 1
%%timeit a = np.arange(100000)
a += 1
%load_ext memory_profiler
%%writefile test.py
import numpy as np
def m():
li = list(range(100000))
a = np.arange(100000)
del li
del a
import test
%mprun -f test.m test.m()
为什么数组会比列表快,而且快很多?
相关属性与操作
数组对象具有如下常用属性:
- ndim
- shape
- dtype
- size
- itemsize
# ndim 返回数组的维度。
a = np.array([[1, 2], [3, 4]], dtype=np.int16)
# print(a.ndim)
# shape 返回数组的形状。形状指数组每个维度的长度(数组每个维度含有元素的个数)。
# 返回元组类型。每个元素代表维度的大小,维度从高到底排列。a.shape[0] a.shape[1]
# print(a.shape)
# dtype 返回数组的数据类型。
# print(a.dtype)
# size 返回数组中元素的个数。返回的是所有的元素(所有维度长度的乘积),而不是某个维度的元素。
# print(a.size)
# itemsize 返回数组中每个元素占用的空间大小。(以字节为单位)
print(a.itemsize)
数据类型
- 在创建数组时,也可以使用dtype来显式指定数组中元素的类型(通过numpy提供的类型进行指定)。
- 如果没有指定,则会根据元素类型进行推断。
- 如果元素的类型不同,则会选择一种兼容的类型。
# 在创建数组时,我们可以通过dtype参数来显式指定元素的类型。如果没有指定,
# 则会根据现有元素类型进行自动推断。如果现有元素的类型不一致,则会找出一种
# 兼容的类型。(ndarray数组与Python中的列表不一致,列表元素可以是不同的类型,而ndarray数组
# 元素必须是相同的类型。)
# a = np.array([1, 2, 3.0], dtype=np.float32)
# a = np.array([1, 2, "a"])
# a.dtype
# 了解:< > 表示数据的存储方式。<表示低端存储(低位在前,高位在后),>表示高端存储(高位在前,低位在后)。
# 在创建数组时,元素的顺序不同,可能会影响到最终的类型变化。
a = np.array([1, 2, "a"])
b = np.array(["a", 1, 2])
a.dtype, b.dtype
类型转换
我们可以通过数组对象的astype函数来进行类型转换。
是否可以直接修改dtype属性进行类型转换?
# a = np.array(["32", "15", "17"])
# astype返回新创建的对象,而不是就地修改。
# a = a.astype(np.int32)
# print(a.dtype)
# print(a)
# 尝试:直接修改dtype属性,而不使用astype方法。
a = np.array([1.0, 2.5, 3.4, 4.1], dtype=np.float32)
# a.dtype = np.int32
# a.dtype = np.int64
# a
# 结论:我们不能直接修改dtype属性,来试图去改变数组数据类型。因为,不同的类型,底层的存储方式
# 是截然不同的。例如,int32类型的1与float32类型的1.0,32位的数据时完全不同的。
a.astype(np.int32)
改变形状
我们可以通过数组对象的reshape方法(或者np的reshape函数)来改变数组的形状。
说明:
- 改变数组形状时,如果维度不小于2,可以将某一个维度设置为-1。
- numpy中存在很多方法,既可以使用np来访问,也可以通过数组对象来访问。
# 关于数组操作的很多方法,我们既可以通过np.方法名来访问,也可以通过数组对象.方法名来访问。
a = np.arange(12)
print(a.shape)
# 通过np.reshape去改变数组的形状。
# b = np.reshape(a, (3, 4))
# print(b.shape)
# 通过数组对象的reshape方法来改变形状。
# b = a.reshape((3, 4))
# print(b)
# 改变成更高的维度。
# a.reshape((2, 3, 2))
# 当数组维度不小于2时,可以将其中一个维度设置为-1,-1表示自动计算该维度的大小。
# 如果改变形状中,指定-1,则-1最多只能有1个。
a.reshape((-1, 4))
# 错误,无法确定数组的最终形状。
# a.reshape((-1, -1, 2))
索引与切片
在Python中,序列类型支持索引与切片操作,在numpy库中,ndarray数组也支持类似的操作。不过,二者之间既有相同点,也有不同点。
相似点
数组对象也支持索引与切片操作,语法与Python中序列类型的索引与切片相似。
当数组是多维数组时,可以使用array[高维, 低维]的方式按维度进行索引或切片。
不同点
数组的切片返回的是原数组数据的视图(回忆:Python中呢?)。如果需要复制底层的数组元素,可以使用数组对象的copy方法。
注意:视图是共享底层的数组元素,但视图并不是赋值。
思考:通过切片,我们可以选取多个元素,但是,如果我们要选取的低维数组(或元素)是不连续的,该怎样做?
# ndarray数组支持索引与切片
# a = np.array([1, 2, 3, 4])
# a[0], a[-2]
a = np.arange(12).reshape((3, 4))
# a[0][2]
# 对于多维数组,ndarray还可以这样访问:
# a[0,2]
# 切片
li = [1, 2, 4, 5]
# 在Python中,列表的切片返回的是浅拷贝。
c = li[0:2]
li[0] = 111
# print(c)
# a = np.arange(12).reshape(3, 4)
# print(a)
# a[0:2, 1:3]
# 在ndarray中,切片返回的是原数组对象的视图。返回的对象与原数组对象共享底层的数据。
# 一个对象如果改变了底层的数据(数组的元素),将会对另外一个对象造成影响。
# a = np.arange(10)
# b = a[2:7]
# print(b)
# a[5] = 1000
# print(a)
# print(b)
# 如果希望数组能够实现真正的复制(一个数组的改变不会影响另外一个数组),我们可以使用数组对象的copy方法。
# copy返回数组的拷贝。
# c = a.copy()
# a[0] = 1000000
# print(a)
# print(c)
a = np.arange(64).reshape((4, 4, 4))
print(a)
print(a[1][1][1], a[3][1][3])
# 提取元素
# 提供多个一维数组,每个一维数组来指定每个维度的索引。
a[[1, 3], [1, 1], [1, 3]]
通过整数数组进行索引
当要选取的元素不连续时,可以提供一个索引数组来选择(或修改)对应索引位置的元素。
说明:
- 通过整数数组索引,返回的是原数组的拷贝,而不是视图。
- 可以提供多个一维数组索引,此时会将每个数组的对应位置元素作为索引,返回对应的元素。
# 通过整数数组进行索引,提取元素。
# a = np.arange(10, 0, -1)
# print(a)
# 如果元素是连续的,或者是间隔有规律的,我们可以使用切片。如果没有以上规律,
# 我们就可以使用整数数组来指定索引位置,进而提取元素。
# index = np.array([0, 1, 9])
# a[index]
# 提供的索引数组未必一定是ndarray类型的,只要是数组类型就可以,例如Python中的列表,也可以。
# a[[0, 1, -2]]
# 通过整数数组提取元素,返回的是原数组对象的拷贝。(与数组的切片不同)
a = np.array([1, 3, 5, 7])
# 通过切片提取元素,返回视图。
# b = a[:2]
# 通过整数数组提取元素,返回拷贝。
b = a[[0, 1]]
print(a)
print(b)
a[0] = 10000
print(a)
print(b)
通过布尔数组进行索引
我们可以提供一个布尔类型的数组(A),然后通过该数组(A)来对另外一个数组(B)进行索引(元素选取)。索引的原则为:如果为True,则选取对应位置的元素,否则不选取。
通过布尔类型的数组进行索引是常见且实用的操作,我们通常用来进行元素选择(或过滤)操作。例如:
- 选择一个公司中所有年龄大于15的年龄。
- 选择两个数组中对应位置相同的元素。
- 将所有大于100的值设置为100。
说明:
- 用于索引的布尔数组通常通过现有数组计算得出。
- 可以通过~对条件进行取反操作(不能使用not)。
- 当存在多个条件时,可以使用&,|符号(不能使用and与or),同时,每个条件需要使用()进行括起。
# 通过布尔类型数组提取元素。布尔数组(A)的长度需要与待提取元素的数组(B)长度相同(元素个数一致)。
# 根据A与B进行对位判断,如果A元素的值为True,则保留B对应位置的元素,否则,A元素的值为False,则丢弃B对应位置的元素。
a = np.array([5, 12, 40, -43])
b = np.array([True, False, False, True])
# print(a[b])
# print(a[[True, False, False, True]])
# 在实际应用中,我们不会自己去创建布尔类型的数组,而是通过计算得出布尔类型数组。
# print(a > 0)
# a[a > 0]
# age = np.array([20, 19, 23, 14, 30, 50, 18, 60])
# 选择所有年龄大于15的人员。
# age[age > 15]
# age2 = np.array([20, 33, 43, 24, 10, 50, 18, 60])
# age[age == age2]
score = np.array([60, 80, 90, 100, 105, 70, 108])
# score[score > 100] = 100
# score
# 注意:赋值运算的特殊性。此时,没有发生广播运算,而是将score绑定了一个新的对象(0)。
# score = 0
# type(score)
# 如果我们需要进行广播的赋值,可以使用这样的语法:
# score[:] = 0
# print(score)
# 多个条件的过滤(或者,并且,不能使用Python中的and与or)
# 并且 使用 &(相当于Python中的and), 或者 使用 | (相当于Python中的or)
# 注意:当使用多条件时,每个条件必须使用括号()括起。
# (score >= 70) & (score <= 90)
# score[(score >= 70) & (score <= 90)]
# score[(score < 70) | (score > 90)]
# 我们可以使用~来对条件进行取反。相当于Python中的not。
~(score >= 70)
# 取反,我们也可以通过反向的条件来实现。
score < 70
数组扁平化
我们可以通过调用ravel或flatten方法,对数组对象进行扁平化处理。
- np.ravel / ravel
- flatten
二者的区别在于,ravel返回原数组的视图,而flatten返回原数组的拷贝。
# 数组的扁平化。ravel与flatten都可以进行数组的扁平化。不同之处在于,ravel返回的是原数组的视图(与原数组共享
# 底层的数组元素,一个发生改变,会影响另外一个)。而flatten返回的是原数组的拷贝,二者之间不会受到相互的影响。
a = np.arange(16).reshape((4, 4))
# b = np.ravel(a)
b = a.ravel()
c = a.flatten()
a[0][0] = 10000
print(a)
print(b)
print(c)
import this
数组的存储顺序
在创建数组时,我们可以通过order参数来指定数组元素的存储顺序。存储顺序分为两种:
- C
- F
# 构建数组时,首先会对构建的元素数组进行扁平化操作(使用order指定的方式),然后,在对扁平化后的结果
# 进行填充(使用order指定的方式)。
c = np.array([[1, 2], [3, 4]], order="C")
f = np.array([[1, 2], [3, 4]], order="F")
print(c)
print(f)
a = np.array([[1, 2, 3], [4, 5, 6]])
c = a.reshape((3, 2), order="C")
f = a.reshape((3, 2), order="F")
print(c)
print(f)
练习
1 直接使用array去创建一个多维数组(如2 * 2),order分别设置为C与F,二者会有不同吗?
2 创建一个2 * 3(3 * 2)的数组,将其reshape为3 * 2(2 * 3),order分别设置为C与F,解释结果。
我们在创建数组时,在读取(解析)现有的数据顺序,以及创建新的数据的结构上,都会使用order指定的顺序。【可以简单认为,我们先使用指定的order将原数据扁平化处理,然后在使用指定的order顺序去构建新的数组(向新的数组中插入值)。】
通用函数ufunc(universal function)
Numpy提供了许多通用函数,这些通用函数可以看做是以前通过Python计算的矢量化版本。
- abs / fabs
- ceil / floor
- exp
- log / log2 / log10
- modf
- sin / sinh / cos / cosh
- sqrt
# 通用函数的功能,其实Python中都具备,只不过,我们numpy当中再次提供,是为了实现矢量化的计算。
# Python提供的功能都是标量化的版本,而numpy与之对应,提供的是Python中相同功能的矢量化版本。
li = [1.3, 2.5, -3, -4.5]
# abs(li)
# np.abs(li)
# 返回数值的小数部分与整数部分。
np.modf(np.array([1.5, 2.4, -3.5, 4.8]))
统计函数
Numpy(或数组对象)具有如下常用的统计函数。
- mean / sum
- max / min
- argmax / argmin
- std / var
- cumsum / cumprod
轴(axis)
可以指定axis参数来改变统计的轴。axis是一个非常重要的参数,关于数组的很多操作与运算,都涉及该参数。轴的取值为0,1,2……其中0表示最高的维度,1表示次高的维度,以此类推。同时,轴也可以为负值,表示倒数第n个维度,例如,-1表示最后(低)一个维度。在二维数组中,0表示沿着竖直方向进行操作,1表示沿着水平方向进行操作。在多维数组中,轴相对复杂一些,可以认为,是沿着轴所指定的下标变化的方向,进行操作。例如,如果轴是1,则根据第1个下标变化的方向上进行操作。
a = np.arange(12).reshape(3, 4)
# mean 求平均值,sum 求和 median 中值。
# np.mean(a)
# a.mean(), a.sum()
# np.median(a)
# max 最大值 min 最小值。
# np.max(a), np.min(a)
# argmax 最大值的索引 argmin 最小值的索引。
# np.argmax(a), np.argmin(a)
# std 标准差 var 方差
# np.std(a), np.var(a)
# cumsum 累积和 cumprod 累积乘积
# a = np.array([3, 2, 4, 1])
# a.cumsum(), a.cumprod()
# 在在ndarray数值进行统计时,如果没有指定轴(axis参数),则统计的是数组中所有的元素。
# 如果需要进行更细致的统计,我们可以设置参数axis。当数组是二维数组时,axis=0,则按照
# 竖直的方向进行统计,axis=1,则按照水平的方向进行统计。
print(a)
# 对整个数组进行统计。
# np.mean(a)
# axis=0,按照竖直方向进行统计。
# np.mean(a, axis=0)
# axis=1, 按照水平方向进行统计。
# np.mean(a, axis=1)
# 轴的取值范围: [0, 数组.ndim - 1],除此之外,轴也可以取负值,表示倒数第n个轴。
np.mean(a, axis=-2)
# 更通用的情况:当数组是多维数组时,轴统计的方向,是按照下标变化的方向来进行统计的。
# 轴与维度是一一对应的,即我们按照哪个轴进行统计,实际上,就是按照哪个维度进行统计。
a = np.arange(2 * 3 * 4).reshape((2, 3, 4))
print(a)
# np.sum(a, axis=0)
# np.sum(a, axis=1)
np.sum(a, axis=2)
随机函数
- np.random.rand
- np.random.random 与rand相同,但是形状通过一个参数指定。
- np.random.randn
- np.random.normal
- np.random.randint
- np.random.seed
- np.random.shuffle
- np.random.uniform
# np.random提供了生成随机数的功能,可以认为是生成随机数的矢量化版本。
# 返回[0. 1)范围的随机数,参数指定数组的形状。
# np.random.rand(3, 4)
# 与rand函数相同,也是生成[0, 1)之间的随机小数,只是指定数组形状的方式不同。rand
# 是使用多个参数,每个参数来指定每个维度的长度,而random函数是使用一个参数(元祖类型)
# 来指定每个维度的长度。
# np.random.random(size=(4, 5))
# 生成具有标准正态分布(均值为0,标准差为1)的随机数。
# np.random.randn(5, 5)
# 生成正态分布。可以指定均值与标准差。默认为标准正态分布。
# np.random.normal(loc=5, scale=0.01, size=(5, 5))
# 产生指定范围的随机整数。(包含起始点,不包含终止点,注意:这与Python中的randint不同。)
# np.random.randint(1, 10, (5, 5))
# 随机种子:用来生成随机数的。随机种子相同,则产生的随机数一定相同。因此,通过设置相同的随机种子,
# 我们就可以让随机序列得到重现。
# np.random.seed(0)
# np.random.random(10)
# 打乱数组元素的顺序(洗牌)。shuffle进行的操作是就地修改操作。
# a = np.arange(10)
# np.random.shuffle(a)
# a
# 产生指定区间的随机小数。包括起始点,不包括终止点。
np.random.uniform(2.5, 3.8, size=(10))
import random
# 设置随机种子
random.seed(10)
for i in range(5):
print(random.random())
# random.seed(10)
random.random()
连接与拆分函数
- np.concatenate 对多个数组按指定轴的方向进行连接。
- np.vstack / np.hstack
- np.split / np.hsplit / np.vsplit
a = np.arange(6).reshape((2, 3))
print(a)
b = np.arange(6, 12).reshape((2, 3))
print(b)
# 用来拼接两个(或更多)的数组。axis指定连接方向。0(竖直方向),1(水平方向),默认为0。
# np.concatenate((a, b), axis=0)
# np.concatenate((a, b), axis=1)
# vstack 与 hstack 竖直与水平方向堆叠数组。
# vstack 相当于是concatenate方法中axis=0的情况,
# hstack 相当于是concatenate方法中axis=1的情况。
# np.vstack((a, b))
# np.hstack((a, b))
a = np.arange(36).reshape((6, 6))
print(a)
# 对数组进行切分。第1个参数:待切分的数组,第2个参数:int类型,表示切分的段数。
# 第3个参数: axis 指定切分的方向。0:竖直方向切分(切分为若干行),1:水平方向切分(切分为若干列)
# np.split(a, 3, axis=0)
# np.split(a, 2, axis=1)
# 当第二个参数为int类型时,表示切分的段数,此时需要注意,切分的段数必须是能够等分的,如果不能等分,
# 将会产生错误。
# np.split(a, 4, axis=1)
# 如果要实现不等分的切割(更加灵活的切割),第2个参数还可以传递数组类型。数组中每个元素来指定切割点。
# 例如,数组元素为 [a, b, c],则按照如下的方式进行切割:
# [:a], [a:b], [b:c], [c:]
# np.split(a, [1, 3], axis=1)
# vsplit 与 hsplit
# vsplit 相当于是 split axis=0的情况。
# hsplit 相当于是 split axis=1的情况。
# np.vsplit(a, 2)
# np.hsplit(a, 2)
其他函数(方法)
- any / all
- transpose(T)
- swapaxes
- dot(@)
- sort / np.sort
- unique
- np.where
- np.save / np.load
- np.savetxt / np.loadtxt
说明:
- sort可以指定排序的轴。
- save在保存数组时,如果没有指定扩展名,则自定补充.npy作为扩展名。
数据对象与numpy都具有sort方法(函数),二者等价吗?
# any 如果数组中有任何一个元素为True(或者能转换为True),则返回True,否则返回False。
a = np.array([1, 0, False])
# a.any()
# all 如果数组中所有元素为True(或者能转换为True),则返回True,否则返回False。
a.all()
# a = np.arange(6).reshape((2, 3))
# print(a.T)
# print(a.transpose())
a = np.arange(24).reshape((2, 3, 4))
print(a)
# 从矩阵的角度讲,是一个转职的操作。如果从更高维的角度讲,是一个轴的颠倒。
# 假设以前的轴为0, 1, 2……n,转换之后为n, n-1 …… 0。
# 轴的变换体现的就是下标的变换。 例如 a[i][j][k] => a[k][j][i]
# print(a.T)
# T属性相当于是无参的transpose方法。transpose可以通过参数来控制轴的交换(更加灵活),
# 而不是仅能进行轴顺序的颠倒。
# (1, 2, 0) 表示的意思就是使用以前的1轴充当现在的0轴,使用以前的2轴,充当现在的1轴,使用以前的0轴,
# 充当现在的2轴。
# print(a.transpose(1, 2, 0))
# 交换参数指定的两个轴。参数的顺序并不重要。
# a[i][j][k] => a[j][i][k]
a.swapaxes(0, 1)
# 我们使用transpose也能够实现swapaxes的功能,只是可能会繁琐一点。
# a.transpose(1, 0, 2)
# dot 运算
# 1 数组与标量进行dot运算。此时使用数组中每个元素与该标量进行乘法运算,相当于 * 运算。
# a = np.array([1, 2, 3])
# np.dot(a, 3)
# a * 3
# 2 两个一维数组进行dot运算。此时,会使用两个数组的元素进行对位相乘,再相加。
# a = np.array([1, 2, 3])
# b = np.array([2, 3, 1])
# np.dot(a, b)
# 3 两个二维数组进行dot运算。此时,执行数学上矩阵的点积运算。
a = np.array([[1, 2], [3, 4]])
b = np.array([[2, 1], [0, 2]])
# np.dot(a, b)
# 在点积运算时,我们也可以使用@符号来进行点积运算。
a @ b
# 4 左侧操作数是多维数组,右侧操作数是一维数组。此时,会使用左侧操作数的最低维,与右侧操作数进行对位相乘,再相加的运算。
# 前提:左侧操作数的最低维与右侧操作数元素个数相同。
# a = np.array([[1, 2, 3], [3, 4, 1]])
# b = np.array([0, 1, 2])
# np.dot(a, b)
# 5 两个多维数组(但是不都是二维数组)进行dot运算。此时,会使用左侧数组的最后一维,与右侧数组的倒数第2维进行对位相乘,再相加的运算。
# 前提:左侧数组的最后一维与右侧数组的倒数第二维长度必须相同。
# a = np.arange(2 * 3 * 2).reshape((2, 3, 2))
# b = np.array([[1, 2], [2, 3]])
# print(a)
# print(b)
# np.dot(a, b)
# a = np.array([3, 2, 5, -1, 4])
# 虽然很多方法既可以通过np.方法进行访问,也可以通过数组对象.方法进行访问。
# 但是,二者并不总是等价的。sort方法就是一个特例。
# np.sort没有修改数组对象,而是返回一个新的对象,新的对象是排序之后的结果。
# 数组对象.sort是进行就地修改,不会返回任何内容(返回内容为None)。
# np.sort(a)
# a.sort()
# print(a)
# a = np.array([3, 2, 5, -1, 4, 2, -1])
# 去掉数组重复的元素,并且对数组进行排序。
# np.unique(a)
# "值1" if 5 < 3 else "值2"
# np.where可以看做是简化版if-else的矢量化版本。
a = np.array([50, 30, 12, 30, 93])
b = np.array([40, 23, 10, -3, 333])
# 第1个参数指定条件。条件为True,返回第2个参数,否则返回第3个参数。
np.where(a > b, a, b)
# 保存 / 读取数据
a = np.arange(40).reshape((5, 8))
print(a)
# 以二进制形式保存ndarray数组数据。如果没有显式指定扩展名,则扩展名为.npy。
# np.save("c:/data", a)
# 加载(读取)之前保存的数组数据。
# np.load("c:/data.npy")
# 以文本形式保存ndarray数组数据。该方法存储数据,要求数组的维度不能超过2,否则会产生错误。
# 如果需要保存更多维度的数据,使用save方法。
# np.savetxt("c:/data2.txt", a, fmt="%d")
# 加载之前保存的文本类型的数组数据。
np.loadtxt("c:/data2.txt", dtype=np.int32)
a = np.array([3, 2, 5, -1, 4])
# [-1, 2, 3, 4, 5]
# 对数组进行排序,返回排序之后的值,在原数组中的索引位置。
np.argsort(a)