- NumPy, часть 2: базовые операции над массивами
- Базовые операции
- Индексы, срезы, итерации
- Объединение массивов
- Разбиение массива
- Копии и представления
- Вообще никаких копий
- Представление или поверхностная копия
- Глубокая копия
- Copies and views#
- View#
- Copy#
- Indexing operations#
- Other operations#
- How to tell if the array is a view or a copy#
- numpy.copy#
- numpy.copy#
NumPy, часть 2: базовые операции над массивами
Здравствуйте! Я продолжаю работу над пособием по python-библиотеке NumPy.
В прошлой части мы научились создавать массивы и их печатать. Однако это не имеет смысла, если с ними ничего нельзя делать.
Сегодня мы познакомимся с операциями над массивами.
Базовые операции
Математические операции над массивами выполняются поэлементно. Создается новый массив, который заполняется результатами действия оператора.
:1: RuntimeWarning: divide by zero encountered in true_divide
Для этого, естественно, массивы должны быть одинаковых размеров.
Также можно производить математические операции между массивом и числом. В этом случае к каждому элементу прибавляется (или что вы там делаете) это число.
NumPy также предоставляет множество математических операций для обработки массивов:
Полный список можно посмотреть здесь.
Многие унарные операции, такие как, например, вычисление суммы всех элементов массива, представлены также и в виде методов класса ndarray.
Индексы, срезы, итерации
Одномерные массивы осуществляют операции индексирования, срезов и итераций очень схожим образом с обычными списками и другими последовательностями Python (разве что удалять с помощью срезов нельзя).
File У многомерных массивов на каждую ось приходится один индекс. Индексы передаются в виде последовательности чисел, разделенных запятыми (то бишь, кортежами):
Когда индексов меньше, чем осей, отсутствующие индексы предполагаются дополненными с помощью срезов:
b[i] можно читать как b[i, ]. В NumPy это также может быть записано с помощью точек, как b[i, . ].
Например, если x имеет ранг 5 (то есть у него 5 осей), тогда
- x[1, 2, . ] эквивалентно x[1, 2, :, :, :],
- x[. , 3] то же самое, что x[:, :, :, :, 3] и
- x[4, . , 5, :] это x[4, :, :, 5, :].
Итерирование многомерных массивов начинается с первой оси:
Однако, если нужно перебрать поэлементно весь массив, как если бы он был одномерным, для этого можно использовать атрибут flat:
Как уже говорилось, у массива есть форма (shape), определяемая числом элементов вдоль каждой оси:
Форма массива может быть изменена с помощью различных команд:
Порядок элементов в массиве в результате функции ravel() соответствует обычному "C-стилю", то есть, чем правее индекс, тем он "быстрее изменяется": за элементом a[0,0] следует a[0,1]. Если одна форма массива была изменена на другую, массив переформировывается также в "C-стиле". Функции ravel() и reshape() также могут работать (при использовании дополнительного аргумента) в FORTRAN-стиле, в котором быстрее изменяется более левый индекс.
Метод reshape() возвращает ее аргумент с измененной формой, в то время как метод resize() изменяет сам массив:
Если при операции такой перестройки один из аргументов задается как -1, то он автоматически рассчитывается в соответствии с остальными заданными:
Объединение массивов
Несколько массивов могут быть объединены вместе вдоль разных осей с помощью функций hstack и vstack.
hstack() объединяет массивы по первым осям, vstack() — по последним:
Функция column_stack() объединяет одномерные массивы в качестве столбцов двумерного массива:
Аналогично для строк имеется функция row_stack().
Разбиение массива
Используя hsplit() вы можете разбить массив вдоль горизонтальной оси, указав либо число возвращаемых массивов одинаковой формы, либо номера столбцов, после которых массив разрезается "ножницами":
Функция vsplit() разбивает массив вдоль вертикальной оси, а array_split() позволяет указать оси, вдоль которых произойдет разбиение.
Копии и представления
При работе с массивами, их данные иногда необходимо копировать в другой массив, а иногда нет. Это часто является источником путаницы. Возможно 3 случая:
Вообще никаких копий
Простое присваивание не создает ни копии массива, ни копии его данных:
Python передает изменяемые объекты как ссылки, поэтому вызовы функций также не создают копий.
Представление или поверхностная копия
Разные объекты массивов могут использовать одни и те же данные. Метод view() создает новый объект массива, являющийся представлением тех же данных.
Срез массива это представление:
Глубокая копия
Метод copy() создаст настоящую копию массива и его данных:
Для вставки кода на Python в комментарий заключайте его в теги
- Книги о Python
- GUI (графический интерфейс пользователя)
- Курсы Python
- Модули
- Новости мира Python
- NumPy
- Обработка данных
- Основы программирования
- Примеры программ
- Типы данных в Python
- Видео
- Python для Web
- Работа для Python-программистов
Copies and views#
When operating on NumPy arrays, it is possible to access the internal data buffer directly using a view without copying data around. This ensures good performance but can also cause unwanted problems if the user is not aware of how this works. Hence, it is important to know the difference between these two terms and to know which operations return copies and which return views.
The NumPy array is a data structure consisting of two parts: the contiguous data buffer with the actual data elements and the metadata that contains information about the data buffer. The metadata includes data type, strides, and other important information that helps manipulate the ndarray easily. See the Internal organization of NumPy arrays section for a detailed look.
View#
It is possible to access the array differently by just changing certain metadata like stride and dtype without changing the data buffer. This creates a new way of looking at the data and these new arrays are called views. The data buffer remains the same, so any changes made to a view reflects in the original copy. A view can be forced through the ndarray.view method.
Copy#
When a new array is created by duplicating the data buffer as well as the metadata, it is called a copy. Changes made to the copy do not reflect on the original array. Making a copy is slower and memory-consuming but sometimes necessary. A copy can be forced by using ndarray.copy .
Indexing operations#
Views are created when elements can be addressed with offsets and strides in the original array. Hence, basic indexing always creates views. For example:
>>> x = np.arange(10) >>> x array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) >>> y = x[1:3] # creates a view >>> y array([1, 2]) >>> x[1:3] = [10, 11] >>> x array([ 0, 10, 11, 3, 4, 5, 6, 7, 8, 9]) >>> y array([10, 11])
Here, y gets changed when x is changed because it is a view.
Advanced indexing , on the other hand, always creates copies. For example:
>>> x = np.arange(9).reshape(3, 3) >>> x array([[0, 1, 2], [3, 4, 5], [6, 7, 8]]) >>> y = x[[1, 2]] >>> y array([[3, 4, 5], [6, 7, 8]]) >>> y.base is None True
Here, y is a copy, as signified by the base attribute. We can also confirm this by assigning new values to x[[1, 2]] which in turn will not affect y at all:
>>> x[[1, 2]] = [[10, 11, 12], [13, 14, 15]] >>> x array([[ 0, 1, 2], [10, 11, 12], [13, 14, 15]]) >>> y array([[3, 4, 5], [6, 7, 8]])
It must be noted here that during the assignment of x[[1, 2]] no view or copy is created as the assignment happens in-place.
Other operations#
The numpy.reshape function creates a view where possible or a copy otherwise. In most cases, the strides can be modified to reshape the array with a view. However, in some cases where the array becomes non-contiguous (perhaps after a ndarray.transpose operation), the reshaping cannot be done by modifying strides and requires a copy. In these cases, we can raise an error by assigning the new shape to the shape attribute of the array. For example:
>>> x = np.ones((2, 3)) >>> y = x.T # makes the array non-contiguous >>> y array([[1., 1.], [1., 1.], [1., 1.]]) >>> z = y.view() >>> z.shape = 6 Traceback (most recent call last): . AttributeError: Incompatible shape for in-place modification. Use `.reshape()` to make a copy with the desired shape.
Taking the example of another operation, ravel returns a contiguous flattened view of the array wherever possible. On the other hand, ndarray.flatten always returns a flattened copy of the array. However, to guarantee a view in most cases, x.reshape(-1) may be preferable.
How to tell if the array is a view or a copy#
The base attribute of the ndarray makes it easy to tell if an array is a view or a copy. The base attribute of a view returns the original array while it returns None for a copy.
>>> x = np.arange(9) >>> x array([0, 1, 2, 3, 4, 5, 6, 7, 8]) >>> y = x.reshape(3, 3) >>> y array([[0, 1, 2], [3, 4, 5], [6, 7, 8]]) >>> y.base # .reshape() creates a view array([0, 1, 2, 3, 4, 5, 6, 7, 8]) >>> z = y[[2, 1]] >>> z array([[6, 7, 8], [3, 4, 5]]) >>> z.base is None # advanced indexing creates a copy True
Note that the base attribute should not be used to determine if an ndarray object is new; only if it is a view or a copy of another ndarray.
numpy.copy#
Controls the memory layout of the copy. ‘C’ means C-order, ‘F’ means F-order, ‘A’ means ‘F’ if a is Fortran contiguous, ‘C’ otherwise. ‘K’ means match the layout of a as closely as possible. (Note that this function and ndarray.copy are very similar, but have different default values for their order= arguments.)
subok bool, optional
If True, then sub-classes will be passed-through, otherwise the returned array will be forced to be a base-class array (defaults to False).
Array interpretation of a.
Preferred method for creating an array copy
Create an array x, with a reference y and a copy z:
>>> x = np.array([1, 2, 3]) >>> y = x >>> z = np.copy(x)
Note that, when we modify x, y changes, but not z:
>>> x[0] = 10 >>> x[0] == y[0] True >>> x[0] == z[0] False
Note that, np.copy clears previously set WRITEABLE=False flag.
>>> a = np.array([1, 2, 3]) >>> a.flags["WRITEABLE"] = False >>> b = np.copy(a) >>> b.flags["WRITEABLE"] True >>> b[0] = 3 >>> b array([3, 2, 3])
Note that np.copy is a shallow copy and will not copy object elements within arrays. This is mainly important for arrays containing Python objects. The new array will contain the same object which may lead to surprises if that object can be modified (is mutable):
>>> a = np.array([1, 'm', [2, 3, 4]], dtype=object) >>> b = np.copy(a) >>> b[2][0] = 10 >>> a array([1, 'm', list([10, 3, 4])], dtype=object)
To ensure all elements within an object array are copied, use copy.deepcopy :
>>> import copy >>> a = np.array([1, 'm', [2, 3, 4]], dtype=object) >>> c = copy.deepcopy(a) >>> c[2][0] = 10 >>> c array([1, 'm', list([10, 3, 4])], dtype=object) >>> a array([1, 'm', list([2, 3, 4])], dtype=object)
numpy.copy#
Controls the memory layout of the copy. ‘C’ means C-order, ‘F’ means F-order, ‘A’ means ‘F’ if a is Fortran contiguous, ‘C’ otherwise. ‘K’ means match the layout of a as closely as possible. (Note that this function and ndarray.copy are very similar, but have different default values for their order= arguments.)
subok bool, optional
If True, then sub-classes will be passed-through, otherwise the returned array will be forced to be a base-class array (defaults to False).
Array interpretation of a.
Preferred method for creating an array copy
Create an array x, with a reference y and a copy z:
>>> x = np.array([1, 2, 3]) >>> y = x >>> z = np.copy(x)
Note that, when we modify x, y changes, but not z:
>>> x[0] = 10 >>> x[0] == y[0] True >>> x[0] == z[0] False
Note that, np.copy clears previously set WRITEABLE=False flag.
>>> a = np.array([1, 2, 3]) >>> a.flags["WRITEABLE"] = False >>> b = np.copy(a) >>> b.flags["WRITEABLE"] True >>> b[0] = 3 >>> b array([3, 2, 3])
Note that np.copy is a shallow copy and will not copy object elements within arrays. This is mainly important for arrays containing Python objects. The new array will contain the same object which may lead to surprises if that object can be modified (is mutable):
>>> a = np.array([1, 'm', [2, 3, 4]], dtype=object) >>> b = np.copy(a) >>> b[2][0] = 10 >>> a array([1, 'm', list([10, 3, 4])], dtype=object)
To ensure all elements within an object array are copied, use copy.deepcopy :
>>> import copy >>> a = np.array([1, 'm', [2, 3, 4]], dtype=object) >>> c = copy.deepcopy(a) >>> c[2][0] = 10 >>> c array([1, 'm', list([10, 3, 4])], dtype=object) >>> a array([1, 'm', list([2, 3, 4])], dtype=object)