Skip to content

Creating and updating figures section #26

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

Merged
merged 19 commits into from
Jun 27, 2019
Merged

Conversation

jonmmease
Copy link
Contributor

The new section on representing, creating, and updating figures. Closes https://github.com/plotly/plotly.py-docs/issues/6.

Let me know what you think @nicolaskruchten @emmanuelle

Doc upgrade checklist:

  • Every example is independently runnable and is optimized for short line count
  • graph_objs has been renamed to graph_objects
  • fig = <something> call is high up in each example
  • minimal creation of intermediate trace objects
  • liberal use of add_trace and update_layout
  • fig.show() at the end of each example
  • v4upgrade: true metadata added
  • minimize usage of hex codes for colors in favour of those in https://github.com/plotly/plotly.py-docs/issues/14


```python
import plotly.graph_objects as go
fig = 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.

basically I would use this example as the first one :)

Copy link
Contributor

Choose a reason for hiding this comment

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

I think I would use the go.Scatter constructor in the first example. We want the first example to look like a typical doc page.

```python
import plotly.graph_objects as go
fig = go.Figure(data=go.Bar(x=[1, 2, 3], y=[1, 3, 2]))
fig.update_layout(title={"text": "A Bar Chart",
Copy link
Contributor

Choose a reason for hiding this comment

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

I would use magic underscores here :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

import plotly.express as px
iris = px.data.iris()
(px.scatter(iris, x="sepal_width", y="sepal_length", color="species", facet_col="species", trendline='ols')
.update_layout(title_text='Iris Dataset')
Copy link
Contributor

Choose a reason for hiding this comment

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

so there's a px kwarg for title... I like showing how to turn the legend horizontal with update_layout as something that's not doable with existing kwargs :)

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 tried the horizontal legend, but it didn't look so great by default in this case (is gets squashed pretty close to the x-axis labels). So I set the title with px and used update_layout to increase the title font size.

fig.data[0].marker.line.width = 4
fig.data[0].marker.line.color = 'black'
fig.show()
```
Copy link
Contributor

Choose a reason for hiding this comment

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

Here I would consider a little example that shows the N ways of updating the same pair of attributes (one nested, one not) with an explanation saying "the following are equivalent": dict (in dict(x= and in literal {"x": forms plus in nested and magic-flat forms) and go

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 added an example of 6 equivalent update_layout operations at the end of the update layout section. How's that?

Copy link
Member

Choose a reason for hiding this comment

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

love this example

Copy link
Contributor

Choose a reason for hiding this comment

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

if we moved property-assignment back up, we could add that kind of example here too.

@nicolaskruchten
Copy link
Contributor

nicolaskruchten commented Jun 21, 2019

This is really great: just what's needed IMO. @emmanuelle I would love your thoughts

@emmanuelle
Copy link
Contributor

This is great. It's exactly what I would have needed when I started using the library :-). A few remarks:

  • as @nicolaskruchten suggests, I would also start with a short example with the preferred syntax, ie with go objects. A paragraph like "your first plotly figure" which will the only one that 90% of readers see because they are in a hurry and go somewhere else. So, something like an executive summary.
  • I think most Python users don't care much about what happens in the javascript library, so I would postpone the first paragraph to a "behind the scene" paragraph which comes after the first example with go.
  • In the doc I have written most chart type examples with dictionaries defined as dict(key1=val1, key2=val2) because there are less quotes and it's easier to use the magic underscore. Would it be possible to use this syntax also here, at least in the update methods?

2. Graph objects contain descriptions of each property as Python docstrings. You can use these docstrings to learn about the available properties as an alternative to consulting the *Full Reference*.
3. Graph objects support higher-level convenience functions for making updates to already constructed figures.

Graph objects are stored in a hierarchy of modules under the `plotly.graph_objects` package. Here is an example of one way that the figure above could be constructed using graph objects.
Copy link
Contributor

Choose a reason for hiding this comment

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

is it important for users (as opposed to devs) to mention the "hierarchy of modules"? How about "stored under the ... package"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Only insofar as it's important that people look for go.layout.title.Font() and don't expect to find go.Font().


```python
import plotly.graph_objects as go
fig = 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.

I think I would use the go.Scatter constructor in the first example. We want the first example to look like a typical doc page.

```

### Plotly express
Plotly express (included in plotly.py as the `plotly.express` module) is a high-level data exploration API that produces graph object figures.
Copy link
Contributor

Choose a reason for hiding this comment

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

"included as the plotly.express module", I think most users only know "plotly" and mentioning "plotly.py" can be confusing.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also mention that px only works with tidy Pandas dataframes.

v1 = np.sin(x1)*y1

fig = ff.create_quiver(x1, y1, u1, v1)
fig.show()
Copy link
Contributor

Choose a reason for hiding this comment

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

Here I tried to explore the fig object to know what was specific in the figure factory so that the resulting trace looks like a quiver but it was quite difficult (I finally found out that it's thanks to None and not connecting gaps, but it was not straightforward). Would it be possible to add something about properties introspection? Like, how can you use tab completion?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Tab completion with getattr access? I don't really want to get into the process of reverse engineering what the figure factories and plotly express do in this section. We could change which figure factory is demonstrated here if another example seems better.


### Magic underscore notation
To make it easier to work with nested properties graph object constructors, and many graph object methods, support magic underscore notation. This allows you to reference nested properties by joining together multiple nested property names with underscores. For example, specifying the figure title in the figure constructor *without* magic underscore notation requires setting the `layout` argument to `{"title": {"text": "A Chart"}}`.

Copy link
Contributor

Choose a reason for hiding this comment

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

"As a rule of thumb most names with underscores denote a magic underscore syntax." (Is it true? Not all names with underscores, since there is "error_x" etc., but ideally it should be the case since it would be clearer).

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, it's a little messy.

Copy link
Member

Choose a reason for hiding this comment

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

i think there are only 3 or 4 properties that break this rule, so we could just call it out here. like:

By “most” we mean all properties except error_x, error_y, paper_bg, ... Those properties were added back in the early days of the library (2012 & 2013!) before we standardized on “no underscores in property names.”

i like using the markdown blocquotes (>) for little sidenotes like these.

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 added a block quote explanation in 9d14e21


Magic underscore notation is supported throughout the graph objects API, and it can often significantly simplify operations involving deeply nested properties.

### Property assignment
Copy link
Contributor

Choose a reason for hiding this comment

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

I would put update_traces and update_layout before property assignment, because this is what we use in the docs. Or have them mentioned in the first "excutive summary" example.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point, moving this to the end

@jonmmease
Copy link
Contributor Author

Thanks for the quick feedback @nicolaskruchten and @emmanuelle.

I've made all of the updates for the suggestions that I immediately agreed with.

For what's left, it's probably better if one of you take a stab at the restructuring you have in mind. I definitely don't mind more changes being made, but the structure/order I have still makes sense to me, so I'm finding it difficult make the changes myself. I'm going to move on to the other new pages now (templates and renderers).

@nicolaskruchten
Copy link
Contributor

Thanks Jon! I have an idea of how to preserve the structure and meet my goals as well, let me propose something in a bit when the kids are down

fig.show()
```

Once you have a figure as a graph object, you can retrieve the dictionary representation using the `fig.to_dict()` method. You can also retrieve the JSON string representation using the `fig.to_json()` method.
Copy link
Contributor

Choose a reason for hiding this comment

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

I would point out that you can also read attributes directly with fig.layout.title.text as this is a big advantage over dicts !

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 added another item to the advantages list above in c0f6c8c


The value of the top-level `"data"` key is a list of trace specifications. Each trace specification has a special `"type"` key that indicates the trace type that is being defined (e.g. a `"bar"`, `"scatter"`, `"contour"`, etc.). The rest of the keys in the trace specification are used to configure the properties of the trace of this type.

The value of the top-level `"layout"` key is a dictionary that specifies the properties of the figure's layout. In contrast to trace configuration options that apply to individual traces, the layout configuration options apply to the figure as a whole.
Copy link
Member

Choose a reason for hiding this comment

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

“like the axes, annotations, shapes, legend, and more.”

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍 28f1d8f


The [*Full Reference*](https://plot.ly/python/reference/) page contains descriptions of all of the supported trace and layout options.

If working from the *Full Reference* to build figures as Python dictionaries suites your needs, go for it! This is a perfectly valid way to use use plotly.py to build figures. On the other hand, if you would like an API that offers a bit more assistance, read on to learn about graph objects.
Copy link
Member

Choose a reason for hiding this comment

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

“and lists”

Copy link
Member

Choose a reason for hiding this comment

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

also maybe “as you become more comfortable with the graph reference, you may find yourself using this “shorthand” instead of the objects below.”?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

"and lists"

👍 in ff64f59

also maybe “as you become more comfortable with the graph reference, you may find yourself using this “shorthand” instead of the objects below.”?

This didn't quite fit the flow I was going for, so I didn't put it in yet. Does it feel like an important clarification to you?

```python
import plotly.express as px
iris = px.data.iris()
fig = px.scatter(iris, x="sepal_width", y="sepal_length", color="species")
Copy link
Member

Choose a reason for hiding this comment

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

what if we added like a # print(fig.to_dict()). # if you inspect the fig, you’ll see it’s just a regular figure with data and layout

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍 cacbc37

```

### Figure factories
Figure factories (included in plotly.py in the `plotly.figure_factory` module) are functions that produce graph object figures, often to satisfy the needs of specialized domains. Here's an example of using the `create_quiver` figure factory to construct a graph object figure that displays a 2D quiver plot.
Copy link
Member

Choose a reason for hiding this comment

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

should we add a “Note - some of these functions are better served by px now, like ff.dist_plot and ff.facet_plot (or w/e)?

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 definitely think this info belongs somewhere, but in my mind it's a level below what this section is trying to communicate. At this level, I think we just want people to understand that the things produced by plotly express and the figure factories are just normal figures.

```python
import plotly.graph_objects as go
fig = go.Figure(
data=[go.Bar(x=[1, 2, 3], y=[1, 3, 2])],
Copy link
Member

Choose a reason for hiding this comment

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

what about having two bar traces, one that uses go.bar.Marker and one that uses just marker={colour: ...} to demonstrate the two ways then nested objects can be defined? could even have go.marker.Line too

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The first example in the "Constructor" section below calls out the fact that you can mix graph objects and dicts in the constructor. Is that sufficient? I think I'd rather add another example to that section if we want to be more explicit about it.

We certainly could add more traces here, but I would want to add them to he prior dict-only example above so that we're working with the same underlying figure. And that would make the first dict-only figure a bit more complicated.

showlegend=False)
fig.add_trace(reference_line, row=1, col=1)
fig.add_trace(reference_line, row=1, col=2)
fig.add_trace(reference_line, row=1, col=3)
Copy link
Member

Choose a reason for hiding this comment

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

nice example!


### Magic underscore notation
To make it easier to work with nested properties graph object constructors, and many graph object methods, support magic underscore notation. This allows you to reference nested properties by joining together multiple nested property names with underscores. For example, specifying the figure title in the figure constructor *without* magic underscore notation requires setting the `layout` argument to `{"title": {"text": "A Chart"}}`.

Copy link
Member

Choose a reason for hiding this comment

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

i think there are only 3 or 4 properties that break this rule, so we could just call it out here. like:

By “most” we mean all properties except error_x, error_y, paper_bg, ... Those properties were added back in the early days of the library (2012 & 2013!) before we standardized on “no underscores in property names.”

i like using the markdown blocquotes (>) for little sidenotes like these.


```python
import plotly.graph_objects as go
fig = go.Figure(layout=dict(title=dict(text="A Chart")))
Copy link
Member

Choose a reason for hiding this comment

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

could we just add a simple trace here too to really enforce the point? with eg marker_color

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍 d7d6a51

fig.data[0].marker.line.width = 4
fig.data[0].marker.line.color = 'black'
fig.show()
```
Copy link
Member

Choose a reason for hiding this comment

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

love this example

@jonmmease
Copy link
Contributor Author

Alright, I made another round of updates. Thanks for the feedback @nicolaskruchten @emmanuelle and @chriddyp . I'm really excited about having this page in our docs soon!

@nicolaskruchten
Copy link
Contributor

I could keep workshopping this but it's certainly good to go as is from where I sit :)

💃

@nicolaskruchten
Copy link
Contributor

Let's merge this one in!

@jonmmease jonmmease merged commit 27badef into master Jun 27, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

creating and mutating figures
4 participants