Время прочтения: 11 мин.

Data Scientist работает с огромным объемом данных, который необходимо проанализировать и обработать. Одним из подходов к анализу данных является их визуализация с использованием графического представления.

Сегодня существует множество библиотек для визуализации данных в Python. Одной из самых популярных является Matplotlib, однако этот инструмент создавался задолго до бурного развития Data Science, и в большей мере ориентирован на отображение массивов NumPy и параметрических функций SciPy. В то же время в Data Science распространен обобщенный тип объектов – датасеты, крупные таблицы с разнородными данными. Для визуализации подобных данных разрабатываются новые библиотеки визуализации, например, Plotly.

Далее предложим вашему вниманию сравнительный анализ библиотек Matplotlib и Plotly.

Сравнение проведем на данных, полученных при решении задачи оптимизации электронно-лучевого процесса. Подробней задача описана в отчете о научно-исследовательской работе «математическое и алгоритмическое обеспечение процесса электронно-лучевой сварки тонкостенных конструкций аэрокосмического назначения». Результаты работы модели сильно зависят от начальных параметров, которые варьируются в широком диапазоне, поэтому для экспресс-анализа полученных данных целесообразно использовать графическое представление переменных и целевой функции.

Программная реализация описанной задачи разбита на два модуля:

  • module_1 содержит функции T_1, T_2, ψ (описание функций приведено ниже);
  • module_2 выполняет расчет распределения температур (T_1 + T_2) по заданным параметрам с последующим графическим выводом в отдельном окне с возможностью сохранения графика в файл.

Для визуализации математического моделирования тепловых процессов необходимо установить библиотеки Plotly и Matplotlib. Plotly не входит ни в Anaconda, ни в стандартный пакет, поэтому устанавливаем через командную строку:

pip install plotly 

Устанавливаем Matplotlib в Jupyter notebook с помощью кода:


!pip install matplotlib 

Также для работы понадобятся библиотеки NumPy, SciPy, Pandas, Math и Csv для работы с сырыми данными:

import plotly
import plotly.graph_objs as go
import plotly.express as px
from plotly.subplots import make_subplots
import numpy as np
import pandas as pd
import os,sys,inspect
import random
import math
import scipy.integrate
import matplotlib.pyplot as plt
import math
import scipy.integrate
import csv

Ниже представлена функция для импортирования настроек из конфигурационного csv файла, принимает 1 аргумент — число — номер модуля, сам csv файл размещен в репозитории.

def import_csv_cofigs(module_num):
    try:
        # Начальная температура изделия
        T_n = 0
        # Время
        t_ = 0
        # Мощность
        q_ = 0
        # Теплоемкость материала
        cp_ = 0
        # Коэффициент температуропроводности
        alpha_ = 0
        # Скорость сварки
        v_ = 0
        # Коэффициент теплопроводности
        lambda_ = 0
        # Толщина
        delta_ = 0
        mainDialect = csv.Dialect
        mainDialect.doublequote = True
        mainDialect.quoting = csv.QUOTE_ALL
        mainDialect.delimiter = ';'
        mainDialect.lineterminator = '\n'
        mainDialect.quotechar = '"'
        with open(f'module_{module_num}/module_{module_num}_input.csv', 'r', encoding='utf8') as fr:
            file_reader = csv.reader(fr, dialect=mainDialect)
            for cnf_row in file_reader:
                if cnf_row[0] == '1':
                    T_n = float(cnf_row[2])
                if cnf_row[0] == '2':
                    t_ = float(cnf_row[2])
                if cnf_row[0] == '3':
                    q_ = float(cnf_row[2])
                if cnf_row[0] == '4':
                    cp_ = float(cnf_row[2])
                if cnf_row[0] == '5':
                    alpha_ = float(cnf_row[2])
                if cnf_row[0] == '6':
                    v_ = float(cnf_row[2])
                if cnf_row[0] == '7':
                    lambda_ = float(cnf_row[2])
                if cnf_row[0] == '8':
                    delta_ = float(cnf_row[2])
        return (T_n, t_, q_, cp_, alpha_, v_, lambda_, delta_)
    except Exception as e:
        print('[ERROR] Config import error!: ', e)

Ниже представлено описание и код этих двух модулей, начнем с module_1:

# Формула для состояния температурного поля при воздействии быстро движущегося точечного источника
def T_1(T_n, V_, t_, q_, cp_, a_, v_):
    """
    T_n - начальная температура изделия
    x_ - координата x
    t_ - время
    q_ - мощность
    cp_ - теплоемкость материала
    a_ - коэффициент температуропроводности
    v_ - скорость сварки
    R_ - длина радиус-вектора
    """
    R_ = math.sqrt(V_.x_**2 + V_.y_**2 + V_.z_**2)

    # Функция - подинтегральное выражение
    def f_(t_1):
        tau_ = t_1
        return math.exp((-v_**2 * tau_)/(4*a_) - (R_**2)/(4*a_*tau_))*(1/(tau_**(3/2)))

    i_ = scipy.integrate.quad(f_, 0, t_, limit=1)
    return T_n + ((2*q_)/(cp_*math.sqrt((4*math.pi*a_)**3))) * math.exp((-v_*V_.x_)/(2*a_)) * i_[0]

# Формула для состояния температурного поля при воздействии быстро движущегося линейного источника
def T_2(T_n, V_, t_, q_, cp_, a_, v_, lambda_, delta_):
    """
    T_n - начальная температура изделия
    x_ - координата x
    t_ - время
    q_ - мощность
    cp_ - теплоемкость материала
    a_ - коэффициент температуропроводности
    v_ - скорость сварки
    lambda_ - коэффициент теплопроводности
    delta_ - толщина
    """

    # Функция - подинтегральное выражение
    def f_(t_1):
        tau_ = t_1
        return math.exp( (-v_**2 * tau_)/(4*a_) - (2*lambda_*tau_)/(cp_*delta_) - (V_.x_**2+V_.y_**2)/(4*a_*tau_) ) * (1/tau_)

    i_ = scipy.integrate.quad(f_, 0, t_, limit=1)
    return T_n + ((q_)/(4*math.pi*lambda_*delta_)) * math.exp((-v_*V_.x_)/(2*a_)) * i_[0]

# Функция ψ(x, y, z, v, t, q)
def PSI_xyzvtq(T_n, V_, t_, q_, cp_, a_, v_, lambda_, delta_):
    """
    T_n - начальная температура изделия
    x_ - координата x
    t_ - время
    q_ - мощность
    cp_ - теплоемкость материала
    a_ - коэффициент температуропроводности
    v_ - скорость сварки
    lambda_ - коэффициент теплопроводности
    delta_ - толщина
    T_t - температура в стадии теплонасыщения
    """
    T_1_result = T_1(T_n, V_, t_, q_, cp_, a_, v_)
    T_2_result = T_2(T_n, V_, t_, q_, cp_, a_, v_, lambda_, delta_)
    T_t = (T_1_result + T_2_result) * 0.9
    return (T_t - T_n)/(T_1_result+T_2_result-T_n)

Дальше рассмотрим module_2:

from module_1.module_1 import T_1, T_2, PSI_xyzvtq, V_xyz, import_csv_cofigs

work_configs = import_csv_cofigs(2)

# Начальная температура изделия
T_n = work_configs[0]
# Время
t_ = work_configs[1]
# Мощность
q_ = work_configs[2]
# Теплоемкость материала
cp_ = work_configs[3]
# Коэффициент температуропроводности
alpha_ = work_configs[4]
# Скорость сварки
v_ = work_configs[5]
# Коэффициент теплопроводности
lambda_ = work_configs[6]
# Толщина
delta_ = work_configs[7]
def main():
    show_matrix = [[], [], []]

    for i in range(-50,100,5):
        for j in range(-50,100,5):
            V_ = V_xyz(i/100,j/100,0)

            tmp_ = T_2(T_n, V_, t_, q_, cp_, alpha_, v_, lambda_, delta_) + T_1(T_n, V_, t_, q_, cp_, alpha_, v_)
            
            show_matrix[0].append(V_.x_)
            show_matrix[1].append(V_.y_)
            show_matrix[2].append(tmp_)

if __name__ == "__main__":
    main()

Данный код и соответствующий ему конфигурационные файлы размещены в репозитории (https://github.com/Saverona/codes.git).

Выполним визуализацию тепловых процессов. Получаем распределение температуры при сварке титанового сплава с учетом геометрических размеров изделия, заданных технологических параметров и заданном режиме ввода электронного луча. 

Для визуализации данных с помощью Plotly используем код:

fig = go.Figure(data=[go.Mesh3d(
                    x=show_matrix[0],
                    y=(show_matrix[1]),
                    z=(show_matrix[2]),
                    opacity=0.5,
                    color='rgba(244,22,100,0.6)'
                  )])

    fig.show()

Для визуализации данных с помощью Matplotlib используем код:

fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    ax.plot_trisurf(np.array(show_matrix[0]), np.array(show_matrix[1]), np.array(show_matrix[2]), cmap='plasma', linewidth=0, antialiased=False)
    plt.show()

В результате получаем графики распределения температуры на поверхности изделия, построенные с помощью Matplotlib (3D график изображен слева) и Plotly (графики изображены справа). В Plotly можем наблюдать распределение с разных осей, а также имеется возможность посмотреть значение в конкретной точке, в Matplotlib таких возможностей нет.

Simple plot – это график, который показывает динамику по одному или нескольким показателям. Его удобно применять, когда нужно сравнить, как меняются с течением времени разные наборы данных. Данные на таком графике отображаются в виде точек, которые соединены линиями. Выполним визуализацию Simple plot в Matplotlib и Plotply. График показывает равномерность нагрева изделия в процессе электронно-лучевой сварки.

В Matplotlib:

  fig, ax = plt.subplots()
    ax.plot(show_matrix[1], show_matrix[2])
    ax.set(xlabel='x', ylabel='y', title='Temp')
    ax.grid()
    plt.show()

В Plotly:

fig = px.line(x=show_matrix[1],y=show_matrix[2])
    fig.show

При работе с Plotly есть возможность сохранять результат сразу в png (к сожалению, у анимационных графиков нет возможности сохранения в gif, что очень неудобно):

Scatter plots – математическая диаграмма, изображающая значения двух переменных в виде точек на декартовой плоскости. На Scatter plots каждому наблюдению соответствует точка, координаты которой равны значениям двух какого-то параметра этого наблюдения. Эти диаграммы используются для визуализации наличия или отсутствия корреляции между двумя переменными. Выполним визуализацию Scatter plots в Matplotlib и Plotply.

Код в Matplotlib:

fig, ax = plt.subplots()   
    sizes = np.random.uniform(15, 80, len(show_matrix[1]))
    colors = np.random.uniform(15, 80, len(show_matrix[2]))
    ax.scatter(x=show_matrix[1], y=show_matrix[2], s=sizes, c=colors)
    plt.show()

Код в Plotly:

fig = px.scatter(x=show_matrix[1],y=show_matrix[2])
    fig.show()

В Plotly, есть возможность выделения конкретной области с помощью Lasso и Box select. В Matplotlib график изображен более красочно, по сравнению с Plotly:

Pie chart – это секторная диаграмма, которая предназначена для визуализации структуры статических совокупностей. Относительная величина каждого значения изображается в виде сектора круга, площадь которого соответствует вкладу этого значения в сумме значений. Этот вид визуализации очень удобно использовать, когда нужно показать долю каждой величины в общем объеме. Сектора могут отображаться как в общем круге, так и отдельно, расположенными на небольшом удалении друг от друга.  Pie chart сохраняет наглядность только в том случае, если количество частей совокупности диаграммы небольшое. Если частей диаграммы много, то применение такой визуализации данных неэффективно по причине несущественного различия сравниваемых структур. Недостаток Pie chart – малая емкость, невозможно отразить более широкий объем полезной информации. Выполним визуализацию Pie chart в Matplotlib и Plotply.

Код в Matplotlib:

fig = plt.pie(np.array(show_matrix[2]), explode=None, labels=None, colors=None, autopct=None, pctdistance=0.6, shadow=False, 
              labeldistance=1.1, startangle=0, radius=1, counterclock=True, wedgeprops=None, textprops=None, 
              center=(0, 0), frame=False, rotatelabels=False, normalize=True, data=None)  

Код в Plotly:

fig = px.pie(show_matrix[2])
    fig.update_layout(uniformtext_minsize=12, uniformtext_mode='hide')
    fig.show()

Сравним результаты. в Plotly есть возможность выбрать конкретное количество значений, которые нам можно или нужно посмотреть, а в Matplotlib невозможно проанализировать данный график.

Styling Markers – это особый способ обработки маркеров, он используется как функцией разброса, так и для построения точечных диаграмм. Выполним визуализацию Styling Markers в Matplotlib и Plotply.

Код в Matplotlib:

fig = plt.figure()
    plt.plot(np.array(show_matrix[2]), marker = 'o', ms = 20, mec = 'hotpink', mfc = 'hotpink')

Код в Plotly:

fig = px.scatter(x = show_matrix[1], y = show_matrix[2])
    fig.update_traces(marker=dict(size=7,
                              line=dict(width=6,
                                        color='hotpink')),
                  selector=dict(mode='markers'))
    fig.show()

У Plotly есть функционал выделения области разными вариантами, удобен просмотр расположения маркера, а также есть возможность посмотреть конкретное значение маркера. В свою очередь у Matlotlib очень приятная визуализация.

А еще в Plotly можем более точно рассмотреть значения распределения температурного поля, как видим в самой верхней точке происходит нагрев при температуре 1914,4. Так же можем выбирать, какой график нам отображать первым или же можем совсем наложить их друг на друга для более точного анализа, ниже представлен код и визуализация:

fig = go.Figure()
            fig.add_trace(go.Scatter(x=show_matrix[1], y=show_matrix[2], mode='lines+markers',  name='Нагрев', 
                        marker=dict(color=show_matrix[2], colorbar=dict(title="Температура"), colorscale='Inferno',
                                 size=50)))
            fig.add_trace(go.Scatter(visible='legendonly', x=show_matrix[1], y=show_matrix[2],  name='T1+T2'))

            fig.update_layout(legend_orientation="h",
                legend=dict(x=.5, xanchor="center"),
                margin=dict(l=0, r=0, t=0, b=0))
            fig.update_traces(hoverinfo="all", hovertemplate="Температура")
            fig.show()

Так же в Plotly есть возможность реализовать анимационный график с express. Функции express принимают на вход датафреймы, вам лишь нужно указать колонки, по которым производится агрегация данных. И можно сразу строить и тепловые карты, и анимации очень небольшим количеством кода, как в этом примере:

import plotly.express as px
df = px.data.gapminder()
    fig = px.scatter(df, x="gdpPercap", y="lifeExp", animation_frame="year", animation_group="country",
           size="pop", color="continent", hover_name="country",
           log_x=True, size_max=55, range_x=[100,100000], range_y=[25,90])

    fig.show()

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

По сравнению с Matplotlib, Plotly предлагает обширный список вариантов построения графиков, начиная с обычных и заканчивая анимационными или графическими картами.

На наш взгляд, Plotly является более информативной и удобной библиотекой для визуализации данных. Библиотека имеет однострочный код для красочной визуализации датасетов, интерактивные элементы для выделения и исследования данных, возможность существенной детализации отображаемой информации.

Желаем приятной работы при визуализации данных!