Plotly.js: support hexagon (binning, tessellation)

Created on 10 Apr 2017  路  2Comments  路  Source: plotly/plotly.js

I understand from this issue at the plotly.js R api, that there is currently no support for hexagons and hexagon binning.

I think that would be a fantastic addition.

community feature

Most helpful comment

@maxheld83 @jackparmer @etpinard

Long time ago, I've written my own implementation of the hexagon chart by using the Python plotly api. The binning itself is done by the matplotlib package.

Here is the code:

import plotly.offline as offline
import plotly.graph_objs as go
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize


def compute_hexbin(x, y, gridsize=100, bins=None, cmap=plt.cm.Blues):
    """Computes the hexagonal binning
    """
    collection = plt.hexbin(x, y, bins=bins, gridsize=gridsize)
    plt.close()

    pts_in_hexagon = collection.get_array()

    #compute colors for the svg shapes
    colors = ["#%02x%02x%02x" % (int(r), int(g), int(b)) for r, g, b, _ in 255*cmap(Normalize()(pts_in_hexagon))]

    # coordinates for single hexagonal patch
    hx = [0, .5, .5, 0, -.5, -.5]
    hy = [-.5/np.cos(np.pi/6), -.5*np.tan(np.pi/6), .5*np.tan(np.pi/6),
          .5/np.cos(np.pi/6), .5*np.tan(np.pi/6), -.5*np.tan(np.pi/6)]

    # number of hexagons needed
    m = len(collection.get_offsets())

    # scale of hexagons
    n = (x.max() - x.min()) / gridsize

    # y_scale to adjust for aspect ratio
    y_scale = (y.max() - y.min())/(x.max() - x.min())

    # coordinates for all hexagonal patches
    hxs = np.array([hx]*m)*n + np.vstack(collection.get_offsets()[:,0])
    hys = np.array([hy]*m)*n*y_scale + np.vstack(collection.get_offsets()[:,1])

    return hxs.tolist(), hys.tolist(), colors, pts_in_hexagon

N = 1000

random_x = np.random.randn(N)
random_y = np.random.randn(N)

x, y, color_list, pts_in_hexagon = compute_hexbin(random_x, random_y, gridsize=20)

shape_container = []
hover_point_x = []
hover_point_y = []

for x_list, y_list, color in zip(x, y, color_list):

    #Create the svg path based on the computed points

    svg_path = 'M {},{} L {},{} L {},{} L {},{} L{},{} L{},{}'\
        .format(x_list[0], y_list[0],
                x_list[1], y_list[1],
                x_list[2], y_list[2],
                x_list[3], y_list[3],
                x_list[4], y_list[4],
                x_list[4], y_list[1])

    #Create hover point from the hexagon, witch is the center of gravity
    hover_point_x.append(round((max(x_list) - min(x_list))/2+min(x_list), 2))
    hover_point_y.append(round((max(y_list) - min(y_list))/2+min(y_list), 2))

    shape_container.append({
          "fillcolor": color,
          "line": {
            "color": color,
            "width": 1.5
          },
          "path": svg_path,
          "type": "path"
        })

trace = go.Scattergl(x=hover_point_x,
                   y=hover_point_y,
                   mode='markers'
                   )

trace['marker']['colorbar'] = {"title": "Amount of points"}
trace['marker']['reversescale'] = True
trace['marker']['colorscale'] = 'Blues'
trace['marker']['color'] = pts_in_hexagon
trace['marker']['size'] = 0
trace['text'] = list(map(lambda z: 'Amount of points: {}'.format(int(z)), pts_in_hexagon))

layout = {'shapes':shape_container,
          'width': 850,
          'height': 700,
          'hovermode':'closest'}

fig = dict(data=[trace], layout=layout)

offline.plot(fig, show_link=False)

And the result is:

newplot

All 2 comments

@empet has made some real nice Python implementations of hex plots using shapes:

https://plot.ly/~empet/13706

image

@maxheld83 @jackparmer @etpinard

Long time ago, I've written my own implementation of the hexagon chart by using the Python plotly api. The binning itself is done by the matplotlib package.

Here is the code:

import plotly.offline as offline
import plotly.graph_objs as go
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize


def compute_hexbin(x, y, gridsize=100, bins=None, cmap=plt.cm.Blues):
    """Computes the hexagonal binning
    """
    collection = plt.hexbin(x, y, bins=bins, gridsize=gridsize)
    plt.close()

    pts_in_hexagon = collection.get_array()

    #compute colors for the svg shapes
    colors = ["#%02x%02x%02x" % (int(r), int(g), int(b)) for r, g, b, _ in 255*cmap(Normalize()(pts_in_hexagon))]

    # coordinates for single hexagonal patch
    hx = [0, .5, .5, 0, -.5, -.5]
    hy = [-.5/np.cos(np.pi/6), -.5*np.tan(np.pi/6), .5*np.tan(np.pi/6),
          .5/np.cos(np.pi/6), .5*np.tan(np.pi/6), -.5*np.tan(np.pi/6)]

    # number of hexagons needed
    m = len(collection.get_offsets())

    # scale of hexagons
    n = (x.max() - x.min()) / gridsize

    # y_scale to adjust for aspect ratio
    y_scale = (y.max() - y.min())/(x.max() - x.min())

    # coordinates for all hexagonal patches
    hxs = np.array([hx]*m)*n + np.vstack(collection.get_offsets()[:,0])
    hys = np.array([hy]*m)*n*y_scale + np.vstack(collection.get_offsets()[:,1])

    return hxs.tolist(), hys.tolist(), colors, pts_in_hexagon

N = 1000

random_x = np.random.randn(N)
random_y = np.random.randn(N)

x, y, color_list, pts_in_hexagon = compute_hexbin(random_x, random_y, gridsize=20)

shape_container = []
hover_point_x = []
hover_point_y = []

for x_list, y_list, color in zip(x, y, color_list):

    #Create the svg path based on the computed points

    svg_path = 'M {},{} L {},{} L {},{} L {},{} L{},{} L{},{}'\
        .format(x_list[0], y_list[0],
                x_list[1], y_list[1],
                x_list[2], y_list[2],
                x_list[3], y_list[3],
                x_list[4], y_list[4],
                x_list[4], y_list[1])

    #Create hover point from the hexagon, witch is the center of gravity
    hover_point_x.append(round((max(x_list) - min(x_list))/2+min(x_list), 2))
    hover_point_y.append(round((max(y_list) - min(y_list))/2+min(y_list), 2))

    shape_container.append({
          "fillcolor": color,
          "line": {
            "color": color,
            "width": 1.5
          },
          "path": svg_path,
          "type": "path"
        })

trace = go.Scattergl(x=hover_point_x,
                   y=hover_point_y,
                   mode='markers'
                   )

trace['marker']['colorbar'] = {"title": "Amount of points"}
trace['marker']['reversescale'] = True
trace['marker']['colorscale'] = 'Blues'
trace['marker']['color'] = pts_in_hexagon
trace['marker']['size'] = 0
trace['text'] = list(map(lambda z: 'Amount of points: {}'.format(int(z)), pts_in_hexagon))

layout = {'shapes':shape_container,
          'width': 850,
          'height': 700,
          'hovermode':'closest'}

fig = dict(data=[trace], layout=layout)

offline.plot(fig, show_link=False)

And the result is:

newplot

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bryaan picture bryaan  路  3Comments

deecay picture deecay  路  3Comments

pynklu picture pynklu  路  3Comments

WG- picture WG-  路  3Comments

maxwell8888 picture maxwell8888  路  3Comments