Movatterモバイル変換


[0]ホーム

URL:


SciPy

NumPy internals

Internal organization of numpy arrays

It helps to understand a bit about how numpy arrays are handled under the covers to help understand numpy better. This section will not go into great detail. Those wishing to understand the full details are referred to Travis Oliphant’s book “Guide to NumPy”.

NumPy arrays consist of two major components, the raw array data (from now on,referred to as the data buffer), and the information about the raw array data.The data buffer is typically what people think of as arrays in C or Fortran,a contiguous (and fixed) block of memory containing fixed sized data items.NumPy also contains a significant set of data that describes how to interpretthe data in the data buffer. This extra information contains (among other things):

  1. The basic data element’s size in bytes
  2. The start of the data within the data buffer (an offset relative to thebeginning of the data buffer).
  3. The number of dimensions and the size of each dimension
  4. The separation between elements for each dimension (the ‘stride’). Thisdoes not have to be a multiple of the element size
  5. The byte order of the data (which may not be the native byte order)
  6. Whether the buffer is read-only
  7. Information (via the dtype object) about the interpretation of the basicdata element. The basic data element may be as simple as a int or a float,or it may be a compound object (e.g., struct-like), a fixed character field,or Python object pointers.
  8. Whether the array is to interpreted as C-order or Fortran-order.

This arrangement allow for very flexible use of arrays. One thing that it allowsis simple changes of the metadata to change the interpretation of the array buffer.Changing the byteorder of the array is a simple change involving no rearrangementof the data. The shape of the array can be changed very easily without changinganything in the data buffer or any data copying at all

Among other things that are made possible is one can create a new array metadataobject that uses the same data bufferto create a new view of that data buffer that has a different interpretationof the buffer (e.g., different shape, offset, byte order, strides, etc) butshares the same data bytes. Many operations in numpy do just this such asslices. Other operations, such as transpose, don’t move data elementsaround in the array, but rather change the information about the shape and strides so that the indexing of the array changes, but the data in the doesn’t move.

Typically these new versions of the array metadata but the same data buffer arenew ‘views’ into the data buffer. There is a different ndarray object, but ituses the same data buffer. This is why it is necessary to force copies throughuse of the .copy() method if one really wants to make a new and independentcopy of the data buffer.

New views into arrays mean the object reference counts for the data bufferincrease. Simply doing away with the original array object will not remove thedata buffer if other views of it still exist.

Multidimensional Array Indexing Order Issues

What is the right way to indexmulti-dimensional arrays? Before you jump to conclusions about the one andtrue way to index multi-dimensional arrays, it pays to understand why this isa confusing issue. This section will try to explain in detail how numpyindexing works and why we adopt the convention we do for images, and when itmay be appropriate to adopt other conventions.

The first thing to understand isthat there are two conflicting conventions for indexing 2-dimensional arrays.Matrix notation uses the first index to indicate which row is being selected andthe second index to indicate which column is selected. This is opposite thegeometrically oriented-convention for images where people generally think thefirst index represents x position (i.e., column) and the second represents yposition (i.e., row). This alone is the source of much confusion;matrix-oriented users and image-oriented users expect two different things withregard to indexing.

The second issue to understand is how indices correspondto the order the array is stored in memory. In Fortran the first index is themost rapidly varying index when moving through the elements of a twodimensional array as it is stored in memory. If you adopt the matrixconvention for indexing, then this means the matrix is stored one column at atime (since the first index moves to the next row as it changes). Thus Fortranis considered a Column-major language. C has just the opposite convention. InC, the last index changes most rapidly as one moves through the array asstored in memory. Thus C is a Row-major language. The matrix is stored byrows. Note that in both cases it presumes that the matrix convention forindexing is being used, i.e., for both Fortran and C, the first index is therow. Note this convention implies that the indexing convention is invariantand that the data order changes to keep that so.

But that’s not the only wayto look at it. Suppose one has large two-dimensional arrays (images ormatrices) stored in data files. Suppose the data are stored by rows rather thanby columns. If we are to preserve our index convention (whether matrix orimage) that means that depending on the language we use, we may be forced toreorder the data if it is read into memory to preserve our indexingconvention. For example if we read row-ordered data into memory withoutreordering, it will match the matrix indexing convention for C, but not forFortran. Conversely, it will match the image indexing convention for Fortran,but not for C. For C, if one is using data stored in row order, and one wantsto preserve the image index convention, the data must be reordered whenreading into memory.

In the end, which you do for Fortran or C depends onwhich is more important, not reordering data or preserving the indexingconvention. For large images, reordering data is potentially expensive, andoften the indexing convention is inverted to avoid that.

The situation withnumpy makes this issue yet more complicated. The internal machinery of numpyarrays is flexible enough to accept any ordering of indices. One can simplyreorder indices by manipulating the internal stride information for arrayswithout reordering the data at all. NumPy will know how to map the new indexorder to the data without moving the data.

So if this is true, why not choosethe index order that matches what you most expect? In particular, why not definerow-ordered images to use the image convention? (This is sometimes referredto as the Fortran convention vs the C convention, thus the ‘C’ and ‘FORTRAN’order options for array ordering in numpy.) The drawback of doing this ispotential performance penalties. It’s common to access the data sequentially,either implicitly in array operations or explicitly by looping over rows of animage. When that is done, then the data will be accessed in non-optimal order.As the first index is incremented, what is actually happening is that elementsspaced far apart in memory are being sequentially accessed, with usually poormemory access speeds. For example, for a two dimensional image ‘im’ defined sothat im[0, 10] represents the value at x=0, y=10. To be consistent with usualPython behavior then im[0] would represent a column at x=0. Yet that datawould be spread over the whole array since the data are stored in row order.Despite the flexibility of numpy’s indexing, it can’t really paper over the factbasic operations are rendered inefficient because of data order or that gettingcontiguous subarrays is still awkward (e.g., im[:,0] for the first row, vsim[0]), thus one can’t use an idiom such as for row in im; for col in im doeswork, but doesn’t yield contiguous column data.

As it turns out, numpy issmart enough when dealing with ufuncs to determine which index is the mostrapidly varying one in memory and uses that for the innermost loop. Thus forufuncs there is no large intrinsic advantage to either approach in most cases.On the other hand, use of .flat with an FORTRAN ordered array will lead tonon-optimal memory access as adjacent elements in the flattened array (iterator,actually) are not contiguous in memory.

Indeed, the fact is that Pythonindexing on lists and other sequences naturally leads to an outside-to insideordering (the first index gets the largest grouping, the next the next largest,and the last gets the smallest element). Since image data are normally storedby rows, this corresponds to position within rows being the last item indexed.

If you do want to use Fortran ordering realize thatthere are two approaches to consider: 1) accept that the first index is just notthe most rapidly changing in memory and have all your I/O routines reorderyour data when going from memory to disk or visa versa, or use numpy’smechanism for mapping the first index to the most rapidly varying data. Werecommend the former if possible. The disadvantage of the latter is that manyof numpy’s functions will yield arrays without Fortran ordering unless you arecareful to use the ‘order’ keyword. Doing this would be highly inconvenient.

Otherwise we recommend simply learning to reverse the usual order of indiceswhen accessing elements of an array. Granted, it goes against the grain, butit is more in line with Python semantics and the natural order of the data.

Table Of Contents

Previous topic

C API Deprecations

Next topic

NumPy C Code Explanations

Quick search

  • © Copyright 2008-2018, The SciPy community.
  • Last updated on Jul 24, 2018.
  • Created usingSphinx 1.6.6.

[8]ページ先頭

©2009-2025 Movatter.jp