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

add_{hline,vline,hrect,vrect} and add_{shape,image,annotation} to multiple facets, squashed #2840

Merged
merged 8 commits into from
Oct 22, 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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ node_modules/

# virtual envs
vv
venv
venv*

# dist files
build
Expand All @@ -46,4 +46,5 @@ temp-plot.html
doc/python/.ipynb_checkpoints
doc/python/.mapbox_token
doc/.ipynb_checkpoints
tags
doc/check-or-enforce-order.py
17 changes: 15 additions & 2 deletions doc/python/figure-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,18 +95,31 @@ The third of the three top-level attributes of a figure is `frames`, whose value

### The `config` Object

At [render-time](/python/renderers/), it is also possible to control certain figure behaviors which are not considered part of the figure proper i.e. the behaviour of the "modebar" and how the figure relates to mouse actions like scrolling etc. The object that contains these options is called the [`config`, and has its own documentation page](/python/configuration-options/). It is exposed in Python as the `config` keyword argument of the `.show()` method on `plotly.graph_objects.Figure` objects.
At [render-time](/python/renderers/), it is also possible to control certain figure behaviors which are not considered part of the figure proper i.e. the behavior of the "modebar" and how the figure relates to mouse actions like scrolling etc. The object that contains these options is called the [`config`, and has its own documentation page](/python/configuration-options/). It is exposed in Python as the `config` keyword argument of the `.show()` method on `plotly.graph_objects.Figure` objects.

### Positioning With Paper or Container Coordinates
### Positioning With Paper, Container Coordinates, or Axis Domain Coordinates

Various figure components configured within the layout of the figure support positioning attributes named `x` or `y`, whose values may be specified in "paper coordinates" (sometimes referred to as "plot fractions" or "normalized coordinates"). Examples include `layout.xaxis.domain` or `layout.legend.x` or `layout.annotation[].x`.

Positioning in paper coordinates is *not* done in absolute pixel terms, but rather in terms relative to a coordinate system defined with an origin `(0,0)` at `(layout.margin.l, layout.margin.b)` and a point `(1,1)` at `(layout.width-layout.margin.r, layout.height-layout.margin.t)` (note: `layout.margin` values are pixel values, as are `layout.width` and `layout.height`). Paper coordinate values less than 0 or greater than 1 are permitted, and refer to areas within the plot margins.

To position an object in "paper" coordinates, the corresponding axis reference
is set to `"paper"`. For instance a shape's `xref` attribute would be set to
`"paper"` so that the `x` value of the shape refers to its position in paper
coordinates.

Note that the contents of the `layout.margin` attribute are by default computed based on the position and dimensions of certain items like the title or legend, and may be made dependent on the position and dimensions of tick labels as well when setting the `layout.xaxis.automargin` attribute to `True`. This has the effect of automatically increasing the margin values and therefore shrinking the physical area defined between the `(0,0)` and `(1,1)` points. Positioning certain items at paper coordinates less than 0 or greater than 1 will also trigger this behavior. The `layout.width` and `layout.height`, however, are taken as givens, so a figure will never grow or shrink based on its contents.

The figure title may be positioned using "container coordinates" which have `(0,0)` and `(1,1)` anchored at the bottom-left and top-right of the figure, respectively, and therefore are independent of the values of layout.margin.

Furthermore, shapes, annotations, and images can be placed relative to an axis's
domain so that, for instance, an `x` value of `0.5` would place the object
halfway along the x-axis, regardless of the domain as specified in the
`layout.xaxis.domain` attribute. This behavior can be specified by adding
`' domain'` to the axis reference in the axis referencing attribute of the object.
For example, setting `yref = 'y2 domain'` for a shape will refer to the length
and position of the axis named `y2`.

### 2D Cartesian Trace Types and Subplots

The most commonly-used kind of subplot is a [two-dimensional Cartesian subplot](/python/axes/). Traces compatible with these subplots support `xaxis` and `yaxis` attributes whose values must refer to corresponding objects in the layout portion of the figure. For example, if `xaxis="x"`, and `yaxis="y"` (which is the default) then this trace is drawn on the subplot at the intersection of the axes configured under `layout.xaxis` and `layout.yaxis`, but if `xaxis="x2"` and `yaxis="y3"` then the trace is drawn at the intersection of the axes configured under `layout.xaxis2` and `layout.yaxis3`. Note that attributes such as `layout.xaxis` and `layout.xaxis2` etc do not have to be explicitly defined, in which case default values will be inferred. Multiple traces of different types can be drawn on the same subplot.
Expand Down
116 changes: 116 additions & 0 deletions doc/python/horizontal-vertical-shapes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
---
jupyter:
jupytext:
notebook_metadata_filter: all
text_representation:
extension: .md
format_name: markdown
format_version: '1.2'
jupytext_version: 1.3.0
kernelspec:
display_name: Python 3
language: python
name: python3
language_info:
codemirror_mode:
name: ipython
version: 3
file_extension: .py
mimetype: text/x-python
name: python
nbconvert_exporter: python
pygments_lexer: ipython3
version: 3.7.3
plotly:
description: How to add annotated horizontal and vertical lines in Python.
display_as: file_settings
language: python
layout: base
name: Shapes
order: 37
permalink: python/horizontal-vertical-shapes/
thumbnail: thumbnail/horizontal-vertical-shapes.jpg
---

### Horizontal and Vertical Lines and Boxes (Autoshapes) in Plotly.py

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
*introduced in plotly 4.12*

Copy link
Contributor

Choose a reason for hiding this comment

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

maybe also mention that users can pan and zoom, the shapes will adapt

Horizontal and vertical lines and rectangles (autoshapes) that span an entire
plot can be added via the `add_hline`, `add_vline`, `add_hrect`, and `add_vrect`
methods of `plotly.graph_objects.Figure`. These shapes are fixed to the
endpoints of one axis, regardless of the range of the plot, and fixed to data
coordinates on the other axis. For example
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
coordinates on the other axis. For example
coordinates on the other axis. Shapes added with these methods are added as [layout shapes](/python/shapes) (as shown when doing `print(fig)`, for example). For example



```python
import plotly.express as px

df = px.data.iris()
fig = px.scatter(df, x="sepal_length", y="sepal_width")

# Add a vertical line that spans the entire y axis
# intersecting the x axis at x=5
fig.add_vline(x=5, line_color="red")
# Add a horizontal line that spans the entire x axis
# intersecting the y axis at y=3
fig.add_hline(y=3, line_color="blue")
# Add a vertical rectangle that spans the entire y axis
# intersecting the x axis at 5.5 and 6.5
fig.add_vrect(x0=5.5, x1=6.5, line_color="purple")
# Add a horizontal rectangle that spans the entire x axis
# intersecting the y axis at 2.5 and 4
fig.add_hrect(y0=2.5, y1=4, line_color="orange")
# (try dragging the plot around)
fig.show()
```

#### Adding Autoshapes to Multiple Facets / Subplots

The same line or box can be added to multiple facets by using the `'all'`
keyword in the `row` and `col` arguments like with `Figure.add_shape`. For
example
```python
import plotly.express as px

df = px.data.tips()
fig = px.scatter(df, x="total_bill", y="tip", facet_row="smoker", facet_col="sex")
# Adds a vertical line to all facets
fig.add_vline(x=30, row="all", col="all", line_color="purple")
# Adds a horizontal line to all the rows of the second column
fig.add_hline(y=6, row="all", col=2, line_color="yellow")
# Adds a vertical rectangle to all the columns of the first row
fig.add_vrect(x0=20, x1=40, row=1, col="all", line_color="green")
fig.show()
```
The default `row` and `col` values are `"all"` so
`fig.add_vline(x=30, line_color="purple")` is equivalent
to `fig.add_vline(x=30, row="all", col="all", line_color="purple")` in the above
example.

#### Adding Text Annotations to Autoshapes

Text can be added to an autoshape using the `annotation` keyword. Using the
above example:
Copy link
Contributor

Choose a reason for hiding this comment

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

could you please add a link to the annotations tutorial?

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 idea, sure! Is this how you do it? [annotations](/python/text-and-annotations)

```python
import plotly.express as px
import plotly.graph_objects as go

df = px.data.tips()
fig = px.scatter(df, x="total_bill", y="tip", facet_row="smoker", facet_col="sex")
# Add annotations anchored to the top right corner of the resulting lines
fig.add_vline(x=30, line_color="purple", annotation=go.layout.Annotation(text="A"))
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 we discourage using go objects like this inside functions, so you could write dict(text="A") instead (@nicolaskruchten can you please confirm?)

Copy link
Contributor

Choose a reason for hiding this comment

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

yeah let's use a dict here please.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wait, this was one of the specifications, that it should accept a go.layout.Annotation object, as well as a dict, this is to show that fact.

# Another way to add annotations when we are only interested in specifying text
fig.add_hline(y=6, row="all", col=2, line_color="yellow", annotation_text="B")
# Specify the position of the resulting annotations
fig.add_vrect(
x0=20,
x1=40,
row=1,
col="all",
line_color="green",
annotation_text="C",
annotation_position="bottom inside left",
)
fig.show()
```
Call `help` on any of the autoshape functions in the Python interpreter to learn
more (e.g., `help(fig.add_vline)`).
11 changes: 7 additions & 4 deletions doc/python/images.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,15 +309,15 @@ fig.show(config={'doubleClick': 'reset'})

_introduced in plotly 4.7_

It can be useful to add shapes to a layout image, for highlighting an object, drawing bounding boxes as part of a machine learning training set, or identifying seeds for a segmentation algorithm.
It can be useful to add shapes to a layout image, for highlighting an object, drawing bounding boxes as part of a machine learning training set, or identifying seeds for a segmentation algorithm.

In order to enable shape drawing, you need to
- define a dragmode corresponding to a drawing tool (`'drawline'`,`'drawopenpath'`, `'drawclosedpath'`, `'drawcircle'`, or `'drawrect'`)
- add [modebar buttons](/python/configuration-options#add-optional-shapedrawing-buttons-to-modebar) corresponding to the drawing tools you wish to use.

The style of new shapes is specified by the `newshape` layout attribute. Shapes can be selected and modified after they have been drawn. More details and examples are given in the [tutorial on shapes](/python/shapes#drawing-shapes-on-cartesian-plots).

Drawing or modifying a shape triggers a `relayout` event, which [can be captured by a callback inside a Dash application](https://dash.plotly.com/interactive-graphing).
Drawing or modifying a shape triggers a `relayout` event, which [can be captured by a callback inside a Dash application](https://dash.plotly.com/interactive-graphing).

```python
import plotly.graph_objects as go
Expand All @@ -339,7 +339,7 @@ fig.add_layout_image(
)
fig.update_xaxes(showgrid=False, range=(0, img_width))
fig.update_yaxes(showgrid=False, scaleanchor='x', range=(img_height, 0))
# Line shape added programatically
# Line shape added programatically
fig.add_shape(
type='line', xref='x', yref='y',
x0=650, x1=1080, y0=380, y1=180, line_color='cyan'
Expand All @@ -359,5 +359,8 @@ fig.show(config={'modeBarButtonsToAdd':['drawline',
]})
```


### Images Placed Relative to the Axes

Copy link
Contributor

Choose a reason for hiding this comment

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

placeholder for example 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.

Yes, woops ;)

#### Reference
See https://plotly.com/python/reference/layout/images/ for more information and chart attribute options!
See https://plotly.com/python/reference/layout/images/ for more information and chart attribute options!
Loading