Python NumPy(12):NumPy 字节交换、NumPy 副本和视图、NumPy 矩阵库(Matrix)
1 NumPy 字节交换
在几乎所有的机器上,多字节对象都被存储为连续的字节序列。字节顺序,是跨越多字节的程序对象的存储规则。
-
大端模式:指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;这和我们的阅读习惯一致。
-
小端模式:指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低。
例如在 C 语言中,一个类型为 int 的变量 x 地址为 0x100,那么其对应地址表达式&x的值为 0x100。且x的四个字节将被存储在存储器的 0x100, 0x101, 0x102, 0x103位置。
numpy.ndarray.byteswap() 函数将 ndarray 中每个元素中的字节进行大小端转换。
import numpy as np
a = np.array([1, 256, 8755], dtype=np.int16)
print('我们的数组是:')
print(a)
print('以十六进制表示内存中的数据:')
print(map(hex, a))
# byteswap() 函数通过传入 true 来原地交换
print('调用 byteswap() 函数:')
print(a.byteswap(True))
print('十六进制形式:')
print(map(hex, a))
# 我们可以看到字节已经交换了
2 NumPy 副本和视图
副本是一个数据的完整的拷贝,如果我们对副本进行修改,它不会影响到原始数据,物理内存不在同一位置。视图是数据的一个别称或引用,通过该别称或引用亦便可访问、操作原有数据,但原有数据不会产生拷贝。如果我们对视图进行修改,它会影响到原始数据,物理内存在同一位置。
视图一般发生在:
- numpy 的切片操作返回原数据的视图。
- 调用 ndarray 的 view() 函数产生一个视图。
副本一般发生在:
- Python 序列的切片操作,调用deepCopy()函数。
- 调用 ndarray 的 copy() 函数产生一个副本。
2.1 无复制
简单的赋值不会创建数组对象的副本。 相反,它使用原始数组的相同id()来访问它。 id()返回 Python 对象的通用标识符,类似于 C 中的指针。此外,一个数组的任何变化都反映在另一个数组上。 例如,一个数组的形状改变也会改变另一个数组的形状。
import numpy as np
a = np.arange(6)
print('我们的数组是:')
print(a)
print('调用 id() 函数:')
print(id(a))
print('a 赋值给 b:')
b = a
print(b)
print('b 拥有相同 id():')
print(id(b))
print('修改 b 的形状:')
b.shape = 3, 2
print(b)
print('a 的形状也修改了:')
print(a)
2.2 视图或浅拷贝
ndarray.view() 方会创建一个新的数组对象,该方法创建的新数组的维数变化不会改变原始数据的维数。
import numpy as np
# 最开始 a 是个 3X2 的数组
a = np.arange(6).reshape(3, 2)
print('数组 a:')
print(a)
print('创建 a 的视图:')
b = a.view()
print(b)
print('两个数组的 id() 不同:')
print('a 的 id():')
print(id(a))
print('b 的 id():')
print(id(b))
# 修改 b 的形状,并不会修改 a
b.shape = 2, 3
print('b 的形状:')
print(b)
print('a 的形状:')
print(a)
使用切片创建视图修改数据会影响到原始数组:
import numpy as np
arr = np.arange(12)
print('我们的数组:')
print(arr)
print('创建切片:')
a = arr[3:]
b = arr[3:]
a[1] = 123
b[2] = 234
print(arr)
print(id(a), id(b), id(arr[3:]))
变量 a,b 都是 arr 的一部分视图,对视图的修改会直接反映到原数据中。但是我们观察 a,b 的 id,他们是不同的,也就是说,视图虽然指向原数据,但是他们和赋值引用还是有区别的。
2.3 副本或深拷贝
ndarray.copy() 函数创建一个副本。 对副本数据进行修改,不会影响到原始数据,它们物理内存不在同一位置。
import numpy as np
a = np.array([[10, 10], [2, 3], [4, 5]])
print('数组 a:')
print(a)
print('创建 a 的深层副本:')
b = a.copy()
print('数组 b:')
print(b)
# b 与 a 不共享任何内容
print('我们能够写入 b 来写入 a 吗?')
print(b is a)
print('修改 b 的内容:')
b[0, 0] = 100
print('修改后的数组 b:')
print(b)
print('a 保持不变:')
print(a)
3 NumPy 矩阵库(Matrix)
NumPy 中包含了一个矩阵库 numpy.matlib,该模块中的函数返回的是一个矩阵,而不是 ndarray 对象。一个 m x n 的矩阵是一个由 m 行(row)n 列(column)元素排列成的矩形阵列。矩阵里的元素可以是数字、符号或数学式。以下是一个由 6 个数字元素构成的 2 行 3 列的矩阵:
3.1 转置矩阵
NumPy 中除了可以使用 numpy.transpose 函数来对换数组的维度,还可以使用 T 属性。例如有个 m 行 n 列的矩阵,使用 t() 函数就能转换为 n 行 m 列的矩阵。
import numpy as np
a = np.arange(12).reshape(3, 4)
print('原数组:')
print(a)
print('\n')
print('转置数组:')
print(a.T)
3.2 matlib.empty()
matlib.empty() 函数返回一个新的矩阵,语法格式为:
numpy.matlib.empty(shape, dtype, order)
- shape: 定义新矩阵形状的整数或整数元组
- Dtype: 可选,数据类型
- order: C(行序优先) 或者 F(列序优先)
import numpy.matlib
import numpy as np
print(np.matlib.empty((2, 2)))
# 填充为随机数据
3.3 numpy.matlib.zeros()
numpy.matlib.zeros() 函数创建一个以 0 填充的矩阵。
import numpy.matlib
import numpy as np
print(np.matlib.zeros((2, 2)))
3.4 numpy.matlib.ones()
numpy.matlib.ones()函数创建一个以 1 填充的矩阵。
import numpy.matlib
import numpy as np
print(np.matlib.ones((2, 2)))
3.5 numpy.matlib.eye()
numpy.matlib.eye() 函数返回一个矩阵,对角线元素为 1,其他位置为零。
numpy.matlib.eye(n, M,k, dtype)
- n: 返回矩阵的行数
- M: 返回矩阵的列数,默认为 n
- k: 对角线的索引
- dtype: 数据类型
import numpy.matlib
import numpy as np
print(np.matlib.eye(n=3, M=4, k=0, dtype=float))
3.6 numpy.matlib.identity()
numpy.matlib.identity() 函数返回给定大小的单位矩阵。单位矩阵是个方阵,从左上角到右下角的对角线(称为主对角线)上的元素均为 1,除此以外全都为 0。
import numpy.matlib
import numpy as np
# 大小为 5,类型位浮点型
print(np.matlib.identity(5, dtype=float))
3.7 numpy.matlib.rand()
numpy.matlib.rand() 函数创建一个给定大小的矩阵,数据是随机填充的。
import numpy.matlib
import numpy as np
print(np.matlib.rand(3, 3))
矩阵总是二维的,而 ndarray 是一个 n 维数组。 两个对象都是可互换的。
import numpy.matlib
import numpy as np
i = np.matrix('1,2;3,4')
print(i)
j = np.asarray(i)
print(j)
k = np.asmatrix(j)
print(k)