Skip to content

Commit 53457aa

Browse files
committed
Initial implementation of scatterplot hyperlinks.
1 parent abda618 commit 53457aa

File tree

5 files changed

+793
-41
lines changed

5 files changed

+793
-41
lines changed

sandbox/scatterplot-hyperlinks.ipynb

Lines changed: 720 additions & 0 deletions
Large diffs are not rendered by default.

toyplot/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,7 @@ def scatterplot(
452452
color=None,
453453
filename=None,
454454
height=None,
455+
hyperlink=None,
455456
label=None,
456457
margin=50,
457458
marker="o",
@@ -517,6 +518,7 @@ def scatterplot(
517518
area=area,
518519
color=color,
519520
filename=filename,
521+
hyperlink=hyperlink,
520522
marker=marker,
521523
mlstyle=mlstyle,
522524
mstyle=mstyle,

toyplot/coordinates.py

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2025,15 +2025,16 @@ def scatterplot(
20252025
a,
20262026
b=None,
20272027
along="x",
2028+
area=None,
20282029
color=None,
2030+
filename=None,
2031+
hyperlink=None,
20292032
marker="o",
2030-
area=None,
2031-
size=None,
2033+
mlstyle=None,
2034+
mstyle=None,
20322035
opacity=1.0,
2036+
size=None,
20332037
title=None,
2034-
mstyle=None,
2035-
mlstyle=None,
2036-
filename=None,
20372038
):
20382039
"""Add a bivariate plot to the axes.
20392040
@@ -2096,6 +2097,7 @@ def scatterplot(
20962097
mstroke = toyplot.color.broadcast(colors=mfill, shape=series.shape)
20972098
mopacity = toyplot.broadcast.scalar(opacity, series.shape)
20982099
mtitle = toyplot.broadcast.pyobject(title, series.shape)
2100+
mhyperlink = toyplot.broadcast.pyobject(hyperlink, series.shape)
20992101
mstyle = toyplot.style.require(mstyle, allowed=toyplot.style.allowed.marker)
21002102
mlstyle = toyplot.style.require(mlstyle, allowed=toyplot.style.allowed.text)
21012103

@@ -2114,8 +2116,9 @@ def scatterplot(
21142116
mstroke_keys = []
21152117
mopacity_keys = []
21162118
mtitle_keys = []
2117-
for index, (series_column, marker_column, msize_column, mfill_column, mstroke_column, mopacity_column, mtitle_column) in enumerate(
2118-
zip(series.T, marker.T, msize.T, mfill.T, mstroke.T, mopacity.T, mtitle.T)):
2119+
mhyperlink_keys = []
2120+
for index, (series_column, marker_column, msize_column, mfill_column, mstroke_column, mopacity_column, mtitle_column, mhyperlink_column) in enumerate(
2121+
zip(series.T, marker.T, msize.T, mfill.T, mstroke.T, mopacity.T, mtitle.T, mhyperlink.T)):
21192122
coordinate_keys.append(coordinate_axes[0])
21202123
coordinate_keys.append(coordinate_axes[1] + str(index))
21212124
marker_keys.append("marker" + str(index))
@@ -2124,6 +2127,7 @@ def scatterplot(
21242127
mstroke_keys.append("stroke" + str(index))
21252128
mopacity_keys.append("opacity" + str(index))
21262129
mtitle_keys.append("title" + str(index))
2130+
mhyperlink_keys.append("hyperlink" + str(index))
21272131
table[coordinate_keys[-1]] = series_column
21282132
_mark_exportable(table, coordinate_keys[-1])
21292133
table[marker_keys[-1]] = marker_column
@@ -2132,6 +2136,7 @@ def scatterplot(
21322136
table[mstroke_keys[-1]] = mstroke_column
21332137
table[mopacity_keys[-1]] = mopacity_column
21342138
table[mtitle_keys[-1]] = mtitle_column
2139+
table[mhyperlink_keys[-1]] = mhyperlink_column
21352140

21362141
return self.add_mark(
21372142
toyplot.mark.Scatterplot(
@@ -2140,6 +2145,7 @@ def scatterplot(
21402145
filename=filename,
21412146
marker=marker_keys,
21422147
mfill=mfill_keys,
2148+
mhyperlink=mhyperlink_keys,
21432149
mlstyle=mlstyle,
21442150
mopacity=mopacity_keys,
21452151
msize=msize_keys,
@@ -2502,16 +2508,18 @@ def colormap(self, colormap, offset=None, width=10, style=None):
25022508
def scatterplot(
25032509
self,
25042510
coordinates,
2505-
offset=None,
2511+
area=None,
25062512
color=None,
2513+
filename=None,
2514+
hyperlink=None,
25072515
marker="o",
2508-
area=None,
2509-
size=None,
2516+
mlstyle=None,
2517+
mstyle=None,
2518+
offset=None,
25102519
opacity=1.0,
2520+
size=None,
25112521
title=None,
2512-
mstyle=None,
2513-
mlstyle=None,
2514-
filename=None):
2522+
):
25152523
"""Add a univariate plot to the axes.
25162524
25172525
Parameters
@@ -2554,6 +2562,7 @@ def scatterplot(
25542562
mstroke = toyplot.color.broadcast(colors=mfill, shape=coordinates.shape)
25552563
mopacity = toyplot.broadcast.scalar(opacity, coordinates.shape)
25562564
mtitle = toyplot.broadcast.pyobject(title, coordinates.shape)
2565+
mhyperlink = toyplot.broadcast.pyobject(hyperlink, coordinates.shape)
25572566
mstyle = toyplot.style.require(mstyle, allowed=toyplot.style.allowed.marker)
25582567
mlstyle = toyplot.style.require(mlstyle, allowed=toyplot.style.allowed.text)
25592568

@@ -2568,15 +2577,17 @@ def scatterplot(
25682577
mstroke_keys = []
25692578
mopacity_keys = []
25702579
mtitle_keys = []
2571-
for index, (coordinate_column, marker_column, msize_column, mfill_column, mstroke_column, mopacity_column, mtitle_column) in enumerate(
2572-
zip(coordinates.T, marker.T, msize.T, mfill.T, mstroke.T, mopacity.T, mtitle.T)):
2580+
mhyperlink_keys = []
2581+
for index, (coordinate_column, marker_column, msize_column, mfill_column, mstroke_column, mopacity_column, mtitle_column, mhyperlink_column) in enumerate(
2582+
zip(coordinates.T, marker.T, msize.T, mfill.T, mstroke.T, mopacity.T, mtitle.T, mhyperlink.T)):
25732583
coordinate_keys.append(coordinate_axes[0] + str(index))
25742584
marker_keys.append("marker" + str(index))
25752585
msize_keys.append("size" + str(index))
25762586
mfill_keys.append("fill" + str(index))
25772587
mstroke_keys.append("stroke" + str(index))
25782588
mopacity_keys.append("opacity" + str(index))
25792589
mtitle_keys.append("title" + str(index))
2590+
mhyperlink_keys.append("hyperlink" + str(index))
25802591
table[coordinate_keys[-1]] = coordinate_column
25812592
_mark_exportable(table, coordinate_keys[-1])
25822593
table[marker_keys[-1]] = marker_column
@@ -2585,6 +2596,7 @@ def scatterplot(
25852596
table[mstroke_keys[-1]] = mstroke_column
25862597
table[mopacity_keys[-1]] = mopacity_column
25872598
table[mtitle_keys[-1]] = mtitle_column
2599+
table[mhyperlink_keys[-1]] = mhyperlink_column
25882600

25892601
if offset is None:
25902602
offset = len(self._children) * self._spacing
@@ -2595,6 +2607,7 @@ def scatterplot(
25952607
filename=filename,
25962608
marker=marker_keys,
25972609
mfill=mfill_keys,
2610+
mhyperlink=mhyperlink_keys,
25982611
mlstyle=mlstyle,
25992612
mopacity=mopacity_keys,
26002613
msize=msize_keys,

toyplot/html.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1780,29 +1780,35 @@ def _render(numberline, mark, context):
17801780

17811781
dimension1 = numpy.ma.column_stack([mark._table[key] for key in mark._coordinates])
17821782
X = numberline.axis.projection(dimension1)
1783-
for x, marker, msize, mfill, mstroke, mopacity, mtitle in zip(
1783+
for x, marker, msize, mfill, mstroke, mopacity, mtitle, mhyperlink in zip(
17841784
X.T,
17851785
[mark._table[key] for key in mark._marker],
17861786
[mark._table[key] for key in mark._msize],
17871787
[mark._table[key] for key in mark._mfill],
17881788
[mark._table[key] for key in mark._mstroke],
17891789
[mark._table[key] for key in mark._mopacity],
17901790
[mark._table[key] for key in mark._mtitle],
1791+
[mark._table[key] for key in mark._mhyperlink],
17911792
):
17921793
not_null = numpy.invert(numpy.ma.getmaskarray(x))
17931794

1794-
series_xml = xml.SubElement(
1795-
mark_xml, "g", attrib={"class": "toyplot-Series"})
1796-
for dx, dmarker, dsize, dfill, dstroke, dopacity, dtitle in zip(
1795+
series_xml = xml.SubElement(mark_xml, "g", attrib={"class": "toyplot-Series"})
1796+
for dx, dmarker, dsize, dfill, dstroke, dopacity, dtitle, dhyperlink in zip(
17971797
x[not_null],
17981798
marker[not_null],
17991799
msize[not_null],
18001800
mfill[not_null],
18011801
mstroke[not_null],
18021802
mopacity[not_null],
18031803
mtitle[not_null],
1804+
mhyperlink[not_null],
18041805
):
18051806
if dmarker:
1807+
if dhyperlink:
1808+
datum_xml = xml.SubElement(series_xml, "a", attrib={"xlink:href": dhyperlink})
1809+
else:
1810+
datum_xml = series_xml
1811+
18061812
dstyle = toyplot.style.combine(
18071813
{
18081814
"fill": toyplot.color.to_css(dfill),
@@ -1811,7 +1817,7 @@ def _render(numberline, mark, context):
18111817
},
18121818
mark._mstyle)
18131819
_draw_marker(
1814-
series_xml,
1820+
datum_xml,
18151821
cx=dx,
18161822
cy=0,
18171823
marker=toyplot.marker.create(size=dsize, mstyle=dstyle, lstyle=mark._mlstyle) + toyplot.marker.convert(dmarker),
@@ -2866,7 +2872,7 @@ def _render(axes, mark, context):
28662872

28672873
_render_table(owner=mark, key="data", label="scatterplot", table=mark._table, filename=mark._filename, context=context)
28682874

2869-
for x, y, marker, msize, mfill, mstroke, mopacity, mtitle in zip(
2875+
for x, y, marker, msize, mfill, mstroke, mopacity, mtitle, mhyperlink in zip(
28702876
X.T,
28712877
Y.T,
28722878
[mark._table[key] for key in mark._marker],
@@ -2875,13 +2881,14 @@ def _render(axes, mark, context):
28752881
[mark._table[key] for key in mark._mstroke],
28762882
[mark._table[key] for key in mark._mopacity],
28772883
[mark._table[key] for key in mark._mtitle],
2884+
[mark._table[key] for key in mark._mhyperlink],
28782885
):
28792886
not_null = numpy.invert(numpy.logical_or(
28802887
numpy.ma.getmaskarray(x), numpy.ma.getmaskarray(y)))
28812888

28822889
series_xml = xml.SubElement(
28832890
mark_xml, "g", attrib={"class": "toyplot-Series"})
2884-
for dx, dy, dmarker, dsize, dfill, dstroke, dopacity, dtitle in zip(
2891+
for dx, dy, dmarker, dsize, dfill, dstroke, dopacity, dtitle, dhyperlink in zip(
28852892
x[not_null],
28862893
y[not_null],
28872894
marker[not_null],
@@ -2890,8 +2897,14 @@ def _render(axes, mark, context):
28902897
mstroke[not_null],
28912898
mopacity[not_null],
28922899
mtitle[not_null],
2900+
mhyperlink[not_null],
28932901
):
28942902
if dmarker:
2903+
if dhyperlink:
2904+
datum_xml = xml.SubElement(series_xml, "a", attrib={"xlink:href": dhyperlink})
2905+
else:
2906+
datum_xml = series_xml
2907+
28952908
dstyle = toyplot.style.combine(
28962909
{
28972910
"fill": toyplot.color.to_css(dfill),
@@ -2900,7 +2913,7 @@ def _render(axes, mark, context):
29002913
},
29012914
mark._mstyle)
29022915
_draw_marker(
2903-
series_xml,
2916+
datum_xml,
29042917
cx=dx,
29052918
cy=dy,
29062919
marker=toyplot.marker.create(size=dsize, mstyle=dstyle, lstyle=mark._mlstyle) + toyplot.marker.convert(dmarker),

toyplot/mark.py

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,7 @@ def __init__(
801801
filename,
802802
marker,
803803
mfill,
804+
mhyperlink,
804805
mlstyle,
805806
mopacity,
806807
msize,
@@ -811,34 +812,37 @@ def __init__(
811812
):
812813
Mark.__init__(self)
813814

814-
# D axis identifiers
815+
self._table = toyplot.require.instance(table, toyplot.data.Table)
816+
817+
# We require at last one coordinate dimension (number of dimensions = D)
815818
self._coordinate_axes = toyplot.require.string_vector(coordinate_axes, min_length=1)
816819
D = len(self._coordinate_axes)
817820

818-
self._table = toyplot.require.instance(table, toyplot.data.Table)
819-
820-
# D * N coordinate columns
821+
# We require D coordinate columns for each series (number of series = N)
821822
self._coordinates = toyplot.require.table_keys(table, coordinates, modulus=D)
822823
N = len(self._coordinates) / D
823824

824-
# N marker columns
825+
# We require 1 export filename
826+
self._filename = toyplot.require.filename(filename)
827+
# We require 1 marker style
828+
self._mstyle = toyplot.style.require(mstyle, allowed=toyplot.style.allowed.marker)
829+
# We require 1 marker label style
830+
self._mlstyle = toyplot.style.require(mlstyle, allowed=toyplot.style.allowed.text)
831+
832+
# We require N marker columns
825833
self._marker = toyplot.require.table_keys(table, marker, length=N)
826-
# N marker size columns
827-
self._msize = toyplot.require.table_keys(table, msize, length=N)
828-
# N marker fill color columns
834+
# We require N marker fill color columns
829835
self._mfill = toyplot.require.table_keys(table, mfill, length=N)
830-
# N marker stroke color columns
831-
self._mstroke = toyplot.require.table_keys(table, mstroke, length=N)
832-
# N marker opacity columns
836+
# We require N marker hyperlink columns
837+
self._mhyperlink = toyplot.require.table_keys(table, mhyperlink, length=N)
838+
# We require N marker opacity columns
833839
self._mopacity = toyplot.require.table_keys(table, mopacity, length=N)
834-
# N marker title columns
840+
# We require N marker size columns
841+
self._msize = toyplot.require.table_keys(table, msize, length=N)
842+
# We require N marker stroke color columns
843+
self._mstroke = toyplot.require.table_keys(table, mstroke, length=N)
844+
# We require N marker title columns
835845
self._mtitle = toyplot.require.table_keys(table, mtitle, length=N)
836-
# Marker style
837-
self._mstyle = toyplot.style.require(mstyle, allowed=toyplot.style.allowed.marker)
838-
# Marker label style
839-
self._mlstyle = toyplot.style.require(mlstyle, allowed=toyplot.style.allowed.text)
840-
# Export filename
841-
self._filename = toyplot.require.filename(filename)
842846

843847
def domain(self, axis):
844848
columns = [coordinate_column for coordinate_axis, coordinate_column in zip(itertools.cycle(self._coordinate_axes), self._coordinates) if coordinate_axis == axis]

0 commit comments

Comments
 (0)