2017-06-28 9 views
0

なぜスライス時にnumpy.arrayがPythonのリストとデフォルト配列と異なる動作をしますか?numpy.arrayスライシング動作

1)文を使用すると、b = a[1:3]文で新しいリストオブジェクトが作成され、bを変更してもaは変更されません。 array.arrayを使用して

>>> a = [1,2,3,4] 
>>> b = a[1:3] 
>>> print(b) 
[2, 3] 
>>> b[0] = -17 
>>> print(b) 
[-17, 3] 
>>> print(a) 
[1, 2, 3, 4] 

2):文b = a[1:3]は再び新しい配列オブジェクトを作成し、bを変更することはaは変更されません。

>>> import array 
>>> a = array.array('i', [1,2,3,4]) 
>>> b = a[1:3] 
>>> print(b) 
array('i', [2, 3]) 
>>> b[0] = -17 
>>> print(b) 
array('i', [-17, 3]) 
>>> print(a) 
array('i', [1, 2, 3, 4]) 

3)numpy.arrayを使用して:文b = a[1:3]は、元のリストの値を参照するようで、それは同様に修正しない修正!

>>> import numpy 
>>> a = numpy.array([1,2,3,4]) 
>>> b = a[1:3] 
>>> print(b) 
[2 3] 
>>> b[0] = -17 
>>> print(b) 
[-17 3] 
>>> print(a) 
[ 1 -17 3 4] 

質問:この現象はなぜnumpyにありますか?

答えて

1

NumPyは高性能のデータ収集であるため、 Pythonが新しいリストを作成するには、新しいリストを作成し、リスト内の各要素へのポインタをすべてインクリメントし、リストに項目を追加してからスライスを返す必要があります。 NumPy(おそらく)は、単に開始配列のオフセットをインクリメントし、配列の終わりを変更します。

numpyのは、このようなものとしてnumpyの配列(はい、これは非常に単純化しすぎている)の

だと思うのスライス:あなたがCがわからない場合

struct array 
{ 
    size_t type_size; 
    size_t length 
    void* start; 
}; 

は、その後、基本的にあることを意味します配列はメモリのアドレスと考えることができ、配列の先頭を指定し、格納する各タイプのサイズ、バッファの長さを格納します。整数配列の場合、type_sizeが4で、この例では長さが5です(20バイトのバッファ)。

データ全体をコピーするのではなく、スライスするとNumPyは単に開始点をインクリメントしてサイズを小さくすることができます。

array slice(array* array, size_t start, size_t end) 
{ 
    array arr = *array; 
    arr.start = (char*)arr.start + start; 
    arr.length = end - start; 
    return arr; 
} 

これは新しいリストにメモリを割り当て、次に割り当てるよりも大幅に安価であるリストにそれらのポインタ(及び増分、Pythonは参照カウントです)。

Pythonのスライス

ここでは簡略化され、Pythonの例です:これはどのくらいのより複雑

PyObject* slice(PyObject* list, size_t start, size_t end) 
{ 
    size_t length = end - start; 
    PyObject* out = PyList_New(length); 
    for (size_t i = start; size_t i < end; ++i) { 
     PyObject*item = PyList_GetItem(list, i); 
     PyList_Append(&out, i); 
    } 

    return out; 
} 

お知らせ?そして、より多くのものがフードの中に入ります。

合理

は、パフォーマンスを考える:numpyのは、元のスライスの振る舞いを持っている(データはメモリ上に連続しているので)、それは、メモリ内の新しいアドレスを占有しなければなりません。これはおそらくmemcpy()経由でデータをコピーすることを意味します。これは高価です:20,000 np.int32(〜80 KB)の配列があるとすれば、このデータをすべて新しい配列にコピーする必要があります。上のスライスの例では、私は24バイト分のメモリしかコピーしません(8バイトのsize_tとポインタを仮定します)。

+1

明確な説明に感謝します。 –

関連する問題