Сегодня я бы хотел продолжить рассказ про замечательную библиотеку для работы с числовыми массивами в php numphp. Ранее я уже делал краткий обзор на неё тут. С тех пор библиотека обросла функционалом, и, что самое главное, научилась работать с многомерными массивами или матрицами. Про них, в основном и будет идти речь.
Без лишних вступлений сразу пример того, как можно создать матрицу 3 на 4, используя возможности numphp.
В результате мы получаем объект np_array. Более того, каждая строка матрицы является также объектом этого типа. Это позволяет делать универсальные выборки.
К примеру, для доступа ко второй строке можно использовать стандартную логику
Но, это было бы неинтересно =)
Благодаря тому, что любой срез матрицы является np_array, мы можем одним махом узнать, к примеру, среднее значение для всей третьей строки
Более того, используя операторы сравнения, можно выбрать все элементы матрицы, удовлетворяющие условию, к примеру больше 5
И получить их сумму
Как я упомянул выше, мы можем использовать всю мощь срезов, реализованных в numphp, применимо к матрицам тоже. К примеру, взять вторую и третью строку
Более детальное описание возможностей можно найти в документации библиотеки.
На данный момент к матрицам можно применять теже операции, что и к векторным массивам. Операция будет применяться к каждому элементу исходной матрицы.
Можно так же суммировать матрицу и вектор. В таком случае вектор будет суммироваться с каждой строкой матрицы
Или даже суммировать две матрицы. Тут так же будет виден один из способов как можно сгенерировать матрицу нужного размера.
Таким образом можно значительно упростить и ускорить работу с матрицами и векторами.
Каждый объект np_array может легко менять свою форму. Под формой я подразумеваю размерность конкретного объекта. К примеру
Упрощенно можно считать, что первое число означает количество строк, второе — количество колонок. Но, важно понимать, что библиотека позволяет работать с n-мерными массивами.
Если мы хотим взять нашу матрицу из примера и сделать из неё, скажем, массив точек на координатной доске, мы можем превратить её в матрицу размерности 6х2
Если нужно представить n-мерный массив как 1-мерный, можно воспользоваться методом flatten
Данный подход удобен для быстрой генерации какого-то тестового набора данных нужной формы.
Последнее что хотел бы описать тут, но далеко не последнее что умеет библиотека — работа с диагональными срезами матриц.
Практически аналогичным способом можно сгенерировать матрицу, с заданной диагональю
На самом деле библиотека включает в себя ещё много приятных и полезных вещей для работы с числовыми массивами и матрицами любой размерности. Комбинируя их, можно очень быстро и просто делать вычисления, основанные на данных. В планах ещё много чего хотелось бы реализовать, поэтому работа над библиотекой продолжается.
Дополнительные возможности и документацию вы можете найти здесь.
Также, если вы хотите внести свой вклад в развитие — буду рад обсудить любые вопросы.
Без лишних вступлений сразу пример того, как можно создать матрицу 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)
mickvav
31.01.2018 00:52Может это, написать инлайновый фортран для php? Будет числодробить так, что numpy курит за остановкой… ;)
rotor
31.01.2018 11:00А какие задачи решит ваша библиотека? С какой целью она создавалась?
apollonin Автор
31.01.2018 15:46Краткую мотивацию описывал в первой статье: habrahabr.ru/post/347470
Что б не повторяться…
gudvinr
Прелесть numpy не сколько в удобстве синтаксиса (чем, как раз, в угоду производительности иногда жертвуют), а в том, что позволяет в тормозном python реализовывать быстрые числодробилки.
Не рассматривали возможность портирования numpy как расширение PHP, например?
apollonin Автор
Пока что нет, думаю этот процесс может быть крайне долгим, учитывая текущее состояния языка и его переход активный на php 7+.
Вообще планировалось предоставить классический подход к использованию зависимостей через composer.
Работа над производительностью будет дальнейшим шагом в развитии библиотеки.
firk
Эм, переход уже завершён довольно давно, а 5.x — legacy которое где-то по инерции используется.
Как обычно в мире пхп всё сделали абы как, скопировав внешние признаки чего-то успешного.
Это не "шаг", там всё заново придётся переписывать (кроме спецификаций).
rotor
Совершенно правильное направление предлагаете. Но портирование numpy в PHP выглядит как костыль.
Более верным было бы сделать нативное расширение. Например, используя Eigen + php-cpp
apollonin Автор
вообще php-cpp выглядит захватывающе. была бы мотивация, а переделать на нативное расширение — дело техники.