Насправді мета 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]])
і h:
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
і 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 для двовимірних графіків.
Візуалізовано:
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
і 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
.
Або навести ще один цікавий приклад, використовуючи моделі астропії (на цей раз я не дуже переймаюся координатами, я просто використовую їх для створення якоїсь сітки):
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
. Таємничою для мене частиною було те, чому вона повертає ту пару результатів і як вони виглядають. Відповідь Хай Фана для цього корисна. Я думаю, що це робиться для зручності, оскільки сюжет хоче таких двох параметрів.