Добавьте планки погрешностей с настраиваемыми верхними и нижними границами в гистограмму в python.

Я хочу добавить HDI (интервалы высокой плотности), которые я вычислил (столбцы hdi_both, hdi_one и lower_upper в df ниже) к гистограмме.

Однако я не могу понять, как добавить планки ошибок/CI, чтобы каждая полоса ошибок имела настраиваемые верхнюю и нижнюю границы, которые не зависят от значения y (в данном случае соответствующего значения в proportion_correct).

Например, интервал HDI для Exp. 1 с guesses_correct both имеет нижнюю границу 0.000000 и верхнюю границу 0.130435, а proportion_correct равно 0.000000.

Все варианты, которые я видел, включают указание верхней и нижней границ относительно значения по оси Y, а это не то, что я ищу.

Любая помощь будет оценена.

Спасибо,

Айяла

import os
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

df = pd.DataFrame({
 'exp': ['Exp. 1', 'Exp. 1', 'Exp. 2', 'Exp. 2', 'Exp. 3', 'Exp. 3', 'Exp. 4', 'Exp. 4', 'Exp. 5', 'Exp. 5',
 'Collapsed', 'Collapsed'],
 'proportion_correct': [0.0, 0.304347826, 0.058823529000000006, 0.31372549, 0.047619048, 0.333333333, 0.12244898, 0.428571429, 0.12244898, 0.367346939, 0.082901554, 0.35751295299999997],
 'guesses_correct': ['both', 'one', 'both', 'one', 'both', 'one', 'both', 'one', 'both', 'one', 'both', 'one'],
 'hdi_both': [0.0, 0.130434783, 0.0, 0.078431373, 0.0, 0.1, 0.0, 0.08, 0.0, 0.081632653, 0.005181347, 0.051813472],
 'hdi_one': [0.130434783, 0.47826087, 0.156862745, 0.41176470600000004, 0.1, 0.5, 0.16, 0.4, 0.163265306, 0.408163265, 0.21761658, 0.341968912],
 'lower_upper': ['lower', 'upper', 'lower', 'upper', 'lower', 'upper', 'lower', 'upper', 'lower', 'upper', 'lower', 'upper']
})

print(df.head())
Out[4]: 
      exp  proportion_correct guesses_correct  hdi_both   hdi_one lower_upper
0  Exp. 1            0.000000            both  0.000000  0.130435       lower
1  Exp. 1            0.304348             one  0.130435  0.478261       upper
2  Exp. 2            0.058824            both  0.000000  0.156863       lower
3  Exp. 2            0.313725             one  0.078431  0.411765       upper
4  Exp. 3            0.047619            both  0.000000  0.100000       lower
# Make bar plot
sns.barplot(x='exp',
            y='proportion_correct',
            hue='guesses_correct',
            data=df)

plt.ylim([0, 0.5])
plt.xlabel('Experiment')
plt.ylabel('Proportion Correct')
plt.legend(title='Correct guesses', loc='upper right')
plt.axhline(y=0.277777, color='dimgray', linestyle='--')
plt.annotate(' chance\n one', (5.5, 0.27))
plt.axhline(y=0.02777, color='dimgray', linestyle='--')
plt.annotate(' chance\n both', (5.5, 0.02))
# Show the plot
plt.show()

Это гистограмма, для которой я хочу добавить HDI введите здесь описание изображения


person ayalaall    schedule 31.12.2020    source источник


Ответы (2)


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

Затем вы можете использовать plt.errorbar() для построения. Обратите внимание, что для использования этой функции все значения ошибок должны быть положительными.

Поскольку вы используете разделение hue=, вам необходимо перебирать разные уровни hue и учитывать сдвиг полос (по умолчанию -0,2 и +0,2 для двух уровней оттенка):

# Make bar plot
x_col = 'exp'
y_col = 'proportion_correct'
hue_col = 'guesses_correct'
low_col = 'hdi_both'
high_col = 'hdi_one'
sns.barplot(x=x_col,
            y=y_col,
            hue=hue_col,
            data=df)

for (h,g),pos in zip(df.groupby(hue_col),[-0.2,0.2]):
    err = g[[low_col, high_col]].subtract(g[y_col], axis=0).abs().T.values
    x = np.arange(len(g[x_col].unique()))+pos
    plt.errorbar(x=x, y=g[y_col], yerr=err, fmt='none', capsize=5, ecolor='k')

введите здесь описание изображения

person Diziet Asahi    schedule 01.01.2021
comment
Привет @Diziet Asahi, я думаю, что есть некоторая ошибка в вычислении планок погрешностей, так что они будут соответствовать абсолютным значениям планок погрешностей. Например в эксп. 4 нижняя и верхняя границы для proportion_correct both 0.12244898 равны 0 и 0.08. Однако из сгенерированного вами графика видно, что нижняя граница равна 0, что правильно, а верхняя граница на графике — ~0.18, что неверно. То же самое относится и к другим полосам ошибок на графиках. Итак, что-то в вашем коде мне кажется неправильным, но я все еще пытаюсь понять, что. Спасибо! - person ayalaall; 02.01.2021
comment
Я думаю, что неправильно понял формат вашего фрейма данных. У меня сложилось впечатление, что границы для Exp. 4 both равны [0.000000,0.160000] (значения в той же строке). Но вы говорите, что они должны быть [0.000000,0.080000] (значения в двух последовательных строках в столбце hdi_both)? - person Diziet Asahi; 02.01.2021
comment
Если бы вы сами создали этот фрейм данных из необработанных данных, было бы гораздо разумнее (по крайней мере, на мой взгляд), чтобы каждая строка соответствовала одному условию (Exp. N,both/one,proportion,hdi_low,hdi_high) со всеми значениями, которые связанные с этим условием (включая границы интервала конф.) в той же строке. - person Diziet Asahi; 02.01.2021
comment
Привет @Дизиет Асахи. Я пробовал это, но он все еще не показывает полосы ошибок должным образом. Я попытался отредактировать свой вопрос, чтобы объяснить, почему, но редактирование не было одобрено. - person ayalaall; 09.01.2021

Я закончил тем, что построил вертикальные линии в качестве планок погрешностей. Вот мой код на случай, если он кому-то поможет.

df = pd.DataFrame({'exp': ['Exp. 1', 'Exp. 1', 'Exp. 2', 'Exp. 2', 'Exp. 3', 'Exp. 3', 'Exp. 4', 'Exp. 4', 'Exp. 5', 'Exp. 5', 'Collapsed', 'Collapsed'],
                   'proportion_correct': [0.0, 0.304347826, 0.058823529000000006, 0.31372549, 0.047619048, 0.333333333, 0.12244898, 0.428571429, 0.12244898, 0.367346939, 0.082901554, 0.35751295299999997],
                   'guesses_correct': ['both', 'one', 'both', 'one', 'both', 'one', 'both', 'one', 'both', 'one', 'both', 'one'], 
                   'hdi_low': [0.0, 0.130434783, 0.0, 0.156862745, 0.0, 0.1, 0.0, 0.16, 0.0, 0.163265306, 0.005181347, 0.21761658],
                   'hdi_high': [0.130434783, 0.47826087, 0.078431373, 0.41176470600000004, 0.1, 0.5, 0.08, 0.4, 0.081632653, 0.408163265, 0.051813472, 0.341968912]
                  })
df.head()
Out[4]: 
  exp  proportion_correct guesses_correct   hdi_low  hdi_high
0  Exp. 1            0.000000            both  0.000000  0.130435
1  Exp. 1            0.304348             one  0.130435  0.478261
2  Exp. 2            0.058824            both  0.000000  0.078431
3  Exp. 2            0.313725             one  0.156863  0.411765
4  Exp. 3            0.047619            both  0.000000  0.100000

Следующие функции axvlines и axhlines были взяты из Как рисовать вертикальные линии на заданном графике в matplotlib. Я не пишу их здесь для ясности.

    # Make bar plot
    x_col = 'exp'
    y_col = 'proportion_correct'
    hue_col = 'guesses_correct'
    low_col = 'hdi_low'
    high_col = 'hdi_high'
    plot = sns.barplot(x=x_col,
                y=y_col,
                hue=hue_col,
                data=df)
    plt.ylim([0, 0.55])
    plt.yticks([0, 0.1, 0.2, 0.3, 0.4, 0.5], [0, 0.1, 0.2, 0.3, 0.4, 0.5])
    plt.xlabel('Experiment')
    plt.ylabel('Proportion Correct')
    plt.legend(title='Correct guesses', loc='upper right')
    plt.axhline(y=0.277777, color='dimgray', linestyle='--')
    plt.annotate(' chance\n one', (5.65, 0.27))
    plt.axhline(y=0.02777, color='dimgray', linestyle='--')
    plt.annotate(' chance\n both', (5.65, 0.02))
    lims_x = list(map(lambda x, y: (x, y), df[low_col].to_list(), df[high_col].to_list()))
    xss = [-0.2, 0.2, 0.8, 1.2, 1.8, 2.2, 2.8, 3.2, 3.8, 4.2, 4.8, 5.2]
    yss = [i for sub in lims_x for i in sub]
    lims_y = [(-0.3, -0.1), (-0.3, -0.1), (0.1, 0.3), (0.1, 0.3), (0.7, 0.9), (0.7, 0.9), (1.1, 1.3), (1.1, 1.3),
              (1.7, 1.9), (1.7, 1.9), (2.1, 2.3), (2.1, 2.3), (2.7, 2.9), (2.7, 2.9), (3.1, 3.3),  (3.1, 3.3),
              (3.7, 3.9), (3.7, 3.9), (4.1, 4.3), (4.1, 4.3), (4.7, 4.9), (4.7, 4.9), (5.1, 5.3), (5.1, 5.3)]
    for xs, lim in zip(xss, lims_x):
        plot = axvlines(xs, lims=lim, color='black')
    for yx, lim in zip(yss, lims_y):
        plot = axhlines(yx, lims=lim, color='black')
    plt.show()

И это сюжет введите здесь описание изображения

person ayalaall    schedule 09.01.2021