Как можно создать такой график в Plotly/Pandas?

Я хотел бы воссоздать следующее изображение в Plotly и Pandas. но я не уверен, как это сделать.

Как можно создать такой график в Plotly/Pandas?

Я рассматривал возможность использования диаграмм Ганта, но каждый раз, когда я вижу их, временная шкала проходит по оси X, а не по оси Y, как на этом рисунке. Есть ли у кого-нибудь советы?

1
0
52
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

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

Я бы рекомендовал вводить время в 24-часовом формате, чтобы можно было правильно рассчитать продолжительность.

Вы можете использовать следующее в качестве шаблона для графика, который хотите создать (изменяя цветовую схему, размер шрифта и т. д. по желанию). Я оставил метки на оси Y, чтобы было легче понять мой код (мы видим, что час 23 дня на самом деле является часом -1 внутри самой фигуры, а час 0 дня остается прежним), но вы можете скрыть метки оси Y на окончательном рисунке.

import pandas as pd
import plotly.graph_objects as go
from datetime import datetime

df = pd.DataFrame({
    'start': ['2024-06-17 00:04', '2024-06-18 00:24', '2024-06-18 23:54', '2024-06-19 23:59'],
    'end': ['2024-06-17 08:18', '2024-06-18 09:05', '2024-06-19 09:02', '2024-06-20 08:13'],
})

df['start'] = pd.to_datetime(df['start'])
df['end'] = pd.to_datetime(df['end'])
df['duration'] = df['end'] - df['start']

fig = go.Figure()

def get_exact_hour(ts):
    ## we want 23:54 to become -1 on the graph
    if ts.hour > 12:
        hour = ts.hour - 24
    else:
        hour = ts.hour
    minute = ts.minute
    return hour + minute/60

def convert_duration_to_hours(td: pd.Timedelta):
    hours = duration.seconds // 3600
    minutes =  (duration.seconds % 3600) / 60
    return hours + minutes/60

for start, end, duration in df.itertuples(index=False):
    day_rounded = start.round('1d') - pd.Timedelta("1D")
    day_name = day_rounded.day_name()
    day_number = day_rounded.day
    day_string = f"{day_name} </br></br> {day_number}"
    start_hour = get_exact_hour(start)
    end_hour = get_exact_hour(end)
    duration_in_hours = convert_duration_to_hours(duration)

    fig.add_trace(
        go.Bar(
            x=[day_string],
            y=[duration_in_hours],
            base=[start_hour],
            marker=dict(color='lightblue'),
        )
    )

    ## add start and end times text annotations
    start_time_formatted = start.strftime("%I:%M")
    end_time_formatted = end.strftime("%I:%M")
    padding = 0.2

    fig.add_trace(
        go.Scatter(
            x=[day_string],
            y=[start_hour-padding],
            mode='text',
            text=[start_time_formatted],
        )
    )

    fig.add_trace(
        go.Scatter(
            x=[day_string],
            y=[end_hour+padding],
            mode='text',
            text=[end_time_formatted],
        )
    )

## prettify the chart
fig.update_xaxes(type='category')
fig.update_layout(
    template='plotly_dark',
    title='TIME IN BED',
    barcornerradius=15,
    showlegend=False
)
fig.show()

Спасибо! Это потрясающе! Это сработало именно так, как я надеялся.

Jack Finnegan 30.06.2024 23:36

Нет проблем – рад, что помог!

Derek O 30.06.2024 23:51

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