Source code for bokeh.charts.builders.heatmap_builder

"""This is the Bokeh charts interface. It gives you a high level API to build
complex plot is a simple way.

This is the HeatMap class which lets you build your HeatMap charts just passing
the arguments to the Chart class and calling the proper functions.
"""
#-----------------------------------------------------------------------------
# Copyright (c) 2012 - 2014, Anaconda, Inc. All rights reserved.
#
# Powered by the Bokeh Development Team.
#
# The full license is in the file LICENSE.txt, distributed with this software.
#-----------------------------------------------------------------------------

#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------
from __future__ import absolute_import, print_function, division

from ..builder import XYBuilder, create_and_build
from ..stats import Bins
from ..properties import Dimension
from ..operations import Aggregate
from ..attributes import ColorAttr
from ..glyphs import BinGlyph
from ..utils import build_agg_tooltip
from ...core.properties import Float, String

from bokeh.palettes import Blues6

#-----------------------------------------------------------------------------
# Classes and functions
#-----------------------------------------------------------------------------


[docs]def HeatMap(data, x=None, y=None, values=None, stat='count', xgrid=False, ygrid=False, hover_tool=True, hover_text=None, **kw): """ Represent 3 dimensions in a HeatMap chart using x, y, and values. Uses the :class:`~bokeh.charts.builders.heatmap_builder.HeatMapBuilder` to render the geometry from values. A HeatMap is a 3 Dimensional chart that crosses two dimensions, then aggregates values that correspond to the intersection of the horizontal and vertical dimensions. The value that falls at the intersection is then mapped to a color in a palette by default. All values that map to the positions on the chart are binned by the number of discrete colors in the palette. Args: data (:ref:`userguide_charts_data_types`): the data source for the chart x (str or list(str), optional): specifies variable(s) to use for x axis y (str or list(str), optional): specifies variable(s) to use for y axis values (str, optional): the values to use for producing the histogram using table-like input data stat (str, optional): the aggregation to use. Defaults to count. If provided `None`, then no aggregation will be attempted. This is useful for cases when the values have already been aggregated. hover_tool (bool, optional): whether to show the hover tool. Defaults to `True` hover_text (str, optional): a string to place beside the value in the hover tooltip. Defaults to `None`. When `None`, a hover_text will be derived from the aggregation and the values column. In addition to the parameters specific to this chart, :ref:`userguide_charts_defaults` are also accepted as keyword parameters. Returns: a new :class:`Chart <bokeh.charts.Chart>` Examples: .. bokeh-plot:: :source-position: above from bokeh.charts import HeatMap, output_file, show # (dict, OrderedDict, lists, arrays and DataFrames are valid inputs) data = {'fruit': ['apples']*3 + ['bananas']*3 + ['pears']*3, 'fruit_count': [4, 5, 8, 1, 2, 4, 6, 5, 4], 'sample': [1, 2, 3]*3} hm = HeatMap(data, x='fruit', y='sample', values='fruit_count', title='Fruits', stat=None) output_file('heatmap.html') show(hm) """ kw['x'] = x kw['y'] = y kw['values'] = values kw['stat'] = stat chart = create_and_build(HeatMapBuilder, data, xgrid=xgrid, ygrid=ygrid, **kw) if hover_tool: tooltip = build_agg_tooltip(hover_text=hover_text, aggregated_col=values, agg_text=stat) chart.add_tooltips([tooltip]) return chart
[docs]class HeatMapBuilder(XYBuilder): """Assists in producing glyphs required to represent values by a glyph attribute. Primary use case is to display the 3rd dimension of a value by binning and aggregating as needed and assigning the results to color. This color is represented on a glyph that is positioned by the x and y dimensions. """ values = Dimension('values') dimensions = ['x', 'y', 'values'] req_dimensions = [['x', 'y'], ['x', 'y', 'values']] """ The heatmap uses color to bin the values and assign discrete colors. The values are sorted descending and assigned to the palette by default. """ default_attributes = {'color': ColorAttr(bin=True, palette=Blues6, sort=True, ascending=False)} bin_width = Float(default=1.0, help=""" A derived property that is used to size the glyph in width. """) bin_height = Float(default=1.0, help=""" A derived property that is used to size the glyph in height. """) spacing_ratio = Float(default=0.95, help=""" Multiplied by the bin height and width to shrink or grow the relative size of the glyphs. The closer to 0 this becomes, the amount of space between the glyphs will increase. When above 1.0, the glyphs will begin to overlap. """) stat = String(default='sum', help=""" The stat to be applied to the values that fall into each x and y intersection. When stat is set to `None`, then no aggregation will occur. """)
[docs] def setup(self): # sort the legend by the color selection, reversed compared to how the values # were assigned to color if self.legend_sort_field is None: self.legend_sort_field = 'color' self.legend_sort_direction = "ascending" # find any bin operations applied to get the bin width and height for op in self._data.operations: if isinstance(op, Bins): if op.centers_column == self.x.selection: if 'bin_width' not in self.properties_with_values(include_defaults=False): self.bin_width = op.bin_width else: if 'bin_height' not in self.properties_with_values(include_defaults=False): self.bin_height = op.bin_width
[docs] def process_data(self): """Perform aggregation and binning as requried.""" # if we have a stat to apply, aggregate on it, and use resulting column if self.stat is not None: agg = Aggregate(dimensions=[self.x.selection, self.y.selection], columns=self.values.selection, stat=self.stat) self._data._data = agg.apply(self._data._data) self.values.selection = agg.agg_column # color by the values selection self.attributes['color'].setup(data=self._data.source, columns=self.values.selection) self.attributes['color'].add_bin_labels(self._data)
[docs] def yield_renderers(self): """Generate a set fo bins for each group of data.""" for group in self._data.groupby(**self.attributes): binned_values = self.values.selection + '_values' comp_glyph = BinGlyph(x=group.get_values(self.x.selection), y=group.get_values(self.y.selection), values=group.get_values(binned_values), width=self.bin_width * self.spacing_ratio, height=self.bin_height * self.spacing_ratio, line_color=group['color'], fill_color=group['color'], label=group.label ) self.add_glyph(group, comp_glyph) # yield the renderers from the comp glyph for renderer in comp_glyph.renderers: yield renderer