【问题标题】:Fill area of overlap between two normal distributions in seaborn / matplotlib填充seaborn / matplotlib中两个正态分布之间的重叠区域
【发布时间】:2018-12-01 20:00:30
【问题描述】:

我想填充两个正态分布之间的重叠区域。我有 x 的最小值和最大值,但我不知道如何设置 y 边界。

我查看了plt documentationsome examples。我认为this related questionthis one 很接近,但没有运气。这是我目前所拥有的。

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

pepe_calories = np.array([361, 291, 263, 284, 311, 284, 282, 228, 328, 263, 354, 302, 293,
       254, 297, 281, 307, 281, 262, 302, 244, 259, 273, 299, 278, 257,
       296, 237, 276, 280, 291, 278, 251, 313, 314, 323, 333, 270, 317,
       321, 307, 256, 301, 264, 221, 251, 307, 283, 300, 292, 344, 239,
       288, 356, 224, 246, 196, 202, 314, 301, 336, 294, 237, 284, 311,
       257, 255, 287, 243, 267, 253, 257, 320, 295, 295, 271, 322, 343,
       313, 293, 298, 272, 267, 257, 334, 276, 337, 325, 261, 344, 298,
       253, 302, 318, 289, 302, 291, 343, 310, 241])



modern_calories = np.array([310, 315, 303, 360, 339, 416, 278, 326, 316, 314, 333, 317, 357,
       304, 363, 387, 279, 350, 367, 321, 366, 311, 308, 303, 299, 363,
       335, 357, 392, 321, 361, 285, 321, 290, 392, 341, 331, 338, 326,
       314, 327, 320, 293, 333, 297, 315, 365, 408, 352, 359, 312, 300,
       263, 358, 345, 360, 336, 378, 315, 354, 318, 300, 372, 305, 336,
       286, 296, 413, 383, 328, 418, 388, 416, 371, 313, 321, 321, 317,
       402, 290, 328, 344, 330, 319, 309, 327, 351, 324, 278, 369, 416,
       359, 381, 324, 306, 350, 385, 335, 395, 308])

ax = sns.distplot(pepe_calories, fit_kws={"color":"blue"}, kde=False,
        fit=stats.norm, hist=None, label="Pepe's");
ax = sns.distplot(modern_calories, fit_kws={"color":"orange"}, kde=False,
        fit=stats.norm, hist=None, label="Modern");

# Get the two lines from the axes to generate shading
l1 = ax.lines[0]
l2 = ax.lines[1]

# Get the xy data from the lines so that we can shade
x1 = l1.get_xydata()[:,0]
y1 = l1.get_xydata()[:,1]
x2 = l2.get_xydata()[:,0]
y2 = l2.get_xydata()[:,1]

x2min = np.min(x2)
x1max = np.max(x1)

ax.fill_between(x1,y1, where = ((x1 > x2min) & (x1 < x1max)), color="red", alpha=0.3)
#> <matplotlib.collections.PolyCollection at 0x1a200510b8>

plt.legend()
#> <matplotlib.legend.Legend at 0x1a1ff2e390>
plt.show()

有什么想法吗?

reprexpy package于 2018-12-01 创建

import reprexpy
print(reprexpy.SessionInfo())
#> Session info --------------------------------------------------------------------
#> Platform: Darwin-18.2.0-x86_64-i386-64bit (64-bit)
#> Python: 3.6
#> Date: 2018-12-01
#> Packages ------------------------------------------------------------------------
#> matplotlib==2.1.2
#> numpy==1.15.4
#> reprexpy==0.1.1
#> scipy==1.1.0
#> seaborn==0.9.0

【问题讨论】:

    标签: python python-3.x matplotlib seaborn


    【解决方案1】:

    虽然从 get_xydata 收集 pdf 数据很聪明,但您现在受制于 matplotlib 的渲染/分割算法。让x1x2 跨越不同的范围也使得比较y1y2 变得困难。

    您可以通过自己拟合法线来避免这些问题,而不是 让sns.distplot 去做。然后你可以更好地控制你的价值观 正在寻找。

    import numpy as np
    import scipy.stats as stats
    import matplotlib.pyplot as plt
    norm = stats.norm
    
    pepe_calories = np.array([361, 291, 263, 284, 311, 284, 282, 228, 328, 263, 354, 302, 293,
           254, 297, 281, 307, 281, 262, 302, 244, 259, 273, 299, 278, 257,
           296, 237, 276, 280, 291, 278, 251, 313, 314, 323, 333, 270, 317,
           321, 307, 256, 301, 264, 221, 251, 307, 283, 300, 292, 344, 239,
           288, 356, 224, 246, 196, 202, 314, 301, 336, 294, 237, 284, 311,
           257, 255, 287, 243, 267, 253, 257, 320, 295, 295, 271, 322, 343,
           313, 293, 298, 272, 267, 257, 334, 276, 337, 325, 261, 344, 298,
           253, 302, 318, 289, 302, 291, 343, 310, 241])
    
    
    
    modern_calories = np.array([310, 315, 303, 360, 339, 416, 278, 326, 316, 314, 333, 317, 357,
           304, 363, 387, 279, 350, 367, 321, 366, 311, 308, 303, 299, 363,
           335, 357, 392, 321, 361, 285, 321, 290, 392, 341, 331, 338, 326,
           314, 327, 320, 293, 333, 297, 315, 365, 408, 352, 359, 312, 300,
           263, 358, 345, 360, 336, 378, 315, 354, 318, 300, 372, 305, 336,
           286, 296, 413, 383, 328, 418, 388, 416, 371, 313, 321, 321, 317,
           402, 290, 328, 344, 330, 319, 309, 327, 351, 324, 278, 369, 416,
           359, 381, 324, 306, 350, 385, 335, 395, 308])
    
    
    pepe_params = norm.fit(pepe_calories)
    modern_params = norm.fit(modern_calories)
    
    xmin = min(pepe_calories.min(), modern_calories.min())
    xmax = max(pepe_calories.max(), modern_calories.max())
    x = np.linspace(xmin, xmax, 100)
    
    pepe_pdf = norm(*pepe_params).pdf(x)
    modern_pdf = norm(*modern_params).pdf(x)
    y = np.minimum(modern_pdf, pepe_pdf)
    
    fig, ax = plt.subplots()
    ax.plot(x, pepe_pdf, label="Pepe's", color='blue')
    ax.plot(x, modern_pdf, label="Modern", color='orange')
    ax.fill_between(x, y, color='red', alpha=0.3)
    plt.legend()
    plt.show()
    


    如果说,sns.distplot(或其他绘图函数)绘制了您不想重现的绘图,那么您可以这样使用来自get_xydata 的数据:

    import numpy as np
    import seaborn as sns
    import scipy.stats as stats
    import matplotlib.pyplot as plt
    
    pepe_calories = np.array([361, 291, 263, 284, 311, 284, 282, 228, 328, 263, 354, 302, 293,
           254, 297, 281, 307, 281, 262, 302, 244, 259, 273, 299, 278, 257,
           296, 237, 276, 280, 291, 278, 251, 313, 314, 323, 333, 270, 317,
           321, 307, 256, 301, 264, 221, 251, 307, 283, 300, 292, 344, 239,
           288, 356, 224, 246, 196, 202, 314, 301, 336, 294, 237, 284, 311,
           257, 255, 287, 243, 267, 253, 257, 320, 295, 295, 271, 322, 343,
           313, 293, 298, 272, 267, 257, 334, 276, 337, 325, 261, 344, 298,
           253, 302, 318, 289, 302, 291, 343, 310, 241])
    
    
    
    modern_calories = np.array([310, 315, 303, 360, 339, 416, 278, 326, 316, 314, 333, 317, 357,
           304, 363, 387, 279, 350, 367, 321, 366, 311, 308, 303, 299, 363,
           335, 357, 392, 321, 361, 285, 321, 290, 392, 341, 331, 338, 326,
           314, 327, 320, 293, 333, 297, 315, 365, 408, 352, 359, 312, 300,
           263, 358, 345, 360, 336, 378, 315, 354, 318, 300, 372, 305, 336,
           286, 296, 413, 383, 328, 418, 388, 416, 371, 313, 321, 321, 317,
           402, 290, 328, 344, 330, 319, 309, 327, 351, 324, 278, 369, 416,
           359, 381, 324, 306, 350, 385, 335, 395, 308])
    
    ax = sns.distplot(pepe_calories, fit_kws={"color":"blue"}, kde=False,
            fit=stats.norm, hist=None, label="Pepe's");
    ax = sns.distplot(modern_calories, fit_kws={"color":"orange"}, kde=False,
            fit=stats.norm, hist=None, label="Modern");
    
    # Get the two lines from the axes to generate shading
    l1 = ax.lines[0]
    l2 = ax.lines[1]
    
    # Get the xy data from the lines so that we can shade
    x1, y1 = l1.get_xydata().T
    x2, y2 = l2.get_xydata().T
    
    xmin = max(x1.min(), x2.min())
    xmax = min(x1.max(), x2.max())
    x = np.linspace(xmin, xmax, 100)
    y1 = np.interp(x, x1, y1)
    y2 = np.interp(x, x2, y2)
    y = np.minimum(y1, y2)
    ax.fill_between(x, y, color="red", alpha=0.3)
    
    plt.legend()
    plt.show()
    

    【讨论】:

      【解决方案2】:

      我想在您想要完全控制结果图的情况下不使用 seaborn 通常是一种有用的策略。因此,只需计算拟合,绘制它们并在曲线之间使用填充,直到它们相互交叉。

      import numpy as np
      import scipy.stats as stats
      import matplotlib.pyplot as plt
      
      pepe_calories = np.array(...)
      modern_calories = np.array(...)
      
      x = np.linspace(150,470,1000)
      
      y1 = stats.norm.pdf(x, *stats.norm.fit(pepe_calories))
      y2 = stats.norm.pdf(x, *stats.norm.fit(modern_calories))
      
      cross = x[y1-y2 <= 0][0]
      
      fig, ax = plt.subplots()
      
      ax.fill_between(x,y1,y2, where=(x<=cross), color="red", alpha=0.3)
      ax.plot(x,y1, label="Pepe's")
      ax.plot(x,y2, label="Modern")
      
      ax.legend()
      plt.show()
      

      【讨论】:

      • 我可能误解了这个问题,在这种情况下,另一个答案似乎正是这样做的?!
      • 不,你是对的。我误读了答案。非常感谢!这太棒了!
      猜你喜欢
      • 2019-05-25
      • 2022-01-13
      • 2017-12-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多