Skip to content

Commit

Permalink
initial impl of px.timeline
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolaskruchten committed Jul 8, 2020
1 parent 3390b34 commit 5960241
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 2 deletions.
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
47 changes: 47 additions & 0 deletions packages/python/plotly/plotly/express/_chart_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,53 @@ def bar(
bar.__doc__ = make_docstring(bar, append_dict=_cartesian_append_dict)


def timeline(
data_frame=None,
x_start=None,
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
36 changes: 34 additions & 2 deletions packages/python/plotly/plotly/express/_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

# Declare all supported attributes, across all plot types
direct_attrables = (
["x", "y", "z", "a", "b", "c", "r", "theta", "size", "base"]
["base", "x", "y", "z", "a", "b", "c", "r", "theta", "size", "x_start", "x_end"]
+ ["hover_name", "text", "names", "values", "parents", "wide_cross"]
+ ["ids", "error_x", "error_x_minus", "error_y", "error_y_minus", "error_z"]
+ ["error_z_minus", "lat", "lon", "locations", "animation_group"]
Expand Down Expand Up @@ -610,7 +610,8 @@ def configure_cartesian_axes(args, fig, orders):
# Set x-axis titles and axis options in the bottom-most row
x_title = get_decorated_label(args, args["x"], "x")
for xaxis in fig.select_xaxes(row=1):
xaxis.update(title_text=x_title)
if "is_timeline" not in args:
xaxis.update(title_text=x_title)
set_cartesian_axis_opts(args, xaxis, "x", orders)

# Configure axis type across all x-axes
Expand All @@ -621,6 +622,9 @@ def configure_cartesian_axes(args, fig, orders):
if "log_y" in args and args["log_y"]:
fig.update_yaxes(type="log")

if "is_timeline" in args:
fig.update_xaxes(type="date")

return fig.layout


Expand Down Expand Up @@ -1354,6 +1358,7 @@ def build_dataframe(args, constructor):
if type(args.get("color", None)) == str and args["color"] == NO_COLOR:
no_color = True
args["color"] = None

# now that things have been prepped, we do the systematic rewriting of `args`

df_output, wide_id_vars = process_args_into_dataframe(
Expand Down Expand Up @@ -1599,6 +1604,30 @@ def aggfunc_continuous(x):
return args


def process_dataframe_timeline(args):
"""
Massage input for bar traces for px.timeline()
"""
args["is_timeline"] = True
if args["x_start"] is None or args["x_end"] is None:
raise ValueError("Both x_start and x_end are required")

try:
x_start = pd.to_datetime(args["data_frame"][args["x_start"]])
x_end = pd.to_datetime(args["data_frame"][args["x_end"]])
except TypeError:
raise TypeError(
"Both x_start and x_end must refer to data convertible to datetimes."
)

# note that we are not adding any columns to the data frame here, so no risk of overwrite
args["data_frame"][args["x_end"]] = (x_end - x_start).astype("timedelta64[ms]")
args["x"] = args["x_end"]
del args["x_end"]
args["base"] = args["x_start"]
del args["x_start"]


def infer_config(args, constructor, trace_patch, layout_patch):
attrs = [k for k in direct_attrables + array_attrables if k in args]
grouped_attrs = []
Expand Down Expand Up @@ -1801,6 +1830,9 @@ def make_figure(args, constructor, trace_patch=None, layout_patch=None):
args = build_dataframe(args, constructor)
if constructor in [go.Treemap, go.Sunburst] and args["path"] is not None:
args = process_dataframe_hierarchy(args)
if constructor == "timeline":
constructor = go.Bar
args = process_dataframe_timeline(args)

trace_specs, grouped_mappings, sizeref, show_colorbar = infer_config(
args, constructor, trace_patch, layout_patch
Expand Down
10 changes: 10 additions & 0 deletions packages/python/plotly/plotly/express/_doc.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@
colref_desc,
"Values from this column or array_like are used to position marks along the z axis in cartesian coordinates.",
],
x_start=[
colref_type,
colref_desc,
"Values from this column or array_like are used to position marks along the x axis in cartesian coordinates.",
],
x_end=[
colref_type,
colref_desc,
"Values from this column or array_like are used to position marks along the x axis in cartesian coordinates.",
],
a=[
colref_type,
colref_desc,
Expand Down

0 comments on commit 5960241

Please sign in to comment.