Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bar base and timeline #2626

Merged
merged 3 commits into from
Jul 14, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).


## [4.9.0] - unreleased

### Added

- `px.NO_COLOR` constant to override wide-form color assignment in Plotly Express ([#2614](https://github.com/plotly/plotly.py/pull/2614))
- `facet_row_spacing` and `facet_col_spacing` added to Plotly Express cartesian 2d functions ([#2614](https://github.com/plotly/plotly.py/pull/2614))
- `base` added to Plotly Express `bar` and `bar_polar` functions
- `plotly.express.timeline()` added as an official alternative to `plotly.figure_factories.create_gantt()`

### Fixed

Expand All @@ -21,6 +22,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).




## [4.8.2] - 2020-06-26

### Updated
Expand Down
6 changes: 3 additions & 3 deletions doc/python/figure-factories.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,12 @@ jupyter:

#### `plotly.figure_factory`

The `plotly.figure_factory` module contains dedicated functions for creating very specific types of plots that were at the time of their creation difficult to create with `plotly.graph_objects` and prior to the existence of [Plotly Express](/python/plotly-express/). As new functionality gets added to [Plotly.js](https://plotly.com/javascript/) and to Plotly Express, certain Figure Factories become unecessary and are therefore deprecated as "legacy", but remain in the module for backwards-compatibility reasons.
The `plotly.figure_factory` module contains dedicated functions for creating very specific types of plots that were at the time of their creation difficult to create with [graph objects](/python/graph-objects/) and prior to the existence of [Plotly Express](/python/plotly-express/). As new functionality gets added to [Plotly.js](https://plotly.com/javascript/) and to Plotly Express, certain Figure Factories become unecessary and are therefore deprecated as "legacy", but remain in the module for backwards-compatibility reasons.

The following types of plots are still difficult to create with Graph Objects or Plotly Express and therefore the corresponding Figure Factories are *not* deprecated:

* [Annotated Heatmaps](/python/annotated-heatmap/)
* [Dendrograms](/python/dendrogram/)
* [Gantt Charts](/python/gantt/)
* [Quiver Plots](/python/quiver-plots/)
* [Streamline Plots](/python/streamline-plots/)
* [Tables](/python/figure-factory-table/)
Expand All @@ -52,7 +51,8 @@ Deprecated "legacy" Figure Factories include:

* [County Choropleth Maps](/python/county-choropleth/), deprecated by regular [Choropleth maps with GeoJSON input](/python/choropleth-maps/)
* [Distplots](/python/distplot/), mostly deprecated by [`px.histogram`](/python/histograms/)
* [Gantt Charts](/python/gantt/), deprecated by [`px.timeline`](/python/gantt/)

#### Reference

For more information about the contents of `plotly.figure_factory`, including deprecated methods, please refer to our [API Reference documentation](https://plotly.com/python-api-reference/plotly.figure_factory.html).
For more information about the contents of `plotly.figure_factory`, including deprecated methods, please refer to our [API Reference documentation](https://plotly.com/python-api-reference/plotly.figure_factory.html).
142 changes: 74 additions & 68 deletions doc/python/gantt.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ jupyter:
text_representation:
extension: .md
format_name: markdown
format_version: '1.1'
jupytext_version: 1.1.1
format_version: '1.2'
jupytext_version: 1.4.2
kernelspec:
display_name: Python 3
language: python
Expand All @@ -20,7 +20,7 @@ jupyter:
name: python
nbconvert_exporter: python
pygments_lexer: ipython3
version: 3.6.7
version: 3.7.7
plotly:
description: How to make Gantt Charts in Python with Plotly. Gantt Charts use
horizontal bars to represent the start and end times of tasks.
Expand All @@ -37,109 +37,100 @@ jupyter:
A [Gantt chart](https://en.wikipedia.org/wiki/Gantt_chart) is a type of bar chart that illustrates a project schedule. The chart lists the tasks to be performed on the vertical axis, and time intervals on the horizontal axis. The width of the horizontal bars in the graph shows the duration of each activity.


Gantt charts can be made using a [figure factory](/python/figure-factories/) as detailed in this page. See also the [bar charts examples](https://plotly.com/python/bar-charts/).
### Gantt Charts and Timelines with plotly.express

[Plotly Express](/python/plotly-express/) is the easy-to-use, high-level interface to Plotly, which [operates on a variety of types of data](/python/px-arguments/) and produces [easy-to-style figures](/python/styling-plotly-express/). With `px.timeline` (*introduced in version 4.9*) each data point is represented as a horizontal bar with a start and end point specified as dates.

#### Simple Gantt Chart
The `px.timeline` function by default sets the X-axis to be of `type=date`, so it can be configured like any [time-series chart](/python/time-series/).

Plotly Express also supports a [general-purpose `px.bar` function for bar charts](/python/bar-charts/).

```python
import plotly.figure_factory as ff
import plotly.express as px
import pandas as pd

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

fig = ff.create_gantt(df)
fig = px.timeline(df, x_start="Start", x_end="Finish", y="Task")
fig.update_yaxes(autorange="reversed") # otherwise tasks are listed from the bottom up
fig.show()
```

#### Index by Numeric Variable
`px.timeline` supports [discrete color](/python/discrete-color/) as above, or [continuous color](/python/colorscales/) as follows.

```python
import plotly.figure_factory as ff
import plotly.express as px
import pandas as pd

df = [dict(Task="Job A", Start='2009-01-01', Finish='2009-02-28', Complete=10),
dict(Task="Job B", Start='2008-12-05', Finish='2009-04-15', Complete=60),
dict(Task="Job C", Start='2009-02-20', Finish='2009-05-30', Complete=95)]
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")
])

fig = ff.create_gantt(df, colors='Viridis', index_col='Complete', show_colorbar=True)
fig = px.timeline(df, x_start="Start", x_end="Finish", y="Task", color="Resource")
fig.update_yaxes(autorange="reversed")
fig.show()
```

#### Index by String Variable

```python
import plotly.figure_factory as ff

df = [dict(Task="Job A", Start='2009-01-01', Finish='2009-02-01', Resource='Apple'),
dict(Task="Job B", Start='2009-03-05', Finish='2009-04-15', Resource='Grape'),
dict(Task="Job C", Start='2009-04-20', Finish='2009-09-30', Resource='Banana')]
import plotly.express as px
import pandas as pd

colors = ['#7a0504', (0.2, 0.7, 0.3), 'rgb(210, 60, 180)']
df = pd.DataFrame([
dict(Task="Job A", Start='2009-01-01', Finish='2009-02-28', Completion_pct=50),
dict(Task="Job B", Start='2009-03-05', Finish='2009-04-15', Completion_pct=25),
dict(Task="Job C", Start='2009-02-20', Finish='2009-05-30', Completion_pct=75)
])

fig = ff.create_gantt(df, colors=colors, index_col='Resource', reverse_colors=True,
show_colorbar=True)
fig = px.timeline(df, x_start="Start", x_end="Finish", y="Task", color="Completion_pct")
fig.update_yaxes(autorange="reversed")
fig.show()
```

#### Use a Dictionary for Colors
It is also possible to have multiple bars on the same horizontal line, say by resource:

```python
import plotly.figure_factory as ff
*Note*: When setting `color` to the same value as `y`, `autorange` should not be set to `reverse`, so as to list the value of the Y axis in the same order as the legend entries.

df = [dict(Task="Job A", Start='2016-01-01', Finish='2016-01-02', Resource='Apple'),
dict(Task="Job B", Start='2016-01-02', Finish='2016-01-04', Resource='Grape'),
dict(Task="Job C", Start='2016-01-02', Finish='2016-01-03', Resource='Banana')]
```python
import plotly.express as px
import pandas as pd

colors = dict(Apple='rgb(220, 0, 0)', Grape='rgb(170, 14, 200)', Banana=(1, 0.9, 0.16))
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")
])

fig = ff.create_gantt(df, colors=colors, index_col='Resource', show_colorbar=True)
fig = px.timeline(df, x_start="Start", x_end="Finish", y="Resource", color="Resource")
fig.show()
```

#### Use a Pandas Dataframe
#### Deprecated Figure Factory

Prior to the introduction of `plotly.express.timeline()` in version 4.9, the recommended way to make Gantt charts was to use the now-deprecated `create_gantt()` [figure factory](/python/figure-factories/), as follows:

```python
import plotly.figure_factory as ff

import pandas as pd
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gantt_example.csv')
df = [dict(Task="Job A", Start='2009-01-01', Finish='2009-02-28'),
dict(Task="Job B", Start='2009-03-05', Finish='2009-04-15'),
dict(Task="Job C", Start='2009-02-20', Finish='2009-05-30')]

fig = ff.create_gantt(df, colors=['#333F44', '#93e4c1'], index_col='Complete',
show_colorbar=True, bar_width=0.2, showgrid_x=True, showgrid_y=True)
fig = ff.create_gantt(df)
fig.show()
```

#### Using Hours and Minutes in Times

```python
import plotly.figure_factory as ff
<!-- #region -->
#### Group Tasks Together

df = [
dict(Task='Morning Sleep', Start='2016-01-01', Finish='2016-01-01 6:00:00', Resource='Sleep'),
dict(Task='Breakfast', Start='2016-01-01 7:00:00', Finish='2016-01-01 7:30:00', Resource='Food'),
dict(Task='Work', Start='2016-01-01 9:00:00', Finish='2016-01-01 11:25:00', Resource='Brain'),
dict(Task='Break', Start='2016-01-01 11:30:00', Finish='2016-01-01 12:00:00', Resource='Rest'),
dict(Task='Lunch', Start='2016-01-01 12:00:00', Finish='2016-01-01 13:00:00', Resource='Food'),
dict(Task='Work', Start='2016-01-01 13:00:00', Finish='2016-01-01 17:00:00', Resource='Brain'),
dict(Task='Exercise', Start='2016-01-01 17:30:00', Finish='2016-01-01 18:30:00', Resource='Cardio'),
dict(Task='Post Workout Rest', Start='2016-01-01 18:30:00', Finish='2016-01-01 19:00:00', Resource='Rest'),
dict(Task='Dinner', Start='2016-01-01 19:00:00', Finish='2016-01-01 20:00:00', Resource='Food'),
dict(Task='Evening Sleep', Start='2016-01-01 21:00:00', Finish='2016-01-01 23:59:00', Resource='Sleep')
]

colors = dict(Cardio = 'rgb(46, 137, 205)',
Food = 'rgb(114, 44, 121)',
Sleep = 'rgb(198, 47, 105)',
Brain = 'rgb(58, 149, 136)',
Rest = 'rgb(107, 127, 135)')

fig = ff.create_gantt(df, colors=colors, index_col='Resource', title='Daily Schedule',
show_colorbar=True, bar_width=0.8, showgrid_x=True, showgrid_y=True)
fig.show()
```

#### Group Tasks Together
The following example shows how to use the now-deprecated `create_gantt()` [figure factory](/python/figure-factories/) to color tasks by a numeric variable.
<!-- #endregion -->

```python
import plotly.figure_factory as ff
Expand All @@ -162,7 +153,22 @@ fig = ff.create_gantt(df, colors=colors, index_col='Resource', show_colorbar=Tru
fig.show()
```

#### Color by Numeric Variable

The following example shows how to use the now-deprecated `create_gantt()` [figure factory](/python/figure-factories/) to color tasks by a numeric variable.

```python
import plotly.figure_factory as ff

df = [dict(Task="Job A", Start='2009-01-01', Finish='2009-02-28', Complete=10),
dict(Task="Job B", Start='2008-12-05', Finish='2009-04-15', Complete=60),
dict(Task="Job C", Start='2009-02-20', Finish='2009-05-30', Complete=95)]

fig = ff.create_gantt(df, colors='Viridis', index_col='Complete', show_colorbar=True)
fig.show()
```

#### Reference


For more info on `ff.create_gantt()`, see the [full function reference](https://plotly.com/python-api-reference/generated/plotly.figure_factory.create_gantt.html)
For more info on `ff.create_gantt()`, see the [full function reference](https://plotly.com/python-api-reference/generated/plotly.figure_factory.create_gantt.html)
2 changes: 2 additions & 0 deletions packages/python/plotly/plotly/express/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
line_geo,
area,
bar,
timeline,
bar_polar,
violin,
box,
Expand Down Expand Up @@ -81,6 +82,7 @@
"parallel_categories",
"area",
"bar",
"timeline",
"bar_polar",
"violin",
"box",
Expand Down
49 changes: 49 additions & 0 deletions packages/python/plotly/plotly/express/_chart_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ def bar(
hover_data=None,
custom_data=None,
text=None,
base=None,
error_x=None,
error_x_minus=None,
error_y=None,
Expand Down Expand Up @@ -357,6 +358,53 @@ def bar(
bar.__doc__ = make_docstring(bar, append_dict=_cartesian_append_dict)


def timeline(
data_frame=None,
x_start=None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if these arguments are required they should not be keywords arguments but positional ones I think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could think of defaults like providing a global start or end parameter which would be the one for all. But I think the easiest solution is to make the arguments positional.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would really like to keep this function consistent with all the others, which have the data_frame argument come first. I agree that in general required arguments should come first and be positional, but in this case I prefer consistency. px.pie() sort of works the same way: you need to provide either values or names otherwise you get an empty figure.

To align with something like pie I could just have x be unbound if x_start and x_end aren't provided instead of raising (i.e. we would render an empty figure just like px.pie(df)) but I think this would be less helpful for users.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see your point but preferring px consistency over python usage (when I see a keyword argument I don't expect the function to raise with its default value, and in particular None usually means optional) is not 100% satisfying. You are right that pie has the same problem, and that if you use all the default values for the other functions, the result is an empty figure so not very interesting. I'd be happy with a modification of the docstring for x_start and x_end mentioning that they are compulsory.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To align with something like pie I could just have x be unbound if x_start and x_end aren't provided instead of raising (i.e. we would render an empty figure just like px.pie(df)) but I think this would be less helpful for users.

but this would be consistent :-)

x_end=None,
y=None,
color=None,
facet_row=None,
facet_col=None,
facet_col_wrap=0,
facet_row_spacing=None,
facet_col_spacing=None,
hover_name=None,
hover_data=None,
custom_data=None,
text=None,
animation_frame=None,
animation_group=None,
category_orders={},
labels={},
color_discrete_sequence=None,
color_discrete_map={},
color_continuous_scale=None,
range_color=None,
color_continuous_midpoint=None,
opacity=None,
range_x=None,
range_y=None,
title=None,
template=None,
width=None,
height=None,
):
"""
In a timeline plot, each row of `data_frame` is represented as a rectangular
mark on an x axis of type `date`, spanning from `x_start` to `x_end`.
"""
return make_figure(
args=locals(),
constructor="timeline",
trace_patch=dict(textposition="auto", orientation="h"),
layout_patch=dict(barmode="overlay"),
)


timeline.__doc__ = make_docstring(timeline)


def histogram(
data_frame=None,
x=None,
Expand Down Expand Up @@ -847,6 +895,7 @@ def bar_polar(
hover_name=None,
hover_data=None,
custom_data=None,
base=None,
animation_frame=None,
animation_group=None,
category_orders={},
Expand Down
Loading