Skip to content

Commit

Permalink
Merge pull request #2614 from plotly/nocolor
Browse files Browse the repository at this point in the history
px.NO_COLOR
  • Loading branch information
nicolaskruchten authored Jul 8, 2020
2 parents b4cb434 + a226a5f commit 29eee77
Show file tree
Hide file tree
Showing 11 changed files with 160 additions and 10 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,22 @@ This project adheres to [Semantic Versioning](http://semver.org/).

## [4.9.0] - unreleased

### Added

- `px.NO_COLOR` constant to override wide-form color assignment in Plotly Express ([#2614](https://github.com/plotly/plotly.py/pull/2614))
- `facet_row_spacing` and `facet_col_spacing` added to Plotly Express cartesian 2d functions ([#2614](https://github.com/plotly/plotly.py/pull/2614))

### Fixed

- trendline traces are now of type `scattergl` when `render_mode="webgl"` in Plotly Express ([#2614](https://github.com/plotly/plotly.py/pull/2614))

### Updated

- Added all cartesian-2d Plotly Express functions, plus `imshow` to Pandas backend with `kind` option
- `plotly.express.imshow` now uses data frame index and columns names and values to populate axis parameters by default ([#2539](https://github.com/plotly/plotly.py/pull/2539))



## [4.8.2] - 2020-06-26

### Updated
Expand Down
25 changes: 21 additions & 4 deletions doc/python/facet-plots.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jupyter:
extension: .md
format_name: markdown
format_version: '1.2'
jupytext_version: 1.3.4
jupytext_version: 1.4.2
kernelspec:
display_name: Python 3
language: python
Expand All @@ -20,7 +20,7 @@ jupyter:
name: python
nbconvert_exporter: python
pygments_lexer: ipython3
version: 3.7.0
version: 3.7.7
plotly:
description: How to make Facet and Trellis Plots in Python with Plotly.
display_as: statistical
Expand Down Expand Up @@ -103,7 +103,7 @@ fig.show()

### Customize Subplot Figure Titles

Since subplot figure titles are [annotations](https://plotly.com/python/text-and-annotations/#simple-annotation), you can use the `for_each_annotation` function to customize them.
Since subplot figure titles are [annotations](https://plotly.com/python/text-and-annotations/#simple-annotation), you can use the `for_each_annotation` function to customize them, for example to remove the equal-sign (`=`).

In the following example, we pass a lambda function to `for_each_annotation` in order to change the figure subplot titles from `smoker=No` and `smoker=Yes` to just `No` and `Yes`.

Expand All @@ -115,8 +115,25 @@ fig.for_each_annotation(lambda a: a.update(text=a.text.split("=")[-1]))
fig.show()
```

### Controlling Facet Spacing

The `facet_row_spacing` and `facet_col_spacing` arguments can be used to control the spacing between rows and columns. These values are specified in fractions of the plotting area in paper coordinates and not in pixels, so they will grow or shrink with the `width` and `height` of the figure.

The defaults work well with 1-4 rows or columns at the default figure size with the default font size, but need to be reduced to around 0.01 for very large figures or figures with many rows or columns. Conversely, if activating tick labels on all facets, the spacing will need to be increased.

```python
import plotly.express as px

df = px.data.gapminder().query("continent == 'Africa'")

fig = px.line(df, x="year", y="lifeExp", facet_col="country", facet_col_wrap=7,
facet_row_spacing=0.04, # default is 0.07 when facet_col_wrap is used
facet_col_spacing=0.04, # default is 0.03
height=600, width=800,
title="Life Expectancy in Africa")
fig.for_each_annotation(lambda a: a.update(text=a.text.split("=")[-1]))
fig.update_yaxes(showticklabels=True)
fig.show()
```

### Synchronizing axes in subplots with `matches`
Expand All @@ -138,4 +155,4 @@ for i in range(1, 4):
fig.add_trace(go.Scatter(x=x, y=np.random.random(N)), 1, i)
fig.update_xaxes(matches='x')
fig.show()
```
```
10 changes: 10 additions & 0 deletions doc/python/wide-form.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,16 @@ fig = px.bar(wide_df, x="nation", y=["gold", "silver", "bronze"], facet_col="var
fig.show()
```

You can also prevent `color` from getting assigned if you're mapping `variable` to some other argument:

```python
import plotly.express as px
wide_df = px.data.medals_wide(indexed=False)

fig = px.bar(wide_df, x="nation", y=["gold", "silver", "bronze"], facet_col="variable", color=px.NO_COLOR)
fig.show()
```

If using a data frame's named indexes, either explicitly or relying on the defaults, the row-index references (i.e. `df.index`) or column-index names (i.e. the value of `df.columns.name`) must be used:

```python
Expand Down
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 @@ -53,6 +53,7 @@
set_mapbox_access_token,
defaults,
get_trendline_results,
NO_COLOR,
)

from ._special_inputs import IdentityMap, Constant, Range # noqa: F401
Expand Down Expand Up @@ -100,4 +101,5 @@
"IdentityMap",
"Constant",
"Range",
"NO_COLOR",
]
22 changes: 22 additions & 0 deletions packages/python/plotly/plotly/express/_chart_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ def scatter(
facet_row=None,
facet_col=None,
facet_col_wrap=0,
facet_row_spacing=None,
facet_col_spacing=None,
error_x=None,
error_x_minus=None,
error_y=None,
Expand Down Expand Up @@ -74,6 +76,8 @@ def density_contour(
facet_row=None,
facet_col=None,
facet_col_wrap=0,
facet_row_spacing=None,
facet_col_spacing=None,
hover_name=None,
hover_data=None,
animation_frame=None,
Expand Down Expand Up @@ -141,6 +145,8 @@ def density_heatmap(
facet_row=None,
facet_col=None,
facet_col_wrap=0,
facet_row_spacing=None,
facet_col_spacing=None,
hover_name=None,
hover_data=None,
animation_frame=None,
Expand Down Expand Up @@ -213,6 +219,8 @@ def line(
facet_row=None,
facet_col=None,
facet_col_wrap=0,
facet_row_spacing=None,
facet_col_spacing=None,
error_x=None,
error_x_minus=None,
error_y=None,
Expand Down Expand Up @@ -260,6 +268,8 @@ def area(
facet_row=None,
facet_col=None,
facet_col_wrap=0,
facet_row_spacing=None,
facet_col_spacing=None,
animation_frame=None,
animation_group=None,
category_orders={},
Expand Down Expand Up @@ -301,6 +311,8 @@ def bar(
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,
Expand Down Expand Up @@ -353,6 +365,8 @@ def histogram(
facet_row=None,
facet_col=None,
facet_col_wrap=0,
facet_row_spacing=None,
facet_col_spacing=None,
hover_name=None,
hover_data=None,
animation_frame=None,
Expand Down Expand Up @@ -417,6 +431,8 @@ def violin(
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,
Expand Down Expand Up @@ -464,6 +480,8 @@ def box(
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,
Expand Down Expand Up @@ -514,6 +532,8 @@ def strip(
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,
Expand Down Expand Up @@ -1398,6 +1418,8 @@ def funnel(
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,
Expand Down
16 changes: 11 additions & 5 deletions packages/python/plotly/plotly/express/_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
_subplot_type_for_trace_type,
)

NO_COLOR = "px_no_color_constant"

# Declare all supported attributes, across all plot types
direct_attrables = (
Expand Down Expand Up @@ -842,7 +843,7 @@ def make_trace_spec(args, constructor, attrs, trace_patch):
# Add trendline trace specifications
if "trendline" in args and args["trendline"]:
trace_spec = TraceSpec(
constructor=go.Scatter,
constructor=go.Scattergl if constructor == go.Scattergl else go.Scatter,
attrs=["trendline"],
trace_patch=dict(mode="lines"),
marginal=None,
Expand Down Expand Up @@ -1349,6 +1350,10 @@ def build_dataframe(args, constructor):
label=_escape_col_name(df_input, "index", [var_name, value_name])
)

no_color = False
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 @@ -1440,7 +1445,8 @@ def build_dataframe(args, constructor):
args["x" if orient_v else "y"] = value_name
args["y" if orient_v else "x"] = wide_cross_name
args["color"] = args["color"] or var_name

if no_color:
args["color"] = None
args["data_frame"] = df_output
return args

Expand Down Expand Up @@ -2054,9 +2060,9 @@ def init_figure(args, subplot_type, frame_list, nrows, ncols, col_labels, row_la
row_heights = [main_size] * (nrows - 1) + [1 - main_size]
vertical_spacing = 0.01
elif args.get("facet_col_wrap", 0):
vertical_spacing = 0.07
vertical_spacing = args.get("facet_row_spacing", None) or 0.07
else:
vertical_spacing = 0.03
vertical_spacing = args.get("facet_row_spacing", None) or 0.03

if bool(args.get("marginal_y", False)):
if args["marginal_y"] == "histogram" or ("color" in args and args["color"]):
Expand All @@ -2067,7 +2073,7 @@ def init_figure(args, subplot_type, frame_list, nrows, ncols, col_labels, row_la
column_widths = [main_size] * (ncols - 1) + [1 - main_size]
horizontal_spacing = 0.005
else:
horizontal_spacing = 0.02
horizontal_spacing = args.get("facet_col_spacing", None) or 0.02
else:
# Other subplot types:
# 'scene', 'geo', 'polar', 'ternary', 'mapbox', 'domain', None
Expand Down
8 changes: 8 additions & 0 deletions packages/python/plotly/plotly/express/_doc.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,14 @@
"Wraps the column variable at this width, so that the column facets span multiple rows.",
"Ignored if 0, and forced to 0 if `facet_row` or a `marginal` is set.",
],
facet_row_spacing=[
"float between 0 and 1",
"Spacing between facet rows, in paper units. Default is 0.03 or 0.0.7 when facet_col_wrap is used.",
],
facet_col_spacing=[
"float between 0 and 1",
"Spacing between facet columns, in paper units Default is 0.02.",
],
animation_frame=[
colref_type,
colref_desc,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import plotly.express as px
import numpy as np


def test_reversed_colorscale():
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import plotly.express as px
from pytest import approx


def test_facets():
df = px.data.tips()
fig = px.scatter(df, x="total_bill", y="tip")
assert "xaxis2" not in fig.layout
assert "yaxis2" not in fig.layout
assert fig.layout.xaxis.domain == (0.0, 1.0)
assert fig.layout.yaxis.domain == (0.0, 1.0)

fig = px.scatter(df, x="total_bill", y="tip", facet_row="sex", facet_col="smoker")
assert fig.layout.xaxis4.domain[0] - fig.layout.xaxis.domain[1] == approx(0.02)
assert fig.layout.yaxis4.domain[0] - fig.layout.yaxis.domain[1] == approx(0.03)

fig = px.scatter(df, x="total_bill", y="tip", facet_col="day", facet_col_wrap=2)
assert fig.layout.xaxis4.domain[0] - fig.layout.xaxis.domain[1] == approx(0.02)
assert fig.layout.yaxis4.domain[0] - fig.layout.yaxis.domain[1] == approx(0.07)

fig = px.scatter(
df,
x="total_bill",
y="tip",
facet_row="sex",
facet_col="smoker",
facet_col_spacing=0.09,
facet_row_spacing=0.08,
)
assert fig.layout.xaxis4.domain[0] - fig.layout.xaxis.domain[1] == approx(0.09)
assert fig.layout.yaxis4.domain[0] - fig.layout.yaxis.domain[1] == approx(0.08)

fig = px.scatter(
df,
x="total_bill",
y="tip",
facet_col="day",
facet_col_wrap=2,
facet_col_spacing=0.09,
facet_row_spacing=0.08,
)
assert fig.layout.xaxis4.domain[0] - fig.layout.xaxis.domain[1] == approx(0.09)
assert fig.layout.yaxis4.domain[0] - fig.layout.yaxis.domain[1] == approx(0.08)
22 changes: 22 additions & 0 deletions packages/python/plotly/plotly/tests/test_core/test_px/test_px.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,25 @@ def test_marginal_ranges():
)
assert fig.layout.xaxis2.range is None
assert fig.layout.yaxis3.range is None


def test_render_mode():
df = px.data.gapminder()
df2007 = df.query("year == 2007")
fig = px.scatter(df2007, x="gdpPercap", y="lifeExp", trendline="ols")
assert fig.data[0].type == "scatter"
assert fig.data[1].type == "scatter"
fig = px.scatter(
df2007, x="gdpPercap", y="lifeExp", trendline="ols", render_mode="webgl"
)
assert fig.data[0].type == "scattergl"
assert fig.data[1].type == "scattergl"
fig = px.scatter(df, x="gdpPercap", y="lifeExp", trendline="ols")
assert fig.data[0].type == "scattergl"
assert fig.data[1].type == "scattergl"
fig = px.scatter(df, x="gdpPercap", y="lifeExp", trendline="ols", render_mode="svg")
assert fig.data[0].type == "scatter"
assert fig.data[1].type == "scatter"
fig = px.density_contour(df, x="gdpPercap", y="lifeExp", trendline="ols")
assert fig.data[0].type == "histogram2dcontour"
assert fig.data[1].type == "scatter"
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,17 @@ def append_special_case(df_in, args_in, args_expect, df_expect):
),
)

# NO_COLOR
df = pd.DataFrame(dict(a=[1, 2], b=[3, 4]))
append_special_case(
df_in=df,
args_in=dict(x=None, y=None, color=px.NO_COLOR),
args_expect=dict(x="index", y="value", color=None, orientation="v",),
df_expect=pd.DataFrame(
dict(variable=["a", "a", "b", "b"], index=[0, 1, 0, 1], value=[1, 2, 3, 4])
),
)


@pytest.mark.parametrize("df_in, args_in, args_expect, df_expect", special_cases)
def test_wide_mode_internal_special_cases(df_in, args_in, args_expect, df_expect):
Expand Down

0 comments on commit 29eee77

Please sign in to comment.