Как в Matplotlib менять оформление линий по умолчанию | jenyay.net

Как в Matplotlib менять оформление линий по умолчанию

Представим себе достаточно частую ситуацию, когда требуется на одном графике построить несколько кривых с помощью функции matplotlib.pyplot.plot(). Если при добавлении очередной кривой не указывать стили линий в явном виде, то будут использоваться сплошные линии, а их цвета для первых семи кривых будут различными, после чего цвета начнут циклически повторяться. В этой статье мы рассмотрим способы, с помощью которых можно менять эту последовательность оформления линий, используемых по умолчанию.

Чтобы это продемонстрировать, напишем небольшой пример, который в дальнейшем будем модифицировать. Этот скрипт будет строить множество функций Гаусса при различных коэффициентах alpha.

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

import matplotlib.pyplot as plt

import numpy as np


def f(x, alpha):
    return np.exp(-(x / alpha) ** 2)


if __name__ == '__main__':
    x = np.linspace(-10.0, 10.0, 200)
    alpha_list = np.arange(0.5, 11.5, 0.5)

    plt.figure()

    for alpha in alpha_list:
        y = f(x, alpha)
        plt.plot(x, y)

    plt.ylim(-0.1, 1.1)
    plt.grid()
    plt.show()

В результате будет показан следующий график.

Первая по счету линия по цвету совпадает с 8-й, 15-й, 22-й и т.д. А что, если нам требуется большее разнообразие стилей? Мы, конечно, можем для каждой линии задавать стиль в явном виде, но иногда это не удобно, лучше сменить стили, используемые по умолчанию. Для этого существуют два способа - мы можем изменить стили линий по умолчанию глобально для всех графиков или только для конкретного графика.

Сначала рассмотрим глобальный способ изменения стилей. Для этого нужно воспользоваться функцией rc(), изменяющей глобальные настройки Matplotlib. Функция rc() устанавливает настройки, которые задаются именем группы (первый строковый параметр функции) и именованными параметрами (последующие параметры функции). Для установки стилей линий по умолчанию нужно установить параметр prop_cycle в группе 'axes'. При этом значением параметра prop_cycle должен быть экземпляр класса Cycler из модуля cycler, который устанавливается вместе с Matplotlib.

Строго говоря, модуль cycler можно использовать и без Matplotlib для своих целей. Этот модуль предоставляет класс Cycler, который позволяет легко работать с периодическими последовательностями, но в этой статье мы будем рассматривать этот класс с точки зрения использования его в Matplotlib.

Для создания класса Cycler можно воспользоваться функцией cycler() из модуля cycler. В качестве параметра этой функции можно передать, например, список цветов, задав их с помощью именованного параметра color, как показано в следующем примере:

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

from cycler import cycler

import matplotlib.pyplot as plt

import numpy as np


def f(x, alpha):
    return np.exp(-(x / alpha) ** 2)


if __name__ == '__main__':
    x = np.linspace(-10.0, 10.0, 200)
    alpha_list = np.arange(0.5, 11.5, 0.5)

    # !!!
    colors = ['black', 'silver', 'firebrick', 'sandybrown', 'gold',
              'olivedrab', 'chartreuse', 'green', 'cyan', 'darkblue', 'coral',
              'orange', 'yellow', 'red', 'greenyellow', 'steelblue']

    # !!!
    plt.rc('axes', prop_cycle=cycler(color=colors))

    plt.figure()

    for alpha in alpha_list:
        y = f(x, alpha)
        plt.plot(x, y)

    plt.ylim(-0.1, 1.1)
    plt.grid()
    plt.show()

Для задания цвета мы можем воспользоваться любым способом задания цвета, которые описаны в статье Способы задания цвета в Matplotlib. В данном примере мы воспользовались именованными цветами и сделали список из 16 цветов, поэтому 17-я кривая повторит цвет первой - она будет отображаться черным цветом.

В результате будет показан следующий график:

В этом примере вызов функции cycler для создания класса Cycler можно было бы записать как cycler('color', colors), результат от этого не изменится.

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

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

from cycler import cycler

import matplotlib.pyplot as plt

import numpy as np


def f(x, alpha):
    return np.exp(-(x / alpha) ** 2)


if __name__ == '__main__':
    x = np.linspace(-10.0, 10.0, 200)
    alpha_list = np.arange(0.5, 11.5, 0.5)

    # !!!
    colors = ['black', 'silver', 'firebrick', 'sandybrown', 'gold',
              'olivedrab', 'chartreuse', 'green', 'cyan', 'darkblue', 'coral',
              'orange', 'yellow', 'red', 'greenyellow', 'steelblue']

    styles = [
        '-', '--', ':', '-.',
        '-', '--', ':', '-.',
        '-', '--', ':', '-.',
        '-', '--', ':', '-.',
    ]

    # !!!
    plt.rc('axes', prop_cycle=cycler(color=colors, linestyle=styles))

    plt.figure()

    for alpha in alpha_list:
        y = f(x, alpha)
        plt.plot(x, y)

    plt.ylim(-0.1, 1.1)
    plt.grid()
    plt.show()

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

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

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

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

from cycler import cycler

import matplotlib.pyplot as plt

import numpy as np


def f(x, alpha):
    return np.exp(-(x / alpha) ** 2)


if __name__ == '__main__':
    x = np.linspace(-10.0, 10.0, 200)
    alpha_list = np.arange(0.5, 11.5, 0.5)

    colors = ['black', 'silver', 'firebrick', 'sandybrown', 'gold',
              'olivedrab', 'chartreuse', 'green', 'cyan', 'darkblue', 'coral',
              'orange', 'yellow', 'red', 'greenyellow', 'steelblue']

    styles = [
        '-', '--', ':', '-.',
        '-', '--', ':', '-.',
        '-', '--', ':', '-.',
        '-', '--', ':', '-.',
    ]

    # !!!
    prop_cycle = cycler(color=colors) + cycler(linestyle=styles)
    plt.rc('axes', prop_cycle=prop_cycle)

    plt.figure()

    for alpha in alpha_list:
        y = f(x, alpha)
        plt.plot(x, y)

    plt.ylim(-0.1, 1.1)
    plt.grid()
    plt.show()

Результат при этом останется прежним.

Также класс Cycler поддерживает и другие арифметические операции. Например, произведение двух экземпляров класса Cycler формирует новый класс Cycler, в котором множество свойств линий будут создано с помощью декартова произведения двух исходных множеств с оформлением. Напомню, что декартово произведение множества {A, B, C} на множество {x, y} - это множество пар (A, x), (A, y), (B, x), (B, y), (C, x), (C, y).

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

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

from cycler import cycler

import matplotlib.pyplot as plt

import numpy as np


def f(x, alpha):
    return np.exp(-(x / alpha) ** 2)


if __name__ == '__main__':
    x = np.linspace(-10.0, 10.0, 200)
    alpha_list = np.arange(0.5, 11.5, 0.5)

    colors = ['black', 'silver', 'firebrick', 'sandybrown', 'gold',
              'olivedrab', 'chartreuse', 'green', 'cyan', 'darkblue', 'coral',
              'orange', 'yellow', 'red', 'greenyellow', 'steelblue']

    # !!!
    styles = ['-', '--', ':', '-.']

    # !!!
    prop_cycle = cycler(color=colors) * cycler(linestyle=styles)
    plt.rc('axes', prop_cycle=prop_cycle)

    plt.figure()

    for alpha in alpha_list:
        y = f(x, alpha)
        plt.plot(x, y)

    plt.ylim(-0.1, 1.1)
    plt.grid()
    plt.show()

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

Однако надо помнить, что декартово произведение не коммутативно, то есть A × B ≠ B × A, поэтому если мы поменяем местами множители, то результат будет выглядеть по-другому. Это показано в следующем примере:

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

from cycler import cycler

import matplotlib.pyplot as plt

import numpy as np


def f(x, alpha):
    return np.exp(-(x / alpha) ** 2)


if __name__ == '__main__':
    x = np.linspace(-10.0, 10.0, 200)
    alpha_list = np.arange(0.5, 11.5, 0.5)

    colors = ['black', 'silver', 'firebrick', 'sandybrown', 'gold',
              'olivedrab', 'chartreuse', 'green', 'cyan', 'darkblue', 'coral',
              'orange', 'yellow', 'red', 'greenyellow', 'steelblue']

    styles = ['-', '--', ':', '-.']

    # !!!
    prop_cycle = cycler(linestyle=styles) * cycler(color=colors)
    plt.rc('axes', prop_cycle=prop_cycle)

    plt.figure()

    for alpha in alpha_list:
        y = f(x, alpha)
        plt.plot(x, y)

    plt.ylim(-0.1, 1.1)
    plt.grid()
    plt.show()

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

Подобным образом можно менять и толщины линий, используемые по умолчанию на графике. Для этого в качестве параметра в функцию cycler нужно передать именованный параметр lw, равный списку толщин, как показано в следующем примере:

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

from cycler import cycler

import matplotlib.pyplot as plt

import numpy as np


def f(x, alpha):
    return np.exp(-(x / alpha) ** 2)


if __name__ == '__main__':
    x = np.linspace(-10.0, 10.0, 200)
    alpha_list = np.arange(0.5, 11.5, 0.5)

    # !!!
    width = [0.2, 0.5, 1, 2, 3, 4]

    # !!!
    prop_cycle = cycler(lw=width)
    plt.rc('axes', prop_cycle=prop_cycle)

    plt.figure()

    for alpha in alpha_list:
        y = f(x, alpha)
        plt.plot(x, y)

    plt.ylim(-0.1, 1.1)
    plt.grid()
    plt.show()

В результате мы получим следующий график:

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

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

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

from cycler import cycler

import matplotlib.pyplot as plt

import numpy as np


def f(x, alpha):
    return np.exp(-(x / alpha) ** 2)


def plot():
    for alpha in alpha_list:
        y = f(x, alpha)
        plt.plot(x, y)

    plt.ylim(-0.1, 1.1)
    plt.grid()



if __name__ == '__main__':
    x = np.linspace(-10.0, 10.0, 200)
    alpha_list = np.arange(0.5, 11.5, 0.5)

    colors = ['black', 'silver', 'firebrick', 'sandybrown', 'gold',
              'olivedrab', 'chartreuse', 'green', 'cyan', 'darkblue', 'coral',
              'orange', 'yellow', 'red', 'greenyellow', 'steelblue']

    styles = ['-', '--', ':', '-.']

    prop_cycle = cycler(linestyle=styles) * cycler(color=colors)

    plt.figure()
    plt.subplot(2, 1, 1)

    # !!!
    axes = plt.gca()
    axes.set_prop_cycle(prop_cycle)
    plot()

    plt.subplot(2, 1, 2)
    plot()

    plt.show()

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

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

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

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




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