Numpy - 从一维数组中删除最后一个元素的最佳方法?
Posted
技术标签:
【中文标题】Numpy - 从一维数组中删除最后一个元素的最佳方法?【英文标题】:Numpy - the best way to remove the last element from 1 dimensional array? 【发布时间】:2016-01-01 04:20:24 【问题描述】:从 numpy 一维数组中删除最后一个元素的最有效方法是什么? (比如 pop for list)
【问题讨论】:
您想要原始数组的副本 还是仅需要与原始数组共享数据的另一个视图就足够了? 我想要一份副本 【参考方案1】:NumPy 数组具有固定大小,因此您无法就地删除元素。例如使用del
不起作用:
>>> import numpy as np
>>> arr = np.arange(5)
>>> del arr[-1]
ValueError: cannot delete array elements
请注意,索引-1
表示最后一个元素。这是因为 Python(和 NumPy)中的负索引是从末尾开始计算的,所以 -1
是最后一个,-2
是最后一个,-len
实际上是第一个元素。这仅供您参考,以防您不知道。
Python 列表是可变大小的,因此很容易添加或删除元素。
所以如果你想删除一个元素,你需要创建一个新的数组或视图。
创建新视图
您可以使用切片表示法创建一个包含除最后一个元素之外的所有元素的新视图:
>>> arr = np.arange(5)
>>> arr
array([0, 1, 2, 3, 4])
>>> arr[:-1] # all but the last element
array([0, 1, 2, 3])
>>> arr[:-2] # all but the last two elements
array([0, 1, 2])
>>> arr[1:] # all but the first element
array([1, 2, 3, 4])
>>> arr[1:-1] # all but the first and last element
array([1, 2, 3])
然而,一个视图与原始数组共享数据,所以如果一个被修改,另一个也会被修改:
>>> sub = arr[:-1]
>>> sub
array([0, 1, 2, 3])
>>> sub[0] = 100
>>> sub
array([100, 1, 2, 3])
>>> arr
array([100, 1, 2, 3, 4])
创建一个新数组
1。复制视图
如果你不喜欢这种内存共享,你必须创建一个新数组,在这种情况下,创建一个视图然后复制它可能是最简单的(例如使用数组的copy()
方法):
>>> arr = np.arange(5)
>>> arr
array([0, 1, 2, 3, 4])
>>> sub_arr = arr[:-1].copy()
>>> sub_arr
array([0, 1, 2, 3])
>>> sub_arr[0] = 100
>>> sub_arr
array([100, 1, 2, 3])
>>> arr
array([0, 1, 2, 3, 4])
2。使用整数数组索引 [docs]
但是,您也可以使用整数数组索引来删除最后一个元素并获得一个新数组。这个整数数组索引将始终(不是 100% 确定)创建副本而不是视图:
>>> arr = np.arange(5)
>>> arr
array([0, 1, 2, 3, 4])
>>> indices_to_keep = [0, 1, 2, 3]
>>> sub_arr = arr[indices_to_keep]
>>> sub_arr
array([0, 1, 2, 3])
>>> sub_arr[0] = 100
>>> sub_arr
array([100, 1, 2, 3])
>>> arr
array([0, 1, 2, 3, 4])
这个整数数组索引对于从数组中删除任意元素很有用(当你想要一个视图时这可能很棘手或不可能):
>>> arr = np.arange(5, 10)
>>> arr
array([5, 6, 7, 8, 9])
>>> arr[[0, 1, 3, 4]] # keep first, second, fourth and fifth element
array([5, 6, 8, 9])
如果您想要一个使用整数数组索引删除最后一个元素的通用函数:
def remove_last_element(arr):
return arr[np.arange(arr.size - 1)]
3。使用布尔数组索引 [docs]
还可以使用布尔索引,例如:
>>> arr = np.arange(5, 10)
>>> arr
array([5, 6, 7, 8, 9])
>>> keep = [True, True, True, True, False]
>>> arr[keep]
array([5, 6, 7, 8])
这也会创建一个副本!一个通用的方法可能如下所示:
def remove_last_element(arr):
if not arr.size:
raise IndexError('cannot remove last element of empty array')
keep = np.ones(arr.shape, dtype=bool)
keep[-1] = False
return arr[keep]
如果您想了解有关 NumPys 索引的更多信息,documentation on "Indexing" 非常好,涵盖了很多情况。
4。使用np.delete()
通常我不会推荐那些“看起来”像是在原地修改数组的 NumPy 函数(如 np.append
和 np.insert
),但确实返回副本,因为这些通常不必要地缓慢且具有误导性。您应该尽可能避免使用它们,这就是为什么这是我回答的最后一点。但是在这种情况下,它实际上是一个完美的选择,所以我不得不提一下:
>>> arr = np.arange(10, 20)
>>> arr
array([10, 11, 12, 13, 14, 15, 16, 17, 18, 19])
>>> np.delete(arr, -1)
array([10, 11, 12, 13, 14, 15, 16, 17, 18])
5.) 使用np.resize()
NumPy 有另一种方法,听起来像是在执行就地操作,但实际上返回的是一个新数组:
>>> arr = np.arange(5)
>>> arr
array([0, 1, 2, 3, 4])
>>> np.resize(arr, arr.size - 1)
array([0, 1, 2, 3])
为了删除最后一个元素,我只是提供了一个比以前小 1 的新形状,它有效地删除了最后一个元素。
就地修改数组
是的,我之前写过你不能就地修改数组。但我这么说是因为在大多数情况下这是不可能的,或者只能通过禁用一些(完全有用的)安全检查来实现。我不确定内部结构,但根据旧大小和新大小,这可能包括(仅限内部)复制操作,因此它可能比创建视图慢。
使用np.ndarray.resize()
如果该数组不与任何其他数组共享其内存,则可以就地调整该数组的大小:
>>> arr = np.arange(5, 10)
>>> arr.resize(4)
>>> arr
array([5, 6, 7, 8])
但是,如果它实际上也被另一个数组引用,则会抛出 ValueError
s:
>>> arr = np.arange(5)
>>> view = arr[1:]
>>> arr.resize(4)
ValueError: cannot resize an array that references or is referenced by another array in this way. Use the resize function
您可以通过设置refcheck=False
禁用该安全检查,但这不应该轻易做到,因为如果其他引用尝试访问已删除的元素,您会使自己容易受到分段错误和内存损坏的影响! 此refcheck
参数应视为专家专用选项!
总结
创建视图非常快,并且不会占用太多额外的内存,因此您应该尽可能多地使用视图。然而,根据用例,使用基本切片删除任意元素并不容易。虽然删除前 n 个元素和/或后 n 个元素或删除每个 x 元素(切片的 step 参数)很容易,但您可以这样做。
但在您删除一维数组的最后一个元素的情况下,我建议:
arr[:-1] # if you want a view
arr[:-1].copy() # if you want a new array
因为这些最清楚地表达了意图,具有 Python/NumPy 经验的每个人都会认识到这一点。
时间
基于这个answer的时序框架:
# Setup
import numpy as np
def view(arr):
return arr[:-1]
def array_copy_view(arr):
return arr[:-1].copy()
def array_int_index(arr):
return arr[np.arange(arr.size - 1)]
def array_bool_index(arr):
if not arr.size:
raise IndexError('cannot remove last element of empty array')
keep = np.ones(arr.shape, dtype=bool)
keep[-1] = False
return arr[keep]
def array_delete(arr):
return np.delete(arr, -1)
def array_resize(arr):
return np.resize(arr, arr.size - 1)
# Timing setup
timings = view: [],
array_copy_view: [], array_int_index: [], array_bool_index: [],
array_delete: [], array_resize: []
sizes = [2**i for i in range(1, 20, 2)]
# Timing
for size in sizes:
print(size)
func_input = np.random.random(size=size)
for func in timings:
print(func.__name__.ljust(20), ' ', end='')
res = %timeit -o func(func_input) # if you use IPython, otherwise use the "timeit" module
timings[func].append(res)
# Plotting
%matplotlib notebook
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure(1)
ax = plt.subplot(111)
for func in timings:
ax.plot(sizes,
[time.best for time in timings[func]],
label=func.__name__)
ax.set_xscale('log')
ax.set_yscale('log')
ax.set_xlabel('size')
ax.set_ylabel('time [seconds]')
ax.grid(which='both')
ax.legend()
plt.tight_layout()
我得到以下时序作为对数图来涵盖所有细节,更短的时间仍然意味着更快,但两个刻度之间的范围代表一个数量级而不是固定数量。如果您对特定值感兴趣,我将它们复制到此gist:
根据这些时间,这两种方法也是最快的。 (Python 3.6 和 NumPy 1.14.0)
【讨论】:
我读过。您是说这被认为是创建此类副本的有效方式? 我知道没有更快(如果你的意思是速度与效率)的方式。 有resize
方法和refcheck=False
,但使用它是一件危险的事情,除非您非常确定旧数据没有其他视图。【参考方案2】:
如果您想快速获取没有最后一个元素的数组(不删除显式),请使用切片:
array[:-1]
【讨论】:
【参考方案3】:要从一维 NumPy 数组中删除最后一个元素,请使用 numpy.delete 方法,如下所示:
import numpy as np
# Create a 1-dimensional NumPy array that holds 5 values
values = np.array([1, 2, 3, 4, 5])
# Remove the last element of the array using the numpy.delete method
values = np.delete(values, -1)
print(values)
输出: [1 2 3 4]
NumPy 数组的最后一个值 5 现在已被删除。
【讨论】:
这对最后的 X 元素(例如最后两个)有何作用? @elcortegano 你的问题的答案***.com/questions/36480086/…以上是关于Numpy - 从一维数组中删除最后一个元素的最佳方法?的主要内容,如果未能解决你的问题,请参考以下文章