Search code examples
pythonjupyter-notebookplotlyplotly-python

Succint way to add line segments to plotly graph (with python/jupyter notebook)?


I want to create a lollipop plot with several horizontal line segments like this - https://python-graph-gallery.com/184-lollipop-plot-with-2-group. I'd like to use plotly since I prefer the graphics (and easy interactivity) but can't find a succint way.

There's both line graphs (https://plot.ly/python/line-charts/) and you can add lines in the layout (https://plot.ly/python/shapes/#vertical-and-horizontal-lines-positioned-relative-to-the-axes), but both of these solutions require each line segment to be added separately, with about 4-8 lines of code each. While I could just for-loop this, would appreciate if anyone can point me to anything with inbuilt vectorization, like the matplotlib solution (first link)!

Edit: Also tried the following code, to first make the plot ala matplotlib, then convert to plotly. The line segments disappear in the process. Starting to think it's just impossible.

mpl_fig = plt.figure()

# make matplotlib plot - WITH HLINES
plt.rcParams['figure.figsize'] = [5,5]
ax = mpl_fig.add_subplot(111)
ax.hlines(y=my_range, xmin=ordered_df['value1'], xmax=ordered_df['value2'], 
color='grey', alpha=0.4)
ax.scatter(ordered_df['value1'], my_range, color='skyblue', alpha=1, 
label='value1')
ax.scatter(ordered_df['value2'], my_range, color='green', alpha=0.4 , 
label='value2')
ax.legend()

# convert to plotly
plotly_fig = tls.mpl_to_plotly(mpl_fig)
plotly_fig['layout']['xaxis1']['showgrid'] = True
plotly_fig['layout']['xaxis1']['autorange'] = True
plotly_fig['layout']['yaxis1']['showgrid'] = True
plotly_fig['layout']['yaxis1']['autorange'] = True

# plot: hlines disappear :/
iplot(plotly_fig)

Solution

  • Plotly doesn't provide a built in vectorization for such chart, because it can be done easily by yourself, see my example based on your provided links:

    import pandas as pd
    import numpy as np
    import plotly.offline as pyo
    import plotly.graph_objs as go
    
    # Create a dataframe
    value1 = np.random.uniform(size = 20)
    value2 = value1 + np.random.uniform(size = 20) / 4
    df = pd.DataFrame({'group':list(map(chr, range(65, 85))), 'value1':value1 , 'value2':value2 })
    
    my_range=range(1,len(df.index)+1)
    
    # Add title and axis names
    data1 = go.Scatter(
            x=df['value1'],
            y=np.array(my_range),
            mode='markers',
            marker=dict(color='blue')
        )
    
    
    data2 = go.Scatter(
            x=df['value2'],
            y=np.array(my_range),
            mode='markers',
            marker=dict(color='green')
        )
    
    # Horizontal line shape
    shapes=[dict(
            type='line',
            x0 = df['value1'].loc[i],
            y0 = i + 1,
            x1 = df['value2'].loc[i],
            y1 = i + 1,
            line = dict(
                color = 'grey',
                width = 2
            )
        ) for i in range(len(df['value1']))]
    
    
    layout = go.Layout(
        shapes = shapes,
        title='Lollipop Chart'
    )
    
    # Plot the chart
    fig = go.Figure([data1, data2], layout)
    
    pyo.plot(fig)
    

    With the result I got:

    enter image description here