Plotly: сортировка многокатегорийной гистограммы

#python #plotly #plotly-python

#python #plotly #plotly-python

Вопрос:

У меня возникли некоторые проблемы с сортировкой многокатегориальной диаграммы.

Несколько примеров кода.

 import pandas as pd
import plotly.graph_objects as go

data = [
    [0, "Born", 4, "Rhino"],  # commenting this line will also reverse sub category sorting
    [0, "Died", -1, "Rhino"],
    [1, "Born", 4, "Lion"],
    [1, "Died", -1, "Lion"],
    [2, "Born", 12, "Rhino"],
    [2, "Died", -5, "Lion"],
]
z_data = list(zip(*data))

df = pd.DataFrame({
    "tick": z_data[0],
    "category": z_data[1],
    "value": z_data[2],
    "type": z_data[3],
})
df = df.sort_values(by=['tick', 'category', 'value', 'type'])
print(df)
fig = go.Figure()
for t in df.type.unique():
    plot_df = df[df.type == t]
    fig.add_trace(go.Bar(
        x=[plot_df.tick, plot_df.category],
        y=abs(plot_df.value),
        name=t,
    ))
fig.update_layout({
    'barmode': 'stack',
    'xaxis': {
        'title_text': "Tick",
        'tickangle': -90,
    },
    'yaxis': {
        'title_text': "Value",
    },
})
fig.write_html(str("./diagram.html"))
  

без комментариев
прокомментировал

Как вы можете видеть, галочка 2 стоит перед галочкой 1. Это происходит потому, что ‘Rhino’ является первым в списке типов, который создаст отметки 0 и 2. Столбцы lion добавляются после отметки 1.
Но как я могу правильно отсортировать столбцы сейчас?

PS. 'barmode': 'stack' специально. Даже если он не используется в этом тестовом примере.

Ответ №1:

Я могу исправить галочку, но не порядок рождения / смерти. Я планирую строить строку за строкой, поэтому мне нужно поиграть с showlegend

Данные

 import pandas as pd
import plotly.graph_objects as go
data = [
    [0, "Born", 4, "Rhino"],  # commenting this line will also reverse sub category sorting
    [0, "Died", -1, "Rhino"],
    [1, "Born", 4, "Lion"],
    [1, "Died", -1, "Lion"],
    [2, "Born", 12, "Rhino"],
    [2, "Died", -5, "Lion"],
]
# you don't really need to zip here
df = pd.DataFrame(data, columns=["tick", "category", "value", "type"])
df["value"] = df["value"].abs()
  

Установите цвет

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

 color_diz = {"Rhino": "blue", "Lion": "red"}
df["color"] = df["type"].map(color_diz)
  

Показать легенду

Здесь я хочу показать легенду для первого вхождения каждого типа

 grp = df.groupby("type")
        .apply(lambda x: x.index.min())
        .reset_index(name="idx")

df = pd.merge(df, grp, on=["type"], how="left")

df["showlegend"] = df.index == df["idx"]
  

Данные для построения

 print(df)
  
    tick category  value   type color  idx  showlegend
0     0     Born      4  Rhino  blue    0        True
1     0     Died      1  Rhino  blue    0       False
2     1     Born      4   Lion   red    2        True
3     1     Died      1   Lion   red    2       False
4     2     Born     12  Rhino  blue    0       False
5     2     Died      5   Lion   red    2       False
  

График

 fig = go.Figure()
for i, row in df.iterrows():
    fig.add_trace(
        go.Bar(x=[[row["tick"]], [row["category"]]],
               y=[row["value"]],
               name=row["type"],
               marker_color=row["color"],
               showlegend=row["showlegend"],
               legendgroup=row["type"] # Fix legend
               ))
    
fig.update_layout({
    'barmode': 'stack',
    'xaxis': {
        'title_text': "Tick",
        'tickangle': -90,
    },
    'yaxis': {
        'title_text': "Value",
    },
})
fig.show()
  

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

Редактировать

Если у вас их больше type , вы можете использовать следующий трюк.

Сначала я генерирую разные типы

 import string
import numpy as np
import pandas as pd
import plotly.express as px

df = pd.DataFrame({"type":np.random.choice(list(string.ascii_lowercase), 100)})
  

Затем я выбираю последовательность цветов из документа и помещаю их в словарь

 color_dict = {k:v for k,v in enumerate(px.colors.qualitative.Plotly)}
  

Затем я помещаю unique type в фрейм данных

 df_col = pd.DataFrame({"type": df["type"].unique()})
  

и я присваиваю каждому из них цвет в соответствии с его индексом

 df_col["color"] = (df_col.index%len(color_dict)).map(color_dict)
  

Наконец, я сливаюсь с исходным df

 df = pd.merge(df, df_col, on=["type"], how="left")
  

Комментарии:

1. Есть ли способ автоматически применять цвета к одной и той же категории, если у вас гораздо больше категорий?

2. Также это вообще не решает проблему с легендой, потому что, если вы хотите скрыть всех львов, показанный элемент легенды связан только с одним баром.

3. @IceflowerS Я исправил использование легенды legendgroup . См. Раздел «График».

4. Спасибо, это очень помогло! Я использовал это для цвета: python df_col = pd.DataFrame({ "type": df["type"].unique(), "color": seaborn.color_palette("bright", len(df["type"].unique())).as_hex(), })