简体   繁体   中英

Fixed bar width/thickness in plotly.express.timeline

I'm using Plotly Express to draw a Gantt graph.

I don't know in advance the number of horizontal lines (the number of "tasks").

I would like the bars to be always the same thickness. (I believe the attribute is width, but since the bars are horizontal, "width" might be ambiguous.)

When plotting the figure directly, it takes the whole space on the page and when resizing the page, the bars change size accordingly.

import datetime as dt
import plotly.express as px


data = [
    {'Task': '1-2', 'Start': '2021-09-26', 'Finish': '2021-09-27', 'Resource': 'R1'},
    {'Task': '2-1', 'Start': '2021-09-23', 'Finish': '2021-09-24', 'Resource': 'R2'},
    {'Task': '2-2', 'Start': '2021-09-26', 'Finish': '2021-09-27', 'Resource': 'R2'},
    {'Task': '3-1', 'Start': '2021-09-30', 'Finish': '2021-10-01', 'Resource': 'R3'},
    {'Task': '3-2', 'Start': '2021-11-26', 'Finish': '2021-11-27', 'Resource': 'R3'},
]

fig = px.timeline(
    data,
    x_start="Start",
    x_end="Finish",
    y="Resource",
)

fig.show()

There is a width attribute on each bar but it controls how much of the allowed space the bar uses, so it doesn't actually fix the width.

A cheap trick is to set the graph height according to the number of lines but one must take into account the surroundings (legend, title, etc.) so it is not really linear. And anyway, it would be at best a sorry workaround.

When I integrate the graph in a page like

Python code

return plotly.io.to_json(fig)

HTML page

<script src="https://cdn.plot.ly/plotly-2.0.0.min.js"></script>
<script type="text/javascript">

let graph = {{ gantt_json|safe }};
Plotly.newPlot('gantt_graph', graph, {});

</script>

the graph doesn't take the whole page, but still the bar width changes when the number of bar changes.


So I can control the total graph size, and the width of each bar relative to its allowed space, but I can't seem to find a way to get a fixed width in pixels.

(I thought this issue and closing PR would help but they didn't.)

Am I missing something?

  • thinking laterally this can be achieved by controlling domain of yaxis
  • have simulated data, which will generate a random number of timelines
  • based on number of timelines set domain min value
import pandas as pd
import numpy as np
import plotly.express as px
T = 8
data = pd.DataFrame(
    {
        "Start": pd.date_range("1-jan-2021", freq="2D", periods=T),
        "Finish": pd.date_range("14-jan-2021", freq="2D", periods=T),
        "Resource": np.random.choice(
            [chr(i) for i in range(ord("A"), ord("A") + T)], T
        ),
    }
)
color_map = {chr(ord("A") + i): px.colors.qualitative.Alphabet[i%len(px.colors.qualitative.Alphabet)] for i in range(T)}
fig = px.timeline(
    data,
    x_start="Start",
    x_end="Finish",
    y="Resource",
    color="Resource",
    color_discrete_map=color_map,
    #    height=10 * len(data)  # This sucks
)
fig.update_xaxes(
    side="top",
    dtick="D1",
    tickformat="%e\n%b\n%Y",
    ticklabelmode="period",
    tickangle=0,
    fixedrange=True,
)
fig.update_yaxes(
    title="",
    tickson="boundaries",
    fixedrange=True,
)
BARHEIGHT = .1
fig.update_layout(
    yaxis={"domain": [max(1 - (BARHEIGHT * len(fig.data)), 0), 1]}, margin={"t": 0, "b": 0}
)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM