Что это за сюжет называется? И как мне построить график с помощью matplotlib?

Я наткнулся на эту статью, в которой ниже представлен этот сюжет. Может кто-нибудь поделиться, как называется такой сюжет? И как я могу построить аналогичную диаграмму с помощью Python, в частности matplotlib? Мне также нужно будет представить прогнозы на основе логарифмической регрессии, отсюда и вопрос.

Спасибо!

выглядит как коробчатая диаграмма или панель ошибок

Tino D 28.05.2024 12:33

Если введены множество отдельных измерений, вы можете создать нечто подобное с помощью seaborn. sns.pointplot(data=..., x=..., y=..., hue=..., linestyle='none')где data указывает на фрейм данных, x и y на столбцы, используемые для осей X и Y, hue на столбец, который группируется в разные цвета.

JohanC 28.05.2024 16:32

Обратите внимание: чтобы ваше сообщение подходило для StackOverflow, оно должно включать в себя некоторые воспроизводимые тестовые данные и ваш лучший код. Для линии регрессии потребуется числовая ось X, что сделает ваш вопрос немного запутанным.

JohanC 28.05.2024 16:35
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
3
103
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

На первый взгляд...

Это почти наверняка панель ошибок с сгруппированными разделами. Поскольку вы спросили, как построить такой график, давайте сначала разберемся с некоторыми основами.


Таблицы ошибок. Учебное пособие

Определение

Столбец ошибок — это график, используемый для моделирования и/или иллюстрации изменчивости и неопределенности в качестве эвристики анализа данных. Он позволяет визуализировать точность точек данных и может использоваться для моделирования стандартного отклонения, стандартной ошибки, доверительных интервалов или диапазона (Cumming, Fidler & Vaux, 2007). Это делается с помощью маркеров, нарисованных на исходном графике и его точках данных, рядом с линиями с заглушками (или заглушками), идущими от центра нанесенной точки данных.

Прописные буквы придают вашему сюжету нотку визуальной эстетики (субъективное мнение), помогая вам быстро концептуализировать границы относительно ваших точек данных. Однако в предоставленном вами образце (если это действительно полоса ошибок) не используются заглавные буквы. Это действительно может улучшить видимость конечных точек шкалы ошибок, что может быть полезно на графиках со многими перекрывающимися элементами.

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

Анатомия панели ошибок (источник)

Кроме того, полосы ошибок могут быть симметричными (одинаковая длина выше и ниже точки данных) или асимметричными (разная длина).

Столбики ошибок можно применять к диаграммам рассеяния, точечным графикам, гистограммам или линейным графикам, чтобы обеспечить дополнительный уровень детализации, расширяющий информацию, представленную исходными данными (Статья «Каталог визуализации данных»).

Значение ошибки на таком графике — это величина, на которую ваши точки данных отклоняются от ожидаемого значения, и может быть указана как фиксированное значение или как процент от точки данных (последнее, я считаю, это то, что представило ваше исходное изображение). .

Интерпретация

Что касается вашего исходного материала, который я также кратко изучил, моя интерпретация (конкретной обрезанной части в вашем вопросе) заключается в том, что авторы, похоже, тестируют вероятностную модель, анализирующую вероятность того, что вакцинированные пациенты будут госпитализированы после возникновения тяжелые инфекции варианта Омикрона по сравнению с фактическими исходами, зарегистрированными в действительности. Таким образом, планка ошибок используется там как мера распределения точности результатов модели. В смысле МО я считаю, что это способ спросить:

Насколько хорошо модель соответствует обучающим данным?


Столбики ошибок в Matplotlib

Простая панель ошибок

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

Вы можете нарисовать простую шкалу ошибок, используя matplotlib.pyplot.errorbar(), следующим образом:

import matplotlib.pyplot as plt

# Sample data
x = [1, 2, 3]
y = [10, 20, 30]
yerr = [2, 3, 1]  # Error values for y

# Create a plot with error bars
plt.errorbar(x, y, yerr=yerr, fmt = "o", capsize=5, label = "Data with error bars")

# Label axes
plt.xlabel("X-axis")
plt.ylabel("Y-axis")

# Show legend
plt.legend()

# Show plot
plt.show()

Выход:

Ключевым выводом является тот факт, что у нас есть готовые значения ошибок, и мы легко передаем их в параметр yerr параметра errorbar(), все остальное в значительной степени тривиально.

Чтобы получить график ошибок без крышки, вы всегда можете установить параметр capsize для errorbar() на 0. Аналогичным образом вы также можете отображать точки данных панели ошибок с помощью маркеров, указав дополнительный аргумент ключевого слова от marker до errorbar() с любым из следующих значений: 'o', 's', '^', 'D', 'P'. Мы сделаем это более сложным способом, когда попытаемся построить график, идентичный вашему образцу.

Итак, «Как я могу построить аналогичную диаграмму в Python с помощью Matplotlib?»

Код для создания графика в вашем образце будет выглядеть примерно так. Это довольно просто и понятно, но поскольку я придумываю (почти близкую к реальным точкам данных, но в конечном итоге фальшивую) точки данных, я стратегически распределил интервалы для точек данных.

import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots()

# Categories/x-axis data and their positions
categories = ["Primary + booster < 1yr", "Primary + booster >= 1yr", "At most primary"]
positions = np.arange(len(categories))

# Set the y-axis label and ticks
ax.set_ylabel("Probability hospital (%)")
ax.set_yticks(np.arange(0, 101, 25))

# Set the x-axis with categorical labels
ax.set_xticks(positions)
ax.set_xticklabels(categories)

# Define the colors and markers
colors = ['black', 'blue', 'green', 'yellow', 'red']
markers = ['o', 's', '^', 'D', 'P']

# Define the offsets for spacing the error bars
offsets = np.linspace(-0.1, 0.1, len(colors))

# Guesstimations of vertical lines with different ranges for each category
y_ranges = [
    [(45, 50), (50, 55), (52, 58), (48, 53), (47, 56)],
    [(55, 60), (60, 65), (62, 68), (58, 63), (57, 66)],
    [(75, 80), (80, 85), (82, 88), (78, 83), (77, 86)]
]

# Plot the error bars for each category
for i, ranges in enumerate(y_ranges):
    for j, (color, marker, offset, (ymin, ymax)) in enumerate(zip(colors, markers, offsets, ranges)):
        x_position = positions[i] + offset  # Adjust x position with offset
        y = (ymin + ymax) / 2  # Center point for marker
        yerr = (ymax - ymin) / 2  # Error value for the error bar      # capsize=5
        ax.errorbar(x_position, y, yerr=yerr, fmt=marker, color=color, capsize=0, label=f'{categories[i]} - Line {j+1}')

# Add internal text-label in the top-left corner
ax.text(0.05, 0.95, "At least 60 yrs", transform=ax.transAxes,
        fontsize=12, verticalalignment='top', bbox=dict(facecolor='white', edgecolor='none'))

# Bborder to the plot
for spine in ax.spines.values():
    spine.set_edgecolor('black')

# Show the plot
plt.show()

Выход:


Последние соображения: что, если это не полоса ошибок (хотя это маловероятно)?

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

import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots()

# Categories/x-axis data and their positions
categories = ["Primary + booster < 1yr", "Primary + booster >= 1yr", "At most primary"]
positions = np.arange(len(categories))

# Set the y-axis label and ticks
ax.set_ylabel("Probability hospital (%)")
ax.set_yticks(np.arange(0, 101, 25))

# Set the x-axis with categorical labels
ax.set_xticks(positions)
ax.set_xticklabels(categories)

# Colors and markers
colors = ['black', 'blue', 'green', 'yellow', 'red']
markers = ['o', 's', '^', 'D', 'P']

# Offsets for spacing the vertical lines
offsets = np.linspace(-0.1, 0.1, len(colors))

# Guesstimations of vertical lines with different ranges for each category
range_limits = [(45, 60), (55, 70), (75, 90)]
y_ranges = [
    [(45, 50), (50, 55), (52, 58), (48, 53), (47, 56)],
    [(55, 60), (60, 65), (62, 68), (58, 63), (57, 66)],
    [(75, 80), (80, 85), (82, 88), (78, 83), (77, 86)]
]

for i, (limits, ranges) in enumerate(zip(range_limits, y_ranges)):
    for j, (color, marker, offset, (ymin, ymax)) in enumerate(zip(colors, markers, offsets, ranges)):
        x_position = positions[i] + offset  # Adjust x position with offset
        y = (ymin + ymax) / 2  # Center point for marker
        ax.vlines(x_position, ymin, ymax, color=color, label=f'{categories[i]} - Line {j+1}')
        ax.scatter(x_position, y, color=color, marker=marker)

# internal text-label in the top-left corner
ax.text(0.05, 0.95, "At least 60 yrs", transform=ax.transAxes,
        fontsize=12, verticalalignment='top', bbox=dict(facecolor='white', edgecolor='none'))

# Border to the plot
for spine in ax.spines.values():
    spine.set_edgecolor('black')

# Voila!
plt.show()

Выход:

Seaborn построен на Matplotlib, поэтому Я черпал вдохновение из комментария JohanC, чтобы использовать Seaborn sns.pointplot(), потому что он обрабатывает статистику по данным, которые представляют собой множество отдельных выборок, в основном «нестандартных»....
Это также во многом основано на превосходном ответе М.А. на данные и многие настройки, выходящие за рамки результата Seaborn, который в основном установлен по умолчанию. Данные здесь расширены за пределы того, что составляет 1000 выборок на пятнадцать точек данных на окончательном графике с 15 000 выборочными точками данных, структурированными в фрейме данных Pandas.

Использование Seaborn: построение кадра данных с помощью sns.pointplot()

Это может показаться большим количеством кода, но сюжет — это всего лишь последний звонок. Большая часть этого блока просто создает данные. Seaborn дает хорошие сюжеты в связке с Pandas.

# JohanC's suggestiom for https://stackoverflow.com/q/78543170/8508004
import numpy as np
import pandas as pd

# to generate mock data use guesstimations from M.A.
y_ranges = [
    [(45, 50), (50, 55), (52, 58), (48, 53), (47, 56)],
    [(55, 60), (60, 65), (62, 68), (58, 63), (57, 66)],
    [(75, 80), (80, 85), (82, 88), (78, 83), (77, 86)]
]
# generate mock data
sig_factor_multiplier = 8
avgs_less_than1year = [np.mean(x) for x in y_ranges[0]]
avgs_greater_than1year = [np.mean(x) for x in y_ranges[1]] 
avgs_at_most_primary = [np.mean(x) for x in y_ranges[2]]
err_range_less_than1year = [(x[1]-x[0])*sig_factor_multiplier for x in y_ranges[0]]
err_range__greater_than1year = [(x[1]-x[0])*sig_factor_multiplier for x in y_ranges[1]] 
err_range__at_most_primary = [(x[1]-x[0])*sig_factor_multiplier for x in y_ranges[2]]
hospitals = ["a","b","c","d","e"]
def make_values_with_mean_covering_range(mu_x, sigma_x, number_values):
    return np.random.normal(mu_x, sigma_x, number_values) # based on https://stackoverflow.com/a/57335302/8508004


# Generate dataframe with mock data values and category labels
# first, make a list of lists with each sample probability for each hospital for under year, over, at most primary
list_o_data = []
groupdata_and_labels = [(avgs_less_than1year,err_range_less_than1year,"Primary +\nbooster < 1 yr"),(avgs_greater_than1year,err_range__greater_than1year,"Primary +\nbooster >= 1 yr"),(avgs_at_most_primary,err_range__at_most_primary,"At most\nprimary")]
for grp_ind,gdnl in enumerate(groupdata_and_labels):
    for hosp_indx,hospital in enumerate(hospitals):
        the_group_label = gdnl[2]
        the_prob_values = make_values_with_mean_covering_range(gdnl[0][hosp_indx], gdnl[1][hosp_indx], 1000)
        for pv in the_prob_values:
            list_o_data.append([hospital,the_group_label,pv])
df_labels = ['hospital', 'Groups','Probability hospital (%)']
df = pd.DataFrame.from_records(list_o_data, columns=df_labels)
## ALL THAT ABOVE WAS ONLY TO MAKE DATAFRAME ##-------------------------------------------------## ALL THAT ABOVE WAS ONLY TO MAKE DATAFRAME ##


#With the dataframe in hand, Seaborn will happily make the plot
import seaborn as sns
p = sns.pointplot(
    data=df, x = "Groups", y = "Probability hospital (%)",
    capsize=0, legend=False, linewidth = 1.78, 
    linestyle = "none", hue = "hospital",  dodge=.34 ,markers=["o", "s","^","D","P"], #marker styles based on https://seaborn.pydata.org/tutorial/properties.html
); # this based on examples in https://seaborn.pydata.org/generated/seaborn.pointplot.html

Это хорошее начало, учитывая, что мы создали фрейм данных, а затем просто вызвали sns.pointplot() с его помощью.
Одна вещь, которая явно является проблемой, заключается в том, что мы не хотим, чтобы «Группы» располагались ниже оси Y. Поэтому мы настроим его ниже.

Использование Seaborn: индивидуализация, превосходящая возможности Seaborn, всего лишь с помощью sns.pointplot()

Это значение по умолчанию используется Seaborn с использованием фрейма данных и указанием некоторых настроек при вызове sns.pointplot.
. Мы можем дополнительно настроить его, чтобы он больше походил на пример, сделанный M.A, но с использованием оригинальных цветов.

# JohanC's suggestiom for https://stackoverflow.com/q/78543170/8508004
import numpy as np
import pandas as pd
#import matplotlib.pyplot as plt; NOT NEEDED ONCE I ADDED LINE BREAKS IN COLUMN LABEL, see below

# to generate mock data use guesstimations from M.A.
y_ranges = [
    [(45, 50), (50, 55), (52, 58), (48, 53), (47, 56)],
    [(55, 60), (60, 65), (62, 68), (58, 63), (57, 66)],
    [(75, 80), (80, 85), (82, 88), (78, 83), (77, 86)]
]
# generate mock data
sig_factor_multiplier = 8
avgs_less_than1year = [np.mean(x) for x in y_ranges[0]]
avgs_greater_than1year = [np.mean(x) for x in y_ranges[1]] 
avgs_at_most_primary = [np.mean(x) for x in y_ranges[2]]
err_range_less_than1year = [(x[1]-x[0])*sig_factor_multiplier for x in y_ranges[0]]
err_range__greater_than1year = [(x[1]-x[0])*sig_factor_multiplier for x in y_ranges[1]] 
err_range__at_most_primary = [(x[1]-x[0])*sig_factor_multiplier for x in y_ranges[2]]
hospitals = ["a","b","c","d","e"]
def make_values_with_mean_covering_range(mu_x, sigma_x, number_values):
    return np.random.normal(mu_x, sigma_x, number_values) # based on https://stackoverflow.com/a/57335302/8508004


# Generate dataframe with mock data values and category labels
# first, make a list of lists with each sample probability for each hospital for under year, over, at most primary
list_o_data = []
groupdata_and_labels = [(avgs_less_than1year,err_range_less_than1year,"Primary +\nbooster < 1 yr"),(avgs_greater_than1year,err_range__greater_than1year,"Primary +\nbooster >= 1 yr"),(avgs_at_most_primary,err_range__at_most_primary,"At most\nprimary")]
for grp_ind,gdnl in enumerate(groupdata_and_labels):
    for hosp_indx,hospital in enumerate(hospitals):
        the_group_label = gdnl[2]
        the_prob_values = make_values_with_mean_covering_range(gdnl[0][hosp_indx], gdnl[1][hosp_indx], 1000)
        for pv in the_prob_values:
            list_o_data.append([hospital,the_group_label,pv])
df_labels = ['hospital', 'Groups','Probability hospital (%)']
df = pd.DataFrame.from_records(list_o_data, columns=df_labels)
## ALL THAT ABOVE WAS ONLY TO MAKE DATAFRAME ##-------------------------------------------------## ALL THAT ABOVE WAS ONLY TO MAKE DATAFRAME ##

#plt.figure(figsize=(7.6, 6)) # similar to https://stackoverflow.com/a/50671064/8508004 for setting plot size ; NOT NEEDED ONCE I ADDED LINE BREAKS IN COLUMN LABEL

#With the dataframe in hand, Seaborn will happily make the plot
import seaborn as sns
my_palette = {
    'a': 'black',
    'b': 'tab:blue',
    'c': 'tab:cyan',
    'd': 'gold',
    'e': 'tab:red',
}
p = sns.pointplot(
    data=df, x = "Groups", y = "Probability hospital (%)",
    capsize=0, legend=False, linewidth = 1.78, palette = my_palette,
    linestyle = "none", hue = "hospital",  dodge=.34 ,markers=["o", "s","^","D","P"], #marker styles based on https://seaborn.pydata.org/tutorial/properties.html
) # this based on examples in https://seaborn.pydata.org/generated/seaborn.pointplot.html

# Customize the plot some
# Set the y-axis label and ticks
#a.set_ylabel("Probability hospital (%)") # works but made it unnecessary by putting label in dataframe column name
p.set_xlabel(None) # remove the 'Groups' label below categories
p.set_yticks(np.arange(0, 101, 25))
p.set_ylim(40, 91)

# internal text-label in the top-left corner
p.text(0.03, 0.95, "At least 60 yrs", transform=p.transAxes,
        fontsize=12, verticalalignment='top', bbox=dict(facecolor='white', edgecolor='none'));

Использование Seaborn: собрать все воедино для чего-то вроде предложенного ОП

И еще больше похоже на предоставленный оригинал, исправив диапазон осей и маркеры:

# JohanC's suggestiom for https://stackoverflow.com/q/78543170/8508004
import numpy as np
import pandas as pd
#import matplotlib.pyplot as plt; NOT NEEDED ONCE I ADDED LINE BREAKS IN COLUMN LABEL, see below

# to generate mock data use guesstimations from M.A.
y_ranges = [
    [(45, 50), (50, 55), (52, 58), (48, 53), (47, 56)],
    [(55, 60), (60, 65), (62, 68), (58, 63), (57, 66)],
    [(75, 80), (80, 85), (82, 88), (78, 83), (77, 86)]
]
# generate mock data
sig_factor_multiplier = 8
avgs_less_than1year = [np.mean(x) for x in y_ranges[0]]
avgs_greater_than1year = [np.mean(x) for x in y_ranges[1]] 
avgs_at_most_primary = [np.mean(x) for x in y_ranges[2]]
err_range_less_than1year = [(x[1]-x[0])*sig_factor_multiplier for x in y_ranges[0]]
err_range__greater_than1year = [(x[1]-x[0])*sig_factor_multiplier for x in y_ranges[1]] 
err_range__at_most_primary = [(x[1]-x[0])*sig_factor_multiplier for x in y_ranges[2]]
hospitals = ["a","b","c","d","e"]
def make_values_with_mean_covering_range(mu_x, sigma_x, number_values):
    return np.random.normal(mu_x, sigma_x, number_values) # based on https://stackoverflow.com/a/57335302/8508004


# Generate dataframe with mock data values and category labels
# first, make a list of lists with each sample probability for each hospital for under year, over, at most primary
list_o_data = []
groupdata_and_labels = [(avgs_less_than1year,err_range_less_than1year,"Primary +\nbooster < 1 yr"),(avgs_greater_than1year,err_range__greater_than1year,"Primary +\nbooster >= 1 yr"),(avgs_at_most_primary,err_range__at_most_primary,"At most\nprimary")]
for grp_ind,gdnl in enumerate(groupdata_and_labels):
    for hosp_indx,hospital in enumerate(hospitals):
        the_group_label = gdnl[2]
        the_prob_values = make_values_with_mean_covering_range(gdnl[0][hosp_indx], gdnl[1][hosp_indx], 1000)
        for pv in the_prob_values:
            list_o_data.append([hospital,the_group_label,pv])
df_labels = ['hospital', 'Groups','Probability hospital (%)']
df = pd.DataFrame.from_records(list_o_data, columns=df_labels)
## ALL THAT ABOVE WAS ONLY TO MAKE DATAFRAME ##-------------------------------------------------## ALL THAT ABOVE WAS ONLY TO MAKE DATAFRAME ##

#plt.figure(figsize=(7.6, 6)) # similar to https://stackoverflow.com/a/50671064/8508004 for setting plot size ; NOT NEEDED ONCE I ADDED LINE BREAKS IN COLUMN LABEL

#With the dataframe in hand, Seaborn will happily make the plot
import seaborn as sns
my_palette = {
    'a': 'black',
    'b': 'tab:blue',
    'c': 'tab:cyan',
    'd': 'gold',
    'e': 'tab:red',
} #based on https://stackoverflow.com/a/68616910/8508004 and https://seaborn.pydata.org/generated/seaborn.pointplot.html saying "a dictionary mapping hue levels to matplotlib colors."
p = sns.pointplot(
    data=df, x = "Groups", y = "Probability hospital (%)",
    capsize=0, legend=False, linewidth = 1.78, palette = my_palette,
    linestyle = "none", hue = "hospital",  dodge=.27 ,markers=["o", "s","D", "^","v",], markersize = 5.0, #marker styles based on https://seaborn.pydata.org/tutorial/properties.html
) # this based on examples in https://seaborn.pydata.org/generated/seaborn.pointplot.html

# Customize the plot some
# Set the y-axis label and ticks
#a.set_ylabel("Probability hospital (%)") # works but made it unnecessary by putting label in dataframe column name
p.set_xlabel(None) # remove the 'Groups' label below categories
p.set_yticks(np.arange(0, 101, 25))
#p.set_ylim(40, 91)

# internal text-label in the top-left corner
p.text(0.03, 0.95, "At least 60 yrs", transform=p.transAxes,
        fontsize=12, verticalalignment='top', bbox=dict(facecolor='white', edgecolor='none'));


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

Перейдите сюда и нажмите значок «Запустить подшивку». (Вы можете выбрать конкретный вариант Jupyter внизу.) Или просто нажмите здесь, чтобы запустить сеанс. Как только начнется сеанс, вы можете вставить любой из этих блоков кода в ячейку и запустить его, и вы получите результаты, подобные опубликованным.

Другие вопросы по теме