python tile函数_Python-Numpy模块tile函数[源码解析]

tile

n. 瓷砖,瓦片

vt. 铺以瓦;铺以瓷砖

1.前言函数格式tile(A,reps)

A和reps都是array_like类型

A参数几乎所有类型都可以:array, list, tuple, dict, matrix这些序列化类型以及Python中基本数据类型int,float,string,bool类型。

reps的参数可以是tuple,list, dict, array, int, bool.但不可以是float, string, matrix(多维度的ndarray数组)类型。

tile函数的功能是重复某个数组。比如tile(A,reps),他的作用就是把A重复reps次,所以也可以理解为什么参数reps不能是float或者string,而matrix为什么不行,后面就会看到为什么matrix不能作为参数。

其实如果可以使用Python广播的话没有必要使用tile函数。下面就是通过源码来简单分析分析他的运作,以及如何简单的使用他。

2.tile源码分析

我们这里通过原码tile源码来分析tile函数,为什么能实现A进行复制的功能。

def tile(A, reps):

try:

tup = tuple(reps)

except TypeError:

tup = (reps,)

d = len(tup)

if all(x == 1 for x in tup) and isinstance(A, _nx.ndarray):

# Fixes the problem that the function does not make a copy if A is a

# numpy array and the repetitions are 1 in all dimensions

return _nx.array(A, copy=True, subok=True, ndmin=d)

else:

# Note that no copy of zero-sized arrays is made. However since they

# have no data there is no risk of an inadvertent overwrite.

c = _nx.array(A, copy=False, subok=True, ndmin=d)

if (d < c.ndim):

tup = (1,)*(c.ndim-d) + tup

shape_out = tuple(s*t for s, t in zip(c.shape, tup))

n = c.size

if n > 0:

for dim_in, nrep in zip(c.shape, tup):

if nrep != 1:

c = c.reshape(-1, n).repeat(nrep, 0)

n //= dim_in

return c.reshape(shape_out)1.首先一个函数体头部

#定义了函数体的名称tile

#参数:

#------A:进行操作的对象

#------reps:重复的次数

def tile(A, reps):2.对reps进行处理

try:

tup = tuple(reps)

except TypeError:

tup = (reps,)

这个地方就很好理解了,他是把传进来的reps转换成Python的元组类型。我们一个一个类型的来看。

import numpy as np

print("list to tuple:",tuple([1,2]))

print("dict to tuple:",tuple({'A':1,'B':2}))

print("ndarray to tuple:",tuple(np.array([1,2])))

# print("int to tuple:",tuple(1))#error抛出TypeError异常执行tup = (reps,)

# print("bool to tuple:",tuple(True))##error抛出TypeError异常执行tup = (reps,)

#不可以作为reps参数的类型

# print("float to tuple:",tuple(1.2))#error抛出TypeError异常执行tup = (reps,)

print("string to tuple:",tuple('12'))

print("matrix to tuple:",tuple(np.array([[1,2],[3,4]])))Result

可以看出不可以作为reps参数的类型在这里也并执行通过,所以这个地方并不是不能使用一些类型作为reps的根源。reps可以为的参数类型reps不可以为的参数类型

其实使用tuple函数转换成元组失败是因为tuple函数他需要的参数是一个可迭代的类型,如果不是的话就会抛出Typeerror的异常,抛出异常在源码中就会把值直接放入元组的第一个位置,这里的(reps,)也是一个元组类型。其实抛出异常对应的无非就是标量值,像int,True以及不能作为参数的float。3.获取元组的长度

#这个其实很好理解

#要注意len((reps,))就是reps的元素个数

d = len(tup)

print(len((True,)))#1

对应上面的分析,这里无非也就是两种情况:像int,True这样的标量值,他们被转换成的元素是(value,),这种形式的,所以获取长度肯定得到的是1;

剩下的一些序列化的参数,他们的len长度>=1,不确定,这就需要看这些参数中有多少个元素。4.判断语句

if all(x == 1 for x in tup) and isinstance(A, _nx.ndarray):

# Fixes the problem that the function does not make a copy if A is a

# numpy array and the repetitions are 1 in all dimensions

return _nx.array(A, copy=True, subok=True, ndmin=d)

else:

# Note that no copy of zero-sized arrays is made. However since they

# have no data there is no risk of an inadvertent overwrite.

c = _nx.array(A, copy=False, subok=True, ndmin=d)

这里有几个函数需要注意:all()函数。all() 函数用于判断给定的可迭代参数 iterable 中的所有元素是否有 0、''、False 或者 iterable 为空。如果没有这些返回 True,否则返回 False。

_nx.array(A, copy=False, subok=True, ndmin=d)函数,简单来说就是创建一个ndarray数组。copy参数:bool,可选。如果为True(默认值),那么对象被复制。否则,副本将仅当__array__返回副本。

subok参数:bool,可选。如果为True,则子类将被传递,通过,否则返回数组将被迫成为一个基类数组(默认)。

ndmin:int,可选。指定结果数组应有尺寸的最小数目。

isinstance(A, _nx.ndarray)函数。isinstance(object, classinfo)函数就是判断object对象类型是否是classinfo类型相同,相同则返回True,否则返回False。

#可以把这个先看成是import numpy as np

import numpy.core.numeric as _nx

print(_nx.ndarray)

#从输出可以看出,isinstance(A, _nx.ndarray)判断A是不是ndarray类型的数据

''''''

这里的all(x == 1 for x in tup)就是为什么matrix不能使用的地方。

import numpy as np

reps = np.array([[1,2],[3,4]])

print(all((x == 1 for x in reps)))

'''Traceback (most recent call last):File "G:/Python源码/numpy_test/numpy_test.py", line 1763, in print(all((x == 1 for x in reps)))ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()'''

然后我们来分析这个判断语句把那些情况筛选出去了:all(x == 1 for x in tup) and isinstance(A, _nx.ndarray)。通过上面的分析,我们可以知道,如果传入的tup中的值都是1并且(and)我们的A是ndarray数组类型的话,直接return _nx.array(A, copy=True, subok=True, ndmin=d)。直接返回的是A这个ndarray数组,当然如果维度小于d的话,会自动增加d维度。

如果不是上面那种情况的话。执行c = _nx.array(A, copy=False, subok=True, ndmin=d)。ndmin参数说的就是c的ndim最少就是d,当然这里很自然的就会有三种情况:c.ndim == d(也就是ndmin)

c.ndim > d[这个时候会继续执行5中的判断语句]

c.ndim < d(这种情况不可能发生,因为指定了ndmin = d)[所以不考虑]5.判断语句

if (d < c.ndim):

tup = (1,)*(c.ndim-d) + tup

这是什么样的情况呢?因为c.ndim也就是c的维度比d也就是元组中的元素个数不匹配,或者说是要进行重复的A的维度和reps重复次数不匹配,这样可想而知是不可以的,所以加入了一个进行处理的代码。

import numpy as np

import numpy.core.numeric as _nx

A = np.array([[1,2],[3,4]])

tup = (2,)

d = len(tup)

c = _nx.array(A, copy=False, subok=True, ndmin=d)

if (d < c.ndim):

tup = (1,)*(c.ndim-d) + tup

print(d)#1

print(c.ndim)#2

print(tup)#(1, 2)result6.形成最终的shape_out

shape_out = tuple(s*t for s, t in zip(c.shape, tup))

因为我们在第五步的时候,已经将我们的c的ndim与我们的tup的维度匹配。我们把shape属性和我们需要进行重复次数的tup中对应的元素相乘形成新的数组,这个结果作为我们最终的shape。7.tile函数的核心代码-对c的维度进行重复

n = c.size

if n > 0:

for dim_in, nrep in zip(c.shape, tup):

#nrep == 1就不需要进行复制了

if nrep != 1:

c = c.reshape(-1, n).repeat(nrep, 0)

n //= dim_in

第六步实现了对最终输出结果的shape的形成。之后就是很重要的如何去进行复制呢?这里的c.size得到的结果是c中元素的个数:n = 0直接输出结果

n > 0执行复制元素的代码。

for dim_in, nrep in zip(c.shape, tup):

#nrep == 1就不需要进行复制了

if nrep != 1:

c = c.reshape(-1, n).repeat(nrep, 0)

n //= dim_in

从上面的分析我们也可以知道,到这一步,我们的shape和tup中的元素个数是相互匹配的。如果tup中的值为1,则直接跳出,也就是不进行任何的复制操作。

如果tup中的值不为1,则执行c = c.reshape(-1, n).repeat(nrep, 0);n //= dim_in这两句代码。这里的 c.reshape(-1,n)直接把c中的全部元素变成是一个一行n列的一个数组。

repeat(nrep, 0)函数会把c.reshape(-1,n)形成的哪个一行n列的数组复制nrep次,形成一个nrep行n列的数组。并且这里的0是参数axis的值,也就是行的方向进行重复。

n //= dim_in执行。" / "就一定表示 浮点数除法,返回浮点结果;" // "表示整数除法。8.返回结果

return c.reshape(shape_out)

3.示例代码

分析完了代码,看看怎么去使用。1.A = array([1,2]);reps = (1,1)。1或者True(看成是1)...也是同样的步骤

从源码的分析上看:首先一个函数体头部

对reps进行处理tup = (1,1)

获取元组的长度d = 2

判断语句if all(x == 1 for x in tup) and isinstance(A, _nx.ndarray)return _nx.array(A, copy=True, subok=True, ndmin=d)。这里因为A的ndim为1,d = 2,所以会把A扩展成ndim为2的数组。

import numpy as np

A = np.array([1,2])

reps = (1,1)

print(np.tile(A,reps))

'''[[1 2]]'''2.A = array([[1,2],[3,4]]);reps = (1,2)。

从源码的分析上看:首先一个函数体头部

对reps进行处理tup = (1,2)

获取元组的长度d == 2

判断语句if all(x == 1 for x in tup) and isinstance(A, _nx.ndarray),因为tup中含有非1的元素,所以all中返回False,执行else语句。

c = _nx.array(A, copy=False, subok=True, ndmin=d),此时的c和A的shape相同。

判断语句if (d < c.ndim),因为d == c.ndim所以不执行。

形成最终的shape_outshape_out = tuple(s*t for s, t in zip(c.shape, tup)),这里会返回shape_out是(2,4)的shape。

tile函数的核心代码-对c的维度进行重复n = c.size = 4

for dim_in, nrep in zip(c.shape, tup)。因为c.shape是(2,2),tup是(1,2),执行第一次的时候,n //= dim_in,4 //= 2 ==2。

所以当执行第二次循环的时候,nrep == 2!1,所以执行 c = c.reshape(-1, n).repeat(nrep, 0)c.reshap(-1,2)的结果是[[1 2][3 4]]。

[[1 2][3 4]].repeat(2,0)的执行结果是[[1 2] [1 2] [3 4] [3 4]]

返回结果return c.reshape(shape_out),也就是array([[1,2,3,4],[1,2,3,4]]).reshape((2,4)),执行的结果是[[1 2 1 2] [3 4 3 4]]

import numpy as np

A = np.array([[1,2],[3,4]])

reps = (1,2)

print(np.tile(A,reps))

'''

[[1 2 1 2]

[3 4 3 4]]

'''

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值