Заполните область перекрытия между двумя нормальными распределениями в seaborn / matplotlib - PullRequest
0 голосов
/ 01 декабря 2018

Я хочу заполнить область, перекрывающуюся между двумя нормальными распределениями.У меня есть x мин и макс, но я не могу понять, как установить границы y .

Я смотрел на документация plt и некоторые примеры .Я думаю этот связанный вопрос и этот близко, но не повезло.Вот что у меня есть.

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()

Есть идеи?

Создано в 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

Ответы [ 2 ]

0 голосов
/ 01 декабря 2018

Я полагаю, что использование 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()

enter image description here

0 голосов
/ 01 декабря 2018

Хотя сбор данных в формате pdf из get_xydata является умным, вы теперь находитесь в зависимости от алгоритма рендеринга / сегментации matplotlib.Наличие x1 и x2 охватывает разные диапазоны, что также затрудняет сравнение y1 и y2.

Вы можете избежать этих проблем, подгоняя нормали самостоятельно, вместо того, чтобы позволить 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()

enter image description here


Если, скажем, 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()

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...