Skip to content
This repository has been archived by the owner on Jun 3, 2024. It is now read-only.

Port to plotly.py v4_subplots and renderers #88

Closed
wants to merge 6 commits into from
Closed

Conversation

jonmmease
Copy link
Contributor

@jonmmease jonmmease commented May 19, 2019

Overview

This PR updates plotly_express to use the plotly.py plotly.subplots.make_subplots functionality to build faceted figures, rather than the current layout.grid approach. By building on plotly.subplots.make_subplots, it will be much easier for users to modify faceted figures in plotly.py.

It also removes ExpressFigure and relies on the plotly.py plotly.io.renderers configuration to display figures.

Because this PR requires plotly.py to be imported with the v4_subplots and renderer_defaults future flags. If plotly_express is imported first then this is done automatically, but if plotly.py is import first, without these future flags, then an error will be raised when plotly_express is imported.

Examples

Update a y-axis based on row

import plotly_express as px
iris = px.data.iris()
fig = (px.scatter(iris, x="sepal_width", y="sepal_length", facet_row='species', range_x=[0, 5], log_y=True))
fig.update_yaxes(title_font_color='red', row=3)

newplot-38

Update a trace based on row

fig = (px.scatter(iris, x="sepal_width", y="sepal_length", facet_row='species', range_x=[0, 5], log_y=True))
fig.update_traces(marker_color='red', row=2)

newplot-39

Marginals and Faceting

Support for combining marginals and faceting

px.scatter(iris, x="sepal_width", y="sepal_length", facet_row='species', marginal_y='histogram')

newplot-40

fig = px.scatter(tips, x="total_bill", y="tip", facet_row="time", facet_col="day", # color="smoker",
                 category_orders={"day": ["Thur", "Fri", "Sat", "Sun"], "time": ["Lunch", "Dinner"]},
                 marginal_y='rug', marginal_x='histogram', color='sex'
                )

fig.update(layout_height=800)

newplot-43

Faceting non-cartesian trace types

This PR doesn't add facet_row/facet_col to any additional plot types, but when these args are added, faceting will "work" automatically. I didn't add it in this PR because the default spacing for other trace types can look pretty bad, so we'll need to work through how these other subplot types shoud look with faceting.

cc @nicolaskruchten

@jonmmease jonmmease changed the title [WIP] Port to plotly.py v4_subplots and renderers Port to plotly.py v4_subplots and renderers May 20, 2019
@jonmmease jonmmease requested a review from nicolaskruchten May 20, 2019 22:36
showgrid=args["marginal_x"] == 'histogram',
row=row
)
fig.update_xaxes(
Copy link
Contributor

Choose a reason for hiding this comment

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

has this been run through black ? :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done now

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks! I know the plotly.py repo doesn't have that in place but this one does for now ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I'm interested in using black in plotly.py too. Have you looked into the git integration https://black.readthedocs.io/en/stable/version_control_integration.html?


# Find row for trace, handling facet_row and marginal_x
if m.facet == 'row':
if has_marginal_x:
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this case for marginals + facets? that's not supported right now in the grouping logic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. With these changes I think marginals + facets are working. See the screenshot in the description above.

Copy link
Contributor

Choose a reason for hiding this comment

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

aha I see, that's interesting. Cool that it works!

I think that for combining them, I was envisioning per-row and per-column marginals rather than per-subplot marginals, which isn't quite as straightforward (although not impossible, just requires a second group-by pass). Thoughts?

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 liked per subplot marginals, but I suppose per row/col could useful to. Would it make sense for this to be the default and then later add an option to change the behavior?

@nicolaskruchten
Copy link
Contributor

I didn't add it in this PR because the default spacing for other trace types can look pretty bad, so we'll need to work through how these other subplot types shoud look with faceting.

can I see an example? Is the spacing too big or too small?

@jonmmease
Copy link
Contributor Author

Sure, here are some examples:
newplot-45

newplot-46

newplot-47

I see two issues:

  • The first is that different subplot types will need different default padding. This should just be a matter of taking the time to work through all of the subplot types.
  • The second is that subplot types with a fixed aspect ratio (ternary and polar) are centered in their domain, so the row/column labels don't line up well. I'm not really sure what to do about this.

# Conflicts:
#	plotly_express/_core.py
If faceting and marginal specified then faceting wins.  It's still
possible to facet columns and have marginals per columns or to facet rows
and have marginals per row.
@jonmmease
Copy link
Contributor Author

@nicolaskruchten, I just updated the PR with master and removed the marginals per facet.

If faceting and marginal specified then faceting wins. It's still
possible to facet columns and have marginals per columns or to facet rows
and have marginals per row.

e.g.
newplot (8)

If this looks alright, I'll start pulling it into the v4_integration branch of plotly.py

)
TraceSpec = namedtuple("TraceSpec", ["constructor", "attrs", "trace_patch"])
TraceSpec = namedtuple("TraceSpec", ["constructor", "attrs", "trace_patch", "marginal"])
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a good idea, and maybe should be generalized later to model the "role" of the trace in the figure... Is it the main event, or a marginal, or a trendline, or some other future thing like the raw scatter that someone might want to add to a density heatmap?

log_key = "log_" + letter
range_key = "range_" + letter
if log_key in args and args[log_key]:
layout[axis]["type"] = "log"
axis["type"] = "log"
Copy link
Contributor

Choose a reason for hiding this comment

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

if axis is a go then it would be nicer to have axis.type here right?

Copy link
Contributor

Choose a reason for hiding this comment

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

(not necessary, just noting)

@@ -38,25 +45,6 @@ def set_mapbox_access_token(token):
MAPBOX_TOKEN = token


class ExpressFigure(go.Figure):
Copy link
Contributor

Choose a reason for hiding this comment

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

should grep the docstrings for ExpressFigure and change that out :)

@nicolaskruchten
Copy link
Contributor

This looks great! Having marginals vs facets work this way on a per-direction basis is really 👍 !!

@nicolaskruchten
Copy link
Contributor

Let's pull this into v4

@jonmmease
Copy link
Contributor Author

Thanks @nicolaskruchten, superseded by plotly/plotly.py#1613

@jonmmease jonmmease closed this Jun 14, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants