diff --git a/.circleci/config.yml b/.circleci/config.yml index 913d3b6d40f..e16e0e9fd7b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -448,7 +448,7 @@ jobs: docker: # specify the version you desire here # use `-browsers` prefix for selenium tests, for example, `3.9-browsers` - - image: cimg/python:3.9-browsers + - image: cimg/python:3.10-browsers steps: - add_ssh_keys: diff --git a/CHANGELOG.md b/CHANGELOG.md index adad2f3cbae..20c880922af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,24 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [5.23.0] - 2024-07-23 + +### Updated +- Updated Plotly.js from version 2.32.0 to version 2.34.0. See the [plotly.js CHANGELOG](https://github.com/plotly/plotly.js/blob/master/CHANGELOG.md#2340----2024-07-18) for more information. These changes are reflected in the auto-generated `plotly.graph_objects` module. Notable changes include: + - Add `subtitle` attribute to `layout.title` to enable adding subtitles to plots [[#7012](https://github.com/plotly/plotly.js/pull/7012)] + - Introduce "u" and "s" pseudo html tags to add partial underline and strike-through styles to SVG text elements [[#7043](https://github.com/plotly/plotly.js/pull/7043)] + - Add geometric mean functionality and 'geometric mean ascending' + 'geometric mean descending' to `category_order` on cartesian axes [[#6223](https://github.com/plotly/plotly.js/pull/6223)], + with thanks to @acxz and @prabhathc for the contribution! + - Add axis property `ticklabelindex` for drawing the label for each minor tick n positions away from a major tick, + with thanks to @my-tien for the contribution! [[#7036](https://github.com/plotly/plotly.js/pull/7036)] + - Add property `ticklabelstandoff` and `ticklabelshift` to cartesian axes to adjust positioning of tick labels, + with thanks to @my-tien for the contribution! [[#7006](https://github.com/plotly/plotly.js/pull/7006)] + - Add `x0shift`, `x1shift`, `y0shift`, `y1shift` to shapes to add control over positioning of shape vertices on (multi-)category axes, + with thanks to @my-tien for the contribution! [[#7005](https://github.com/plotly/plotly.js/pull/7005)] +- Specify Python version 3.8-3.11 for development virtual environments and pin `pytest` at version 8.1.1 to match. +- Update `IntegerValidator` to handle `extras` option to allow supporting additional keyword values. For example, 'bold' and 'normal' as well as integers as used in font weights [#4612]. + + ## [5.22.0] - 2024-05-01 ### Updated diff --git a/README.md b/README.md index d12f427e06e..d8039732b70 100644 --- a/README.md +++ b/README.md @@ -31,9 +31,16 @@ +
nonnegative
, tozero
, and normal
Rangemode
-When you don't specify a range, autorange is used. It's also used for bounds set to `None` when providing a `range`.
+When you don't specify a range, autorange is used. It's also used for bounds set to `None` when providing a `range`.
The axis auto-range calculation logic can be configured using the `rangemode` axis parameter.
@@ -898,7 +958,7 @@ fig.update_xaxes(autorangeoptions=dict(maxallowed=5))
fig.show()
```
-##### Clip Minimum and Maximum
+##### Clip Minimum and Maximum
You can also clip an axis range at a specific maximum or minimum value with `autorangeoptions.clipmax` and `autorangeoptions.clipmin`.
@@ -916,7 +976,7 @@ fig.show()
##### Specify Values to be Included
-Use `autorangeoptions.include` to specify a value that should always be included within the calculated autorange. In this example, we specify that for the autorange calculated on the x-axis, 5 should be included.
+Use `autorangeoptions.include` to specify a value that should always be included within the calculated autorange. In this example, we specify that for the autorange calculated on the x-axis, 5 should be included.
```python
import plotly.express as px
diff --git a/doc/python/box-plots.md b/doc/python/box-plots.md
index a22762c0750..dbaca729096 100644
--- a/doc/python/box-plots.md
+++ b/doc/python/box-plots.md
@@ -458,12 +458,12 @@ x_data = ['Carmelo Anthony', 'Dwyane Wade',
N = 50
-y0 = (10 * np.random.randn(N) + 30).astype(np.int)
-y1 = (13 * np.random.randn(N) + 38).astype(np.int)
-y2 = (11 * np.random.randn(N) + 33).astype(np.int)
-y3 = (9 * np.random.randn(N) + 36).astype(np.int)
-y4 = (15 * np.random.randn(N) + 31).astype(np.int)
-y5 = (12 * np.random.randn(N) + 40).astype(np.int)
+y0 = (10 * np.random.randn(N) + 30).astype(int)
+y1 = (13 * np.random.randn(N) + 38).astype(int)
+y2 = (11 * np.random.randn(N) + 33).astype(int)
+y3 = (9 * np.random.randn(N) + 36).astype(int)
+y4 = (15 * np.random.randn(N) + 31).astype(int)
+y5 = (12 * np.random.randn(N) + 40).astype(int)
y_data = [y0, y1, y2, y3, y4, y5]
diff --git a/doc/python/figure-labels.md b/doc/python/figure-labels.md
index 0e00f5e575b..f687fcda1de 100644
--- a/doc/python/figure-labels.md
+++ b/doc/python/figure-labels.md
@@ -6,7 +6,7 @@ jupyter:
extension: .md
format_name: markdown
format_version: '1.3'
- jupytext_version: 1.16.1
+ jupytext_version: 1.16.3
kernelspec:
display_name: Python 3 (ipykernel)
language: python
@@ -20,7 +20,7 @@ jupyter:
name: python
nbconvert_exporter: python
pygments_lexer: ipython3
- version: 3.10.11
+ version: 3.10.14
plotly:
description: How to set the global font, title, legend-entries, and axis-titles
in python.
@@ -236,5 +236,44 @@ fig.update_layout(
fig.show()
```
+### Adding a Plot Subtitle
+
+*New in 5.23*
+
+Add a subtitle to a plot with `layout.title.subtitle`. In the following example, we set the subtitle's `text`, and configure the `font` `color` and `size`. By default, if you don't set a font size for the subtitle, it will be `0.7` of the `title` font size.
+
+```python
+import plotly.graph_objects as go
+from plotly import data
+
+df = data.gapminder().query("continent == 'Europe' and (year == 1952 or year == 2002)")
+
+df_pivot = df.pivot(index="country", columns="year", values="lifeExp")
+
+fig = go.Figure(
+ [
+ go.Bar(
+ x=df_pivot.index, y=df_pivot[1952], name="1952", marker_color="IndianRed"
+ ),
+ go.Bar(
+ x=df_pivot.index, y=df_pivot[2002], name="2002", marker_color="LightSalmon"
+ ),
+ ],
+ layout=dict(
+ title=dict(
+ text="Life Expectancy",
+ subtitle=dict(
+ text="Life expectancy by European country in 1952 and in 2002",
+ font=dict(color="gray", size=13),
+ ),
+ )
+ ),
+)
+
+
+fig.show()
+
+```
+
#### Reference
See https://plotly.com/python/reference/layout/ for more information!
diff --git a/doc/python/getting-started.md b/doc/python/getting-started.md
index 3827de29c4f..0aa23dd37f8 100644
--- a/doc/python/getting-started.md
+++ b/doc/python/getting-started.md
@@ -58,13 +58,13 @@ We also encourage you to join the [Plotly Community Forum](http://community.plot
`plotly` may be installed using `pip`:
```
-$ pip install plotly==5.22.0
+$ pip install plotly==5.23.0
```
or `conda`:
```
-$ conda install -c plotly plotly=5.22.0
+$ conda install -c plotly plotly=5.23.0
```
This package contains everything you need to write figures to standalone HTML files.
@@ -152,7 +152,7 @@ The instructions above apply to JupyterLab 3.x. **For JupyterLab 2 or earlier**,
```
# JupyterLab 2.x renderer support
-jupyter labextension install jupyterlab-plotly@5.22.0 @jupyter-widgets/jupyterlab-manager
+jupyter labextension install jupyterlab-plotly@5.23.0 @jupyter-widgets/jupyterlab-manager
```
Please check out our [Troubleshooting guide](/python/troubleshooting/) if you run into any problems with JupyterLab, particularly if you are using multiple python environments inside Jupyter.
diff --git a/doc/python/icicle-charts.md b/doc/python/icicle-charts.md
index 540f9f7e82c..38569a7ea03 100644
--- a/doc/python/icicle-charts.md
+++ b/doc/python/icicle-charts.md
@@ -305,7 +305,7 @@ def build_hierarchical_dataframe(df, levels, value_column, color_columns=None):
Levels are given starting from the bottom to the top of the hierarchy,
ie the last level corresponds to the root.
"""
- df_all_trees = pd.DataFrame(columns=['id', 'parent', 'value', 'color'])
+ df_list = []
for i, level in enumerate(levels):
df_tree = pd.DataFrame(columns=['id', 'parent', 'value', 'color'])
dfg = df.groupby(levels[i:]).sum()
@@ -317,11 +317,12 @@ def build_hierarchical_dataframe(df, levels, value_column, color_columns=None):
df_tree['parent'] = 'total'
df_tree['value'] = dfg[value_column]
df_tree['color'] = dfg[color_columns[0]] / dfg[color_columns[1]]
- df_all_trees = df_all_trees.append(df_tree, ignore_index=True)
+ df_list.append(df_tree)
total = pd.Series(dict(id='total', parent='',
value=df[value_column].sum(),
- color=df[color_columns[0]].sum() / df[color_columns[1]].sum()))
- df_all_trees = df_all_trees.append(total, ignore_index=True)
+ color=df[color_columns[0]].sum() / df[color_columns[1]].sum()), name=0)
+ df_list.append(total)
+ df_all_trees = pd.concat(df_list, ignore_index=True)
return df_all_trees
diff --git a/doc/python/mixed-subplots.md b/doc/python/mixed-subplots.md
index 13737b5f6d1..24ee5123beb 100644
--- a/doc/python/mixed-subplots.md
+++ b/doc/python/mixed-subplots.md
@@ -55,8 +55,8 @@ df = pd.read_csv(
)
# frequency of Country
-freq = df
-freq = freq.Country.value_counts().reset_index().rename(columns={"index": "x"})
+freq = df['Country'].value_counts().reset_index()
+freq.columns = ['x', 'Country']
# read in 3d volcano surface data
df_v = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/volcano.csv")
diff --git a/doc/python/ml-regression.md b/doc/python/ml-regression.md
index b8be347943a..c74b49e92bd 100644
--- a/doc/python/ml-regression.md
+++ b/doc/python/ml-regression.md
@@ -120,7 +120,7 @@ from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
df = px.data.tips()
-X = df.total_bill[:, None]
+X = df.total_bill.to_numpy()[:, None]
X_train, X_test, y_train, y_test = train_test_split(X, df.tip, random_state=0)
model = LinearRegression()
@@ -129,7 +129,6 @@ model.fit(X_train, y_train)
x_range = np.linspace(X.min(), X.max(), 100)
y_range = model.predict(x_range.reshape(-1, 1))
-
fig = go.Figure([
go.Scatter(x=X_train.squeeze(), y=y_train, name='train', mode='markers'),
go.Scatter(x=X_test.squeeze(), y=y_test, name='test', mode='markers'),
diff --git a/doc/python/scattermapbox.md b/doc/python/scattermapbox.md
index 8cb2b2b6abf..1bd58271686 100644
--- a/doc/python/scattermapbox.md
+++ b/doc/python/scattermapbox.md
@@ -6,7 +6,7 @@ jupyter:
extension: .md
format_name: markdown
format_version: '1.3'
- jupytext_version: 1.14.1
+ jupytext_version: 1.16.2
kernelspec:
display_name: Python 3 (ipykernel)
language: python
@@ -20,7 +20,7 @@ jupyter:
name: python
nbconvert_exporter: python
pygments_lexer: ipython3
- version: 3.8.0
+ version: 3.10.0
plotly:
description: How to make scatter plots on Mapbox maps in Python.
display_as: maps
@@ -265,6 +265,65 @@ fig.show()
```
+#### Font Customization
+
+You can customize the font on `go.Scattermapbox` traces with `textfont`. For example, you can set the font `family`.
+
+```python
+import plotly.graph_objects as go
+
+token = open(".mapbox_token").read() # you need your own token
+
+fig = go.Figure(go.Scattermapbox(
+ mode = "markers+text+lines",
+ lon = [-75, -80, -50], lat = [45, 20, -20],
+ marker = {'size': 20, 'symbol': ["bus", "harbor", "airport"]},
+ text = ["Bus", "Harbor", "airport"], textposition = "bottom right",
+ textfont = dict(size=18, color="black", family="Open Sans Bold")
+ ))
+
+fig.update_layout(
+ mapbox = {
+ 'accesstoken': token,
+ 'style': "outdoors", 'zoom': 0.7},
+ showlegend = False,)
+
+fig.show()
+```
+
+`go.Scattermapbox` supports the following values for `textfont.family`:
+
+'Metropolis Black Italic', 'Metropolis Black', 'Metropolis Bold Italic', 'Metropolis Bold', 'Metropolis Extra Bold Italic', 'Metropolis Extra Bold', 'Metropolis Extra Light Italic', 'Metropolis Extra Light', 'Metropolis Light Italic', 'Metropolis Light', 'Metropolis Medium Italic', 'Metropolis Medium', 'Metropolis Regular Italic', 'Metropolis Regular', 'Metropolis Semi Bold Italic', 'Metropolis Semi Bold', 'Metropolis Thin Italic', 'Metropolis Thin', 'Open Sans Bold Italic', 'Open Sans Bold', 'Open Sans Extrabold Italic', 'Open Sans Extrabold', 'Open Sans Italic', 'Open Sans Light Italic', 'Open Sans Light', 'Open Sans Regular', 'Open Sans Semibold Italic', 'Open Sans Semibold', 'Klokantech Noto Sans Bold', 'Klokantech Noto Sans CJK Bold', 'Klokantech Noto Sans CJK Regular', 'Klokantech Noto Sans Italic', and 'Klokantech Noto Sans Regular'.
+
+
+##### Font Weight
+
+*New in 5.23*
+
+You can specify a numeric font weight on `go.Scattermapbox` with `textfont.weight`.
+
+```python
+import plotly.graph_objects as go
+
+token = open(".mapbox_token").read() # you need your own token
+
+fig = go.Figure(go.Scattermapbox(
+ mode = "markers+text+lines",
+ lon = [-75, -80, -50], lat = [45, 20, -20],
+ marker = dict(size=20, symbol=["bus", "harbor", "airport"]),
+ text = ["Bus", "Harbor", "airport"], textposition = "bottom right",
+ textfont = dict(size=18, color="black", weight=900)
+ ))
+
+fig.update_layout(
+ mapbox = dict(
+ accesstoken=token,
+ style="outdoors", zoom=0.7),
+ showlegend = False,)
+
+fig.show()
+```
+
#### Reference
See [function reference for `px.(scatter_mapbox)`](https://plotly.com/python-api-reference/generated/plotly.express.scatter_mapbox) or https://plotly.com/python/reference/scattermapbox/ for more information and options!
diff --git a/doc/python/shapes.md b/doc/python/shapes.md
index d4796f6a900..5d67fbf598c 100644
--- a/doc/python/shapes.md
+++ b/doc/python/shapes.md
@@ -6,7 +6,7 @@ jupyter:
extension: .md
format_name: markdown
format_version: '1.3'
- jupytext_version: 1.16.1
+ jupytext_version: 1.16.3
kernelspec:
display_name: Python 3 (ipykernel)
language: python
@@ -20,7 +20,7 @@ jupyter:
name: python
nbconvert_exporter: python
pygments_lexer: ipython3
- version: 3.10.11
+ version: 3.10.14
plotly:
description: How to make SVG shapes in python. Examples of lines, circle, rectangle,
and path.
@@ -579,6 +579,72 @@ fig.update_layout(
fig.show()
```
+#### Shifting Shapes on Categorical Axes
+
+*New in 5.23*
+
+When drawing shapes where `xref` or `yref` reference axes of type category or multicategory, you can shift `x0`, `x1`, `y0`, and `y1` away from the center of the category using `x0shift`, `x1shift`, `y0shift`, and `y1shift` by specifying a value between -1 and 1.
+
+-1 is the center of the previous category, 0 is the center of the referenced category, and 1 is the center of the next category.
+
+In the following example, the `x0` and `x1` values for both shapes reference category values on the x-axis.
+
+In this example, the first shape:
+- Shifts `x0` half way between the center of category "Germany" and the center of the previous category by setting `x0shift=-0.5`
+- Shifts `x1`half way between the center of category "Germany" and the center of the next category by setting `x1shift=0.5`
+
+The second shape:
+- Shifts `x0` back to the center of the previous category by setting `x0shift=-1`
+- Shifts `x1`forward to the center of the next category by setting `x1shift=1`
+
+```python
+import plotly.graph_objects as go
+import plotly.express as px
+
+df = px.data.gapminder().query("continent == 'Europe' and year == 1952")
+
+fig = go.Figure(
+ data=go.Bar(x=df["country"], y=df["lifeExp"], marker_color="LightSalmon"),
+ layout=dict(
+ shapes=[
+ dict(
+ type="rect",
+ x0="Germany",
+ y0=0,
+ x1="Germany",
+ y1=0.5,
+ xref="x",
+ yref="paper",
+ x0shift=-0.5,
+ x1shift=0.5,
+ line=dict(color="LightGreen", width=4),
+ ),
+ dict(
+ type="rect",
+ x0="Spain",
+ y0=0,
+ x1="Spain",
+ y1=0.5,
+ xref="x",
+ yref="paper",
+ x0shift=-1,
+ x1shift=1,
+ line=dict(color="MediumTurquoise", width=4),
+ ),
+ ]
+ ),
+)
+
+fig.update_layout(
+ title="GDP per Capita in Europe (1972)",
+ xaxis_title="Country",
+ yaxis_title="GDP per Capita",
+)
+
+fig.show()
+
+```
+
### Drawing shapes with a Mouse on Cartesian plots
_introduced in plotly 4.7_
diff --git a/doc/python/sunburst-charts.md b/doc/python/sunburst-charts.md
index e22f9f9dd7b..06cfb38e57d 100644
--- a/doc/python/sunburst-charts.md
+++ b/doc/python/sunburst-charts.md
@@ -355,7 +355,7 @@ def build_hierarchical_dataframe(df, levels, value_column, color_columns=None):
Levels are given starting from the bottom to the top of the hierarchy,
ie the last level corresponds to the root.
"""
- df_all_trees = pd.DataFrame(columns=['id', 'parent', 'value', 'color'])
+ df_list = []
for i, level in enumerate(levels):
df_tree = pd.DataFrame(columns=['id', 'parent', 'value', 'color'])
dfg = df.groupby(levels[i:]).sum()
@@ -367,11 +367,12 @@ def build_hierarchical_dataframe(df, levels, value_column, color_columns=None):
df_tree['parent'] = 'total'
df_tree['value'] = dfg[value_column]
df_tree['color'] = dfg[color_columns[0]] / dfg[color_columns[1]]
- df_all_trees = df_all_trees.append(df_tree, ignore_index=True)
+ df_list.append(df_tree)
total = pd.Series(dict(id='total', parent='',
value=df[value_column].sum(),
- color=df[color_columns[0]].sum() / df[color_columns[1]].sum()))
- df_all_trees = df_all_trees.append(total, ignore_index=True)
+ color=df[color_columns[0]].sum() / df[color_columns[1]].sum()), name=0)
+ df_list.append(total)
+ df_all_trees = pd.concat(df_list, ignore_index=True)
return df_all_trees
diff --git a/doc/python/text-and-annotations.md b/doc/python/text-and-annotations.md
index 2b3a68c5365..ca8dbd63dfa 100644
--- a/doc/python/text-and-annotations.md
+++ b/doc/python/text-and-annotations.md
@@ -6,7 +6,7 @@ jupyter:
extension: .md
format_name: markdown
format_version: '1.3'
- jupytext_version: 1.16.1
+ jupytext_version: 1.16.3
kernelspec:
display_name: Python 3 (ipykernel)
language: python
@@ -20,7 +20,7 @@ jupyter:
name: python
nbconvert_exporter: python
pygments_lexer: ipython3
- version: 3.10.11
+ version: 3.10.14
plotly:
description: How to add text labels and annotations to plots in python.
display_as: file_settings
@@ -395,6 +395,162 @@ fig.show()
```
+## Numeric Font Weight
+
+*New in 5.23*
+
+In the previous example, we set a font `weight` using a keyword value. You can also set font `weight` using a numeric value.
+
+The font weights that are available depend on the font family that is set. If you set a font `weight` that isn't available for a particular font family, the weight will be rounded to the nearest available value.
+
+
+```python
+import plotly.graph_objects as go
+from plotly import data
+
+df = data.medals_wide()
+
+fig = go.Figure(
+ data=[
+ go.Bar(
+ x=df.nation,
+ y=df.gold,
+ name="Gold",
+ marker=dict(color="Gold"),
+ text="Gold",
+ textfont=dict(weight=900, size=17),
+ ),
+ go.Bar(
+ x=df.nation,
+ y=df.silver,
+ name="Silver",
+ marker=dict(color="MediumTurquoise"),
+ text="Silver",
+ textfont=dict(size=17),
+ ),
+ go.Bar(
+ x=df.nation,
+ y=df.bronze,
+ name="Bronze",
+ marker=dict(color="LightGreen"),
+ text="Bronze",
+ textfont=dict(size=17),
+ ),
+ ],
+ layout=dict(barcornerradius=15, showlegend=False),
+)
+
+fig.show()
+```
+
+[scattergl](https://plotly.com/python/reference/scattergl) traces do not support all numeric font weights. When you specify a numeric font weight on `scattergl`, weights up to 500 are mapped to the keyword font weight "normal", while weights above 500 are mapped to "bold".
+
+
+## Text Case
+
+*New in 5.23*
+
+You can configure text case using the `textfont.textcase` property. In this example, we set `textfont.textcase="upper"` to transform the text on all bars to uppercase.
+
+```python
+import plotly.graph_objects as go
+from plotly import data
+
+df = data.gapminder()
+
+grouped = df[df.year == 2007].loc[df[df.year == 2007].groupby('continent')['lifeExp'].idxmax()]
+
+fig = go.Figure(
+ data=go.Bar(
+ x=grouped['lifeExp'],
+ y=grouped['continent'],
+ text=grouped['country'],
+ orientation='h',
+ textfont=dict(
+ family="sans serif",
+ size=14,
+ # Here we set textcase to "upper.
+ # Set to lower" for lowercase text, or "word caps" to capitalize the first letter of each word
+ textcase="upper"
+
+ )
+ ),
+ layout=go.Layout(
+ title_text='Country with Highest Life Expectancy per Continent, 2007',
+ yaxis=dict(showticklabels=False)
+ )
+)
+
+fig.show()
+```
+
+## Text Lines
+
+*New in 5.23*
+
+You can add decoration lines to text using the `textfont.lineposition` property. This property accepts `"under"`, `"over"`, and `"through"`, or a combination of these separated by a `+`.
+
+```python
+import plotly.graph_objects as go
+from plotly import data
+
+df = data.gapminder()
+
+grouped = df[df.year == 2002].loc[df[df.year == 2002].groupby('continent')['lifeExp'].idxmax()]
+
+fig = go.Figure(
+ data=go.Bar(
+ x=grouped['lifeExp'],
+ y=grouped['continent'],
+ text=grouped['country'],
+ orientation='h',
+ marker_color='MediumSlateBlue',
+ textfont=dict(
+ lineposition="under" # combine different line positions with a "+" to add more than one: "under+over"
+ )
+ ),
+ layout=go.Layout(
+ title_text='Country with Highest Life Expectancy per Continent, 2002',
+ yaxis=dict(showticklabels=False)
+ )
+)
+
+fig.show()
+```
+
+## Text Shadow
+
+*New in 5.23*
+
+You can apply a shadow effect to text using the `textfont.shadow` property. This property accepts shadow specifications in the same format as the [text-shadow CSS property](https://developer.mozilla.org/en-US/docs/Web/CSS/text-shadow).
+
+```python
+import plotly.graph_objects as go
+from plotly import data
+
+df = data.gapminder()
+
+grouped = df[df.year == 1997].loc[df[df.year == 1997].groupby('continent')['lifeExp'].idxmax()]
+
+fig = go.Figure(
+ data=go.Bar(
+ x=grouped['lifeExp'],
+ y=grouped['continent'],
+ text=grouped['country'],
+ orientation='h',
+ textfont=dict(
+ shadow="1px 1px 2px pink"
+ )
+ ),
+ layout=go.Layout(
+ title_text='Country with Highest Life Expectancy per Continent, 1997',
+ yaxis=dict(showticklabels=False)
+ )
+)
+
+fig.show()
+```
+
### Styling and Coloring Annotations
```python
@@ -497,6 +653,54 @@ fig.update_layout(
fig.show()
```
+### HTML Tags in Text
+
+The `text` attribute supports the following HTML tags: `0?[0]:[]);if(o.enter().append("g").classed(f.containerClassName,!0).style("cursor","pointer"),o.exit().each((function(){n.select(this).selectAll("g."+f.headerGroupClassName).each(a)})).remove(),0!==r.length){var l=o.selectAll("g."+f.headerGroupClassName).data(r,p);l.enter().append("g").classed(f.headerGroupClassName,!0);for(var u=s.ensureSingle(o,"g",f.dropdownButtonGroupClassName,(function(t){t.style("pointer-events","all")})),c=0;cl?r.y-l:0;return Math.sqrt(u*u+f*f)}for(var p=h(u);p;){if((u+=p+r)>f)return;p=h(u)}for(p=h(f);p;){if(u>(f-=p+r))return;p=h(f)}return{min:u,max:f,len:f-u,total:c,isClosed:0===u&&f===c&&Math.abs(n.x-i.x)<.1&&Math.abs(n.y-i.y)<.1}},e.findPointOnPath=function(t,e,r,n){for(var i,a,o,s=(n=n||{}).pathLength||t.getTotalLength(),l=n.tolerance||.001,u=n.iterationLimit||30,c=t.getPointAtLength(0)[r]>t.getPointAtLength(s)[r]?-1:1,f=0,h=0,p=s;f0?p=i:h=i,f++}return a}},33040:function(t,e,r){"use strict";var n=r(38248),i=r(49760),a=r(72160),o=r(8932),s=r(22548).defaultLine,l=r(38116).isArrayOrTypedArray,u=a(s);function c(t,e){var r=t;return r[3]*=e,r}function f(t){if(n(t))return u;var e=a(t);return e.length?e:u}function h(t){return n(t)?t:1}t.exports={formatColor:function(t,e,r){var n=t.color;n&&n._inputArray&&(n=n._inputArray);var i,s,p,d,v,g=l(n),y=l(e),m=o.extractOpts(t),x=[];if(i=void 0!==m.colorscale?o.makeColorScaleFuncFromTrace(t):f,s=g?function(t,e){return void 0===t[e]?u:a(i(t[e]))}:f,p=y?function(t,e){return void 0===t[e]?1:h(t[e])}:h,g||y)for(var b=0;b