На самом деле цель np.meshgrid
уже упоминается в документации:
np.meshgrid
Вернуть координатные матрицы из координатных векторов.
Создайте ND координатные массивы для векторизованных вычислений ND скалярных / векторных полей над ND сетками, учитывая одномерные координатные массивы x1, x2, ..., xn.
Поэтому его основная цель состоит в создании координатных матриц.
Вы, наверное, просто спросили себя:
Зачем нам нужно создавать координатные матрицы?
Причина, по которой вам нужны координатные матрицы с Python / NumPy, заключается в том, что нет прямой связи между координатами и значениями, кроме случаев, когда ваши координаты начинаются с нуля и являются чисто положительными целыми числами. Тогда вы можете просто использовать индексы массива в качестве индекса. Однако, когда это не так, вам нужно как-то хранить координаты вместе с вашими данными. Вот где приходят сетки.
Предположим, ваши данные:
1 2 1
2 5 2
1 2 1
Однако каждое значение представляет область шириной 2 километра по горизонтали и 3 километра по вертикали. Предположим, ваше происхождение - это верхний левый угол, и вы хотите, чтобы массивы представляли расстояние, которое вы могли бы использовать:
import numpy as np
h, v = np.meshgrid(np.arange(3)*3, np.arange(3)*2)
где v это:
array([[0, 0, 0],
[2, 2, 2],
[4, 4, 4]])
и ч:
array([[0, 3, 6],
[0, 3, 6],
[0, 3, 6]])
Итак, если у вас есть два индекса, скажем, x
и y
(именно поэтому возвращаемое значение meshgrid
обычно xx
или xs
вместо того, что x
в этом случае я выбрал h
для горизонтали!), То вы можете получить координату x точки, координату y точки и значение в этой точке с помощью:
h[x, y] # horizontal coordinate
v[x, y] # vertical coordinate
data[x, y] # value
Это значительно облегчает отслеживание координат и (что еще более важно) вы можете передавать их функциям, которые должны знать координаты.
Немного более длинное объяснение
Тем не менее, np.meshgrid
сам по себе не часто используется напрямую, в основном один просто использует один из похожих объектов np.mgrid
или np.ogrid
. Здесь np.mgrid
представляет собой sparse=False
и np.ogrid
в sparse=True
случае (я ссылаться на sparse
аргумент np.meshgrid
). Обратите внимание, что между np.meshgrid
and np.ogrid
и есть существенная разница
np.mgrid
: первые два возвращаемых значения (если их два или более) меняются местами. Часто это не имеет значения, но вы должны давать значимые имена переменных в зависимости от контекста.
Например, в случае двумерной сетки matplotlib.pyplot.imshow
имеет смысл назвать первый возвращаемый элемент np.meshgrid
x
и второй, в y
то время как для np.mgrid
и наоборот np.ogrid
.
np.ogrid
и разреженные сетки
>>> import numpy as np
>>> yy, xx = np.ogrid[-5:6, -5:6]
>>> xx
array([[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5]])
>>> yy
array([[-5],
[-4],
[-3],
[-2],
[-1],
[ 0],
[ 1],
[ 2],
[ 3],
[ 4],
[ 5]])
Как уже говорилось, результат по сравнению с обратным np.meshgrid
, поэтому я распаковал его yy, xx
вместо xx, yy
:
>>> xx, yy = np.meshgrid(np.arange(-5, 6), np.arange(-5, 6), sparse=True)
>>> xx
array([[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5]])
>>> yy
array([[-5],
[-4],
[-3],
[-2],
[-1],
[ 0],
[ 1],
[ 2],
[ 3],
[ 4],
[ 5]])
Это уже выглядит как координаты, в частности, линии x и y для 2D-графиков.
Визуализация:
yy, xx = np.ogrid[-5:6, -5:6]
plt.figure()
plt.title('ogrid (sparse meshgrid)')
plt.grid()
plt.xticks(xx.ravel())
plt.yticks(yy.ravel())
plt.scatter(xx, np.zeros_like(xx), color="blue", marker="*")
plt.scatter(np.zeros_like(yy), yy, color="red", marker="x")
np.mgrid
и плотные / плотные сетки
>>> yy, xx = np.mgrid[-5:6, -5:6]
>>> xx
array([[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5]])
>>> yy
array([[-5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5],
[-4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4],
[-3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3],
[-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2],
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
[ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4],
[ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]])
То же самое применимо и здесь: результат обратный по сравнению с np.meshgrid
:
>>> xx, yy = np.meshgrid(np.arange(-5, 6), np.arange(-5, 6))
>>> xx
array([[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5]])
>>> yy
array([[-5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5],
[-4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4],
[-3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3],
[-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2],
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
[ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4],
[ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]])
В отличие от ogrid
этих массивов все содержат xx
и yy
координаты в -5 <= xx <= 5; -5 <= yy <= 5 сетки.
yy, xx = np.mgrid[-5:6, -5:6]
plt.figure()
plt.title('mgrid (dense meshgrid)')
plt.grid()
plt.xticks(xx[0])
plt.yticks(yy[:, 0])
plt.scatter(xx, yy, color="red", marker="x")
функциональность
Это не ограничивается только 2D, эти функции работают для произвольных измерений (ну, в Python есть максимальное количество аргументов для функции и максимальное количество измерений, которое допускает NumPy):
>>> x1, x2, x3, x4 = np.ogrid[:3, 1:4, 2:5, 3:6]
>>> for i, x in enumerate([x1, x2, x3, x4]):
... print('x{}'.format(i+1))
... print(repr(x))
x1
array([[[[0]]],
[[[1]]],
[[[2]]]])
x2
array([[[[1]],
[[2]],
[[3]]]])
x3
array([[[[2],
[3],
[4]]]])
x4
array([[[[3, 4, 5]]]])
>>> # equivalent meshgrid output, note how the first two arguments are reversed and the unpacking
>>> x2, x1, x3, x4 = np.meshgrid(np.arange(1,4), np.arange(3), np.arange(2, 5), np.arange(3, 6), sparse=True)
>>> for i, x in enumerate([x1, x2, x3, x4]):
... print('x{}'.format(i+1))
... print(repr(x))
# Identical output so it's omitted here.
Даже если они также работают для 1D, есть две (гораздо более распространенные) функции создания сетки 1D:
Помимо аргумента start
and stop
он также поддерживает step
аргумент (даже сложные шаги, которые представляют количество шагов):
>>> x1, x2 = np.mgrid[1:10:2, 1:10:4j]
>>> x1 # The dimension with the explicit step width of 2
array([[1., 1., 1., 1.],
[3., 3., 3., 3.],
[5., 5., 5., 5.],
[7., 7., 7., 7.],
[9., 9., 9., 9.]])
>>> x2 # The dimension with the "number of steps"
array([[ 1., 4., 7., 10.],
[ 1., 4., 7., 10.],
[ 1., 4., 7., 10.],
[ 1., 4., 7., 10.],
[ 1., 4., 7., 10.]])
Приложения
Вы специально спросили о цели, и на самом деле, эти сетки очень полезны, если вам нужна система координат.
Например, если у вас есть функция NumPy, которая вычисляет расстояние в двух измерениях:
def distance_2d(x_point, y_point, x, y):
return np.hypot(x-x_point, y-y_point)
И вы хотите знать расстояние каждой точки:
>>> ys, xs = np.ogrid[-5:5, -5:5]
>>> distances = distance_2d(1, 2, xs, ys) # distance to point (1, 2)
>>> distances
array([[9.21954446, 8.60232527, 8.06225775, 7.61577311, 7.28010989,
7.07106781, 7. , 7.07106781, 7.28010989, 7.61577311],
[8.48528137, 7.81024968, 7.21110255, 6.70820393, 6.32455532,
6.08276253, 6. , 6.08276253, 6.32455532, 6.70820393],
[7.81024968, 7.07106781, 6.40312424, 5.83095189, 5.38516481,
5.09901951, 5. , 5.09901951, 5.38516481, 5.83095189],
[7.21110255, 6.40312424, 5.65685425, 5. , 4.47213595,
4.12310563, 4. , 4.12310563, 4.47213595, 5. ],
[6.70820393, 5.83095189, 5. , 4.24264069, 3.60555128,
3.16227766, 3. , 3.16227766, 3.60555128, 4.24264069],
[6.32455532, 5.38516481, 4.47213595, 3.60555128, 2.82842712,
2.23606798, 2. , 2.23606798, 2.82842712, 3.60555128],
[6.08276253, 5.09901951, 4.12310563, 3.16227766, 2.23606798,
1.41421356, 1. , 1.41421356, 2.23606798, 3.16227766],
[6. , 5. , 4. , 3. , 2. ,
1. , 0. , 1. , 2. , 3. ],
[6.08276253, 5.09901951, 4.12310563, 3.16227766, 2.23606798,
1.41421356, 1. , 1.41421356, 2.23606798, 3.16227766],
[6.32455532, 5.38516481, 4.47213595, 3.60555128, 2.82842712,
2.23606798, 2. , 2.23606798, 2.82842712, 3.60555128]])
Вывод был бы идентичен, если бы он проходил в плотной сетке вместо открытой сетки. Вещание NumPys делает это возможным!
Давайте представим результат:
plt.figure()
plt.title('distance to point (1, 2)')
plt.imshow(distances, origin='lower', interpolation="none")
plt.xticks(np.arange(xs.shape[1]), xs.ravel()) # need to set the ticks manually
plt.yticks(np.arange(ys.shape[0]), ys.ravel())
plt.colorbar()
И это также, когда NumPys mgrid
и ogrid
становится очень удобным, потому что позволяет легко изменять разрешение ваших сеток:
ys, xs = np.ogrid[-5:5:200j, -5:5:200j]
# otherwise same code as above
Однако, поскольку imshow
не поддерживает x
и y
вводит, нужно поменять галочки вручную. Было бы очень удобно , если она будет принимать x
и y
координаты, не так ли?
С помощью NumPy легко написать функции, которые естественно работают с сетками. Кроме того, в NumPy, SciPy, matplotlib есть несколько функций, которые ожидают, что вы перейдете в таблицу.
Мне нравятся изображения, поэтому давайте рассмотрим matplotlib.pyplot.contour
:
ys, xs = np.mgrid[-5:5:200j, -5:5:200j]
density = np.sin(ys)-np.cos(xs)
plt.figure()
plt.contour(xs, ys, density)
Обратите внимание, что координаты уже установлены правильно! Это не было бы так, если бы вы только что прошли в density
.
Или дать еще один пример прикольный используя astropy модели ( на этот раз я не забочусь много о координатах, я просто использовать их , чтобы создать некоторую сетку):
from astropy.modeling import models
z = np.zeros((100, 100))
y, x = np.mgrid[0:100, 0:100]
for _ in range(10):
g2d = models.Gaussian2D(amplitude=100,
x_mean=np.random.randint(0, 100),
y_mean=np.random.randint(0, 100),
x_stddev=3,
y_stddev=3)
z += g2d(x, y)
a2d = models.AiryDisk2D(amplitude=70,
x_0=np.random.randint(0, 100),
y_0=np.random.randint(0, 100),
radius=5)
z += a2d(x, y)
Хотя это просто "для внешнего вида", некоторые функции, связанные с функциональными моделями и примеркой (например scipy.interpolate.interp2d
,
scipy.interpolate.griddata
даже показывать примеры использования np.mgrid
) в Scipy и т. Д., Требуют сеток. Большинство из них работают с открытыми сетками и плотными сетками, однако некоторые работают только с одним из них.
xx
иyy
. Для меня загадочной частью было то, почему он возвращает ту пару результатов и как они выглядят. Ответ Хай Фана удобен для этого. Я полагаю, это сделано для удобства, так как plot хочет два таких параметра.