np.tile()函数,numpy.tile()函数的通俗的详细解释,python中有哪些函数可扩展数组数据?np.repeat/np.stack

因为网上对这个函数的很多教程,解释不够通俗易懂,或者说规律不够简单明白,所以我总结了一下,写成文分享给大家。

一、前后形状的变化有何规律?

函数的语法是np.tile(a, reps),a表示类数组元素(不仅可以是ndarray数组,也可以是列表、元组等),reps用来定义各个方向上的拷贝数量。reps参数可以记忆成repeat shape,也即拷贝性扩展的形状。

假设a的原形状为(2, 3),reps=(2)或reps=2(这两种表示方式等价)时,返回数组的形状计算规律如图中所示“右对齐+逐个元素相乘+缺则补一”。可得返回数组的形状为(2, 6)。

import numpy as np

arr = np.array([[1, 2, 3], [4, 5, 6]])
arr1 = np.tile(arr,2)
print(arr1)  # [[1 2 3 1 2 3],[4 5 6 4 5 6]]
print(arr1.shape)  # (2, 6)

二、如何一步步推导该函数求解的过程?

 从上面的形状变化规律我们大概也可以猜出,到底是怎么拷贝的?以下面的代码为例子进行更深度的阐释。

import numpy as np

arr = np.array([[1, 2, 3], [4, 5, 6]])
arr1 = np.tile(arr, (1, 2, 1, 3))
print(arr1)  
print('-------------------')
print(arr1.shape)  
# [[[[1 2 3 1 2 3 1 2 3]
#    [4 5 6 4 5 6 4 5 6]]
#   [[1 2 3 1 2 3 1 2 3]
#    [4 5 6 4 5 6 4 5 6]]]]
# -------------------
# (1, 2, 2, 9)

原数组的形状为(2, 3),而repr=(1, 2, 1, 3),从本文的第一部分得到的粗体字对应的规律,可以得到arr.shape的第2个参数3对应repr的第4个参数3,arr.shape的第1个参数2对应repr的第3个参数1。以此类推,可以得到返回数组的形状为(1, 2, 2, 9)。

假设repr的参数是从右向左开始起作用,那么第4个参数3的作用就是使arr在axis=1的维度上拷贝3份得到[[1, 2, 3, 1, 2, 3, 1, 2, 3], [4, 5, 6, 4, 5, 6, 4, 5, 6]]。

同理也可推得第3个参数1的作用就是使arr在axis=0的维度上拷贝1份(也即不变)得到[[1, 2, 3, 1, 2, 3, 1, 2, 3], [4, 5, 6, 4, 5, 6, 4, 5, 6]]。

接着进行!剩下的两个参数就有点难度了。

repr的第2个参数2的作用是什么呢?就是将第2步得到的数组拷贝两份,得到[[1, 2, 3, 1, 2, 3, 1, 2, 3], [4, 5, 6, 4, 5, 6, 4, 5, 6]],[[1, 2, 3, 1, 2, 3, 1, 2, 3], [4, 5, 6, 4, 5, 6, 4, 5, 6]];又因为这时候已经不是在原数组的形状维度上了,所以必须升维增加中括号,得到[[[1, 2, 3, 1, 2, 3, 1, 2, 3], [4, 5, 6, 4, 5, 6, 4, 5, 6]],[[1, 2, 3, 1, 2, 3, 1, 2, 3], [4, 5, 6, 4, 5, 6, 4, 5, 6]]]。

同第3步的道理,也可以推得第1个参数1的作用是将第3步得到的数组拷贝一份(结果不同于第3步的数组,因为不是一个维度),那么得到[[[1, 2, 3, 1, 2, 3, 1, 2, 3], [4, 5, 6, 4, 5, 6, 4, 5, 6]],[[1, 2, 3, 1, 2, 3, 1, 2, 3], [4, 5, 6, 4, 5, 6, 4, 5, 6]]],又因为第1个参数对应的维度是比第3步得到的数组所拥有的维度还要更高一维的,所以又要加1个中括号,得到[[[[1, 2, 3, 1, 2, 3, 1, 2, 3], [4, 5, 6, 4, 5, 6, 4, 5, 6]],[[1, 2, 3, 1, 2, 3, 1, 2, 3], [4, 5, 6, 4, 5, 6, 4, 5, 6]]]]。

检查一下程序运行结果,和我们用这个流程手算的相同,因此可以应用。

三、其他相关函数或操作符

3.1 利用*操作符实现对列表、字符串、元组等容器的拷贝性扩展

 下面是对列表用*操作符进行扩展,可见不管是一维列表还是多维列表,都是在axis=0的方向上进行拷贝性扩展。注意不能乘以一个浮点数,因为列表类型不支持numpy中的broadcast机制,所以没办法让一个标量和一个矢量进行点乘。

print([1,2,3] * 2)
print([[1,2],[4,5]] * 2)
# [1, 2, 3, 1, 2, 3]
# [[1, 2], [4, 5], [1, 2], [4, 5]]

下面是对字符串用*操作符进行扩展。

print('I love you, my honey.' * 2)
print('老婆,我错了' * 3)
# I love you, my honey.I love you, my honey.
# 老婆,我错了老婆,我错了老婆,我错了

 对于数组,如果直接乘以某一个整数,会生成一个对每一个元素都乘以这个整数的数组,因为它有broadcast机制,允许这样相乘,从而也导致无法直接通过*操作符来实现对数组的拷贝性扩展。

方法总比困难多,直接不行绕条道玩“间接”,也是可以的,通过list()函数转换成列表后再用不就行了!

3.2 利用numpy.repeat()、ndarray.repeat()实现拷贝性扩展

numpy.repeat(arr_like, repeats, axis)和ndarray.repeat(repeats, axis)用法相近;它们都无法改变原arr,都有经扩展后的返回值;唯一的不同是后者只能对ndarray使用,而前者对ndarray以外的列表、元组、字符串等也能使用。

# 1、单个数值拷贝扩展变成了一个数组
print(np.repeat(3, 4))  # array([3, 3, 3, 3])

# 2、如果axis=None,那么传入的a展成一维后再逐元素拷贝扩展,最后生成一维数组
x = np.array([[1,2],[3,4]])
print(np.repeat(x, 2))  # array([1, 1, 2, 2, 3, 3, 4, 4])

# 3、如果axis=1,则返回的数组维度不会改变
print(np.repeat(x, 3, axis=1))
#array([[1, 1, 1, 2, 2, 2],
#       [3, 3, 3, 4, 4, 4]])

# 4、如果repeats不等于一个整数,而是一个列表,列表中的每一个元素对应着axis指定方向上的元素,因此这种语法适用于对每一个单元拷贝份数不同的情况
print(np.repeat(x, [1, 2], axis=0))
#array([[1, 2],
#       [3, 4],
#       [3, 4]])

# 5、当repeats是一个列表时,如果len(repeats) != a.shape[axis],就会报错
print(np.repeat(x, [1, 2, 3], axis=0))  # ValueError: operands could not be broadcast together with shape (2,) (3,)

3.3 numpy.repeat()和numpy.tile()有什么异同点?

3.3.1共同点:

        都可以实现拷贝性扩展。

3.3.2差别点

(1)numpy.tile()可以实现升维,但是numpy.repeat()只能在现有维度框架上拷贝扩展。

(2)numpy.tile()是把指定维度上的数据整体进行拷贝,也即如果原来是[1,2,3],拷贝后应该是[1,2,3,1,2,3],而numpy.repeat()是逐个进行拷贝,如果原来是[1,2,3],拷贝后应该是[1,1,2,2,3,3]。

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
xiaoxingxing的头像xiaoxingxing管理团队
上一篇 2023年8月28日
下一篇 2023年8月28日

相关推荐