Matplotlib создать текст с цветами, соответствующими серии - PullRequest
0 голосов
/ 14 ноября 2018

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

Код, который я использовал, выглядит следующим образом:

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import scipy.stats as stats

df = pd.DataFrame({'x': [21000, 16900, 18200, 32000, 35000, 7500], 'y':[3000, 2100, 1500, 3000, 2500, 2000], 'z':['a', 'b', 'c', 'd', 'e', 'f']})

fig, ax = plt.subplots(figsize=(8,6))

text_list = []
color_list = []

for i, row in df.iterrows():
    mu, sigma, group = row['x'], row['y'], row['z']       
    x = np.linspace(mu - 4*sigma, mu + 4*sigma, 100)

    sns.lineplot(x, stats.norm.pdf(x, mu, sigma), ax=ax)
    color = ax.get_lines()[-1].get_c()

    ax = plt.gca()
    ax.text(mu*1.05, max(stats.norm.pdf(x, mu, sigma)), group, fontsize=16, color=color) #only retrieve RGB so blank text is not too light      

    text = r'{0}: {1} $\pm$ {2}'.format(group, mu, sigma)
    text_list.append(text)
    color_list.append(color)

plt.gcf().text(0.68, 0.6, '\n'.join(text_list), bbox=dict(facecolor='white', edgecolor='black', pad=10.0, alpha=1), fontsize=14)
fig.show()

, который дает следующий график: enter image description here

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

Мне удалось сохранить два списка текстов и цветов в text_box_content и color_list. Я также попытался добавить plt.gcf().text() в цикле for с динамически обновляемыми текстовыми местоположениями, но ограничивающие рамки создаются для каждой строки вместо общего ограничивающего поля для всего текста.

Было бы лучше, если бы что-то концептуально сходное с

plt.gcf().text(zip(text_list, color_list)) чтобы каждая строка имела свой цвет?

1 Ответ

0 голосов
/ 14 ноября 2018

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

import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.legend_handler import HandlerBase
from matplotlib.text import Text
import seaborn as sns
import numpy as np
import scipy.stats as stats

class TextHandler(HandlerBase):
    def create_artists(self, legend,tup ,xdescent, ydescent,
                        width, height, fontsize,trans):
        tx = Text(width/2.,height/2,tup[0], fontsize=fontsize,
                  ha="center", va="center", color=tup[1], fontweight="bold")
        return [tx]

df = pd.DataFrame({'x': [21000, 16900, 18200, 32000, 35000, 7500], 
                   'y':[3000, 2100, 1500, 3000, 2500, 2000], 
                   'z':['a', 'b', 'c', 'd', 'e', 'f']})

fig, ax = plt.subplots(figsize=(8,6))

handles = []
labels = []

for i, row in df.iterrows():
    mu, sigma, group = row['x'], row['y'], row['z']       
    x = np.linspace(mu - 4*sigma, mu + 4*sigma, 100)

    sns.lineplot(x, stats.norm.pdf(x, mu, sigma), ax=ax)
    color = ax.get_lines()[-1].get_c()

    ax.text(mu*1.05, max(stats.norm.pdf(x, mu, sigma)), group, fontsize=16, color=color)

    handles.append(("{}:".format(group), color))
    labels.append("{} $\pm$ {}".format(mu, sigma))

leg = ax.legend(handles=handles, labels=labels, handler_map={tuple : TextHandler()},
          facecolor='white', edgecolor='black', borderpad=0.9, framealpha=1, 
          fontsize=10, handlelength=0.5)

for h, t in zip(leg.legendHandles, leg.get_texts()):
    t.set_color(h.get_color())

plt.show()

enter image description here

Это вдохновлено и частично использует код из моего ответа на этот вопрос , который также содержит альтернативу в случае, если вы не хотите использовать легенду.

...