matplotlib - черно-белая карта цветов (с тире, точками и т. д.) - PullRequest
34 голосов
/ 09 сентября 2011

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

Gnuplot автоматически изменяет штриховые рисунки для разных линий, возможно ли нечто подобное с matplotlib?

Ответы [ 4 ]

48 голосов
/ 09 сентября 2011

Ниже я предоставляю функции для преобразования цветной линии в черную линию с уникальным стилем.Мой быстрый тест показал, что после 7 строк цвета повторялись.Если это не так (и я допустил ошибку), то для «константы» COLORMAP в предоставленной подпрограмме требуется небольшая корректировка.

Вот процедура и пример:

import matplotlib.pyplot as plt
import numpy as np

def setAxLinesBW(ax):
    """
    Take each Line2D in the axes, ax, and convert the line style to be 
    suitable for black and white viewing.
    """
    MARKERSIZE = 3

    COLORMAP = {
        'b': {'marker': None, 'dash': (None,None)},
        'g': {'marker': None, 'dash': [5,5]},
        'r': {'marker': None, 'dash': [5,3,1,3]},
        'c': {'marker': None, 'dash': [1,3]},
        'm': {'marker': None, 'dash': [5,2,5,2,5,10]},
        'y': {'marker': None, 'dash': [5,3,1,2,1,10]},
        'k': {'marker': 'o', 'dash': (None,None)} #[1,2,1,10]}
        }


    lines_to_adjust = ax.get_lines()
    try:
        lines_to_adjust += ax.get_legend().get_lines()
    except AttributeError:
        pass

    for line in lines_to_adjust:
        origColor = line.get_color()
        line.set_color('black')
        line.set_dashes(COLORMAP[origColor]['dash'])
        line.set_marker(COLORMAP[origColor]['marker'])
        line.set_markersize(MARKERSIZE)

def setFigLinesBW(fig):
    """
    Take each axes in the figure, and for each line in the axes, make the
    line viewable in black and white.
    """
    for ax in fig.get_axes():
        setAxLinesBW(ax)

xval = np.arange(100)*.01

fig = plt.figure()
ax = fig.add_subplot(211)

ax.plot(xval,np.cos(2*np.pi*xval))
ax.plot(xval,np.cos(3*np.pi*xval))
ax.plot(xval,np.cos(4*np.pi*xval))
ax.plot(xval,np.cos(5*np.pi*xval))
ax.plot(xval,np.cos(6*np.pi*xval))
ax.plot(xval,np.cos(7*np.pi*xval))
ax.plot(xval,np.cos(8*np.pi*xval))

ax = fig.add_subplot(212)
ax.plot(xval,np.cos(2*np.pi*xval))
ax.plot(xval,np.cos(3*np.pi*xval))
ax.plot(xval,np.cos(4*np.pi*xval))
ax.plot(xval,np.cos(5*np.pi*xval))
ax.plot(xval,np.cos(6*np.pi*xval))
ax.plot(xval,np.cos(7*np.pi*xval))
ax.plot(xval,np.cos(8*np.pi*xval))

fig.savefig("colorDemo.png")
setFigLinesBW(fig)
fig.savefig("bwDemo.png")

Это обеспечивает следующие два графика: Сначала в цвете: enter image description here Затем в черно-белом: enter image description here

Вы можете настроить способ преобразования каждого цвета в стиль.Если вы просто хотите играть только со стилем тире (-. Против - против любого паттерна, который хотите), установите COLORMAP соответствующее значение «маркера» на None и скорректируйте паттерн «тире», или наоборотнаоборот.

Например, последний цвет в словаре - 'k' (для черного);Первоначально у меня был только пунктирный паттерн [1,2,1,10], соответствующий одному показанному пикселю, два нет, показан один, 10 нет, что является точечно-пространственным рисунком.Затем я прокомментировал это, установив черту в (Нет, Нет), очень формальный способ сказать сплошную линию, и добавил маркер «о» для круга.

Я также установил «константу» MARKERSIZE, которая будет устанавливать размер каждого маркера, потому что я нашел размер по умолчанию немного большим.

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

8 голосов
/ 30 июня 2016

TL; DR

import matplotlib.pyplot as plt
from cycler import cycler
monochrome = (cycler('color', ['k']) * cycler('marker', ['', '.']) *
              cycler('linestyle', ['-', '--', ':', '=.']))
plt.rc('axes', prop_cycle=monochrome)

Расширенный ответ

Более новые matplotlib релизы представили новую rcParams, а именно axes.prop_cycle

In [1]: import matplotlib.pyplot as plt

In [2]: plt.rcParams['axes.prop_cycle']
Out[2]: cycler('color', ['b', 'g', 'r', 'c', 'm', 'y', 'k'])

Для предустановленных стилей, доступных plt.style.use(...) или with plt.style.context(...):, prop_cycle эквивалентен традиционному и устарел axes.color_cycle

In [3]: plt.rcParams['axes.color_cycle']
/.../__init__.py:892: UserWarning: axes.color_cycle is deprecated and replaced with axes.prop_cycle; please use the latter.
  warnings.warn(self.msg_depr % (key, alt_key))
Out[3]: ['b', 'g', 'r', 'c', 'm', 'y', 'k']

, но объект cycler имеет гораздо больше возможностей, в частности, комплекс cycler может быть составлен из более простых, ссылаясь на различные свойства, используя + и *, что означает соответственно zip и декартово произведение.

Здесь мы импортируем вспомогательную функцию cycler, определяем 3 простых cycler, которые ссылаются на различные свойства и, наконец, составляем их, используя декартово произведение

In [4]: from cycler import cycler
In [5]: color_c = cycler('color', ['k'])
In [6]: style_c = cycler('linestyle', ['-', '--', ':', '-.'])
In [7]: markr_c = cycler('marker', ['', '.', 'o'])
In [8]: c_cms = color_c * markr_c * style_c
In [9]: c_csm = color_c * style_c * markr_c

Здесь у нас есть два разных (?) Комплекса cycler и да, они разные, потому что эта операция некоммутативна, смотрите

In [10]: for d in c_csm: print('\t'.join(d[k] for k in d))
-               k
-       .       k
-       o       k
--              k
--      .       k
--      o       k
:               k
:       .       k
:       o       k
-.              k
-.      .       k
-.      o       k

In [11]: for d in c_cms: print('\t'.join(d[k] for k in d))
-               k
--              k
:               k
-.              k
-       .       k
--      .       k
:       .       k
-.      .       k
-       o       k
--      o       k
:       o       k
-.      o       k

Элементарный цикл, который изменяется быстрее, является последним в продукте и т. Д., Это важно, если мы хотим определенного порядка в стиле линий.

Как использовать состав cycler с? С помощью plt.rc или эквивалентного способа изменить rcParams из matplotlib. Например.,

In [12]: %matplotlib
Using matplotlib backend: Qt4Agg
In [13]: import numpy as np
In [14]: x = np.linspace(0, 8, 101)
In [15]: y = np.cos(np.arange(7)+x[:,None])
In [16]: plt.rc('axes', prop_cycle=c_cms)
In [17]: plt.plot(x, y);
In [18]: plt.grid();

enter image description here

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

PS Я забыл упомянуть, что этот подход автоматически обрабатывает образцы строк в окне легенды, enter image description here

2 голосов
/ 31 июля 2015

Я активно использовал код Янна, но сегодня я прочитал ответ от Могу ли я циклически пролистывать стили линий в matplotlib Так что теперь я буду строить свои графики BW следующим образом:

import pylab as plt
from itertools import cycle
lines = ["k-","k--","k-.","k:"]
linecycler = cycle(lines)
plt.figure()
for i in range(4):
    x = range(i,i+10)
    plt.plot(range(10),x,next(linecycler))
plt.show()

enter image description here

1 голос
/ 09 сентября 2011

Такие вещи, как plot(x,y,'k-.'), создадут черную ('k') пунктирную ('-.') линию.Разве это не то, что вы ищете?

...