2017-02-20 31 views
2

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

ax.fill_between(x, quantile1, quantile2, alpha=0.2) 

в цикле, я сделать участки, как это путем вычисления квантилей 1 и 2 (как указано в легенде) в качестве 0% до 100% квантилей, затем 10% до 90%, и так далее, каждый fill_between строчка сверху предыдущего слоя.

Вот выход с тремя слоями прозрачного цвета, а также срединной линии (0.5):
Three layers of transparent colors along with the median line (0.5)

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

ax.legend([0.5]+[['0.0%', '100.0%'], ['10.0%', '90.0%'], ['30.0%', '70.0%']]) 

Каков наилучший способ переписать значение цвета лица в команде легенды?

Я хотел бы избежать этого, сначала построив 0% до 10% с прозрачностью «0,2», затем от 10% до 30% с прозрачностью «0,4» и т. Д., Так как это займет в два раза больше времени вычислить и сделает код более сложным.

ответ

1

Вы можете использовать художников-прокси, чтобы разместить в легенде, которые имеют ту же прозрачность, что и результат наложения из сюжета.
В качестве прокси-исполнителя вы можете использовать простой прямоугольник. Однако прозрачность должна рассчитываться как два объекта с прозрачностью. 0.2 вместе будут отображаться как один объект с прозрачностью 0.36 (а не 0.4!).

import matplotlib.pyplot as plt 
import numpy as np 
import matplotlib.patches 

a = np.sort(np.random.rand(6,18), axis=0) 
x = np.arange(len(a[0])) 

def alpha(i, base=0.2): 
    l = lambda x: x+base-x*base 
    ar = [l(0)] 
    for j in range(i): 
     ar.append(l(ar[-1])) 
    return ar[-1] 

fig, ax = plt.subplots(figsize=(4,2)) 

handles = [] 
labels=[] 
for i in range(len(a)/2): 
    ax.fill_between(x, a[i, :], a[len(a)-1-i, :], color="blue", alpha=0.2) 
    handle = matplotlib.patches.Rectangle((0,0),1,1,color="blue", alpha=alpha(i, base=0.2)) 
    handles.append(handle) 
    label = "quant {:.1f} to {:.1f}".format(float(i)/len(a)*100, 100-float(i)/len(a)*100) 
    labels.append(label) 

plt.legend(handles=handles, labels=labels, framealpha=1) 
plt.show() 

enter image description here


Один должен решить, действительно ли это стоит усилий. Решение без прозрачности, но с тем же результатом можно добиться гораздо короче:

import matplotlib.pyplot as plt 
import numpy as np 

a = np.sort(np.random.rand(6,18), axis=0) 
x = np.arange(len(a[0])) 

fig, ax = plt.subplots(figsize=(4,2)) 

for i in range(len(a)/2): 
    label = "quant {:.1f} to {:.1f}".format(float(i)/len(a)*100, 100-float(i)/len(a)*100) 
    c = plt.cm.Blues(0.2+.6*(float(i)/len(a)*2) ) 
    ax.fill_between(x, a[i, :], a[len(a)-1-i, :], color=c, label=label) 

plt.legend(framealpha=1) 
plt.show() 

enter image description here

+0

Большое спасибо. Решение без прозрачности является хорошим! – GunnarGeir

+0

Кстати, для первого подхода не требуется определение альфа-функции. Вместо этого for-loop может обновить значение alpha, используя. alpha = 0.2 if i == 0 else alpha * (1-0.2) +0.2 Линия дескриптора затем будет использовать alpha = alpha. – GunnarGeir