Предисловие переводчика


Доброго времени суток, Хабр. Запускаю цикл статей, которые являются переводом небольшого мана по numpy, ссылочка. Приятного чтения.

Введение


NumPy это open-source модуль для python, который предоставляет общие математические и числовые операции в виде пре-скомпилированных, быстрых функций. Они объединяются в высокоуровневые пакеты. Они обеспечивают функционал, который можно сравнить с функционалом MatLab. NumPy (Numeric Python) предоставляет базовые методы для манипуляции с большими массивами и матрицами. SciPy (Scientific Python) расширяет функционал numpy огромной коллекцией полезных алгоритмов, таких как минимизация, преобразование Фурье, регрессия, и другие прикладные математические техники.

Установка


Если у вас есть Python(x, y) (Примечание переводчика: Python(x, y), это дистрибутив свободного научного и инженерного программного обеспечения для численных расчётов, анализа и визуализации данных на основе языка программирования Python и большого числа модулей (библиотек)) на платформе Windows, то вы готовы начинать. Если же нет, то после установки python, вам нужно установить пакеты самостоятельно, сначала NumPy потом SciPy. Установка доступна здесь. Следуйте установке на странице, там всё предельно понятно.

Немного дополнительной информации


Сообщество NumPy и SciPy поддерживает онлайн руководство, включающие гайды и туториалы, тут: docs.scipy.org/doc.

Импорт модуля numpy

Есть несколько путей импорта. Стандартный метод это — использовать простое выражение:

>>> import numpy

Тем не менее, для большого количества вызовов функций numpy, становиться утомительно писать numpy.X снова и снова. Вместо этого намного легче сделать это так:

>>> import numpy as np

Это выражение позволяет нам получать доступ к numpy объектам используя np.X вместо numpy.X. Также можно импортировать numpy прямо в используемое пространство имен, чтобы вообще не использовать функции через точку, а вызывать их напрямую:

>>> from numpy import *

Однако, этот вариант не приветствуется в программировании на python, так как убирает некоторые полезные структуры, которые модуль предоставляет. До конца этого туториала мы будем использовать второй вариант импорта (import numpy as np).

Массивы


Главной особенностью numpy является объект array. Массивы схожи со списками в python, исключая тот факт, что элементы массива должны иметь одинаковый тип данных, как float и int. С массивами можно проводить числовые операции с большим объемом информации в разы быстрее и, главное, намного эффективнее чем со списками.

Создание массива из списка:

a = np.array([1, 4, 5, 8], float)
>>> a
array([ 1.,  4.,  5.,  8.])
>>> type(a)
<class 'numpy.ndarray'>

Здесь функция array принимает два аргумента: список для конвертации в массив и тип для каждого элемента. Ко всем элементам можно получить доступ и манипулировать ими также, как вы бы это делали с обычными списками:

>>> a[:2]
array([ 1.,  4.])
>>> a[3]
8.0
>>> a[0] = 5.
>>> a
array([ 5.,  4.,  5.,  8.])

Массивы могут быть и многомерными. В отличии от списков можно задавать команды в скобках. Вот пример двумерного массива (матрица):

>>> a = np.array([[1, 2, 3], [4, 5, 6]], float)
>>> a
array([[ 1.,  2.,  3.],
[ 4.,  5.,  6.]])
>>> a[0,0]
1.0
>>> a[0,1]
2.0

Array slicing работает с многомерными массивами аналогично, как и с одномерными, применяя каждый срез, как фильтр для установленного измерения. Используйте ":" в измерении для указывания использования всех элементов этого измерения:

>>> a = np.array([[1, 2, 3], [4, 5, 6]], float)
>>> a[1,:]
array([ 4.,  5.,  6.])
>>> a[:,2]
array([ 3.,  6.])
>>> a[-1:, -2:]
array([[ 5.,  6.]])

Метод shape возвращает количество строк и столбцов в матрице:

>>> a.shape
(2, 3)

Метод dtype возвращает тип переменных, хранящихся в массиве:

>>> a.dtype
dtype('float64')

Тут float64, это числовой тип данных в numpy, который используется для хранения вещественных чисел двойной точности. Так как же float в Python.

Метод len возвращает длину первого измерения (оси):

a = np.array([[1, 2, 3], [4, 5, 6]], float)
>>> len(a)
2

Метод in используется для проверки на наличие элемента в массиве:

>>> a = np.array([[1, 2, 3], [4, 5, 6]], float)
>>> 2 in a
True
>>> 0 in a
False

Массивы можно переформировать при помощи метода, который задает новый многомерный массив. Следуя следующему примеру, мы переформатируем одномерный массив из десяти элементов во двумерный массив, состоящий из пяти строк и двух столбцов:

>>> a = np.array(range(10), float)
>>> a
array([ 0.,  1.,  2., 3.,  4.,  5.,  6.,  7.,  8.,  9.])
>>> a = a.reshape((5, 2))
>>> a
array([[ 0.,  1.],
[ 2.,  3.],
[ 4.,  5.],
[ 6.,  7.],
[ 8.,  9.]])
>>> a.shape
(5, 2)

Обратите внимание, метод reshape создает новый массив, а не модифицирует оригинальный.

Имейте ввиду, связывание имен в python работает и с массивами. Метод copy используется для создания копии существующего массива в памяти:

>>> a = np.array([1, 2, 3], float)
>>> b = a
>>> c =  a.copy()
>>> a[0] = 0
>>> a
array([0., 2., 3.])
>>> b
array([0., 2., 3.])
>>> c
array([1., 2., 3.])

Списки можно тоже создавать с массивов:

>>> a = np.array([1, 2, 3], float)
>>> a.tolist()
[1.0, 2.0, 3.0]
>>> list(a)
[1.0, 2.0, 3.0]

Можно также переконвертировать массив в бинарную строку (то есть, не human-readable форму). Используйте метод tostring для этого. Метод fromstring работает в для обратного преобразования. Эти операции иногда полезны для сохранения большого количества данных в файлах, которые могут быть считаны в будущем.

>>> a = array([1, 2, 3], float)
>>> s = a.tostring()
>>> s
'\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x08@'
>>> np.fromstring(s)
array([ 1.,  2.,  3.])

Заполнение массива одинаковым значением.

>>> a = array([1, 2, 3], float)
>>> a
array([ 1.,  2.,  3.])
>>> a.fill(0)
>>> a
array([ 0.,  0.,  0.])

Транспонирование массивов также возможно, при этом создается новый массив:

>>> a = np.array(range(6), float).reshape((2, 3))
>>> a
array([[ 0.,  1.,  2.],
[ 3.,  4.,  5.]])
>>> a.transpose()
array([[ 0.,  3.],
[ 1.,  4.],
[ 2.,  5.]])

Многомерный массив можно переконвертировать в одномерный при помощи метода flatten:

>>> a = np.array([[1, 2, 3], [4, 5, 6]], float)
>>> a
array([[ 1.,  2.,  3.],
[ 4.,  5.,  6.]])
>>> a.flatten()
array([ 1.,  2.,  3.,  4.,  5.,  6.])

Два или больше массивов можно сконкатенировать при помощи метода concatenate:

>>> a = np.array([1,2], float)
>>> b = np.array([3,4,5,6], float)
>>> c = np.array([7,8,9], float)
>>> np.concatenate((a, b, c))
array([1., 2., 3., 4., 5., 6., 7., 8., 9.])

Если массив не одномерный, можно задать ось, по которой будет происходить соединение. По умолчанию (не задавая значения оси), соединение будет происходить по первому измерению:

>>> a = np.array([[1, 2], [3, 4]], float)
>>> b = np.array([[5, 6], [7,8]], float)
>>> np.concatenate((a,b))
array([[ 1.,  2.],
[ 3.,  4.],
[ 5.,  6.],
[ 7.,  8.]])
>>> np.concatenate((a,b), axis=0)
array([[ 1.,  2.],
[ 3.,  4.],
[ 5.,  6.],
[ 7.,  8.]])
>>>
np.concatenate((a,b), axis=1)
array([[ 1.,  2.,  5.,  6.],
[ 3.,  4.,  7.,  8.]])

В заключении, размерность массива может быть увеличена при использовании константы newaxis в квадратных скобках:

>>> a = np.array([1, 2, 3], float)
>>> a
array([1., 2., 3.])
>>> a[:,np.newaxis]
array([[ 1.],
[ 2.],
[ 3.]])
>>> a[:,np.newaxis].shape
(3,1)
>>> b[np.newaxis,:]
array([[ 1.,  2.,  3.]])
>>> b[np.newaxis,:].shape
(1,3)

Заметьте, тут каждый массив двумерный; созданный при помощи newaxis имеет размерность один. Метод newaxis подходит для удобного создания надлежаще-мерных массивов в векторной и матричной математике.

На этом у нас конец первой части перевода. Спасибо за внимание.

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


  1. RedMachine
    02.04.2018 22:36
    +1

    Спасибо большое за перевод! Хоть и не совсем новичок в numpy — почитал с удовольствием.

    Не знаю, как зайдёт новичкам — а мне прям освежило память)

    Маленькие ремарки, которые я бы добавил:

    Заполнение массива одинаковым значением.
    >>> a = array([1, 2, 3], float)

    Если новичок шёл последовательно по статье и импортировал numpy через классическое
    import numpy as np — возникнет ошибка, не указана библиотека импортирования — np.array() — это единственный момент такой в статье.

    Транспонировать матрицу можно и простым a.T — лаконичнее намного)

    В остальном — отлично.
    П.с.: может стоит подумать, как concatenate более по-русски перевести.
    Буду ждать следующих статей!


    1. rainbowpenguin Автор
      03.04.2018 17:50

      Благодарю за позитивный отзыв. :)


    1. trapwalker
      04.04.2018 12:58

      Можно так и перевести: «конкатенация». Это распространённый и признанный термин даже в русскоязычной среде. Эдак и дезъюнкцию с конъюнкцией переводить, что ли?
      Кстати, давным давно в институте нам пытались привить русский перевод как «сцепить», «сцепка», но… не /зашло, знаете ли.


  1. lagranzh
    03.04.2018 12:23
    +3

    Спасибо за перевод. Мне кажется что для понимания вопроса, следует проговорить в слух некоторые вещи.


    Массив в numpy, это мета-данные (shape, dtype) и указатель на память, где сами данные лежат. Когда вы делаете reshape, transpose, etc — создаются новые мета-данные, но сами данные не клонируются.
    На конкретном примере, думаю, будет понятней:


    >>> import numpy as np
    >>> a = np.array(range(12))  # создаем одномерный массив чисел от 0 до 11
    >>> a
    array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])
    >>> b = a.reshape(3,4) # смотрим на него как на матрицу 3х4
    >>> b
    array([[ 0,  1,  2,  3],
           [ 4,  5,  6,  7],
           [ 8,  9, 10, 11]])
    >>> b[1,:] = 0 # обнулим среднюю строку
    >>> b
    array([[ 0,  1,  2,  3],
           [ 0,  0,  0,  0],
           [ 8,  9, 10, 11]])
    >>> a # увидим, что содержимое массива 'а' тоже поменялось
    array([ 0,  1,  2,  3,  0,  0,  0,  0,  8,  9, 10, 11])


    1. rainbowpenguin Автор
      03.04.2018 17:49

      Спасибо за пример.


  1. apollonin
    03.04.2018 13:52

    А есть numpy для php =)

    github.com/apollonin/numphp


    1. Kwent
      03.04.2018 14:23
      +1

      Мне думается, что NumPy — это типа аббревиатуры от Number (Numerical) и Python, поэтому говорить но «numpy» для PHP не совсем корректно, тем более я так понимаю у numpy и numphp разные создатели и вообще не вижу пересечений.

      UPD. А, вижу, вы и есть автор и вдохновлялись numpy, это многое объясняет :D


      1. apollonin
        03.04.2018 15:03

        ага, именно так. вдохновлялся numpy. Сейчас пишу на пайтоне и очень радуюсь этой либе.
        Поэтому была идея сделать аналог в базовом виде на php.


  1. Stefanio
    03.04.2018 14:29

    А существует ли аналог numpy для C#?


    1. masai
      05.04.2018 15:29

      Если нужна библиотека общего назначения для работы с матрицами — Math.NET Numerics. Она может использовать для расчётов высокопроизводительную библиотеку Intel MKL. По функционалу близка к BLAS/LAPACK.


  1. 0serg
    03.04.2018 17:24
    +2

    Обратите внимание, метод reshape создает новый массив, а не модифицирует оригинальный.

    Это не совсем так. Если есть возможность (а это обычно именно так) создается новый view в те же самые данные. Ну то есть numpy-объект (мета-данные) действительно создается новый («старый массив не изменился»), но вот указывает он на тот же массив данных, копирования не происходит. Если изменить данные в одном из подобных view, то изменится и содержимое всех остальных view.

    Можно кроме того явно потребовать reshape который будет in-place и гарантированно ничего не скопирует. Для этого достаточно просто присвоить новое значение аттрибуту .shape:

    a.shape = (5,2)


  1. 0serg
    03.04.2018 17:29
    +3

    Эти операции иногда полезны для сохранения большого количества данных в файлах

    Для операций с файлами есть ndarray.save / ndarray.savez и ndarray.load а также .tofile / .fromfile — их и следует использовать. Для human-readable вариантов есть ndarray.savetxt и ndarray.loadtxt. Все эти методы гораздо полезнее и чаще используются чем .tostring. Кроме того .tostring вообще устарел, вместо него следует использовать .tobytes


    1. rainbowpenguin Автор
      03.04.2018 17:44

      Хорошо, спасибо.


  1. 3-15
    03.04.2018 17:34

    Спасибо за статью. Единственное, чего мне в ней не хватает — это упоминания о type coercion в numpy-списках.


    1. rainbowpenguin Автор
      03.04.2018 17:43

      Я в конце цикла, от себя включу что-то, может. По комментам пособираю. Спасибо за замечание!


  1. Morgan_iv
    03.04.2018 17:34

    Поправьте, numpy.reshape возвращает view на массив если это возможно, а не копию. Этим как раз активно пользуются — без копирования менять представление данных для разных задач


    1. rainbowpenguin Автор
      03.04.2018 17:35

      уже написали про это:)


  1. beerware
    03.04.2018 17:35

    Мне представляется что самое главное в numpy — возможность избежать медленного перебора элементов массива для выполнения операции над ним. За счет этого достигается скорость.

    Также хорошо было отобразить тему numpy array vs python list. Именно ради numpy я познакомился с Питоном, и уже после массивов питоновские списки начали выглядеть алогичными )


    1. 0serg
      03.04.2018 19:27

      С перебором элементов массива в numpy все забавно. Итерация по numpy-массиву работает часто медленнее чем если этот массив вначале преобразовать в список а затем итерировать список

      for row in my_data:
        do_smthing(row) # slow
      for row in my_data.tolist():
        do_smthing(row) # faster !!! 
      

      Причина этого в том что при итерации по массиву надо создавать новые объекты row а при итерации по списку питон просто идет по уже лежащим в списке элементам. Создание же row-ов пачкой в методе .tolist() происходит сильно быстрее чем создании row-ов по одному при итерации :)


      1. beerware
        04.04.2018 15:57

        Вполне возможно, хотя это как раз тот способ который в numpy предпочли бы избежать )
        Скорее всего для do_smthing() в numpy найдется функция, которая сделает все над массивом сразу )


    1. iroln
      05.04.2018 12:05

      Самое главное в numpy — это многомерные массивы и векторизованные математические операции над ними. В numpy чаще всего вы оперируете массивами, а не отдельными элементами. Часто циклы писать не надо вообще. :)


  1. 0serg
    03.04.2018 19:29

    Раз уж тут кидают идеи автору статьи, то ловите еще одну: напишите про pandas.
    Это очень классный фреймворк для работы со структурированными данными (таблицами) который шикарно интегрирован с numpy. Для обработки реальных данных в numpy он колоссально облегчает жизнь.



  1. excoder
    03.04.2018 22:40

    Python(x,y) старенький уже.