Как строить трехмерные графики в Matplotlib по неравномерным данным

Немного рекламы

В статье Как рисовать трехмерные графики говорилось о том, как строить трехмерные поверхности, когда имеются данные в узлах прямоугольной равномерной сетки.

Скрипт для построения такой поверхности (взятый из статьи Как рисовать трехмерные графики, в дальнейших примерах мы будем от него отталкиваться) выглядит следующим образом:

# -*- coding: utf-8 -*-

import pylab
from mpl_toolkits.mplot3d import Axes3D
import numpy


def makeData():
    x = numpy.arange(-10, 10, 0.5)
    y = numpy.arange(-10, 10, 0.5)
    xgrid, ygrid = numpy.meshgrid(x, y)

    zgrid = numpy.sin(xgrid * 0.3) * numpy.cos(ygrid * 0.75)
    return xgrid, ygrid, zgrid


if __name__ == '__main__':
    x, y, z = makeData()

    fig = pylab.figure()
    axes = Axes3D(fig)
    axes.plot_surface(x, y, z, rstride=1, cstride=1)
    pylab.show()

Здесь функция makeData создает данные, которые будут использоваться Matplotlib для построения поверхности. В результате будет показана следующая трехмерная поверхность.

Теперь рассмотрим способы построения трехмерных поверхностей, когда узлы для расчета функции распределены не равномерно (случайно). Возможны два случая:

Первый случай. Сетка остается прямоугольной, но отсчеты по осям установлены случайным образом. Такую сетку будем называть прямоугольной неравномерной.

Второй случай. Все узлы расположены случайным образом на плоскости и не образуют прямоугольную сетку. Такую сетку будем называть непрямоугольной случайной.

Рассмотрим способы для построения графиков в обоих случаях.

Случай прямоугольной неравномерной сетки

Изменим нашу программу таким образом, чтобы координаты узлов сетки были установлены случайным образом. Часть скрипта, отвечающего за отображение графика мы оставим без изменения. Этот скрипт не будет корректно работать, но зато мы наглядно увидим проблему.

# -*- coding: utf-8 -*-

import pylab
from mpl_toolkits.mplot3d import Axes3D
import numpy


def makeData():
    # Координаты задаются случайным образом
    x = numpy.random.rand(100) * 20.0 - 10.0
    y = numpy.random.rand(100) * 20.0 - 10.0
    xgrid, ygrid = numpy.meshgrid(x, y)

    zgrid = numpy.sin(xgrid * 0.3) * numpy.cos(ygrid * 0.75)
    return xgrid, ygrid, zgrid


if __name__ == '__main__':
    x, y, z = makeData()

    fig = pylab.figure()
    axes = Axes3D(fig)

    axes.plot_surface(x, y, z, rstride=1, cstride=1)

    pylab.xlim(-10, 10)
    pylab.ylim(-10, 10)
    pylab.show()

Мы изменили только способ создания координат для задания узлов сетки. Результат этого скрипта будет следующим.

Если вы не поклонник художников-кубистов, то это вряд ли то, что вы хотели бы видеть, хотя очертания правильного графика прослеживаются. Проблема в том, что matplotlib отображает грани поверхности последовательно в том порядке, как они заданы на сетке. Если в первом примере для каждой оси мы задали отсчеты (массивы x и y, по которым затем будут созданы матрицы xgrid, ygrid для сетки) в виде последовательности чисел [-10.0, -9.9, -9.8, ...], то в новом примере отсчеты расположены в хаотичном порядке. Именно в этом порядке matplotlib и соединяет узлы. Таким образом, чтобы правильно отобразить график, достаточно отсортировать исходные массивы x и y, как показано в следующем примере:

# -*- coding: utf-8 -*-

import pylab
from mpl_toolkits.mplot3d import Axes3D
import numpy


def makeData():
    x = numpy.random.rand(100) * 20.0 - 10.0
    y = numpy.random.rand(100) * 20.0 - 10.0
    x.sort()
    y.sort()
    xgrid, ygrid = numpy.meshgrid(x, y)

    zgrid = numpy.sin(xgrid * 0.3) * numpy.cos(ygrid * 0.75)
    return xgrid, ygrid, zgrid


if __name__ == '__main__':
    x, y, z = makeData()

    fig = pylab.figure()
    axes = Axes3D(fig)

    axes.plot_surface(x, y, z, rstride=1, cstride=1)

    pylab.xlim(-10, 10)
    pylab.ylim(-10, 10)
    pylab.show()

Этот скрипт отобразит график в следующем виде:

В данном примере взято по 100 отсчетов по каждой оси (т.е. всего 10000 узлов сетки), и на графике видно неравномерное расположение линий сетки.

Случай непрямоугольной случайной сетки

В случае непрямоугольной случайной сетки мы уже не можем воспользоваться функцией meshgrid, которая создает двумерные матрицы сетки по одномерным массивам с отсчетами по осям.

Чтобы стало окончательно понятно, что мы подразумеваем под непрямоугольной случайной сеткой, изменим исходный скрипт таким образом, чтобы создавались точки со случайными координатами, и отобразим их в объеме с помощью метода scatter.

# -*- coding: utf-8 -*-

import pylab
from mpl_toolkits.mplot3d import Axes3D
import numpy


def makeData():
    x = numpy.random.rand(500) * 20.0 - 10.0
    y = numpy.random.rand(len(x)) * 20.0 - 10.0

    z = numpy.sin(x * 0.3) * numpy.cos(y * 0.75)
    return x, y, z


if __name__ == '__main__':
    x, y, z = makeData()

    fig = pylab.figure()
    axes = Axes3D(fig)

    axes.scatter(x, y, z)

    pylab.xlim(-10, 10)
    pylab.ylim(-10, 10)
    pylab.show()

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

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

# -*- coding: utf-8 -*-

import pylab
from mpl_toolkits.mplot3d import Axes3D
import numpy


def makeData():
    x = numpy.random.rand(500) * 20.0 - 10.0
    y = numpy.random.rand(len(x)) * 20.0 - 10.0

    z = numpy.sin(x * 0.3) * numpy.cos(y * 0.75)
    return x, y, z


if __name__ == '__main__':
    x, y, z = makeData()

    fig = pylab.figure()
    axes = Axes3D(fig)

    axes.plot_trisurf(x, y, z)

    pylab.xlim(-10, 10)
    pylab.ylim(-10, 10)
    pylab.show()

Результат будет выглядеть следующим образом:

В этом примере взято всего 500 точек, чтобы было лучше видно, что сетка, по которой строится поверхность стала треугольной, полученной в результате триангуляции случайно сгенерированных точек.

Если увеличить количество точек, то график будет более гладким:

Таким образом, мы научились рисовать трехмерные поверхности по неравномерным данным.

Другие статьи про Matplotlib

Немного рекламы

Вы можете подписаться на новости сайта через RSS, Группу Вконтакте или Канал в Telegram.
4.5 stars

Рейтинг 4.5/5. Всего 2 голос(а, ов)




Подписаться на комментарии
Автор:
Тема:
 Ваш комментарий
 
 
Введите код 568