绘制多个单变量正态分布

Drawing multiple univariate normal distribution

有谁知道如何使用 Python 在单个图上绘制多个高斯分布? 我得到了一些正态分布的数据,它们具有不同的均值和标准差,我需要绘制这些数据。非常感谢 我只能画一个。请对我简单点,我刚刚开始使用 Python

假设您有 3 种不同的均值 mu 和标准差 sigma 组合。您可以选择任意数量,但出于示例目的,我使用了 3 个。

from matplotlib import pyplot as mp
import numpy as np

def gaussian(x, mu, sig):
    return 1./(np.sqrt(2*np.pi)*sigma)*np.exp(-0.5 * (1./sigma*(x - mu))**2)

for mu, sig in [(0.5, 0.1), (1.0, 0.2), (1.5, 0.3)]: #(mu,sigma)
    mp.plot(gaussian(np.linspace(-8, 8, 100), mu, sig))

mp.show()

在这一行定义你的musigma,你可以添加任意多的组合:

for mu, sig in [(0.5, 0.1), (1.0, 0.2), (1.5, 0.3)]: #(mu,sigma)

在我的例子中是

  • mu = 0.5, sigma = 0.1
  • mu = 1.0, sigma = 0.2
  • mu = 1.5, sigma = 0.3

结果:

*编辑

%matplotlib inline
from matplotlib import pyplot as mp
import numpy as np

def gaussian(x, mu, sig):
    return 1./(np.sqrt(2*np.pi)*sigma)*np.exp(-0.5 * (1./sigma*(x - mu))**2)

for mu, sigma in [(1, 2), (0.5, 1), (0, 0.5)]: #(mu,sigma)
    mp.plot(gaussian(np.linspace(-4, 6, 100, ), mu, sigma))
    mp.xlim(0,110)  #set x-axes limits
    mp.ylim(0,1)  #set y-axes limits

mp.show()

结果:

@dejanmarich 提出的解决方案存在一个小问题。 x 轴上的值与数据分布中的实际值不对应。要解决此问题,我们不应生成任意线性间隔范围。

相反,我们想要一个图,其中 x 的范围从下限到上限,这样均值位于中间。以下代码片段实现了这一目标:

#!/usr/bin/python

import numpy as np
import matplotlib.pyplot as plt

class Gaussian:
  @staticmethod
  def plot(mean, std, lower_bound=None, upper_bound=None, resolution=None,
    title=None, x_label=None, y_label=None, legend_label=None, legend_location="best"):
    
    lower_bound = ( mean - 4*std ) if lower_bound is None else lower_bound
    upper_bound = ( mean + 4*std ) if upper_bound is None else upper_bound
    resolution  = 100
    
    title        = title        or "Gaussian Distribution"
    x_label      = x_label      or "x"
    y_label      = y_label      or "N(x|μ,σ)"
    legend_label = legend_label or "μ={}, σ={}".format(mean, std)
    
    X = np.linspace(lower_bound, upper_bound, resolution)
    dist_X = Gaussian._distribution(X, mean, std)
    
    plt.title(title)
    
    plt.plot(X, dist_X, label=legend_label)
    
    plt.xlabel(x_label)
    plt.ylabel(y_label)
    plt.legend(loc=legend_location)
    
    return plt
  
  @staticmethod
  def _distribution(X, mean, std):
    return 1./(np.sqrt(2*np.pi)*std)*np.exp(-0.5 * (1./std*(X - mean))**2)

一旦定义了class,我们可以按以下方式绘制高斯分布:

Gaussian.plot(0.5, 1).show()

# Or, for multiple plots:

plot = Gaussian.plot(1, 2)
plot = Gaussian.plot(0.5, 1)
plot = Gaussian.plot(0, 0.5)
plot.show()

这里我们定义或计算边界 x 范围。计算使用平均值和标准差的 4 倍以使图居中。

这是一个非常简单的 class,可以对其进行扩展以解决一些问题。 1) 除非绘制了多个图,否则我们(似乎)看的是同一张图,并且 2) 绘制多个分布时尾部被切断。

1) 通过在 plot 方法中添加 plt.ylim(0,1) 即可轻松修复。 2) 另一方面,要求我们在绘图时考虑所有地块的形状。

为此,我们可以将 class 更改为 builder class。我们首先汇总所有绘图信息,并仅在准备好绘制所有绘图时才计算 x 范围。

以下class实现了这个目标:

#!/usr/bin/python

import numpy as np
import matplotlib.pyplot as plt

class GaussianPlot:
  def __init__(self, title="Gaussian Distribution", x_label="x", y_label=None,
    y_limit=None, lower_bound=None, upper_bound=None, 
    with_grid=True, fill_below=True, legend_location="best"):
    
    self.title           = title
    self.x_label         = x_label
    self.y_label         = y_label or "N({}|μ,σ)".format(x_label)
    self.y_limit         = y_limit
    self.lower_bound     = lower_bound
    self.upper_bound     = upper_bound
    self.with_grid       = with_grid
    self.fill_below      = fill_below
    self.legend_location = legend_location
    
    self.plots = []
  
  def plot(self, mean, std, resolution=None, legend_label=None):
    self.plots.append({
      "mean":         mean,
      "std":          std,
      "resolution":   resolution,
      "legend_label": legend_label
    })
    
    return self
  
  def show(self):
    self._prepare_figure()
    self._draw_plots()
    
    plt.legend(loc=self.legend_location)
    plt.show()
  
  def _prepare_figure(self):
    plt.figure()
    plt.title(self.title)
    
    plt.xlabel(self.x_label)
    plt.ylabel(self.y_label)
    
    if self.y_limit is not None:
      plt.ylim(0, self.y_limit)
    
    if self.with_grid: 
      plt.grid()
  
  def _draw_plots(self):
    lower_bound = self.lower_bound if self.lower_bound is not None else self._compute_lower_bound()
    upper_bound = self.upper_bound if self.upper_bound is not None else self._compute_upper_bound()
    
    for plot_data in self.plots:
      mean         = plot_data["mean"]
      std          = plot_data["std"]
      resolution   = plot_data["resolution"]
      legend_label = plot_data["legend_label"]
      
      self._draw_plot(lower_bound, upper_bound, mean, std, resolution, legend_label)
  
  def _draw_plot(self, lower_bound, upper_bound, mean, std, resolution, legend_label):
    resolution   = resolution or max(100, int(upper_bound - lower_bound)*10)
    legend_label = legend_label or "μ={}, σ={}".format(mean, std)
    
    X = np.linspace(lower_bound, upper_bound, resolution)
    dist_X = self._distribution(X, mean, std)
    
    if self.fill_below: plt.fill_between(X, dist_X, alpha=0.1)
    plt.plot(X, dist_X, label=legend_label)
  
  def _compute_lower_bound(self):
    return np.min([plot["mean"] - 4*plot["std"] for plot in self.plots])
  
  def _compute_upper_bound(self):
    return np.max([plot["mean"] + 4*plot["std"] for plot in self.plots])
  
  def _distribution(self, X, mean, std):
    return 1./(np.sqrt(2.*np.pi)*std)*np.exp(-np.power((X - mean)/std, 2.)/2)

class 与其更简单的前身相比,使用方式略有不同:

gaussian_plot = GaussianPlot()\
  .plot(1, 2)\
  .plot(0.5, 1)\
  .plot(0, 0.5)

gaussian_plot.show()

查看 __init__plot 的各种参数以自定义绘图的外观。另请注意,在高斯分布不够平滑的情况下,每个图的分辨率可能会发生变化。

例如,上图中的分布 (μ=0, σ=0.5) 可以受益于更高的分辨率:

gaussian_plot = GaussianPlot()\
  .plot(1, 2)\
  .plot(0.5, 1)\
  .plot(0, 0.5, resolution=400)

gaussian_plot.show()