Сегодня я бы хотел продолжить рассказ про замечательную библиотеку для работы с числовыми массивами в php numphp. Ранее я уже делал краткий обзор на неё тут. С тех пор библиотека обросла функционалом, и, что самое главное, научилась работать с многомерными массивами или матрицами. Про них, в основном и будет идти речь.

Без лишних вступлений сразу пример того, как можно создать матрицу 3 на 4, используя возможности numphp.

$matrix = new np_array([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]);

// matrix
[[ 0,  1,  2,  3],
 [ 4,  5,  6,  7],
 [ 8,  9, 10, 11]]

В результате мы получаем объект np_array. Более того, каждая строка матрицы является также объектом этого типа. Это позволяет делать универсальные выборки.
К примеру, для доступа ко второй строке можно использовать стандартную логику

$row = $matrix[1];

//row
[4, 5, 6, 7]

Но, это было бы неинтересно =)

Благодаря тому, что любой срез матрицы является np_array, мы можем одним махом узнать, к примеру, среднее значение для всей третьей строки

$avg = $matrix[2]->mean();

// avg
9.5

Более того, используя операторы сравнения, можно выбрать все элементы матрицы, удовлетворяющие условию, к примеру больше 5

$result = $matrix[$matrix->gt(5)]
// or
$result = $matrix[$matrix['> 5']]

// result
[ 6,  7,  8,  9, 10, 11]

И получить их сумму

$result = $matrix[$matrix['> 5']]->sum()

// result
51

Как я упомянул выше, мы можем использовать всю мощь срезов, реализованных в numphp, применимо к матрицам тоже. К примеру, взять вторую и третью строку

$result = $matrix['1:3'];

// result
[[ 4,  5,  6,  7],
 [ 8,  9, 10, 11]]

Более детальное описание возможностей можно найти в документации библиотеки.

Математические операции


На данный момент к матрицам можно применять теже операции, что и к векторным массивам. Операция будет применяться к каждому элементу исходной матрицы.

$result = $matrix->mul(5); // multiply

// result
[[ 0,  5, 10, 15],
 [20, 25, 30, 35],
 [40, 45, 50, 55]]

Можно так же суммировать матрицу и вектор. В таком случае вектор будет суммироваться с каждой строкой матрицы

$result = $matrix->add([1, 2, 3, 4]);

[[ 1,  3,  5,  7],
 [ 5,  7,  9, 11],
 [ 9, 11, 13, 15]]

Или даже суммировать две матрицы. Тут так же будет виден один из способов как можно сгенерировать матрицу нужного размера.

$diffMartrix = Generator::ones([3, 4]);

// diffMartrix
[[1, 1, 1, 1],
 [1, 1, 1, 1],
 [1, 1, 1, 1]]

$result = $matrix->add($diffMatrix);

// result
[[ 1,  2,  3,  4],
 [ 5,  6,  7,  8],
 [ 9, 10, 11, 12]]

Таким образом можно значительно упростить и ускорить работу с матрицами и векторами.

Изменение размеров


Каждый объект np_array может легко менять свою форму. Под формой я подразумеваю размерность конкретного объекта. К примеру

$list = new np_array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
$list->shape

// [10]

$matrix = new np_array([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]);
$matrix->shape

// [3, 4]

Упрощенно можно считать, что первое число означает количество строк, второе — количество колонок. Но, важно понимать, что библиотека позволяет работать с n-мерными массивами.

Если мы хотим взять нашу матрицу из примера и сделать из неё, скажем, массив точек на координатной доске, мы можем превратить её в матрицу размерности 6х2

$newMatrix = $matrix->reshape([6, 2]);

// newMatrix

[[ 0,  1],
 [ 2,  3],
 [ 4,  5],
 [ 6,  7],
 [ 8,  9],
 [10, 11]]

Если нужно представить n-мерный массив как 1-мерный, можно воспользоваться методом flatten

$result = $matrix->flatten();

// result
[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11]

Данный подход удобен для быстрой генерации какого-то тестового набора данных нужной формы.

$result = Generator::arange(1, 15)->reshape([2, 7]);

// result
[[ 1,  2,  3,  4,  5,  6,  7],
 [ 8,  9, 10, 11, 12, 13, 14]]

Последнее что хотел бы описать тут, но далеко не последнее что умеет библиотека — работа с диагональными срезами матриц.

$matrix = Generator::arange(16)->reshape([4, 4]);

// matrix
[[ 0,  1,  2,  3],
 [ 4,  5,  6,  7],
 [ 8,  9, 10, 11],
 [12, 13, 14, 15]]

// get matrix diagonal
[ 0,  5, 10, 15]

Практически аналогичным способом можно сгенерировать матрицу, с заданной диагональю

$matrix = Generator::diagonal([5, 3, 1]);

// matrix
[[5, 0, 0],
 [0, 3, 0],
 [0, 0, 1]]

На самом деле библиотека включает в себя ещё много приятных и полезных вещей для работы с числовыми массивами и матрицами любой размерности. Комбинируя их, можно очень быстро и просто делать вычисления, основанные на данных. В планах ещё много чего хотелось бы реализовать, поэтому работа над библиотекой продолжается.

Дополнительные возможности и документацию вы можете найти здесь.

Также, если вы хотите внести свой вклад в развитие — буду рад обсудить любые вопросы.

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


  1. gudvinr
    30.01.2018 16:56

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


    Не рассматривали возможность портирования numpy как расширение PHP, например?


    1. apollonin Автор
      30.01.2018 17:09

      Пока что нет, думаю этот процесс может быть крайне долгим, учитывая текущее состояния языка и его переход активный на php 7+.
      Вообще планировалось предоставить классический подход к использованию зависимостей через composer.

      Работа над производительностью будет дальнейшим шагом в развитии библиотеки.


      1. firk
        30.01.2018 17:20
        +2

        текущее состояния языка и его переход активный на php 7+

        Эм, переход уже завершён довольно давно, а 5.x — legacy которое где-то по инерции используется.


        классический подход к использованию зависимостей через composer

        Как обычно в мире пхп всё сделали абы как, скопировав внешние признаки чего-то успешного.


        Работа над производительностью будет дальнейшим шагом

        Это не "шаг", там всё заново придётся переписывать (кроме спецификаций).


    1. rotor
      31.01.2018 10:52

      Совершенно правильное направление предлагаете. Но портирование numpy в PHP выглядит как костыль.
      Более верным было бы сделать нативное расширение. Например, используя Eigen + php-cpp


      1. apollonin Автор
        31.01.2018 16:14

        вообще php-cpp выглядит захватывающе. была бы мотивация, а переделать на нативное расширение — дело техники.


  1. mickvav
    31.01.2018 00:52

    Может это, написать инлайновый фортран для php? Будет числодробить так, что numpy курит за остановкой… ;)


  1. rotor
    31.01.2018 11:00

    А какие задачи решит ваша библиотека? С какой целью она создавалась?


    1. apollonin Автор
      31.01.2018 15:46

      Краткую мотивацию описывал в первой статье: habrahabr.ru/post/347470
      Что б не повторяться…