0

I would like to plot line segments with plotly. below demo works but it use different traces per segments, which should not be the right way to do it.

How can I implement line segments plot?

import pandas as pd
import numpy as np

import plotly.graph_objects as go
from plotly.subplots import make_subplots

def plot_segments(df):    
    xname = "ts"
    yname = "duration"
    dfg = df.groupby('name')

    fig = go.Figure()
    colors=['#4f81bd','#c0504d','#9bbb59','#8064a2','#4bacc6','#f79646','#0000ff']
    traces = []
    dy = 1.1
    for i,[gname, df] in enumerate(dfg):
        for index,row in df.iterrows():
            x1 = row['ts']
            x2 = x1 + pd.to_timedelta(row['duration'],unit = 's')

            x = [x1,x2]
            y = [dy,dy]
            trace1 = go.Scatter(
                x=x,
                y=y,
                mode='lines+markers',            
                marker=dict(
                    size=4,
                    line=dict(width=0,color=colors[i])),
                line=dict(width=1,color=colors[i]),
            )
            traces.append(trace1)

        dy += .03

    fig.add_traces(traces)
    
    fontsize = 10
    fig.add_annotation(dict(
        font=dict(color="black",size=fontsize),
        x=0.5,
        xshift=0,
        y=0,
        yshift=-30,
        showarrow=False,
        text='Timestamp',
        textangle=0,
        xref="paper",
        yref="paper",
        xanchor='center',
        yanchor='top',
    ))
    
    fig.add_annotation(dict(
        font=dict(color="black",size=fontsize),
        x=-0,
        xshift=-20,
        y=0.5,
        showarrow=False,
        text='Category',
        textangle=-90,
        xref="paper",
        yref="paper",
        xanchor='right',
        yanchor='middle',
    ))

    xpading=.05
    fig.update_layout(
        margin=dict(l=50,t=40,r=10,b=40),
        plot_bgcolor='#ffffff',#'rgb(12,163,135)',
        paper_bgcolor='#ffffff',        
        title="Segments",
        #xaxis2_title="Timestamp",
        #yaxis_title="Interval(secs)",
        title_x=0.5,
        showlegend=False,
        legend=dict(x=.02,y=1.05),        
        barmode='group',
        bargap=0.05,
        bargroupgap=0.0,
        font=dict(
            family="Courier New, monospace",
            size=fontsize,
            color="black"
        ),
        xaxis=dict(
            visible=True,            
            tickangle=-15,            
            tickformat = '%m-%d %H:%M:%S',#datetime format
            showline=True,
            linecolor='black',
            color='black',
            linewidth=.5,
            ticks='outside',
            showgrid=False,
            gridcolor='grey',
            gridwidth=.5,
            griddash='solid',#'dot',            
        ),
        yaxis=dict(            
            range=[0,1.2],            
            showline=True,
            linecolor='black',
            color='black',
            linewidth=.5,            
            showgrid=True,
            gridcolor='grey',
            gridwidth=.5,
            griddash='solid',#'dot',
            zeroline=True,
            zerolinecolor='grey',
            zerolinewidth=.5,
            showticklabels=True,
        ),        
    )

    fig.show()
    return

data = [
    ['04-21 20:54:10.247','A',2],
    ['04-21 20:54:15.247','A',1],
    ['04-21 20:54:20.247','A',2],
    ['04-21 20:54:25.247','A',1],
    ['04-21 20:54:11.247','B',2],
    ['04-21 20:54:26.247','B',1],
    ['04-21 20:54:31.247','B',2],
    ['04-21 20:54:36.247','B',1]
    ]


df = pd.DataFrame(data,columns=['ts','name','duration'])

df['ts'] = pd.to_datetime(df['ts'],format="%m-%d %H:%M:%S.%f")
plot_segments(df)

Output: enter image description here

lucky1928
  • 8,708
  • 10
  • 43
  • 92

1 Answers1

1

In your plot_segments function you can modify the first lines to look like this:

from itertools import cycle

def plot_segments(df):
    fig = go.Figure()
    colors=['#4f81bd','#c0504d','#9bbb59','#8064a2','#4bacc6','#f79646','#0000ff']

    def calculate_segments(df):
        start = df['ts']
        end = start + pd.to_timedelta(df['duration'], unit='s')
        segments = list(zip(start, end, cycle([None])))
        return segments

    segments = df.groupby('name').apply(calculate_segments)

    dy = 1.1
    for data, color in zip(segments,colors):
        data = np.array(data).flatten()
        trace = go.Scatter(
            x=data,
            y=np.repeat(dy, data.size),
            mode='lines+markers',
            marker=dict(
                size=4,
                line=dict(width=0, color=color)),
            line=dict(width=1, color=color),
        )

        fig.add_trace(trace)
        dy += .03

        # Rest of code remains unchanged

This will just call add_trace twice. It is creating multiple line segments per trace by using None between each segment x data.

Brener Ramos
  • 256
  • 5