numpy中的flatten和ravel函数有什么区别?

435
import numpy as np
y = np.array(((1,2,3),(4,5,6),(7,8,9)))
OUTPUT:
print(y.flatten())
[1   2   3   4   5   6   7   8   9]
print(y.ravel())
[1   2   3   4   5   6   7   8   9]

这两个函数返回相同的列表。 那么为什么需要两个不同的函数执行同样的工作呢。


25
拉维尔通常返回现有数组的视图(有时返回副本)。展平操作会返回一个新的数组。 - Alex
5
可能是与Numpy中的flatten和ravel有什么区别?重复的内容。 - finnw
1
这里有一个实际演示微妙差别的例子,与numpy中的flatten和ravel函数有关。 - prosti
有人能举个例子,什么情况下最好将数组压平,什么情况下最好将其展开? - Aleksandar
谢谢您的提问,我也有同样的问题。 - LoneCodeRanger
3个回答

538

当前API的情况是:

  • flatten 总是返回一个副本。
  • ravel 尽可能返回原始数组的视图。这在打印输出中不可见,但如果您修改ravel返回的数组,则可能会修改原始数组中的条目。如果您修改从flatten返回的数组中的条目,则永远不会发生这种情况。由于没有复制内存,因此ravel通常会更快,但您必须更加谨慎地修改它返回的数组。
  • reshape((-1,)) 如果数组的步幅允许,则获取视图,即使这意味着您不总是获得一个连续的数组。

67
为什么NumPy的开发者没有坚持只使用一个函数,并带有参数copy=[True,False],您有任何想法吗? - Franck Dernoncourt
64
向后兼容性保证有时会导致出现奇怪的情况,例如:numpy开发人员最近(在1.10版中)添加了一个先前隐含的保证,即ravel将返回一个连续的数组(这是编写C扩展非常重要的属性),因此现在API是a.flatten()以确保获得副本,a.ravel()避免大多数复制但仍保证返回的数组是连续的,以及a.reshape((-1,))真正获取视图只要数组的步幅允许即使这意味着您并不总是获得连续的数组。 - IanH
7
@Hossein已经解释了,ravel保证返回一个连续的数组,因此不能保证它返回的是视图;reshape总是返回一个视图,因此不能保证它返回的是一个连续的数组。 - iled
5
@Hossein 那将是一个全新的问题。简单来说,读写连续的内存空间速度更快。这里在SO上有几个相关的问题和答案(一个好例子在这里),如果你有任何进一步的问题,请随时提出一个新的问题。 - iled
17
为什么它被称为“ravel”?这个名字背后的意念是什么? - off99555
显示剩余7条评论

87

这里所解释的关键区别在于:

  • flatten是ndarray对象的一个方法,因此只能应用于真正的numpy数组。

  • ravel是一个库级函数,因此可以应用于任何可以成功解析的对象。

例如,ravel将在ndarrays列表上工作,而flatten不可用于该类型的对象。

@IanH在他的回答中还指出了重要的内存处理差异。


7
谢谢您提供有关ravel()在“ndarray”列表上运行的信息。 - WestCoastProjects
1
不仅仅是数组列表,还有列表的列表 :) - timtody

31

以下是函数的正确命名空间:

这两个函数都返回指向新内存结构的扁平化的1D数组。

import numpy
a = numpy.array([[1,2],[3,4]])

r = numpy.ravel(a)
f = numpy.ndarray.flatten(a)  

print(id(a))
print(id(r))
print(id(f))

print(r)
print(f)

print("\nbase r:", r.base)
print("\nbase f:", f.base)

---returns---
140541099429760
140541099471056
140541099473216

[1 2 3 4]
[1 2 3 4]

base r: [[1 2]
 [3 4]]

base f: None
在上面的例子中:
  • 结果的内存位置不同
  • 结果看起来相同
  • flatten会返回一个副本
  • ravel会返回一个视图
如何检查某个对象是否是副本? 使用ndarray.base属性。如果它是一个视图,基础将是原始数组;如果它是副本,则基础将为None
检查a2是否是a1的副本。
import numpy
a1 = numpy.array([[1,2],[3,4]])
a2 = a1.copy()
id(a2.base), id(a1.base)

输出:

(140735713795296, 140735713795296)

1
id(a1.base) should be the same as id(a2.base) - prosti
1
a1.base 和 a2.base 都是 None,这就是为什么基础的 id 可能会相同。但由于复制,id(a1) 和 id(a2) 是不同的。如果 a2 是 a1 的切片,则基础将不同,在这种情况下,“a1.base 为 None”但“a2.base 为 a1”。 - P2000

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接