这个plt.fill_between()方法可以在循环中修改吗?

时间:2017-10-08 10:37:00

标签: python-3.x loops matplotlib where fill

我试图在图的特定区域中遮蔽(x,y)区域。作为简化示例,请考虑使用confidence intervals的正态分布。我想设置置信区间,使得一个标准差(或一个sigma)内的区域最暗,两个标准偏差(或2个sigma)内的区域稍微轻一些,等等。我有办法做到这一点,但我试图让我的脚本更灵活。代码如下。

keyorder = ['Name', 'Status', 'Remarks']   
res = []
for key in keyorder:  
    res.append(key + ':' + compliance[key])

', '.join(res)

'Name:ABC, Status:True, Remarks:No remarks'

现在我们有x和函数f(x),我们可以制作一个图。我留下了有效的代码部分,并注释了我对解决方案的尝试。如果它有效,我更喜欢我的求解方法,因为基于所需间隔的数量更加方便遮蔽并且代码不重复。

## imports
import numpy as np
import matplotlib.pyplot as plt
from math import pi

## y = f(x)
def get_f(x, mu, sigma):
    """ Normal Distribution Probability Density Function """
    norm_constant = (sigma* (2*pi)**(1/2))
    return [norm_constant * np.exp((-1) * (x[idx] - mu)**2 / (2* sigma**2)) for idx in range(len(x))]

x = np.linspace(0, 100, 5000)

运行正确的代码会生成this plot。我尝试使用更方便的解决方案会生成this plot并生成下面的输出(通过print语句),但我无法找到错误的来源。

## generate plot
def get_plot(x, num_intervals=None, line_color='g', shade_color='b', mu=48, sigma=7):
    """ Returns (x,y) plot; confidence intervals shading is optional """
    y = get_f(x, mu, sigma)
    plt.plot(x, y, line_color)
    if num_intervals is not None:

        ## THIS CODE SEGMENT BELOW WORKS BUT I WOULD LIKE TO MAKE IT BETTER

        plt.fill_between(x, y, where=(mu - sigma <= x), alpha=0.18, color=shade_color)
        plt.fill_between(x, y, where=(x <= mu + sigma), alpha=0.18, color=shade_color)
        plt.fill_between(x, y, where=(mu - 2*sigma <= x), alpha=0.11, color=shade_color)
        plt.fill_between(x, y, where=(x <= mu + 2*sigma), alpha=0.11, color=shade_color)
        plt.fill_between(x, y, where=(mu - 3*sigma <= x), alpha=0.02, color=shade_color)
        plt.fill_between(x, y, where=(x <= mu + 3*sigma), alpha=0.02, color=shade_color)

        ## THIS CODE SEGMENT BELOW DOES NOT WORK AS I WOULD LIKE
        ## IT WILL SHADE THE REGIONS IN THE WRONG SHADE/DARKNESS 

        ## choose shading level via dictionary
        # alpha_keys = [idx+1 for idx in range(num_intervals)]
        # alpha_vals = [0.18, 0.11, 0.02]
        # alpha_dict = dict(zip(alpha_keys, alpha_vals))
        # for idx in range(num_intervals):
            # print("\nidx & stdev = %d & %d, \nmu - (stdev * sigma) = %.2f, \nmu + (stdev * sigma) = %.2f, alpha = %.2f" %(idx, stdev, mu - stdev*sigma, mu + stdev*sigma, alpha_dict[stdev]), "\n")
            # stdev = idx + 1 ## number of standard deviations away from mu
            # plt.fill_between(x, y, where=(mu - stdev * sigma <= x), alpha=alpha_dict[stdev], color=shade_color)
            # plt.fill_between(x, y, where=(x >= mu + stdev * sigma), alpha=alpha_dict[stdev], color=shade_color)


    plt.show()

我的方法是否适合更方便的解决方案?

1 个答案:

答案 0 :(得分:2)

在这里,我提供了比我更紧凑的正态分布图。我使用Scipy包中的Normal分布函数而不是重新发明轮子。

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

# mean and standard deviation
mu,sigma = 48,7  

# normal_dist(mu,sigma)
anorm = norm(loc=mu, scale=sigma)
factors = [1,2,3]            # multiple of sigma
alphas = [0.18, 0.11, 0.08]  # level of alpha

fig, ax = plt.subplots(1, 1)
fig.set_size_inches(10,8)

# plot full normal curve
segs = 100
x = np.linspace(anorm.ppf(0.0005), anorm.ppf(0.9995), segs)
ax.plot(x, anorm.pdf(x), 'b-', lw=0.5, alpha=0.6)

# plot color-filled portions
for fac, alp in zip(factors, alphas):
    # print(mu-fac*sigma, mu+fac*sigma, alp)
    lo = mu-fac*sigma
    hi = mu+fac*sigma
    xs = np.linspace(lo, hi, fac*segs/4)  # prep array of x's
    plt.fill_between(xs, anorm.pdf(xs), y2=0, where= xs >= lo , \
                     interpolate=False, \
                     color='blue', alpha=alp)

plt.ylim(0, 0.06)

plt.show()

结果图:

enter image description here