Эффективный способ затенения нескольких областей под кривой - PullRequest
0 голосов
/ 29 января 2019

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

Сначала я искал функцию, которая может обрабатывать раскраску, и наткнулся на метод fill_between в matplotlib.Затем я использовал np.linspace для определения трех x-векторов и использовал цикл for и fill_between для затенения областей.Затем я понял, что fill_between «соединит» две области, даже если я хочу, чтобы средняя часть была не заштрихована (что относится к «выше и ниже среднего» и «намного выше и ниже среднего»).Так что этот метод не работает.

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

Вот мой код:

import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import norm

plotdata = {"mean": 50,"sd": 10}

# plot normal distribution
x_normdist = np.linspace(plotdata["mean"] - 3 * plotdata["sd"], plotdata["mean"] + 3 * plotdata["sd"],1000)
y = norm.pdf(x_normdist,plotdata["mean"],plotdata["sd"])
plt.plot(x_normdist,y)

# create logical lists
average = (x_normdist >= (plotdata["mean"] - 1 * plotdata["sd"])) & (x_normdist <= (plotdata["mean"] + 1 * plotdata["sd"]))

above_and_below_average = (x_normdist >= (plotdata["mean"] - 2 * plotdata["sd"])) & (x_normdist < (plotdata["mean"] - 1 * plotdata["sd"])) | (x_normdist > (plotdata["mean"] + 1 * plotdata["sd"])) & (x_normdist <= (plotdata["mean"] + 2 * plotdata["sd"]))

far_above_and_belowe_average = (x_normdist >= (plotdata["mean"] - 3 * plotdata["sd"])) & (x_normdist < (plotdata["mean"] - 2 * plotdata["sd"])) | (x_normdist > (plotdata["mean"] + 2 * plotdata["sd"])) & (x_normdist <= (plotdata["mean"] + 3 * plotdata["sd"]))

# bind lists
regions = [average,above_and_below_average,far_above_and_belowe_average]

# set alpha values
alpha_values = [0.75,0.5,0.25]

# plot regions with corresponding alpha values
for idx,region in enumerate(regions):
    y = norm.pdf(x_normdist, plotdata["mean"], plotdata["sd"])
    plt.fill_between(x_normdist, y,color="C0",alpha=alpha_values[idx],where=regions[idx])

plt.show()

1 Ответ

0 голосов
/ 29 января 2019

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

from scipy.stats import norm
m=50.
sd=10.

fig, ax = plt.subplots()
x = np.linspace(m-3*sd,m+3*sd,1000)
y = norm.pdf(x,m,sd)
ax.plot(x,y,c='C0')

cutoffs = [0,0.25,0.6,1.2,3]  # expressed in sd
colors = ['C0','C1','C2','C3']
alphas = [1.00,0.75,0.50,0.25]

where_x = np.zeros(len(x))
for cut in cutoffs:
    where_x+=np.where(np.abs(x-m)>cut*sd,1,0)
for cond,c,a in zip(range(1,len(cutoffs)),colors,alphas):
    ax.fill_between(x,y,color=c,alpha=a,where=(where_x==cond))

enter image description here

...