我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...

本篇文章说的这几个函数,可以复制指定ndarrayshape信息。然后在这些指定形状的空位上,填充上相同的目标数据。有点像卡卡西的忍术复制功能。这些特殊的numpy功能函数就是:.zeros_like().ones_like()以及.full_like()

苏南大叔:如何根据已知ndarray获得形状相同且全为N的新ndarray? - numpy-n-like
如何根据已知ndarray获得形状相同且全为N的新ndarray?(图2-1)

苏南大叔的“程序如此灵动”技术博客,记录苏南大叔的代码经验总结。本文测试环境:win10python@3.11.0numpy@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)

苏南大叔:如何根据已知ndarray获得形状相同且全为N的新ndarray? - numpy-n-like-code
如何根据已知ndarray获得形状相同且全为N的新ndarray?(图2-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()判断里面,为啥要把0FalseNone还有空字符串,划为等号。

思考题目一

猜猜看,下面的代码输出是什么?

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精彩文章,请参考苏南大叔的博客:

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   python