![NumPyLogo](https://habrastorage.org/getpro/habr/post_images/efb/951/d2b/efb951d2b902c55fb772ec1dab5db663.png)
Первая часть учебника рассказывает об основах работы с NumPy: создании массивов, их атрибутах, базовых операциях, поэлементном применении функций, индексах, срезах, итерировании. Рассматриваются различные манипуляции с преобразованием формы массива, объединение массивов из нескольких и наоборот — разбиение одного на несколько более мелких. В конце мы обсудим поверхностное и глубокое копирование.
Основы
Если вы еще не устанавливали NumPy, то взять его можно здесь. Используемая версия Python — 2.6.
Основным объектом NumPy является однородный многомерный массив. Это таблица элементов (обычно чисел), всех одного типа, индексированных последовательностями натуральных чисел.
Под «многомерностью» массива мы понимаем то, что у него может быть несколько измерений или осей. Поскольку слово «измерение» является неоднозначным, вместо него мы чаще будем использовать слова «ось» (axis) и «оси» (axes). Число осей называется рангом (rank).
Например, координаты точки в трехмерном пространстве [1, 2, 1] это массив ранга 1 у него есть только одна ось. Длина этой оси — 3. Другой пример, массив
[[ 1., 0., 0.],
[ 0., 1., 2.]]
представляет массив ранга 2 (то есть это двухмерный массив). Длина первого измерения (оси) — 2, длина второй оси — 3. Для получения дополнительной информации смотрите глоссарий Numpy.
Класс многомерных массивов называется
ndarray
. Заметим, что это не то же самое, что класс array
стандартной библиотеки Python, который используется только для одномерных массивов. Наиболее важные атрибуты объектов ndarray
:ndarray.ndim — число осей (измерений) массива. Как уже было сказано, в мире Python число измерений часто называют рангом.
ndarray.shape — размеры массива, его форма. Это кортеж натуральных чисел, показывающий длину массива по каждой оси. Для матрицы из n строк и m столбов,
shape
будет (n,m)
. Число элементов кортежа shape
равно рангу массива, то есть ndim
.ndarray.size — число всех элементов массива. Равно произведению всех элементов атрибута
shape
.ndarray.dtype — объект, описывающий тип элементов массива. Можно определить
dtype
, используя стандартные типы данных Python. NumPy здесь предоставляет целый букет возможностей, например: bool_, character, int_, int8, int16, int32, int64, float_, float8, float16, float32, float64, complex_, complex64, object_
.ndarray.itemsize — размер каждого элемента массива в байтах. Например, для массива из элементов типа
float64
значение itemsize
равно 8 (=64/8), а для complex32
этот атрибут равен 4 (=32/8).ndarray.data — буфер, содержащий фактические элементы массива. Обычно нам не будет нужно использовать этот атрибут, потому как мы будем обращаться к элементам массива с помощью индексов.
Пример
Определим следующий массив:
Copy Source | Copy HTML<br/>>>> from numpy import *<br/>>>> a = arange(10).reshape(2,5)<br/>>>> a<br/>array([[ 0, 1, 2, 3, 4],<br/> [5, 6, 7, 8, 9]]) <br/>
Мы только что создали объект массива с именем a. У массива a есть несколько атрибутов или свойств. В Python атрибуты отдельного объекта обозначаются как
name_of_object.attribute
. В нашем случае:a.shape
это (2,5)a.ndim
это 2 (что равно длинеa.shape
)a.size
это 10a.dtype.name
это int32a.itemsize
это 4, что означает, что int32 занимает 4 байта памяти.
Вы можете проверить все эти атрибуты, просто набрав их в интерактивном режиме:
Copy Source | Copy HTML<br/>>>> a.shape<br/>(2, 5)<br/>>>> a.dtype.name<br/>'int32' <br/>
И так далее.
Создание массивов
Есть много способов для того, чтобы создать массив. Например, вы можете создать массив из обычных списков или кортежей Python, используя функцию
array()
:Copy Source | Copy HTML<br/>>>> from numpy import *<br/>>>> a = array( [2,3,4] )<br/>>>> a<br/>array([2, 3, 4])<br/>>>> type(a)<br/><type 'numpy.ndarray'> <br/>
Функция
array()
трансформирует вложенные последовательности в многомерные массивы. Тип массива зависит от типа элементов исходной последовательности.Copy Source | Copy HTML<br/>>>> b = array( [ (1.5,2,3), (4,5,6) ] ) # это станет массивом float элементов<br/>>>> b<br/>array([[ 1.5, 2. , 3. ],<br/> [ 4. , 5. , 6. ]]) <br/>
Раз у нас есть массив, мы можем взглянуть на его атрибуты:
Copy Source | Copy HTML<br/>>>> b.ndim # число осей<br/>2<br/>>>> b.shape # размеры<br/>(2, 3)<br/>>>> b.dtype # тип (8-байтовый float)<br/>dtype('float64')<br/>>>> b.itemsize # размер элемента данного типа<br/>8 <br/>
Тип массива может быть явно указан в момент создания:
Copy Source | Copy HTML<br/>>>> c = array( [ [1,2], [3,4] ], dtype=complex )<br/>>>> c<br/>array([[ 1.+ 0.j, 2.+ 0.j],<br/> [ 3.+ 0.j, 4.+ 0.j]]) <br/>
Часто встречающаяся ошибка состоит в вызове функции
array()
с множеством числовых аргументов вместо предполагаемого единственного аргумента в виде списка чисел:Copy Source | Copy HTML<br/>>>> a = array(1,2,3,4) # WRONG<br/>>>> a = array([1,2,3,4]) # RIGHT <br/>
Функция
array()
не единственная функция для создания массивов. Обычно элементы массива вначале неизвестны, а массив, в котором они будут храниться уже нужен. Поэтому имеется несколько функций для того, чтобы создавать массивы с каким-то исходным содержимым. По умолчанию тип создаваемого массива — float64
. Функция
zeros()
создает массив нулей, а функция ones()
— массив единиц:Copy Source | Copy HTML<br/>>>> zeros( (3,4) ) # аргумент задает форму массива<br/>array([[ 0., 0., 0., 0.],<br/> [ 0., 0., 0., 0.],<br/> [ 0., 0., 0., 0.]])<br/>>>> ones( (2,3,4), dtype=int16 ) # то есть также может быть задан dtype<br/>array([[[ 1, 1, 1, 1],<br/> [ 1, 1, 1, 1],<br/> [ 1, 1, 1, 1]],<br/> [[ 1, 1, 1, 1],<br/> [ 1, 1, 1, 1],<br/> [ 1, 1, 1, 1]]], dtype=int16) <br/>
Функция
empty()
создает массив без его заполнения. Исходное содержимое случайно и зависит от состояния памяти на момент создания массива (то есть от того мусора, что в ней хранится):Copy Source | Copy HTML<br/>>>> empty( (2,3) )<br/>array([[ 3.73603959e-262, 6.02658058e-154, 6.55490914e-260],<br/> [ 5.30498948e-313, 3.14673309e-307, 1.00000000e+000]])<br/>>>> empty( (2,3) ) # содержимое меняется при новом вызове<br/>array([[ 3.14678735e-307, 6.02658058e-154, 6.55490914e-260],<br/> [ 5.30498948e-313, 3.73603967e-262, 8.70018275e-313]]) <br/>
Для создания последовательностей чисел, в NumPy имеется функция, аналогичная
range()
, только вместо списков она возвращает массивы:Copy Source | Copy HTML<br/>>> arange( 10, 30, 5 )<br/>array([10, 15, 20, 25])<br/>>>> arange( 0, 2, 0.3 )<br/>array([ 0. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8]) <br/>
При использовании
arange()
с аргументами типа float
, сложно быть уверенным в том, сколько элементов будет получено (из-за ограничения точности чисел с плавающей запятой). Поэтому, в таких случаях обычно лучше использовать функцию linspace()
которая вместо шага в качестве одного из аргументов принимает число, равное количеству нужных элементов:Copy Source | Copy HTML<br/>>>> linspace( 0, 2, 9 ) # 9 чисел от 0 до 2<br/>array([ 0. , 0.25, 0.5 , 0.75, 1. , 1.25, 1.5 , 1.75, 2. ])<br/>>>> x = linspace( 0, 2*pi, 100 ) # полезно для вычисления значений функции в множестве точек<br/>>>> f = sin(x) <br/>
Печать массивов
Когда вы печатаете массив, NumPy показывает их схожим с вложенными списками образом, но размещает немного иначе:
- последняя ось печатается слева направо,
- предпоследняя — сверху вниз,
- и оставшиеся — также сверху вниз, разделяя пустой строкй.
Одномерные массивы выводятся как строки, двухмерные — как матрицы, а трехмерные — как списки матриц.
Copy Source | Copy HTML<br/>>>> a = arange(6) # 1d array<br/>>>> print a<br/>[0 1 2 3 4 5]<br/>>>><br/>>>> b = arange(12).reshape(4,3) # 2d array<br/>>>> print b<br/>[[ 0 1 2]<br/> [ 3 4 5]<br/> [ 6 7 8]<br/> [ 9 10 11]]<br/>>>><br/>>>> c = arange(24).reshape(2,3,4) # 3d array<br/>>>> print c<br/>[[[ 0 1 2 3]<br/> [ 4 5 6 7]<br/> [ 8 9 10 11]]<br/> [[12 13 14 15]<br/> [16 17 18 19]<br/> [20 21 22 23]]] <br/>
Если массив слишком большой, чтобы его печатать, NumPy автоматически скрывает центральную часть массива и выводит только его уголки:
Copy Source | Copy HTML<br/>>>> print arange(10000)<br/>[ 0 1 2 ..., 9997 9998 9999]<br/>>>><br/>>>> print arange(10000).reshape(100,100)<br/>[[ 0 1 2 ..., 97 98 99]<br/> [ 100 101 102 ..., 197 198 199]<br/> [ 200 201 202 ..., 297 298 299]<br/> ...,<br/> [9700 9701 9702 ..., 9797 9798 9799]<br/> [9800 9801 9802 ..., 9897 9898 9899]<br/> [9900 9901 9902 ..., 9997 9998 9999]] <br/>
Если вам действительно нужно увидеть все, что происходит в большом массиве, выведя его полностью, используйте функцию установки печати
set_printoptions()
:Copy Source | Copy HTML<br/>>>> set_printoptions(threshold=nan) <br/>
Базовые операции
Арифметические операции над массивами выполняются поэлементно. Создается новый массив, который заполняется результатами действия оператора.
Copy Source | Copy HTML<br/>>>> a = array( [20,30,40,50] )<br/>>>> b = arange( 4 )<br/>>>> c = a-b<br/>>>> c<br/>array([20, 29, 38, 47])<br/>>>> b**2<br/>array([ 0, 1, 4, 9])<br/>>>> 10*sin(a)<br/>array([ 9.12945251, -9.88031624, 7.4511316 , -2.62374854])<br/>>>> a<35<br/>array([True, True, False, False], dtype=bool) <br/>
В отличие от матричного подхода, оператор произведения * в массивах NumPy работает также поэлементно. Матричное произведение может быть осуществлено либо функцией
dot()
, либо созданием объектов матриц, которое будет рассмотрено далее (во второй части пособия).Copy Source | Copy HTML<br/>>>> A = array( [[1,1],<br/>... [ 0,1]] )<br/>>>> B = array( [[2, 0],<br/>... [3,4]] )<br/>>>> A*B # поэлементное произведение<br/>array([[2, 0],<br/> [ 0, 4]])<br/>>>> dot(A,B) # матричное произведение<br/>array([[5, 4],<br/> [3, 4]]) <br/>
Некоторые операции делаются «на месте», без создания нового массива.
Copy Source | Copy HTML<br/>>>> a = ones((2,3), dtype=int)<br/>>>> b = random.random((2,3))<br/>>>> a *= 3<br/>>>> a<br/>array([[3, 3, 3],<br/> [3, 3, 3]])<br/>>>> b += a<br/>>>> b<br/>array([[ 3.69092703, 3.8324276 , 3.0114541 ],<br/> [ 3.18679111, 3.3039349 , 3.37600289]])<br/>>>> a += b # b конвертируется к типу int<br/>>>> a<br/>array([[6, 6, 6],<br/> [6, 6, 6]]) <br/>
При работе с массивами разных типов, тип результирующего массива соответствует более общему или более точному типу.
Copy Source | Copy HTML<br/>>>> a = ones(3, dtype=int32)<br/>>>> b = linspace( 0,pi,3)<br/>>>> b.dtype.name<br/>'float64'<br/>>>> c = a+b<br/>>>> c<br/>array([ 1. , 2.57079633, 4.14159265])<br/>>>> c.dtype.name<br/>'float64'<br/>>>> d = exp(c*1j)<br/>>>> d<br/>array([ 0.54030231+ 0.84147098j, - 0.84147098+ 0.54030231j,<br/> - 0.54030231- 0.84147098j])<br/>>>> d.dtype.name<br/>'complex128' <br/>
Многие унарные операции, такие как вычисление суммы всех элементов массива, представлены в виде методов класса
ndarray
.Copy Source | Copy HTML<br/>>>> a = random.random((2,3))<br/>>>> a<br/>array([[ 0.6903007 , 0.39168346, 0.16524769],<br/> [ 0.48819875, 0.77188505, 0.94792155]])<br/>>>> a.sum()<br/>3.4552372100521485<br/>>>> a.min()<br/> 0.16524768654743593<br/>>>> a.max()<br/> 0.9479215542670073 <br/>
По умолчанию, эти операции применяются к массиву, как если бы он был списком чисел, независимо от его формы. Однако, указав параметр
axis
можно применить операцию по указанной оси массива:Copy Source | Copy HTML<br/>>>> b = arange(12).reshape(3,4)<br/>>>> b<br/>array([[ 0, 1, 2, 3],<br/> [ 4, 5, 6, 7],<br/> [ 8, 9, 10, 11]])<br/>>>><br/>>>> b.sum(axis= 0) # сумма в каждом столбце<br/>array([12, 15, 18, 21])<br/>>>><br/>>>> b.min(axis=1) # наименьшее число в каждой строке<br/>array([ 0, 4, 8])<br/>>>><br/>>>> b.cumsum(axis=1) # накопительная сумма каждой строки<br/>array([[ 0, 1, 3, 6],<br/> [ 4, 9, 15, 22],<br/> [ 8, 17, 27, 38]]) <br/>
Универсальные функции
NumPy обеспечивает работу с известными математическими функциями sin, cos, exp и так далее. Но в NumPy эти функции называются универсальными (
ufunc
). Причина присвоения такого имени кроется в том, что в NumPy эти функции работают с массивами также поэлементно, и на выходе получается массив значений.Copy Source | Copy HTML<br/>>>> B = arange(3)<br/>>>> B<br/>array([ 0, 1, 2])<br/>>>> exp(B)<br/>array([ 1. , 2.71828183, 7.3890561 ])<br/>>>> sqrt(B)<br/>array([ 0. , 1. , 1.41421356])<br/>>>> C = array([2., -1., 4.])<br/>>>> add(B, C)<br/>array([ 2., 0., 6.]) <br/>
Индексы, срезы, итерации
Одномерные массивы осуществляют операции индексирования, срезов и итераций очень схожим образом с обычными списками и другими последовательностями Python.
Copy Source | Copy HTML<br/>>>> a = arange(10)**3<br/>>>> a<br/>array([ 0, 1, 8, 27, 64, 125, 216, 343, 512, 729])<br/>>>> a[2]<br/>8<br/>>>> a[2:5]<br/>array([ 8, 27, 64])<br/>>>> a[:6:2] = -1000 # изменить элементы в a<br/>>>> a<br/>array([-1000, 1, -1000, 27. -1000, 125, 216, 343, 512, 729])<br/>>>> a[::-1] # перевернуть a<br/>array([ 729, 512, 343, 216, 125, -1000, 27, -1000, 1, -1000])<br/>>>> for i in a:<br/>... print i**(1/3.),<br/>...<br/>nan 1. 0 nan 3. 0 nan 5.0 6.0 7.0 8.0 9. 0 <br/>
У многомерных массивов на каждую ось приходится один индекс. Индексы передаются в виде последовательности чисел, разделенных запятыми:
Copy Source | Copy HTML<br/>>>> def f(x,y):<br/>... return 10*x+y<br/>...<br/>>>> b = fromfunction(f,(5,4),dtype=int)<br/>>>> b<br/>array([[ 0, 1, 2, 3],<br/> [10, 11, 12, 13],<br/> [20, 21, 22, 23],<br/> [30, 31, 32, 33],<br/> [40, 41, 42, 43]])<br/>>>> b[2,3]<br/>23<br/>>>> b[:,1] # второй столбец массива b<br/>array([ 1, 11, 21, 31, 41])<br/>>>> b[1:3,:] # вторая и третья строки массива b<br/>array([[10, 11, 12, 13],<br/> [20, 21, 22, 23]]) <br/>
Когда индексов меньше, чем осей, отсутствующие индексы предполагаются дополненными с помощью срезов:
Copy Source | Copy HTML<br/>>>> b[-1] # последняя строка. Эквивалентно b[-1,:]<br/>array([40, 41, 42, 43]) <br/>
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, :]
.
Copy Source | Copy HTML<br/>>>> c = array( [ [[ 0, 1, 2], # 3d array<br/>... [ 10, 12, 13]],<br/>...<br/>... [[100,101,102],<br/>... [110,112,113]] ] )<br/>>>> c.shape<br/>(2, 2, 3)<br/>>>> c[1,...] # то же, что c[1,:,:] или c[1]<br/>array([[100, 101, 102],<br/> [110, 112, 113]])<br/>>>> c[...,2] # то же, что c[:,:,2]<br/>array([[ 2, 13],<br/> [102, 113]]) <br/>
Итерирование многомерных массивов начинается с первой оси:
Copy Source | Copy HTML<br/>>>> for row in b:<br/>... print row<br/>...<br/>[0 1 2 3]<br/>[10 11 12 13]<br/>[20 21 22 23]<br/>[30 31 32 33]<br/>[40 41 42 43] <br/>
Однако, если нужно перебрать поэлементно весь массив, как если бы он был одномерным, для этого можно использовать атрибут
flat
:Copy Source | Copy HTML<br/>>>> for element in b.flat:<br/>... print element,<br/>...<br/>0 1 2 3 10 11 12 13 20 21 22 23 30 31 32 33 40 41 42 43 <br/>
Манипуляции с формой
Как уже говорилось, у массива есть форма (
shape
), определяемая числом элементов вдоль каждой оси:Copy Source | Copy HTML<br/>>>> a = floor(10*random.random((3,4)))<br/>>>> a<br/>array([[ 7., 5., 9., 3.],<br/> [ 7., 2., 7., 8.],<br/> [ 6., 8., 3., 2.]])<br/>>>> a.shape<br/>(3, 4) <br/>
Форма массива может быть изменена с помощью различных команд:
Copy Source | Copy HTML<br/>>>> a.ravel() # делает массив плоским<br/>array([ 7., 5., 9., 3., 7., 2., 7., 8., 6., 8., 3., 2.])<br/>>>> a.shape = (6, 2)<br/>>>> a.transpose()<br/>array([[ 7., 9., 7., 7., 6., 3.],<br/> [ 5., 3., 2., 8., 8., 2.]]) <br/>
Порядок элементов в массиве в результате функции
ravel()
соответствует обычному «C-стилю», то есть, чем правее индекс, тем он «быстрее изменяется»: за элементом a[0,0]
следует a[0,1]
. Если одна форма массива была изменена на другую, массив переформировывается также в «C-стиле». В таком порядке NumPy обычно и создает массивы, так что для функции ravel()
обычно не требуется копировать аргумент, но если массив был создан из срезов другого массива, копия может потребоваться. Функции ravel()
и reshape()
также могут работать (при использовании дополнительного аргумента) в FORTRAN-стиле, в котором быстрее изменяется более левый индекс.Функция
reshape()
возвращает ее аргумент с измененной формой, в то время как метод resize()
изменяет сам массив:Copy Source | Copy HTML<br/>>>> a<br/>array([[ 7., 5.],<br/> [ 9., 3.],<br/> [ 7., 2.],<br/> [ 7., 8.],<br/> [ 6., 8.],<br/> [ 3., 2.]])<br/>>>> a.resize((2,6))<br/>>>> a<br/>array([[ 7., 5., 9., 3., 7., 2.],<br/> [ 7., 8., 6., 8., 3., 2.]]) <br/>
Если при операции такой перестройки один из аргументов задается как -1, то он автоматически рассчитывается в соответствии с остальными заданными:
Copy Source | Copy HTML<br/>>>> a.reshape(3,-1)<br/>array([[ 7., 5., 9., 3.],<br/> [ 7., 2., 7., 8.],<br/> [ 6., 8., 3., 2.]]) <br/>
Объединение массивов
Несколько массивов могут быть объединены вместе вдоль разных осей:
Copy Source | Copy HTML<br/>>>> a = floor(10*random.random((2,2)))<br/>>>> a<br/>array([[ 1., 1.],<br/> [ 5., 8.]])<br/>>>> b = floor(10*random.random((2,2)))<br/>>>> b<br/>array([[ 3., 3.],<br/> [ 6., 0.]])<br/>>>> vstack((a,b))<br/>array([[ 1., 1.],<br/> [ 5., 8.],<br/> [ 3., 3.],<br/> [ 6., 0.]])<br/>>>> hstack((a,b))<br/>array([[ 1., 1., 3., 3.],<br/> [ 5., 8., 6., 0.]]) <br/>
Функция
column_stack()
объединяет одномерные массивы в качестве столбцов двумерного массива:Copy Source | Copy HTML<br/>>>> column_stack((a,b))<br/>array([[ 1., 1., 3., 3.],<br/> [ 5., 8., 6., 0.]])<br/>>>> a=array([4.,2.])<br/>>>> b=array([2.,8.])<br/>>>> a[:,newaxis] # Это дает нам 2D-ветор<br/>array([[ 4.],<br/> [ 2.]])<br/>>>> column_stack((a[:,newaxis],b[:,newaxis]))<br/>array([[ 4., 2.],<br/> [ 2., 8.]])<br/>>>> vstack((a[:,newaxis],b[:,newaxis])) # Поведение vstack другое<br/>array([[ 4.],<br/> [ 2.],<br/> [ 2.],<br/> [ 8.]]) <br/>
Аналогично для строк имеется функция
row_stack()
. Для массивов с более, чем двумя осями, hstack()
объединяет массивы по первым осям, vstack()
— по последним, дополнительные аргументы позволяют задать число осей по которым должно произойти объединение.В сложных случаях, могут быть полезны
r_[]
и с_[]
, позволяющие создавать одномерные массивы, с помощью последовательностей чисел вдоль одной оси. В них также имеется возможность использовать ":" для задания диапазона литералов:Copy Source | Copy HTML<br/>>>> r_[1:4, 0,4]<br/>array([1, 2, 3, 0, 4]) <br/>
Разделение одного массива на несколько более мелких
Используя
hsplit()
вы можете разбить массив вдоль горизонтальной оси, указав либо число возвращаемых массивов одинаковой формы, либо номера столбцов, после которых массив разрезается ножницами:Copy Source | Copy HTML<br/>>>> a = floor(10*random.random((2,12)))<br/>>>> a<br/>array([[ 8., 8., 3., 9., 0., 4., 3., 0., 0., 6., 4., 4.],<br/> [ 0., 3., 2., 9., 6., 0., 4., 5., 7., 5., 1., 4.]])<br/>>>> hsplit(a,3) # Разбить на 3 массива<br/>[array([[ 8., 8., 3., 9.],<br/> [ 0., 3., 2., 9.]]), array([[ 0., 4., 3., 0.],<br/> [ 6., 0., 4., 5.]]), array([[ 0., 6., 4., 4.],<br/> [ 7., 5., 1., 4.]])]<br/>>>> hsplit(a,(3,4)) # Разрезать a после третьего и четвертого столбца<br/>[array([[ 8., 8., 3.],<br/> [ 0., 3., 2.]]), array([[ 9.],<br/> [ 9.]]), array([[ 0., 4., 3., 0., 0., 6., 4., 4.],<br/> [ 6., 0., 4., 5., 7., 5., 1., 4.]])] <br/>
Функция
vsplit()
разбивает массив вдоль вертикальной оси, а array_split()
позволяет указать оси, вдоль которых произойдет разбиение.Копии и представления
При работе с массивами, их данные иногда необходимо копировать в другой массив, а иногда нет. Это часто является источником путаницы среди новичков. Возможно всего три случая:
Вообще никаких копий
Простое присваивание не создает ни копии массива, ни копии его данных:
Copy Source | Copy HTML<br/>>>> a = arange(12)<br/>>>> b = a # никакого нового объекта создано не было<br/>>>> b is a # a и b это два имени для одного объекта ndarray <br/>True<br/>>>> b.shape = 3,4 # изменит форму a<br/>>>> a.shape<br/>(3, 4) <br/>
Python передает изменяемые объекты как ссылки, поэтому вызовы функций также не создают копий:
Copy Source | Copy HTML<br/>>>> def f(x):<br/>... print id(x)<br/>...<br/>>>> id(a)<br/>148293216<br/>>>> f(a)<br/>148293216 <br/>
Представление или поверхностная копия
Разные объекты массивов могут использовать одни и те же данные. Метод
view()
создает новый объект массива, являющийся представлением тех же данных.Copy Source | Copy HTML<br/>>>> c = a.view()<br/>>>> c is a<br/>False<br/>>>> c.base is a # c это представление данных, принадлежащих a<br/>True<br/>>>> c.flags.owndata<br/>False<br/>>>><br/>>>> c.shape = 2,6 # форма а не поменяется<br/>>>> a.shape<br/>(3, 4)<br/>>>> c[ 0,4] = 1234 # данные а изменятся<br/>>>> a<br/>array([[ 0, 1, 2, 3],<br/> [1234, 5, 6, 7],<br/> [ 8, 9, 10, 11]]) <br/>
Срез массива это представление:
Copy Source | Copy HTML<br/>>>> s = a[:,1:3]<br/>>>> s[:] = 10 # s[:] это представление s. Заметьте разницу между s=10 и s[:]=10<br/>>>> a<br/>array([[ 0, 10, 10, 3],<br/> [1234, 10, 10, 7],<br/> [ 8, 10, 10, 11]]) <br/>
Глубокая копия
Метод
copy()
создает настоящую копию массива и его данных:Copy Source | Copy HTML<br/>>>> d = a.copy() # создается новый объект массива с новыми данными<br/>>>> d is a<br/>False<br/>>>> d.base is a # d не имеет ничего общего с а<br/>False<br/>>>> d[ 0, 0] = 9999<br/>>>> a<br/>array([[ 0, 10, 10, 3],<br/> [1234, 10, 10, 7],<br/> [ 8, 10, 10, 11]]) <br/>
В заключение
Итак, в первой части мы рассмотрели самые важные базовые операции работы с массивами. В дополнение к этой части, я советую хорошую шпаргалку. Во второй части мы поговорим о более специфических вещах: об индексировании с помощью массивов индексов или булевых величин, реализации операций линейной алгебры и классе
matrix
и разных полезных трюках.