web-dev-qa-db-ja.com

Numpy-1次元配列から最後の要素を削除する最良の方法は?

Numpy 1次元配列から最後の要素を削除する最も効率的な方法は何ですか? (リストのポップなど)

15
Meni

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のインデックス作成に関する詳細情報が必要な場合は、 "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小さい新しい図形を提供しました。これにより、最後の要素が事実上削除されます。

配列をインプレースで変更する

はい、以前に書いたことがありますが、その場で配列を変更することはできません。しかし、ほとんどの場合、不可能であるか、またはいくつかの(完全に有用な)安全性チェックを無効にすることによってのみ可能だからです。私は内部についてはわかりませんが、古いサイズと新しいサイズによっては、これに(内部のみの)コピー操作が含まれるため、mightが遅くなる可能性がありますビューを作成するよりも。

np.ndarray.resize() の使用

配列が他の配列とメモリを共有していない場合は、配列を適切なサイズに変更できます。

_>>> arr = np.arange(5, 10)
>>> arr.resize(4)
>>> arr
array([5, 6, 7, 8])
_

ただし、別の配列によって実際に参照されている場合は、ValueErrorsがスローされます。

_>>> 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要素(スライスのステップ引数)を削除するのは簡単ですが、これでできることはこれだけです。

しかし、1次元配列の最後の要素を削除する場合は、以下をお勧めします。

_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()
_

すべての詳細をカバーするためのログ-ログプロットとして次のタイミングを取得します。時間を短くすると依然として高速になりますが、2つのティック間の範囲は固定量ではなく1桁を表します。特定の値に興味がある場合は、これらにコピーしました Gist

enter image description here

これらのタイミングによると、これらの2つのアプローチは最速です。 (Python 3.6およびNumPy 1.14.0)

28
MSeifert

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は削除されました。

1