Почему pyplot.contour () требует, чтобы Z был 2D-массивом?

Функция matplotlib.pyplot.contour() принимает 3 входных массива X , Y и Z
Массивы X и Y задают x- и y-координаты точек, а Z задает соответствующее значение интересующей функции, оцениваемой в точках.

Я понимаю, что np.meshgrid() упрощает создание массивов, которые служат аргументами для contour() :

  • OpenCV TypeError: контур не является массивом numpy, ни скалярным
  • Скрыть контур linestroke на pyplot.contourf, чтобы получить только заливки
  • Вычислить центр контура / Область
  •  X = np.arange(0,5,0.01) Y = np.arange(0,3,0.01) X_grid, Y_grid = np.meshgrid(X,Y) Z_grid = X_grid**2 + Y_grid**2 plt.contour(X_grid, Y_grid, Z_grid) # Works fine 

    Это прекрасно работает. И удобно, это тоже отлично работает:

     plt.contour(X, Y, Z_grid) # Works fine too 

    Однако почему Z вход должен быть 2D-массивом?

    Почему что-то вроде следующего недопустимо, хотя оно указывает все одинаковые данные, соответствующие соответствующим образом?

     plt.contour(X_grid.ravel(), Y_grid.ravel(), Z_grid.ravel()) # Disallowed 

    Кроме того, какова семантика, когда задано только Z (без соответствующих X и Y )?

  • Как пропустить пустые даты (выходные) в финансовом графике Matplotlib Python?
  • Установка цветового диапазона matplotlib
  • Как я могу повернуть xticklabels в matplotlib, чтобы расстояние между каждой xtlllabel было равно?
  • Нет отображаемого имени и переменной $ DISPLAY среды с помощью tkinter через ssh
  • Не удалось установить matplotlib на Mac OS X
  • matplotlib color в 3d-графике из набора данных x, y, z без использования контура
  • 3 Solutions collect form web for “Почему pyplot.contour () требует, чтобы Z был 2D-массивом?”

    Рассматривая документацию по contour вы обнаружите, что существует несколько способов вызова этой функции, например, contour(Z) или contour(X,Y,Z) . Таким образом, вы обнаружите, что он не требует каких-либо значений X или Y

    Однако для построения контура базовая сетка должна быть известна функции. contour Matplotlib основан на прямоугольной сетке. Но даже в этом случае, позволяя contour(z) , когда z является массивом 1D, сделало бы невозможным знать, как должно быть построено поле. В случае contour(Z) где Z является двумерным массивом, его форма однозначно устанавливает сетку для графика.

    Как только эта сетка известна, неважно, будут ли факультативные массивы X и Y сплющены или нет; что на самом деле говорит нам документация:

    X и Y должны быть двухмерными с той же формой, что и Z, или они должны быть одинаковыми, чтобы len (X) – количество столбцов в Z, а len (Y) – количество строк в Z.

    Также довольно очевидно, что plt.contour(X_grid.ravel(), Y_grid.ravel(), Z_grid.ravel()) как plt.contour(X_grid.ravel(), Y_grid.ravel(), Z_grid.ravel()) не может создать контурный график, потому что вся информация о форме сетки теряется и нет так как функция контура могла бы знать, как интерпретировать данные. Например, если len(Z_grid.ravel()) == 12 , форма базовой сетки может быть любой из (1,12), (2,6), (3,4), (4,3), (6,2), (12,1) .

    Разумеется, возможным выходом может быть использование 1D массивов и введение shape аргумента, например plt.contour(x,y,z, shape=(6,2)) . Это, однако, не так, поэтому вам нужно жить с тем, что Z должен быть 2D.

    Однако, если вы ищете способ получить графский график со сплюснутыми (выгнутыми) массивами, это возможно с помощью plt.tricontour() .

     plt.tricontour(X_grid.ravel(), Y_grid.ravel(), Z_grid.ravel()) 

    Здесь треугольная сетка будет создаваться внутренне с использованием треугольника Делоне. Поэтому даже полностью рандомизированные точки приведут к хорошему результату, как можно видеть на следующем рисунке, где это сравнивается с теми же случайными точками, что и для contour .

    введите описание изображения здесь

    (Вот код для создания этого изображения )

    Фактический код алгоритма plt.contour можно найти в файле _countour.cpp . Это довольно сложный C-код, поэтому его трудно точно следовать, но если бы я пытался создать код, генерирующий контуры, я бы сделал это следующим образом. Выберите некоторую точку (x, y) на границе и исправьте ее значение z . Итерируйте по соседним точкам и выберите тот, для которого z-значение является самым близким к z-значению первой точки. Продолжайте итерацию для новой точки, выберите ближайшую точку с близким к z значением z (но убедитесь, что вы не вернетесь к точке, которую вы только что посетили, поэтому вам нужно идти в каком-то «направлении»), и продолжайте, пока не получите цикл или достичь некоторой границы.

    Кажется, что-то близко (но немного сложнее) реализовано в _counter.cpp .

    Как видно из неофициального описания алгоритма, вы должны найти точку, которая находится «рядом» с текущей. Это легко сделать, если у вас прямоугольная сетка точек (требуется примерно 4 или 8 итераций: (x[i+1][j], y[i+1][j]) , (x[i][j+1], y[i][j+1]) , (x[i-1][j], y[i-1][j]) и т. д.). Но если у вас есть случайно выбранные точки (без какого-либо определенного порядка), эта проблема становится сложной: вам нужно перебирать все точки, которые вы должны найти рядом, и сделать следующий шаг. Сложностью такого шага является O(n) , где n – количество точек (обычно это квадрат размера изображения). Таким образом, алгоритм становится намного медленнее, если у вас нет прямоугольной сетки.

    Вот почему вам действительно нужны три 2d-массива, которые соответствуют значениям x, y и z некоторых точек, расположенных над некоторой прямоугольной сеткой.

    Как вы правильно отметили, x и y могут быть 1d-массивами. В этом случае соответствующие 2d-массивы реконструируются с помощью meshgrid . Однако в этом случае вы должны иметь z как 2d-массив.

    Если задано только z , x и y являются range соответствующих длин.

    РЕДАКТИРОВАТЬ. Вы можете попытаться «подделать» двумерные массивы x , y и z таким образом, чтобы x и y не формировали прямоугольную сетку, чтобы проверить правильность моих допущений.

     import matplotlib.pyplot as plt import numpy as np %matplotlib inline x = np.random.uniform(-3, 3, size=10000) y = np.random.uniform(-3, 3, size=10000) z = x**2 + y**2 X, Y, Z = (u.reshape(100, 100) for u in (x, y, z)) plt.contour(X, Y, Z) 

    Неверный результат

    Как вы видите, изображение не похоже на что-либо близкое к правильному графу, если (x, y, z) являются лишь некоторыми случайными точками.

    Предположим теперь, что x сортируется как шаг предварительной обработки, как предлагает @dhrummel в комментариях. Обратите внимание, что мы не можем сортировать x и y одновременно, поскольку они не являются независимыми (мы хотим сохранить одни и те же точки).

     x = np.random.uniform(-3, 3, size=10000) y = np.random.uniform(-3, 3, size=10000) z = x**2 + y**2 xyz = np.array([x, y, z]).T x, y, z = xyz[xyz[:, 0].argsort()].T assert (x == np.sort(x)).all() X, Y, Z = (u.reshape(100, 100) for u in (x, y, z)) plt.contour(X, Y, Z) 

    x сортируется сейчас

    Опять же, картина неправильная, из-за того, что y не сортируются (в каждом столбце), как если бы мы имели прямоугольную сетку вместо некоторых случайных точек.

    Причиной X и Y для 2D является следующее. Z соответствует каждой координате (x, y) в системе осей соответствующей «глубины» для создания 3D-графика с координатами x, y и z.

    Теперь предположим, что мы хотим указать на произвольную точку в системе осей. Мы можем это сделать, предоставив для этой точки координаты x и y (x, y). Например (0,0). Теперь рассмотрим «строку» со значением x 1. На этой строке есть несколько значений ny, которые выглядят как-то вроде:

    введите описание изображения здесь

    Если мы построим эти строки для всех значений x и значений y, мы получим что-л. как:

    введите описание изображения здесь

    Как вы можете видеть, у нас есть 2D-аннотация, состоящая из 2 2D- массивов, одна для значений x, которые имеют форму:

     1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 #--> Two dimensional x values array 

    и один для значений y, которые имеют форму:

     10 10 10 10 10 10 10 10 10 10 9 9 9 9 9 9 9 9 9 9 8 8 8 8 8 8 8 8 8 8 ... 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 #--> Two dimensional y values array 

    Эти два вместе обеспечивают координаты (x, y) для каждой точки в системе координат. Теперь мы можем построить для каждой точки «глубина» значение Z (координата z). Теперь также очевидно, почему переменная Z должна быть двумерной с формой (len (x), len (y)), поскольку в противном случае она не может обеспечить значение для всех точек.

    Такое поведение может быть реализовано посредством предоставления массивов 2D x, y и z функции OR: предоставить функции 1D x и y функции, а функция внутренне создает 2D-сетку из значений x и y с помощью smth. как X, Y = np.meshgrid (x, y), но тем не менее z должно быть двумерным.

    Python - лучший язык программирования в мире.