This docs on this page refers to a PREVIOUS VERSION. For the latest stable release, go to https://docs.bokeh.org/

Archived docs for versions <= 1.0.4 have had to be modified from their original published configuration, and may be missing some features (e.g. source listing)

All users are encourage to update to version 1.1 or later, as soon as they are able.

bokeh.charts.builders.line_builder — Bokeh 0.12.5 documentation

Source code for bokeh.charts.builders.line_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 Line class which lets you build your Line 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

from six import iteritems
from itertools import chain
from ..builder import XYBuilder, create_and_build
from ..glyphs import LineGlyph, PointGlyph
from ..attributes import DashAttr, ColorAttr, MarkerAttr
from ..data_source import NumericalColumnsAssigner
from ...models.sources import ColumnDataSource
from ...core.properties import Bool, String, List
from ..operations import Stack, Dodge
from ..utils import add_tooltips_columns


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


[docs]def Line(data=None, x=None, y=None, **kws): """ Create a line chart using :class:`LineBuilder <bokeh.charts.builders.line_builder.LineBuilder>` to render the glyphs. The line chart is typically is used with column oriented data, where each column contains comparable measurements and the column names are treated as a categorical variable for differentiating the measurement values. One of the columns can be used as an index for either the x or y axis. .. note:: Only the x or y axis can display multiple variables, while the other is used as an index. Args: data (list(list), numpy.ndarray, pandas.DataFrame, list(pd.Series)): a 2d data source with columns of data for each line. 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 In addition to the parameters specific to this chart, :ref:`userguide_charts_defaults` are also accepted as keyword parameters. .. note:: This chart type differs on input types as compared to other charts, due to the way that line charts typically are plotting labeled series. For example, a column for AAPL stock prices over time. Another way this could be plotted is to have a DataFrame with a column of `stock_label` and columns of `price`, which is the stacked format. Both should be supported, but the former is the expected one. Internally, the latter format is being derived. Returns: :class:`Chart`: includes glyph renderers that generate the lines Examples: .. bokeh-plot:: :source-position: above import numpy as np from bokeh.charts import Line, output_file, show # (dict, OrderedDict, lists, arrays and DataFrames are valid inputs) xyvalues = np.array([[2, 3, 7, 5, 26], [12, 33, 47, 15, 126], [22, 43, 10, 25, 26]]) line = Line(xyvalues, title="line", legend="top_left", ylabel='Languages') output_file('line.html') show(line) """ kws['x'] = x kws['y'] = y return create_and_build(LineBuilder, data, **kws)
[docs]class LineBuilder(XYBuilder): """This is the Line class and it is in charge of plotting Line charts in an easy and intuitive way. Essentially, we provide a way to ingest the data, make the proper calculations and push the references into a source object. We additionally make calculations for the ranges. And finally add the needed lines taking the references from the source. """ series_names = List(String, help="""Names that represent the items being plotted.""") stack = Bool(default=False) default_attributes = {'color': ColorAttr(), 'dash': DashAttr(), 'marker': MarkerAttr()} dimensions = ['y', 'x'] column_selector = NumericalColumnsAssigner glyph = LineGlyph @property def measures(self): if isinstance(self.y.selection, list): return self.y.selection elif isinstance(self.x.selection, list): return self.x.selection else: return None @property def measure_input(self): return isinstance(self.y.selection, list) or isinstance(self.x.selection, list) @property def stack_flags(self): # Check if we stack measurements and by which attributes # This happens if we used the same series labels for dimensions as attributes return {k: self.attr_measurement(k) for k in list( self.attributes.keys())}
[docs] def get_id_cols(self, stack_flags): # collect the other columns used as identifiers, that aren't a measurement name id_cols = [self.attributes[attr].columns for attr, stack in iteritems(stack_flags) if not stack and self.attributes[attr].columns != self.measures and self.attributes[attr].columns is not None] return list(chain.from_iterable(id_cols))
[docs] def setup(self): """Handle input options that require transforming data and/or user selections.""" # handle special case of inputs as measures if self.measure_input: stack_flags = self.stack_flags id_cols = self.get_id_cols(stack_flags) # if we have measures input, we need to stack by something, set default if all(attr is False for attr in list(stack_flags.values())): stack_flags['color'] = True # stack the measurement dimension while keeping id columns self._stack_measures(ids=id_cols) # set the attributes to key off of the name of the stacked measurement source = ColumnDataSource(self._data.df) for attr_name, stack_flag in iteritems(stack_flags): if stack_flags[attr_name]: default_attr = self.attributes[attr_name] default_attr.setup(columns='series', data=source) # Handle when to use special column names if self.x.selection is None and self.y.selection is not None: self.x.selection = 'index' elif self.x.selection is not None and self.y.selection is None: self.y.selection = 'index'
[docs] def attr_measurement(self, attr_name): """Detect if the attribute has been given measurement columns.""" cols = self.attributes[attr_name].columns return (cols is not None and (cols == self.y.selection or cols == self.x.selection))
[docs] def set_series(self, col_name): series = self._data.df[col_name].drop_duplicates().tolist() series = [str(item) for item in series] self.series_names = series
def _stack_measures(self, ids, var_name='series'): """Stack data and keep the ids columns. Args: ids (list(str)): the column names that describe the measures """ if isinstance(self.y.selection, list): dim = 'y' if self.x.selection is not None: ids.append(self.x.selection) else: dim = 'x' if self.y.selection is not None: ids.append(self.y.selection) if len(ids) == 0: ids = None dim_prop = getattr(self, dim) # transform our data by stacking the measurements into one column self._data.stack_measures(measures=dim_prop.selection, ids=ids, var_name=var_name) # update our dimension with the updated data dim_prop.set_data(self._data) self.set_series('series')
[docs] def get_builder_attr(self): attrs = self.properties() return {attr: getattr(self, attr) for attr in attrs if attr in self.glyph.properties()}
[docs] def yield_renderers(self): build_attr = self.get_builder_attr() # get the list of builder attributes and only pass them on if glyph supports attrs = list(self.attributes.keys()) attrs = [attr for attr in attrs if attr in self.glyph.properties()] for group in self._data.groupby(**self.attributes): group_kwargs = self.get_group_kwargs(group, attrs) group_kwargs.update(build_attr) glyph = self.glyph(label=group.label, x=group.get_values(self.x.selection), y=group.get_values(self.y.selection), **group_kwargs) # dash=group['dash'] # save reference to composite glyph self.add_glyph(group, glyph) # yield each renderer produced by composite glyph for renderer in glyph.renderers: if self.tooltips: renderer = add_tooltips_columns(renderer, self.tooltips, group) yield renderer if self.stack: Stack().apply(self.comp_glyphs) Dodge().apply(self.comp_glyphs)
class PointSeriesBuilder(LineBuilder): glyph = PointGlyph