#python #pandas #dataframe #boxplot
Вопрос:
У меня есть многоиндексный фрейм данных «Панды», который я хочу построить в виде блок-схемы. Это должно быть легко сделать, но я обнаружил, что не могу получить именно то, что хочу. Данные выглядят следующим образом:
hedges mask model_name hedges_std hedges_min
period season
2021-2025 winter 0.864328 1.0 ensemble 0.301748 0.124708
spring 0.740410 1.0 ensemble 0.202963 0.049319
summer 0.526264 1.0 ensemble 0.105750 0.162856
fall 0.531141 1.0 ensemble 0.046278 0.388827
2025-2050 winter 1.715075 1.0 ensemble 0.373866 0.582819
spring 1.252963 1.0 ensemble 0.370402 0.408695
summer 0.854958 1.0 ensemble 0.076193 0.528038
fall 0.759645 1.0 ensemble 0.068928 0.498271
2050-2075 winter 2.981373 1.0 ensemble 0.928940 1.139801
spring 2.042320 1.0 ensemble 0.748642 0.716289
summer 1.299277 1.0 ensemble 0.092611 0.812979
fall 1.108852 1.0 ensemble 0.109014 0.653199
2021-2025 winter 0.864328 1.0 ensemble 0.301748 0.124708
spring 0.740410 1.0 ensemble 0.202963 0.049319
summer 0.526264 1.0 ensemble 0.105750 0.162856
fall 0.531141 1.0 ensemble 0.046278 0.388827
2025-2050 winter 1.715075 1.0 ensemble 0.373866 0.582819
spring 1.252963 1.0 ensemble 0.370402 0.408695
summer 0.854958 1.0 ensemble 0.076193 0.528038
fall 0.759645 1.0 ensemble 0.068928 0.498271
2050-2075 winter 2.981373 1.0 ensemble 0.928940 1.139801
spring 2.042320 1.0 ensemble 0.748642 0.716289
summer 1.299277 1.0 ensemble 0.092611 0.812979
fall 1.108852 1.0 ensemble 0.109014 0.653199
hedges_max model_scenario
period season
2021-2025 winter 1.760912 ssp245
spring 1.189956 ssp245
summer 0.662142 ssp245
fall 0.687793 ssp245
2025-2050 winter 2.423660 ssp245
spring 2.040903 ssp245
summer 1.055890 ssp245
fall 0.965831 ssp245
2050-2075 winter 5.179203 ssp245
spring 3.898118 ssp245
summer 1.536149 ssp245
fall 1.435503 ssp245
2021-2025 winter 1.760912 ssp585
spring 1.189956 ssp585
summer 0.662142 ssp585
fall 0.687793 ssp585
2025-2050 winter 2.423660 ssp585
spring 2.040903 ssp585
summer 1.055890 ssp585
fall 0.965831 ssp585
2050-2075 winter 5.179203 ssp585
spring 3.898118 ssp585
summer 1.536149 ssp585
fall 1.435503 ssp585
Я хочу построить данные, показывающие по одному квадрату для каждого периода и сезона, разделенных цветом по сценарию. Каждое поле будет определяться его средним значением (хеджирование), стандартным отклонением (std) и потенциально минимальным и максимальным диапазоном. Идея заключается в том, чтобы показать, как будущие периоды изменят оценочные распределения хеджирования. Я пробовал различные комбинации вокруг:
sns.boxplot(data=df, x="season", y="hedges", hue="model_scenario")
Моя ошибка Could not interpret input 'season'
связана с мультииндексом, который мне явно нужно как-то сгруппировать или разделить, но именно здесь я продолжаю терпеть неудачу. Мы будем признательны за предложения о том, как отображать эти данные.
Комментарии:
1. Вы на 100% уверены, что «сезон» является доступной колонкой в кадре? Согласно исходному коду seaborn, это сообщение печатается, когда «сезон» отсутствует в кадре данных. Если проблема в том, что «сезон» является частью индекса, вы можете подумать о вызове
df.reset_index()
перед построением графика.2. @dsillman2000 При попытке печати
df["season"]
я получаю ошибку «Ошибка ключа: «сезон»». Похоже, что период и сезон определяют мультииндекс. Если я разделю их или реорганизую, чтобы иметь только один индекс, я потеряю структуру, которую хочу построить.
Ответ №1:
Я предполагаю, что ваша цель-создать такую фигуру, как эта:
Поскольку у вас уже рассчитана статистика ваших ящиков, функция sns.boxplot()
, а также matplotlib.axes.Axes.boxplot()
из matplotlib (которая является серверной частью seaborn и вызывается внутри sns.boxplot()
), больше не являются функциями, которые вы можете использовать. Попытка ax.boxplot()
рассчитать статистику сама по себе, поэтому это не тот путь, по которому нужно идти.
После вычисления boxplot-статистика matplotlib.axes.Axes.boxplot()
вызывает [ matplotlib.axes.Axes.bxp()
](https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.bxp.html) и эту функцию вы тоже можете использовать.
Функция matplotlib.axes.Axes.boxplot()
принимает диктант с этим соглашением об именах:
- med: Медиана (скалярный поплавок),
- q1: Первый квартиль (25-й процентиль) (скалярный поплавок),
- q3: Третий квартиль (75-й процентиль) (скалярный поплавок),
- whislo: Нижняя граница нижнего уса (скалярный поплавок),
- whishi: Верхняя граница верхнего уса (скалярный поплавок),
и только с небольшими изменениями мы можем переименовать или создать нужные столбцы вашего фрейма данных. Но сначала сбросьте мультииндекс.
# df is defined and the multiinde
df = df.rename({'hedges':'med', 'hedges_min':'whislo', 'hedges_max':'whishi'}, axis=1)
df['q1'] = df['med'] - df['hedges_std']
df['q3'] = df['med'] df['hedges_std']
df['label'] = df.apply(lambda x: '(' x['period'] ' , ' x['season'] ')', axis=1)
df = df[['med', 'whislo','whishi','q1','q3', 'label']] # this are the columns we need
>>> df.head(5)
med whislo whishi q1 q3 label
0 0.864328 0.124708 1.760912 0.562580 1.166076 (2021-2025 , winter)
1 0.740410 0.049319 1.189956 0.537447 0.943373 (2021-2025 , spring)
2 0.526264 0.162856 0.662142 0.420514 0.632014 (2021-2025 , summer)
3 0.531141 0.388827 0.687793 0.484863 0.577419 (2021-2025 , fall)
4 1.715075 0.582819 2.423660 1.341209 2.088941 (2025-2050 , winter)
Я решил создать ярлык period
, объединяющий season
и. Каждая метка появляется дважды, каждый model_scenario
раз ровно один раз.
Вот код, как я создал рисунок выше. Это не идеально, но это показывает, как это работает. Некоторые из разделов а соответствуют кодексу sns.boxplot()
.
from matplotlib import rcParams
import matplotlib.pyplot as plt
colors = ['lightblue', 'olive']
model_scenario = ["ssp245", "ssp585"]
fig, ax = plt.subplots(figsize=(9, 4))
ax.set_title('box plot')
x_tick_label = []
x_tick_position = []
for i, group in enumerate(data_to_plt.groupby('label')):
for j in range(group[1].shape[0]):
x_tick_label.append(group[0])
x_tick_position.append(i)
if j ==0:
p = i - 0.15
else:
p = i 0.15
artist_dict = ax.bxp(
bxpstats=[group[1].drop('label', axis=1).iloc[j].to_dict()],
showfliers=False,
patch_artist=True,
positions=[p]
)
for box in artist_dict["boxes"]:
box.update(dict(facecolor=colors[j],
zorder=.9,
edgecolor='gray',
linewidth=rcParams["lines.linewidth"])
)
if i == 0:
rect = plt.Rectangle([0,0], 0, 0,
linewidth=0,
edgecolor='gray',
facecolor=colors[j],
label=model_scenario[j])
ax.add_patch(rect)
ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
plt.xticks(x_tick_position, x_tick_label, rotation = 90)
Чтобы обобщить то, что я делаю с matplotlib:
- Я группируюсь по
model_scenario
акаlabels
- Я создаю метки для легенды
- Я рисую коробки, используя
bxp()
- Я переписываю крестики-нолики
Комментарии:
1. Это звучит здорово, но у меня все еще есть проблема с мультииндексом. Как вы его разделили? Я продолжаю получать
KeyError: 'period'
, когда пытаюсь убежатьdf.apply(lambda x:...
. Был ли какой-то шаг, который вы здесь не включили? Спасибо2. Заставил его работать с простым
df=df.reset_index()
. Спасибо за помощь!