Skip to content

Commit 4678b18

Browse files
authored
Merge pull request #518 from plotly/2d-density
Added _make_colorscale method and 2D Density Plot
2 parents 25683ec + 7044922 commit 4678b18

File tree

3 files changed

+261
-0
lines changed

3 files changed

+261
-0
lines changed

plotly/graph_reference/default-schema.json

+1
Original file line numberDiff line numberDiff line change
@@ -5977,6 +5977,7 @@
59775977
"width": {
59785978
"arrayOk": true,
59795979
"description": "Sets the width (in px) of the lines bounding the marker points.",
5980+
"dflt": 0,
59805981
"min": 0,
59815982
"role": "style",
59825983
"valType": "number"

plotly/tests/test_core/test_tools/test_figure_factory.py

+105
Original file line numberDiff line numberDiff line change
@@ -1365,6 +1365,111 @@ def test_gantt_all_args(self):
13651365
self.assertEqual(test_gantt_chart['layout'],
13661366
exp_gantt_chart['layout'])
13671367

1368+
1369+
class Test2D_Density(TestCase):
1370+
1371+
def test_validate_2D_density(self):
1372+
1373+
# validate that x and y contain only numbers
1374+
x = [1, 2]
1375+
y = ['a', 2]
1376+
1377+
pattern = ("All elements of your 'x' and 'y' lists must be numbers.")
1378+
1379+
self.assertRaisesRegexp(PlotlyError, pattern,
1380+
tls.FigureFactory.create_2D_density, x, y)
1381+
1382+
# validate that x and y are the same length
1383+
x2 = [1]
1384+
y2 = [1, 2]
1385+
1386+
pattern2 = ("Both lists 'x' and 'y' must be the same length.")
1387+
1388+
self.assertRaisesRegexp(PlotlyError, pattern2,
1389+
tls.FigureFactory.create_2D_density, x2, y2)
1390+
1391+
def test_2D_density_all_args(self):
1392+
1393+
# check if 2D_density data matches with expected output
1394+
x = [1, 2]
1395+
y = [2, 4]
1396+
1397+
colorscale = ['#7A4579', '#D56073', 'rgb(236,158,105)',
1398+
(1, 1, 0.2), (0.98, 0.98, 0.98)]
1399+
1400+
test_2D_density_chart = tls.FigureFactory.create_2D_density(
1401+
x, y, colorscale=colorscale, hist_color='rgb(255, 237, 222)',
1402+
point_size=3, height=800, width=800)
1403+
1404+
exp_2D_density_chart = {
1405+
'data': [{'marker': {'color': 'rgb(0.0, 0.0, 127.5)',
1406+
'opacity': 0.4,
1407+
'size': 3},
1408+
'mode': 'markers',
1409+
'name': 'points',
1410+
'type': 'scatter',
1411+
'x': [1, 2],
1412+
'y': [2, 4]},
1413+
{'colorscale': [[0.0, 'rgb(122.0, 69.0, 121.0)'],
1414+
[0.25, 'rgb(213.0, 96.0, 115.0)'],
1415+
[0.5, 'rgb(236.0, 158.0, 105.0)'],
1416+
[0.75, 'rgb(255.0, 255.0, 51.0)'],
1417+
[1.0, 'rgb(249.9, 249.9, 249.9)']],
1418+
'name': 'density',
1419+
'ncontours': 20,
1420+
'reversescale': True,
1421+
'showscale': False,
1422+
'type': 'histogram2dcontour',
1423+
'x': [1, 2],
1424+
'y': [2, 4]},
1425+
{'marker': {'color': 'rgb(255.0, 237.0, 222.0)'},
1426+
'name': 'x density',
1427+
'type': 'histogram',
1428+
'x': [1, 2],
1429+
'yaxis': 'y2'},
1430+
{'marker': {'color': 'rgb(255.0, 237.0, 222.0)'},
1431+
'name': 'y density',
1432+
'type': 'histogram',
1433+
'xaxis': 'x2',
1434+
'y': [2, 4]}],
1435+
'layout': {'autosize': False,
1436+
'bargap': 0,
1437+
'height': 800,
1438+
'hovermode': 'closest',
1439+
'margin': {'t': 50},
1440+
'showlegend': False,
1441+
'title': 'Love',
1442+
'width': 800,
1443+
'xaxis': {'domain': [0, 0.85],
1444+
'showgrid': False,
1445+
'zeroline': False},
1446+
'xaxis2': {'domain': [0.85, 1],
1447+
'showgrid': False,
1448+
'zeroline': False},
1449+
'yaxis': {'domain': [0, 0.85],
1450+
'showgrid': False,
1451+
'zeroline': False},
1452+
'yaxis2': {'domain': [0.85, 1],
1453+
'showgrid': False,
1454+
'zeroline': False}}
1455+
}
1456+
1457+
self.assertEqual(test_2D_density_chart['data'][0],
1458+
exp_2D_density_chart['data'][0])
1459+
1460+
self.assertEqual(test_2D_density_chart['data'][1],
1461+
exp_2D_density_chart['data'][1])
1462+
1463+
self.assertEqual(test_2D_density_chart['data'][2],
1464+
exp_2D_density_chart['data'][2])
1465+
1466+
self.assertEqual(test_2D_density_chart['data'][3],
1467+
exp_2D_density_chart['data'][3])
1468+
1469+
self.assertEqual(test_2D_density_chart['layout'],
1470+
exp_2D_density_chart['layout'])
1471+
1472+
13681473
# class TestDistplot(TestCase):
13691474

13701475
# def test_scipy_import_error(self):

plotly/tools.py

+155
Original file line numberDiff line numberDiff line change
@@ -1476,6 +1476,161 @@ class FigureFactory(object):
14761476
more information and examples of a specific chart type.
14771477
"""
14781478

1479+
@staticmethod
1480+
def _make_linear_colorscale(colors):
1481+
"""
1482+
Makes a list of colors into a colorscale-acceptable form
1483+
1484+
For documentation regarding to the form of the output, see
1485+
https://plot.ly/python/reference/#mesh3d-colorscale
1486+
"""
1487+
scale = 1./(len(colors) - 1)
1488+
return[[i * scale, color] for i, color in enumerate(colors)]
1489+
1490+
@staticmethod
1491+
def create_2D_density(x, y, colorscale='Earth', ncontours=20,
1492+
hist_color=(0, 0, 0.5), point_color=(0, 0, 0.5),
1493+
point_size=2, height=600, width=600):
1494+
"""
1495+
Returns figure for a 2D density plot
1496+
1497+
:param (list|array) x: x-axis data for plot generation
1498+
:param (list|array) y: y-axis data for plot generation
1499+
:param (str|tuple|list) colorscale: either a plotly scale name, an rgb
1500+
or hex color, a color tuple or a list or tuple of colors. An rgb
1501+
color is of the form 'rgb(x, y, z)' where x, y, z belong to the
1502+
interval [0, 255] and a color tuple is a tuple of the form
1503+
(a, b, c) where a, b and c belong to [0, 1]. If colormap is a
1504+
list, it must contain the valid color types aforementioned as its
1505+
members.
1506+
:param (int) ncontours: the number of 2D contours to draw on the plot
1507+
:param (str) hist_color: the color of the plotted histograms
1508+
:param (str) point_color: the color of the scatter points
1509+
:param (str) point_size: the color of the scatter points
1510+
:param (float) height: the height of the chart
1511+
:param (float) width: the width of the chart
1512+
1513+
Example 1: Simple 2D Density Plot
1514+
```
1515+
import plotly.plotly as py
1516+
from plotly.tools import FigureFactory as FF
1517+
1518+
# Make data points
1519+
t = np.linspace(-1,1.2,2000)
1520+
x = (t**3)+(0.3*np.random.randn(2000))
1521+
y = (t**6)+(0.3*np.random.randn(2000))
1522+
1523+
# Create a figure
1524+
fig = FF.create_2D_density(x, y)
1525+
1526+
# Plot the data
1527+
py.iplot(fig, filename='simple-2d-density')
1528+
```
1529+
1530+
Example 2: Using Parameters
1531+
```
1532+
import plotly.plotly as py
1533+
from plotly.tools import FigureFactory as FF
1534+
1535+
# Make data points
1536+
t = np.linspace(-1,1.2,2000)
1537+
x = (t**3)+(0.3*np.random.randn(2000))
1538+
y = (t**6)+(0.3*np.random.randn(2000))
1539+
1540+
# Create custom colorscale
1541+
colorscale = ['#7A4579', '#D56073', 'rgb(236,158,105)',
1542+
(1, 1, 0.2), (0.98,0.98,0.98)]
1543+
1544+
# Create a figure
1545+
fig = FF.create_2D_density(
1546+
x, y, colorscale=colorscale,
1547+
hist_color='rgb(255, 237, 222)', point_size=3)
1548+
1549+
# Plot the data
1550+
py.iplot(fig, filename='use-parameters')
1551+
```
1552+
"""
1553+
from plotly.graph_objs import graph_objs
1554+
from numbers import Number
1555+
1556+
# validate x and y are filled with numbers only
1557+
for array in [x, y]:
1558+
if not all(isinstance(element, Number) for element in array):
1559+
raise exceptions.PlotlyError(
1560+
"All elements of your 'x' and 'y' lists must be numbers."
1561+
)
1562+
1563+
# validate x and y are the same length
1564+
if len(x) != len(y):
1565+
raise exceptions.PlotlyError(
1566+
"Both lists 'x' and 'y' must be the same length."
1567+
)
1568+
1569+
colorscale = FigureFactory._validate_colors(colorscale, 'rgb')
1570+
colorscale = FigureFactory._make_linear_colorscale(colorscale)
1571+
1572+
# validate hist_color and point_color
1573+
hist_color = FigureFactory._validate_colors(hist_color, 'rgb')
1574+
point_color = FigureFactory._validate_colors(point_color, 'rgb')
1575+
1576+
trace1 = graph_objs.Scatter(
1577+
x=x, y=y, mode='markers', name='points',
1578+
marker=dict(
1579+
color=point_color[0],
1580+
size=point_size,
1581+
opacity=0.4
1582+
)
1583+
)
1584+
trace2 = graph_objs.Histogram2dcontour(
1585+
x=x, y=y, name='density', ncontours=ncontours,
1586+
colorscale=colorscale, reversescale=True, showscale=False
1587+
)
1588+
trace3 = graph_objs.Histogram(
1589+
x=x, name='x density',
1590+
marker=dict(color=hist_color[0]), yaxis='y2'
1591+
)
1592+
trace4 = graph_objs.Histogram(
1593+
y=y, name='y density',
1594+
marker=dict(color=hist_color[0]), xaxis='x2'
1595+
)
1596+
data = [trace1, trace2, trace3, trace4]
1597+
1598+
layout = graph_objs.Layout(
1599+
showlegend=False,
1600+
autosize=False,
1601+
title='Love',
1602+
height=height,
1603+
width=width,
1604+
xaxis=dict(
1605+
domain=[0, 0.85],
1606+
showgrid=False,
1607+
zeroline=False
1608+
),
1609+
yaxis=dict(
1610+
domain=[0, 0.85],
1611+
showgrid=False,
1612+
zeroline=False
1613+
),
1614+
margin=dict(
1615+
t=50
1616+
),
1617+
hovermode='closest',
1618+
bargap=0,
1619+
xaxis2=dict(
1620+
domain=[0.85, 1],
1621+
showgrid=False,
1622+
zeroline=False
1623+
),
1624+
yaxis2=dict(
1625+
domain=[0.85, 1],
1626+
showgrid=False,
1627+
zeroline=False
1628+
)
1629+
)
1630+
1631+
fig = graph_objs.Figure(data=data, layout=layout)
1632+
return fig
1633+
14791634
@staticmethod
14801635
def _validate_gantt(df):
14811636
"""

0 commit comments

Comments
 (0)