从numpy 1维数组中删除最后一个元素的最有效方法是什么? (比如pop for list)
答案 0 :(得分:25)
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])
如果您不喜欢这种内存共享,则必须创建一个新数组,在这种情况下,最简单的方法是创建一个视图然后复制(例如使用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])
但是,您也可以使用整数数组索引来删除最后一个元素并获取一个新数组。此整数数组索引将始终(不是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)]
还可以使用布尔索引,例如:
>>> 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"非常好并涵盖了很多案例。
np.delete()
通常情况下,我不会推荐NumPy功能,而且#34;似乎"就像他们就地修改数组一样(比如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])
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
:
>>> 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元素(切片的步骤参数),但这就是你可以用它做的全部。
但是在删除一维数组的最后一个元素的情况下,我建议:
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)
答案 1 :(得分:2)
如果要快速获取没有最后一个元素的数组(不删除显式元素),请使用切片:
array[:-1]
答案 2 :(得分:1)
要从一维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 。