jgm_GIS
jgm_GIS

Reputation: 225

Add go.Scatter plots and gantt plot to the same plotly subplot

Is it possible to add a gantt plot to a fig created with make_subplots that contains go.Scatter time series plots. I have set shared_xaxes=true and fixedrange=True for the y-axis in the subplot, so the go.Scatter plots are linked and zoom in and out together on the x-axis.

My goal, at a minimum, is to line the x-axis of the go.Scatter plots up with the x-axis of the gantt plot. Currently I have added the subplot and the gantt plot, created by px.timeline, to an HTML page as two separate figures and they don't quite line up. Ideally I would like the x-axis of the gantt plot to be linked to the go.Scatter plots so they zoom in and out together.

Building off of Rob's answer and in response to Derek's comment, here is an example of what I am trying to do with sub plots.

import plotly.express as px
import pandas as pd
import numpy as np
from plotly.subplots import make_subplots
import plotly.graph_objects as go

fig = make_subplots(rows=2, cols=1, subplot_titles = ('Scatter1', 'Scatter2'), shared_xaxes=True)

df = pd.DataFrame(
    [
        dict(Task="Job A", Start="2009-01-01", Finish="2009-02-28", Resource="Alex"),
        dict(Task="Job B", Start="2009-03-05", Finish="2009-04-15", Resource="Alex"),
        dict(Task="Job C", Start="2009-02-20", Finish="2009-05-30", Resource="Max"),
    ]
)
df["Start"] = pd.to_datetime(df["Start"])
df["Finish"] = pd.to_datetime(df["Finish"])


fig2 = px.timeline(df, x_start="Start", x_end="Finish", y="Task", color="Resource")
#fig2.update_yaxes(autorange="reversed")

df2 = pd.DataFrame(
    {
        "Date": pd.date_range(
            df.loc[:, ["Start", "Finish"]].values.min(),
            df.loc[:, ["Start", "Finish"]].values.max(),
            freq="W-MON",
        )
    }
).pipe(lambda d: d.assign(Value=np.random.randint(1, 20, len(d))))

df3 = pd.DataFrame(
    {
        "Date": pd.date_range(
            df.loc[:, ["Start", "Finish"]].values.min(),
            df.loc[:, ["Start", "Finish"]].values.max(),
            freq="W-MON",
        )
    }
).pipe(lambda d: d.assign(Value=np.random.randint(1, 20, len(d))))
trace1 = go.Scatter(x=df2.Date, y=df2.Value)
trace2 = go.Scatter(x=df3.Date, y=df3.Value)
                                                      
fig.add_trace(trace1, row=1, col=1)
fig.add_trace(trace2, row=2, col=1)
fig.update_yaxes(fixedrange=True)
fig.update_layout(xaxis1_showticklabels=True, xaxis2_showticklabels=True)

fig.show()
fig2.show()

Which yields three plots. The two scatter plots in the subplot are aligned, the third gantt plot isn't.

enter image description here

Ideally, I would like to add the gantt plot to the subplot with another add_trace command specifying col3, row1. I have been unable to do that successfully so far. If I can't add the gantt plot to the subplot, I would like to align the x-axis' so they are the same width and the dates align vertically.

If I add the gantt plot to the subplot like this

fig.add_trace(fig2.data[0], row=3, col=1)

The plot is displayed incorrectly, as shown below.

enter image description here

I believe that add_trace is not correctly interpreting the base and x fields of the fig2.data shown below. Instead of adding the units in the x field to the dates in base, it is plotting them as numerical.

(Bar({
    'alignmentgroup': 'True',
    'base': array([datetime.datetime(2009, 1, 1, 0, 0),
                   datetime.datetime(2009, 3, 5, 0, 0)], dtype=object),
    'hovertemplate': 'Resource=Alex<br>Start=%{base}<br>Finish=%{x}<br>Task=%{y}<extra></extra>',
    'legendgroup': 'Alex',
    'marker': {'color': '#636efa'},
    'name': 'Alex',
    'offsetgroup': 'Alex',
    'orientation': 'h',
    'showlegend': True,
    'textposition': 'auto',
    'x': array([5.0112e+09, 3.5424e+09]),
    'xaxis': 'x',
    'y': array(['Job A', 'Job B'], dtype=object),
    'yaxis': 'y'
})

Upvotes: 1

Views: 2060

Answers (2)

jgm_GIS
jgm_GIS

Reputation: 225

The way to do this is to pass the gantt figure to make_subplots as a parameter.

import plotly.express as px
import pandas as pd
import numpy as np
from plotly.subplots import make_subplots
import plotly.graph_objects as go

df = pd.DataFrame(
    [
        dict(Task="Job A", Start="2009-01-01", Finish="2009-02-28", Resource="Alex"),
        dict(Task="Job B", Start="2009-03-05", Finish="2009-04-15", Resource="Alex"),
        dict(Task="Job C", Start="2009-02-20", Finish="2009-05-30", Resource="Max"),
    ]
)
df["Start"] = pd.to_datetime(df["Start"])
df["Finish"] = pd.to_datetime(df["Finish"])


fig2 = px.timeline(df, x_start="Start", x_end="Finish", y="Task", color="Resource")

df2 = pd.DataFrame(
    {
        "Date": pd.date_range(
            df.loc[:, ["Start", "Finish"]].values.min(),
            df.loc[:, ["Start", "Finish"]].values.max(),
            freq="W-MON",
        )
    }
).pipe(lambda d: d.assign(Value=np.random.randint(1, 20, len(d))))

df3 = pd.DataFrame(
    {
        "Date": pd.date_range(
            df.loc[:, ["Start", "Finish"]].values.min(),
            df.loc[:, ["Start", "Finish"]].values.max(),
            freq="W-MON",
        )
    }
).pipe(lambda d: d.assign(Value=np.random.randint(1, 20, len(d))))
trace1 = go.Scatter(x=df2.Date, y=df2.Value)
trace2 = go.Scatter(x=df3.Date, y=df3.Value)


fig = make_subplots(rows=3, cols=1, figure=fig2, shared_xaxes=True)
fig.add_trace(trace1, row=2, col=1)
fig.add_trace(trace2, row=3, col=1)
fig.update_layout(xaxis1_showticklabels=True, xaxis2_showticklabels=True, xaxis3_showticklabels=True)
fig.show()

enter image description here

Upvotes: 0

Rob Raymond
Rob Raymond

Reputation: 31226

  • you have not provided sample data, so have simulated. Assumed y values of scatter are different to y values of gantt
  • simple case of adding scatter to figure and configuring axes appropriately
import plotly.express as px
import pandas as pd
import numpy as np

df = pd.DataFrame(
    [
        dict(Task="Job A", Start="2009-01-01", Finish="2009-02-28", Resource="Alex"),
        dict(Task="Job B", Start="2009-03-05", Finish="2009-04-15", Resource="Alex"),
        dict(Task="Job C", Start="2009-02-20", Finish="2009-05-30", Resource="Max"),
    ]
)
df["Start"] = pd.to_datetime(df["Start"])
df["Finish"] = pd.to_datetime(df["Finish"])


fig = px.timeline(df, x_start="Start", x_end="Finish", y="Task", color="Resource")
fig.update_yaxes(autorange="reversed")

df2 = pd.DataFrame(
    {
        "Date": pd.date_range(
            df.loc[:, ["Start", "Finish"]].values.min(),
            df.loc[:, ["Start", "Finish"]].values.max(),
            freq="W-MON",
        )
    }
).pipe(lambda d: d.assign(Value=np.random.randint(1, 20, len(d))))

fig.add_traces(
    px.scatter(df2, x="Date", y="Value").update_traces(yaxis="y2").data
).update_layout(
    yaxis2={"overlaying": "y", "side": "right"}, xaxis={"domain": [0, 0.98]}
)

enter image description here

Upvotes: 1

Related Questions