如何根据已知ndarray获得形状相同且全为N的新ndarray?
发布于 作者:苏南大叔 来源:程序如此灵动~本篇文章说的这几个函数,可以复制指定ndarray
的shape
信息。然后在这些指定形状的空位上,填充上相同的目标数据。有点像卡卡西的忍术复制功能。这些特殊的numpy
功能函数就是:.zeros_like()
、.ones_like()
以及.full_like()
。
苏南大叔的“程序如此灵动”技术博客,记录苏南大叔的代码经验总结。本文测试环境:win10
,python@3.11.0
,numpy@1.24.2
。
前文回顾
在以前的文章中,苏南大叔曾经描述过:如何快速获得全为N的ndarray,并且也是有参数可以指定shape
的。和本文的使用场景非常类似,只不过是本文的场景中,是去拓扑替换一个已有的ndarray
。
本文对比专用范例:
import numpy as np
s1 = np.zeros(3) # [0. 0. 0.]
s2 = np.ones((1, 3)) # [[1. 1. 1.]]
s3 = np.full((1, 3), 3.14) # [[3.14 3.14 3.14]]
print(s1, s1.ndim, s1.shape) # [0. 0. 0.] 1 (3,)
print(s2, s2.ndim, s3.shape) # [[1. 1. 1.]] 2 (1, 3)
print(s3, s2.ndim, s3.shape) # [[3.14 3.14 3.14]] 2 (1, 3)
输出:
[0. 0. 0.] 1 (3,)
[[1. 1. 1.]] 2 (1, 3)
[[3.14 3.14 3.14]] 2 (1, 3)
参考文章:
基础测试代码
函数名称是:.zeros_like()
、.ones_like()
以及.full_like()
。那么可以望文生义一下,然后再看看下面代码的运行结果。
import numpy as np
sn = np.full((1, 2), 6)
s0 = np.zeros_like(sn)
s1 = np.ones_like(sn)
s2 = np.full_like(sn, 7)
print(sn, sn.ndim, sn.shape)
print(s0, s0.ndim, s0.shape)
print(s1, s1.ndim, s1.shape)
print(s2, s2.ndim, s2.shape)
输出:
[[6 6]] 2 (1, 2)
[[0 0]] 2 (1, 2)
[[1 1]] 2 (1, 2)
[[7 7]] 2 (1, 2)
稍稍变换的测试代码
如果你以为轻松的通过望文生义了解了这几个_like
系列函数,那么,请看下面的代码:
import numpy as np
sn = np.full((1, 2), "苏")
s0 = np.zeros_like(sn)
s1 = np.ones_like(sn)
s2 = np.full_like(sn, 7)
s3 = np.full_like(sn, 789)
s4 = np.full_like(sn, "技术博客")
print(sn, sn.ndim, sn.shape)
print(s0, s0.ndim, s0.shape)
print(s1, s1.ndim, s1.shape)
print(s2, s2.ndim, s2.shape)
print(s3, s3.ndim, s3.shape)
print(s4, s4.ndim, s4.shape)
输出:
[['苏' '苏']] 2 (1, 2)
[['' '']] 2 (1, 2)
[['1' '1']] 2 (1, 2)
[['7' '7']] 2 (1, 2)
[['7' '7']] 2 (1, 2)
[['技' '技']] 2 (1, 2)
很显然,每个结果似乎都出乎意料,都不符合预期。
zeros_like
,变成了空字符串。ones_like
,变成了字符串1。full_like
,不但变成了字符串,而且每个元素的长度也被限制为1。
所以,结论是:表面上这些_like()
函数,复制的是目标变量的形状。实际上连目标变量的元素类型和长度也是完全复制的。这有些像苏南大叔以前写过的一篇文章:
表格总结
函数 | 说明 |
---|---|
np.zeros_like(target) | 原始元素是字符串的时候,返回的也是字符串,并且是空字符 |
np.ones_like(target) | 数字1或者字符串1 |
np.full_like(target,item) | item的类型及长度受原始元素的限制 |
这里可以想想看,在any()
或者all()
判断里面,为啥要把0
、False
、None
还有空字符串,划为等号。
思考题目一
猜猜看,下面的代码输出是什么?
import numpy as np
sn = np.full((1, 2), "苏")
s0 = np.full_like(sn, 0)
s1 = np.full_like(sn, False)
s2 = np.full_like(sn, None)
s3 = np.full_like(sn, '')
print(sn, sn.ndim, sn.shape)
print(s0, s0.ndim, s0.shape)
print(s1, s1.ndim, s1.shape)
print(s2, s2.ndim, s2.shape)
print(s3, s3.ndim, s3.shape)
输出:
[['苏' '苏']] 2 (1, 2)
[['0' '0']] 2 (1, 2)
[['F' 'F']] 2 (1, 2)
[['N' 'N']] 2 (1, 2)
[['' '']] 2 (1, 2)
是不是,依然非常出乎意料?
思考题目二
下面的代码,运行结果更加出乎意料:
import numpy as np
sn = np.full((1, 2), 9)
s0 = np.full_like(sn, 0)
s1 = np.full_like(sn, False)
# s2 = np.full_like(sn, None)
# TypeError: int() argument must be a string, a bytes-like object or a real number, not 'NoneType'
# s3 = np.full_like(sn, '')
# ValueError: invalid literal for int() with base 10: ''
print(sn, sn.ndim, sn.shape)
print(s0, s0.ndim, s0.shape)
print(s1, s1.ndim, s1.shape)
输出:
[[9 9]] 2 (1, 2)
[[0 0]] 2 (1, 2)
[[0 0]] 2 (1, 2)
- 填充
False
,这次变成了整数0,而是不是字符串F
。 - 填充
None
,直接报错,而是不是字符串N
。 - 填充``,直接报错。
可能更好的方案
对比np.xxx_like()
的不可控(原变量的dtype
影响新变量),苏南大叔还是比较喜欢结果可控的代码:
import numpy as np
sn = np.full((1, 2), "苏")
s0 = np.full(sn.shape, 0)
s1 = np.full(sn.shape, False)
s2 = np.full(sn.shape, None)
s3 = np.full(sn.shape, '')
print(sn, sn.ndim, sn.shape)
print(s0, s0.ndim, s0.shape)
print(s1, s1.ndim, s1.shape)
print(s2, s2.ndim, s2.shape)
print(s3, s3.ndim, s3.shape)
输出结果,完全都在意料之中!可控!靠谱!
[['苏' '苏']] 2 (1, 2)
[[0 0]] 2 (1, 2)
[[False False]] 2 (1, 2)
[[None None]] 2 (1, 2)
[['' '']] 2 (1, 2)
结束语
综合上述观点,np.xxx_like()
系列的函数,不光复制形状,还复制其它的特殊信息。这个过程是很不可控的。所以,如果可能,还是尽量使用本来最开始的那个np.zeros(shape)
、np.ones(shape)
、np.full(shape,item)
来完成这个需求吧。
更多python
精彩文章,请参考苏南大叔的博客:
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。