python作为数据科学中最受欢迎的编程语言,它的优势就在于对数据的转换,还可以灵活的处理多维数据。下面我们就来看看各种包里边的那些对数据维度操作的函数。
不放官网解释,以通俗语言来解释,先写再整理。要是各位看官想具体了解每个方法,可以逐个百度。
1.numpy
- reshape和resize
reshape和resize是numpy里最重要也是最常用的数组,区别就是resize是改变原来数组的维度,而reshape不改变。
import numpy as np
a = np.arange(15).reshape(3,5)
a.reshape(-1,3)
#输出
array([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11],
[12, 13, 14]])
a.resize(5,3)
#没有输出,直接改变a
注意resize不接受负数作为参数传递
- 广播机制
广播机制使维度不同的数组可以操作,这个是numpy中比较重要的特性,稍微不注意也可能出错,因此应该十分注意。
广播机制发生在两种情况下:一种是两个数组的维数不相等,但是它们的后缘维度的轴长相符,另外一种是有一方的长度为1。
举例子说明:
#第一种情况
import numpy as np
arr1 = np.array([[0, 0, 0],[1, 1, 1],[2, 2, 2], [3, 3, 3]])
arr2 = np.array([1, 2, 3])
arr1+arr2
#输出:
array([[1, 2, 3],
[2, 3, 4],
[3, 4, 5],
[4, 5, 6]])
#解释:arr1二维(4,3),arr2一维(3,)后缘维度相同,即倒数第一个维度相同都为3可以自动扩充,进行相加。
#第二种情况
arr1 = np.array([[0, 0, 0],[1, 1, 1],[2, 2, 2], [3, 3, 3]]) #arr1.shape = (4,3)
arr2 = np.array([[1],[2],[3],[4]])
arr1+arr2
#输出:
array([[1, 1, 1],
[3, 3, 3],
[5, 5, 5],
[7, 7, 7]])
#解释:arr1二维(4,3)arr2二维(4,1),满足维度相同,且有一个数组维度为1的条件,自动将arr2扩充为二维。
- expand_dims
这个方法的作用是按某一维度进行扩充,本质是增加数组的维度,通常用于计算时候的维度对其,在深度学习这种维度比较多的时候比较常用。
arr = np.array([[1,2,3],[3,4,5]])
arr_new = np.expand_dims(arr, axis=2)
arr_new.shape
#输出:
(2, 3, 1)
#解释,原来的(2,3)维度不变,变成(2,3,1)的维度形式,数组本身发生了改变
- tile
上边那个方法呢只能增加一个维度,但是你要想复制原本数组中的数据怎么办呢,这时候需要np.tile()函数,可以指定沿着x/y轴复制数据,默认是沿着x轴。
import numpy as np
a = np.arange(9).reshape(3,3)
np.tile(a,2)
#输出:
array([[0, 1, 2, 0, 1, 2],
[3, 4, 5, 3, 4, 5],
[6, 7, 8, 6, 7, 8]])
np.tile(a,(2,2))
#输出:
array([[0, 1, 2, 0, 1, 2],
[3, 4, 5, 3, 4, 5],
[6, 7, 8, 6, 7, 8],
[0, 1, 2, 0, 1, 2],
[3, 4, 5, 3, 4, 5],
[6, 7, 8, 6, 7, 8]])
- flattern和ravel
这两个方法作用就是将数组拉平,简单的说就是多维数组统统转化为1维的。flattern和ravel二者的功能都是一致的,区别在于flatten可以重新分配一个内存空间,ravel不够,也就是说ravel以后的新数组由于是原数组的展示方式,所以你更改他的值原数组的值也会跟着变,因此使用中一般使用flattern即可。
a=np.arange(12).reshape(3,4)
a.flatten()
a.ravel()
输出:
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
- transpose和T
这两个函数就是二维数组转置用的,没啥好解释的,就是行列互换,array/matrix包括pandas的dataframe都可以用这个函数。此处不举例子。 - tranpose
当超过三维的时候,transpose就是交换数组的维度。
transpose如果不加参数,就是倒叙转换,如(2,1,1)倒序就变为(1,1,2)。
当然你可以自己指定参数,用个括号括起来,如(1,0,2)。举例子来理解:
a = np.arange(12).reshape(2,2,3)
输出:
array([[[ 0, 1, 2],
[ 3, 4, 5]],
[[ 6, 7, 8],
[ 9, 10, 11]]])
a.transpose((2,1,0))
输出:
array([[[ 0, 6],
[ 3, 9]],
[[ 1, 7],
[ 4, 10]],
[[ 2, 8],
[ 5, 11]]])
解释:
因为维度要对齐,其实就是现有维度的两两组合。
- squeeze
这个方法是减少数组的维度,减少的那个维度是1维的,有没有都一样,所以可以去掉。
a = np.arange(10).reshape(1,1,10)
print(a.shape)
输出:(1, 1, 10)
print(a.squeeze().shape)
输出:(10,)
- meshgrid
这个方法比较高级,一般用在画图里边。给你两个数组,然后生成一个网格点的坐标体系,这个笔者就在matplotlib的气泡图里用过,简直不要太方便。
x = np.array([[0, 1, 2, 3],
[0, 1, 2, 3],
[0, 1, 2, 3],
[0, 1, 2, 3]])
y = np.array([[0, 0, 0, 0],
[1, 1, 1, 1],
[2, 2, 2, 2],
[3, 3, 3, 3]])
X,Y = np.meshgrid(x,y)
print(X)
print(Y)
#输出太长了,自己打出来看吧
有了numpy的维度处理基础知识,其他任何框架的维度转换基本就是这些方法的转换,有时候会有一些新方法出现,但大抵的思路都是差不多的。
2.pytorch
pytorch的维度变化和numpy有区别,需要注意一下。
- view
view相当于reshape,用法和reshape完全一样
import torch as t
t1 = t.Tensor([[1,2],[3,4]])
t1.view(4,)
输出:
tensor([1., 2., 3., 4.])
- gather
这个函数作用就是根据索引提取数据,什么作用呢,多分类问题中可以把maxindex传进去提取正确的结果。
使用方法:torch.gather(input, dim, index, out=None)
input就是你输入的数组,dim是要在哪一维度上操作,index是要取数据的下标,跟dim取值有关,如果是1就按列取数,如果是0就按行取数。
import torch as t
t1 = t.Tensor([[1,2],[3,4]])
print(t1)
t.gather(t1, dim=0, index=t.LongTensor([[0,1],[0,1]]))
#输出
tensor([[1., 4.],
[1., 4.]])
#解释
#dim=0横向,比如第一个[0,1]中的就是第2行第2列
注意:index需要是pytorch中的LongTensor数据类型,Tensor都不可以。
- squeeze和unsqueeze
这两个方法和numpy中的一样,squeeze就是压缩第n维为1的数。注意只有在使用squeeze时,只有维度为1才会被压缩,否则不起作用,但是你如果不加参数,所有维度为1的维度都会被压缩。
import torch
a = torch.Tensor([[[1, 2, 3], [4, 5, 6]]])
b = a.view(-1,3,2)
print(b.size())
#输出:torch.Size([1, 3, 2])
b1 = b.squeeze(1)
print(b1.size())
#输出:torch.Size([1, 3, 2])
b2 = b.squeeze(0)
print(b2.size())
#输出:torch.Size([3, 2])
b3 = b.squeeze()
print(b3.size())
#输出:torch.Size([3, 2])
unsqueeze就是在指定维度增加一个维度1。
b.unsqueeze(2).size()
#输出:torch.Size([1, 3, 1, 2])
- transpose和permute
这两个方法都是维度转换用的,本质是一样的,就是交换一个tensor的两个或几个指定的维度。但是transpose是通过torch.transpose调用,而permute要通过直接通过permute调用。permute比transpose好在可以同时对多个维度进行交换,具体用哪个看你的习惯吧。
b.size()
#输出:torch.Size([1, 3, 2])
torch.transpose(b,2,1).size()
#输出:torch.Size([1, 2, 3])
b.size()
#输出:torch.Size([1, 3, 2])
b.permute(2, 0, 1).size()
#输出:torch.Size([2, 1, 3])
- expand和repeate
这两个函数和numpy的tile差不多吧,都是扩张维度。
首先是expand,他有个特性,只有维度为1的那一维才可以扩张,维度不为1的那一维传参的时候只能写的和原来的维度一样。他返回的是一个视图,不占内存的。
然后是repeate,repeate生成新张量,占内存的。而且这个方法不再指定只有维度为1的可以扩张,所有维度都可以扩张。
x = torch.Tensor([[1], [2], [3]])
x.size()
#输出:torch.Size([3, 1])
x.expand(3,4).size()
#输出:torch.Size([3, 4])
x.repeat(3,4).size()
#输出: torch.Size([9, 4])
- 转置
转置只适用于二维,直接tensor.t就好了,不举例子了。
pytorch基本就这些了,如果遇到新的,我会再补充。
3.tensorflow
tensorflow的维度变化函数和numpy基本是一样的,reshape,transpose,squeeze函数是经常使用的方法,只不过前缀变为tf,处理的数据类型也变为张量了。
- shape和ndim
返回张量的维度,这个没有啥好说的,其中shape返回详细的维度,ndim返回张量有几个维度。值得注意的是在tensorflow里shape和ndim是tensor的属性,不是方法,不用加括号。
import tensorflow as tf
tf.__version__
a = tf.convert_to_tensor([[1,1],[1,1]])
a.shape
#输出:TensorShape([2, 2])
a.ndim
#输出:2
- reshape
这个是老生常谈的一个方法了,就是改变数组的维度,可以自动计算支持-1作为默认维度。
a = tf.random.normal([4, 28, 28, 3])
tf.reshape(a, [4, 28*28, 3]).shape
tf.reshape(a, [4, -1, 3]).shape
#输出都是TensorShape([4, 784, 3])
- transpose
眼熟不,这个transpose简直哪个包里都有,tensorflow里边有个perm参数,可以随意交换位置,这个和torch的permute差不多。当然了tf2.0里边也有相应的permute函数,tf.keras.backend.permute_dimensions( x,pattern),pattern和perm一样,我就不举例子了。
a = tf.random.normal([4, 28, 28, 3])
a.shape # TensorShape([4, 28, 28, 3])
tf.transpose(a, perm=[0,3,1,2]).shape
#输出:TensorShape([4, 3, 28, 28])
- expand_dims
这个是tensorflow里的扩展维度方法,可以设置沿着指定的轴扩展。
a = tf.ones([4,35,10])
a.shape
# 输出:TensorShape([4, 35, 10])
tf.expand_dims(a, axis=0).shape
#输出:TensorShape([1, 4, 35, 10])
- squeeze
squeeze是压缩维度为1的维度,和numpy,pytorch相同的,但是tf里没有unsqueeze这个方法,大家要注意。
tf.expand_dims(a, axis=0).shape
a = tf.ones([1,4,35,10])
tf.squeeze(a).shape
#输出:TensorShape([4, 35, 10])
- broadcast_to
这个函数的作用很显然了,就是手动扩展维度,类似于numpy里的广播机制,作用呢就是使两个tensor可以矩阵乘。
a = tf.ones([4,5,6])
b = tf.fill([6,4], 2.)
bb = tf.broadcast_to(b, [4,6,4])
(a@bb).shape
#输出:TensorShape([4, 5, 4])
- concat
tensorflow里拼接两个张量的方法,可以设置沿指定轴拼接。
t1 = tf.convert_to_tensor([[1, 2, 3], [4, 5, 6]])
t2 = tf.convert_to_tensor([[7, 8, 9], [10, 11, 12]])
tf.concat([t1, t2], 0) # [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
tf.concat([t1, t2], 1)
#输出:
tensorflow里对维度处理的函数比较少,要是想扩展功能咋办,你可以先转化为numpy的array处理完再转换为tensor就好了。
4.keras
- Reshape
同reshape,在keras.layers库里,是一个层。
注意啊tensorflow升级为2.0以后,以前的导入方法不管用了,要在前边加一个tensorflow。
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Reshape
model = Sequential()
# 改变数据形状为3行4列
# 模型的第1层必须指定输入的维度,注意不需要指定batch的大小
model.add(Reshape((3, 4), input_shape=(12, )))
# 改变数据形状为6行2列
model.add(Reshape((6, 2)))
# 改变数据形状为 第2,3维为(2,2),根据数据元素数量自动确定第1维大小为3
model.add(Reshape((-1, 2, 2)))
# 改变数据形状为 第1,2维为(2,2),根据数据元素数量自动确定第3维大小为3
model.add(Reshape((2, 2, -1)))
model.summary()
#输出:
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
reshape (Reshape) (None, 3, 4) 0
_________________________________________________________________
reshape_1 (Reshape) (None, 6, 2) 0
_________________________________________________________________
reshape_2 (Reshape) (None, None, 2, 2) 0
_________________________________________________________________
reshape_3 (Reshape) (None, 2, 2, None) 0
=================================================================
Total params: 0
Trainable params: 0
Non-trainable params: 0
- Permute
毫无疑问,Permute在keras里也变成层了,但功能是不变的。
model = Sequential()
modle.add(Permute((2,1),input_shape=(10,64)))
#现在变为(64,10)了