Пользователь
0,0
рейтинг
10 июня 2011 в 17:48

Разработка → NumPy, пособие для новичков. Часть 1 перевод

NumPyLogoNumPy — это расширение языка Python, добавляющее поддержку больших многомерных массивов и матриц, вместе с большой библиотекой высокоуровневых математических функций для операций с этими массивами.

Первая часть учебника рассказывает об основах работы с 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 это 10
  • a.dtype.name это int32
  • a.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 и разных полезных трюках.
Перевод: scipy.org
Матюшкин Лёва @LeoMat
карма
91,2
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (38)

  • 0
    Вы напомнили мне про NumPy и про то, что я с ней не работал)
    • 0
      Звучит, как воспоминание о боевой подруге.
  • +2
    Из той же оперы еще SymPy неплох.
    • 0
      Соглашусь с тем, что неплох. Я уже описывал его в курсе на Викиверситете, как пример отличной библиотеки для символьных вычислений. Но то, что из одной оперы, то здесь композиторы совсем разные и по большей части для разных вещей. Хотя в SymPy тоже поддерживаются, например, операции с линейной алгеброй.
      • 0
        Да, SymPy удобен для операций с различными уравнениями, системами. А про оперу, я имел ввиду научное направление :) Хотя NumPy вообще много для чего удобен
  • +1
    А еще массивы numpy без шаманства пиклятся и передаются в динамические библиотеки.
    • +1
      Да, например, с помощью ndarray.dump(file).
      • +1
        Если, что то это было утверждение =)
        Просто данная функциональность немаловажна при многопоточном счете большого количества данных (например, вычислительная аэродинамика), когда требуется много перекидывать между потокамии и передавать в счетные библиотеки.
        • +1
          Да, это я что-то вроде как кивнул.
  • 0
    Спасибо! А то вроде как простых туториалов для numpy и нет, а в каком-нибудь проекте используешь списки — и все пинают в numpy — да и действительно, он значительно быстрее.
    • 0
      Странно, что такие статьи не особо ценят.

      И вот, назрел вопрос: как обрабатываются вещи типа a[1, :, ...] с точки зрения синтаксиса языка?
      • 0
        Точно также. То же самое, что a[1, ...] и что просто a[1]. Легко проверяется в интерактивном режиме. Или в чем вопрос?
        • 0
          Насколько я знаю, такое обращение вызывает магический метод типа __getitem__ или __getslice__. Что же ему передаётся в параметрах?
          • 0
            Исследовал этот вопрос:

            In [3]: class X:
            ...: def __getitem__(self, x):
            ...: print x
            ...:
            ...:

            In [4]: X()[1,...]
            (1, Ellipsis)
            • 0
              При этом просто многоточие является ошибкой — это видимо синтаксический сахар только для квадратных скобок.
              • 0
                Да. Это спец объект Python под названием Ellipsis.
  • 0
    Хорошая статья. Вот только NumPy это не расширение для языка Python, а модуль для CPython. Как мне его не хватает в PyPy :-(
    • 0
      а позвольте поинтересоваться, каковы причины необходимости именно PyPy?
      • 0
        Эм. Многократное ускорение?

        В частности, на raytrace коде он даёт почти десятикратное увеличение скорости работы.
  • 0
    Спасибо, хорошая статья, я для себя даже кое-что новое узнал :)) Ну и за ссылку, данкешон :)
  • 0
    хотелось бы не сухих комментариев к мануалу, а реальных примеров решения классических задач (3d графика, convex hull, voronoi, итп.)
    • 0
      Это прямой перевод пособия, размещенного на сайте. В нем рассматриваются общие вопросы работы с массивами, само название говорит, для чего оно нужно. Более специфическим вопросам будут посвящены другие топики.
    • 0
      scipy/numpy не умеют convex hull. см delaunay package, который всё равно не pure python.

      что вы понимаете под '3d графика'?
      • 0
        delny package: bitbucket.org/flub/delny
        • 0
          Вру, умеют: scipy.spatial.qhull
          • 0
            ну так это обёртка к «канонической» C-шной реализации.
            numpy тут вобще ни при чом.
            • 0
              Как это «не причём»?

              Вы исходник scipy.spatial.qhull видели, прежде чем хоть какие-то выводы делать?

              Если нет — показываю:

              Файл: scipy/scipy/spatial/qhull.pyx

              import numpy as np
              cimport numpy as np
              • 0
                Что-то сомневаюсь, что интерфейс к сишной реализации хорошо иллюстрирует применение numpy в питоне.
                • 0
                  Уверен, что неплохо продемонстрирует применение numpy в pyrex'е.

                  Это я к тому, что numpy тут вообще-то причём.
      • 0
        я имел ввиду именно примеры решения задач с использованием numpy, чтобы, так сказать, наглядно продемонстрировать приемущества использования в этих задачах numpy перед pure python.

        в 3d графике используются вектора, а также матричная арифметика (причом в каждой 3д или игровой библиотеке реализованы свои вектора и матрицы, с шахматами и поэтэссами)
        • 0
          Я попробовал взять toy raytracer'ер[1] и заменить в нём их классы Vector и Point на numpy массивы получается обратная ситуация — код замедляется. Это, видимо, связано с тем, что проще перемножить/сложить 3 числа так, чем переходить для этого в C код Numpy. Я глубоко не копал в этом, точно сказать немогу.

          При этом, если запустить код под pypy-c-jit, то pure python без Numpy выигрывает в несколько раз по сравнению с кодом с CPython + numpy.

          Я прямо сейчас занимаюсь тем, что пишу 3d графику для Python. Правда, задача у меня несколько специфичная.

          [1] www.lshift.net/blog/2008/10/29/toy-raytracer-in-python
    • 0
      ну или вообще — для чего нужен этот numpy, в каких задачах его применение оправдано, а в каких нет.
  • 0
    У меня вопрос. я как раз смотрел на эту либу:

    Есть вариант такой

    объект A, у него есть характеристики ( Цвет: Зеленый, размер: 10, вес: 100, противоводный: ДА )
    объект B, у него есть характеристики ( Цвет: Красный, размер: 10, вес: 90, противоводный: НЕТ )
    объект C, у него есть характеристики ( Цвет: Зеленый, размер: 10, вес: 100, противоводный: ДА )
    объект D, у него есть характеристики ( размер: 10, вес: 100, противоводный: ДА, Скидка: ДА )

    Нужно если например На входе фуннкции объект A, построить объекты в таком виде

    объект C
    объект B
    объект D
    т.е. объект С наиболее схож с объектом А и так далее.

    Как я понимаю эту задачу я могу решить с этой либой ( п.с. размер: 10, вес: 100 — канечно это все будет идентификаторы, типа prp_id = 1( свойство размер), p2w_id= 2( значения свойства размер = 10),
    prp_id = 2( свойство вес), p2w_id= 3( значения свойства размер = 100) )

    ) буду раз если Камрады помогут решить эту задачу.

    Сейчас это все делаеться обходом цыкла, но хочеться красоты и прочего)

    Ну и вторая задача выходит из этого

    на входе: Цвет: Зеленый, размер: 10, вес: 100, на выходе все тот же список объектов, наиболее релевантные по входным параметрам.
  • 0
    Скажите пожалуйста, а можно ли выбирать конкретные столбцы в массиве?
    Например, имея массив
    array([[ 7., 5., 9., 3.], [ 7., 2., 7., 8.], [ 6., 8., 3., 2.]])
    я хочу выбрать первый и 4й столбцы, чтобы получить
    array([[ 7., 3.], [ 7., 8.], [ 6., 2.]])
    Можно ли это сделать просто что-то типа array[:,0::4]?
    • 0
      случаем обычный питоний zip в np не перегружен ли?
    • 0
      лучше поздно, чем никогда, может кому-то пригодится

      import numpy as np
      
      a = np.array([[7., 5., 9., 3.],
                    [7., 2., 7., 8.],
                    [6., 8., 3., 2.]])
      
      print(a[:, [0, 3]])
      


      [[ 7. 3.] [ 7. 8.] [ 6. 2.]]
  • 0
    Отличный мануал, спасибо!

    Только видимо что-то отъехало в хабре — html-код в примерах какой-то полез.
    • 0
      Да, исходно все было в порядке.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.