Skip to content

Commit de02fca

Browse files
authored
v4 colorscale updates (#1647)
* Added 'rebeccapurple' named css color * Support color lists in colorscale validator * Support strings of named colors from plotly.colors as colorscale * Add all legacy plotly.js colorscale definitions to plotly.colors and always coerce color string to colorscale definitions. * Rename Plotly sequential colorscale to Plotly3 since this was only the theme default for version 3 * Added sequence colorscale tests * Change default colorscale for annotated heatmap figure factory to Plasma
1 parent bdd5a3a commit de02fca

File tree

285 files changed

+5239
-4312
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

285 files changed

+5239
-4312
lines changed

packages/python/plotly/_plotly_utils/basevalidators.py

+84-51
Original file line numberDiff line numberDiff line change
@@ -1234,6 +1234,7 @@ class ColorValidator(BaseValidator):
12341234
"red",
12351235
"rosybrown",
12361236
"royalblue",
1237+
"rebeccapurple",
12371238
"saddlebrown",
12381239
"salmon",
12391240
"sandybrown",
@@ -1503,86 +1504,118 @@ class ColorscaleValidator(BaseValidator):
15031504
},
15041505
"""
15051506

1506-
named_colorscales = [
1507-
"Greys",
1508-
"YlGnBu",
1509-
"Greens",
1510-
"YlOrRd",
1511-
"Bluered",
1512-
"RdBu",
1513-
"Reds",
1514-
"Blues",
1515-
"Picnic",
1516-
"Rainbow",
1517-
"Portland",
1518-
"Jet",
1519-
"Hot",
1520-
"Blackbody",
1521-
"Earth",
1522-
"Electric",
1523-
"Viridis",
1524-
"Cividis",
1525-
]
1526-
15271507
def __init__(self, plotly_name, parent_name, **kwargs):
15281508
super(ColorscaleValidator, self).__init__(
15291509
plotly_name=plotly_name, parent_name=parent_name, **kwargs
15301510
)
15311511

1512+
# named colorscales initialized on first use
1513+
self._named_colorscales = None
1514+
1515+
@property
1516+
def named_colorscales(self):
1517+
if self._named_colorscales is None:
1518+
import inspect
1519+
import itertools
1520+
from plotly import colors
1521+
1522+
colorscale_members = itertools.chain(
1523+
inspect.getmembers(colors.sequential),
1524+
inspect.getmembers(colors.diverging),
1525+
inspect.getmembers(colors.cyclical),
1526+
)
1527+
1528+
self._named_colorscales = {
1529+
c[0].lower(): c[1]
1530+
for c in colorscale_members
1531+
if isinstance(c, tuple)
1532+
and len(c) == 2
1533+
and isinstance(c[0], str)
1534+
and isinstance(c[1], list)
1535+
}
1536+
1537+
return self._named_colorscales
1538+
15321539
def description(self):
1540+
colorscales_str = "\n".join(
1541+
textwrap.wrap(
1542+
repr(sorted(list(self.named_colorscales))),
1543+
initial_indent=" " * 12,
1544+
subsequent_indent=" " * 13,
1545+
break_on_hyphens=False,
1546+
width=80,
1547+
)
1548+
)
1549+
15331550
desc = """\
15341551
The '{plotly_name}' property is a colorscale and may be
15351552
specified as:
1553+
- A list of colors that will be spaced evenly to create the colorscale.
1554+
Many predefined colorscale lists are included in the sequential, diverging,
1555+
and cyclical modules in the plotly.colors package.
15361556
- A list of 2-element lists where the first element is the
15371557
normalized color level value (starting at 0 and ending at 1),
15381558
and the second item is a valid color string.
15391559
(e.g. [[0, 'green'], [0.5, 'red'], [1.0, 'rgb(0, 0, 255)']])
15401560
- One of the following named colorscales:
1541-
['Greys', 'YlGnBu', 'Greens', 'YlOrRd', 'Bluered', 'RdBu',
1542-
'Reds', 'Blues', 'Picnic', 'Rainbow', 'Portland', 'Jet',
1543-
'Hot', 'Blackbody', 'Earth', 'Electric', 'Viridis', 'Cividis']
1544-
""".format(
1545-
plotly_name=self.plotly_name
1561+
{colorscales_str}
1562+
""".format(
1563+
plotly_name=self.plotly_name, colorscales_str=colorscales_str
15461564
)
15471565

15481566
return desc
15491567

15501568
def validate_coerce(self, v):
15511569
v_valid = False
15521570

1553-
if v is None:
1554-
# Pass None through
1555-
pass
15561571
if v is None:
15571572
v_valid = True
15581573
elif isinstance(v, string_types):
1559-
v_match = [
1560-
el
1561-
for el in ColorscaleValidator.named_colorscales
1562-
if el.lower() == v.lower()
1563-
]
1564-
if v_match:
1574+
v_lower = v.lower()
1575+
if v_lower in self.named_colorscales:
1576+
# Convert to color list
1577+
v = self.named_colorscales[v_lower]
1578+
1579+
# Convert to list of lists colorscale
1580+
d = len(v) - 1
1581+
v = [[(1.0 * i) / (1.0 * d), x] for i, x in enumerate(v)]
1582+
15651583
v_valid = True
15661584

15671585
elif is_array(v) and len(v) > 0:
1568-
invalid_els = [
1569-
e
1570-
for e in v
1571-
if (
1572-
not is_array(e)
1573-
or len(e) != 2
1574-
or not isinstance(e[0], numbers.Number)
1575-
or not (0 <= e[0] <= 1)
1576-
or not isinstance(e[1], string_types)
1577-
or ColorValidator.perform_validate_coerce(e[1]) is None
1578-
)
1579-
]
1586+
# If firset element is a string, treat as colorsequence
1587+
if isinstance(v[0], string_types):
1588+
invalid_els = [
1589+
e for e in v if ColorValidator.perform_validate_coerce(e) is None
1590+
]
1591+
1592+
if len(invalid_els) == 0:
1593+
v_valid = True
1594+
1595+
# Convert to list of lists colorscale
1596+
d = len(v) - 1
1597+
v = [[(1.0 * i) / (1.0 * d), x] for i, x in enumerate(v)]
1598+
else:
1599+
invalid_els = [
1600+
e
1601+
for e in v
1602+
if (
1603+
not is_array(e)
1604+
or len(e) != 2
1605+
or not isinstance(e[0], numbers.Number)
1606+
or not (0 <= e[0] <= 1)
1607+
or not isinstance(e[1], string_types)
1608+
or ColorValidator.perform_validate_coerce(e[1]) is None
1609+
)
1610+
]
15801611

1581-
if len(invalid_els) == 0:
1582-
v_valid = True
1612+
if len(invalid_els) == 0:
1613+
v_valid = True
15831614

1584-
# Convert to list of lists
1585-
v = [[e[0], ColorValidator.perform_validate_coerce(e[1])] for e in v]
1615+
# Convert to list of lists
1616+
v = [
1617+
[e[0], ColorValidator.perform_validate_coerce(e[1])] for e in v
1618+
]
15861619

15871620
if not v_valid:
15881621
self.raise_invalid_val(v)

packages/python/plotly/plotly/colors/__init__.py packages/python/plotly/_plotly_utils/colors/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
from numbers import Number
8080
import six
8181

82-
from plotly import exceptions
82+
from _plotly_utils import exceptions
8383

8484

8585
# Built-in qualitative color sequences and sequential,

packages/python/plotly/plotly/colors/diverging.py packages/python/plotly/_plotly_utils/colors/diverging.py

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from .cmocean import balance, delta, curl # noqa: F401
2121
from .carto import Armyrose, Fall, Geyser, Temps, Tealrose, Tropic, Earth # noqa: F401
2222

23+
from .plotlyjs import Picnic, Portland # noqa: F401
2324

2425
from ._swatches import _swatches
2526

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
# Copied from
2+
# https://github.com/plotly/plotly.js/blob/master/src/components/colorscale/scales.js
3+
_plotlyjs_scales = {
4+
"Greys": [[0, "rgb(0,0,0)"], [1, "rgb(255,255,255)"]],
5+
"YlGnBu": [
6+
[0, "rgb(8,29,88)"],
7+
[0.125, "rgb(37,52,148)"],
8+
[0.25, "rgb(34,94,168)"],
9+
[0.375, "rgb(29,145,192)"],
10+
[0.5, "rgb(65,182,196)"],
11+
[0.625, "rgb(127,205,187)"],
12+
[0.75, "rgb(199,233,180)"],
13+
[0.875, "rgb(237,248,217)"],
14+
[1, "rgb(255,255,217)"],
15+
],
16+
"Greens": [
17+
[0, "rgb(0,68,27)"],
18+
[0.125, "rgb(0,109,44)"],
19+
[0.25, "rgb(35,139,69)"],
20+
[0.375, "rgb(65,171,93)"],
21+
[0.5, "rgb(116,196,118)"],
22+
[0.625, "rgb(161,217,155)"],
23+
[0.75, "rgb(199,233,192)"],
24+
[0.875, "rgb(229,245,224)"],
25+
[1, "rgb(247,252,245)"],
26+
],
27+
"YlOrRd": [
28+
[0, "rgb(128,0,38)"],
29+
[0.125, "rgb(189,0,38)"],
30+
[0.25, "rgb(227,26,28)"],
31+
[0.375, "rgb(252,78,42)"],
32+
[0.5, "rgb(253,141,60)"],
33+
[0.625, "rgb(254,178,76)"],
34+
[0.75, "rgb(254,217,118)"],
35+
[0.875, "rgb(255,237,160)"],
36+
[1, "rgb(255,255,204)"],
37+
],
38+
"Bluered": [[0, "rgb(0,0,255)"], [1, "rgb(255,0,0)"]],
39+
# modified RdBu based on
40+
# http:#www.kennethmoreland.com/color-maps/
41+
"RdBu": [
42+
[0, "rgb(5,10,172)"],
43+
[0.35, "rgb(106,137,247)"],
44+
[0.5, "rgb(190,190,190)"],
45+
[0.6, "rgb(220,170,132)"],
46+
[0.7, "rgb(230,145,90)"],
47+
[1, "rgb(178,10,28)"],
48+
],
49+
# Scale for non-negative numeric values
50+
"Reds": [
51+
[0, "rgb(220,220,220)"],
52+
[0.2, "rgb(245,195,157)"],
53+
[0.4, "rgb(245,160,105)"],
54+
[1, "rgb(178,10,28)"],
55+
],
56+
# Scale for non-positive numeric values
57+
"Blues": [
58+
[0, "rgb(5,10,172)"],
59+
[0.35, "rgb(40,60,190)"],
60+
[0.5, "rgb(70,100,245)"],
61+
[0.6, "rgb(90,120,245)"],
62+
[0.7, "rgb(106,137,247)"],
63+
[1, "rgb(220,220,220)"],
64+
],
65+
"Picnic": [
66+
[0, "rgb(0,0,255)"],
67+
[0.1, "rgb(51,153,255)"],
68+
[0.2, "rgb(102,204,255)"],
69+
[0.3, "rgb(153,204,255)"],
70+
[0.4, "rgb(204,204,255)"],
71+
[0.5, "rgb(255,255,255)"],
72+
[0.6, "rgb(255,204,255)"],
73+
[0.7, "rgb(255,153,255)"],
74+
[0.8, "rgb(255,102,204)"],
75+
[0.9, "rgb(255,102,102)"],
76+
[1, "rgb(255,0,0)"],
77+
],
78+
"Rainbow": [
79+
[0, "rgb(150,0,90)"],
80+
[0.125, "rgb(0,0,200)"],
81+
[0.25, "rgb(0,25,255)"],
82+
[0.375, "rgb(0,152,255)"],
83+
[0.5, "rgb(44,255,150)"],
84+
[0.625, "rgb(151,255,0)"],
85+
[0.75, "rgb(255,234,0)"],
86+
[0.875, "rgb(255,111,0)"],
87+
[1, "rgb(255,0,0)"],
88+
],
89+
"Portland": [
90+
[0, "rgb(12,51,131)"],
91+
[0.25, "rgb(10,136,186)"],
92+
[0.5, "rgb(242,211,56)"],
93+
[0.75, "rgb(242,143,56)"],
94+
[1, "rgb(217,30,30)"],
95+
],
96+
"Jet": [
97+
[0, "rgb(0,0,131)"],
98+
[0.125, "rgb(0,60,170)"],
99+
[0.375, "rgb(5,255,255)"],
100+
[0.625, "rgb(255,255,0)"],
101+
[0.875, "rgb(250,0,0)"],
102+
[1, "rgb(128,0,0)"],
103+
],
104+
"Hot": [
105+
[0, "rgb(0,0,0)"],
106+
[0.3, "rgb(230,0,0)"],
107+
[0.6, "rgb(255,210,0)"],
108+
[1, "rgb(255,255,255)"],
109+
],
110+
"Blackbody": [
111+
[0, "rgb(0,0,0)"],
112+
[0.2, "rgb(230,0,0)"],
113+
[0.4, "rgb(230,210,0)"],
114+
[0.7, "rgb(255,255,255)"],
115+
[1, "rgb(160,200,255)"],
116+
],
117+
"Earth": [
118+
[0, "rgb(0,0,130)"],
119+
[0.1, "rgb(0,180,180)"],
120+
[0.2, "rgb(40,210,40)"],
121+
[0.4, "rgb(230,230,50)"],
122+
[0.6, "rgb(120,70,20)"],
123+
[1, "rgb(255,255,255)"],
124+
],
125+
"Electric": [
126+
[0, "rgb(0,0,0)"],
127+
[0.15, "rgb(30,0,100)"],
128+
[0.4, "rgb(120,0,100)"],
129+
[0.6, "rgb(160,90,0)"],
130+
[0.8, "rgb(230,200,0)"],
131+
[1, "rgb(255,250,220)"],
132+
],
133+
"Viridis": [
134+
[0, "#440154"],
135+
[0.06274509803921569, "#48186a"],
136+
[0.12549019607843137, "#472d7b"],
137+
[0.18823529411764706, "#424086"],
138+
[0.25098039215686274, "#3b528b"],
139+
[0.3137254901960784, "#33638d"],
140+
[0.3764705882352941, "#2c728e"],
141+
[0.4392156862745098, "#26828e"],
142+
[0.5019607843137255, "#21918c"],
143+
[0.5647058823529412, "#1fa088"],
144+
[0.6274509803921569, "#28ae80"],
145+
[0.6901960784313725, "#3fbc73"],
146+
[0.7529411764705882, "#5ec962"],
147+
[0.8156862745098039, "#84d44b"],
148+
[0.8784313725490196, "#addc30"],
149+
[0.9411764705882353, "#d8e219"],
150+
[1, "#fde725"],
151+
],
152+
"Cividis": [
153+
[0.000000, "rgb(0,32,76)"],
154+
[0.058824, "rgb(0,42,102)"],
155+
[0.117647, "rgb(0,52,110)"],
156+
[0.176471, "rgb(39,63,108)"],
157+
[0.235294, "rgb(60,74,107)"],
158+
[0.294118, "rgb(76,85,107)"],
159+
[0.352941, "rgb(91,95,109)"],
160+
[0.411765, "rgb(104,106,112)"],
161+
[0.470588, "rgb(117,117,117)"],
162+
[0.529412, "rgb(131,129,120)"],
163+
[0.588235, "rgb(146,140,120)"],
164+
[0.647059, "rgb(161,152,118)"],
165+
[0.705882, "rgb(176,165,114)"],
166+
[0.764706, "rgb(192,177,109)"],
167+
[0.823529, "rgb(209,191,102)"],
168+
[0.882353, "rgb(225,204,92)"],
169+
[0.941176, "rgb(243,219,79)"],
170+
[1.000000, "rgb(255,233,69)"],
171+
],
172+
}
173+
174+
# Create variable named after each scale that contains the sequence of colors only
175+
for scale_name, scale_pairs in _plotlyjs_scales.items():
176+
scale_sequence = [c[1] for c in scale_pairs]
177+
exec (
178+
"{scale_name} = {scale_sequence}".format(
179+
scale_name=scale_name, scale_sequence=scale_sequence
180+
)
181+
)

packages/python/plotly/plotly/colors/sequential.py packages/python/plotly/_plotly_utils/colors/sequential.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def swatches():
1414

1515
swatches.__doc__ = _swatches.__doc__
1616

17-
Plotly = [
17+
Plotly3 = [
1818
"#0508b8",
1919
"#1910d8",
2020
"#3c19f0",
@@ -92,6 +92,8 @@ def swatches():
9292
"#f0f921",
9393
]
9494

95+
from .plotlyjs import Blackbody, Bluered, Electric, Hot, Jet, Rainbow # noqa: F401
96+
9597
from .colorbrewer import ( # noqa: F401
9698
Blues,
9799
BuGn,

0 commit comments

Comments
 (0)