Esempio n. 1
0
class MyModel(VBoxModelForm):
    """Input Widgets, define the fields you want to
    read from the input here as bokeh properties
    input_specs is a list of dictionary, specifying
    how the kind of input widget you want for each
    property.  the name field must match
    one of the properties, for example here,
    we use names of offset and scale.  You can
    also specify title, if you want a different
    label in the generated form
    """
    offset = Float(1.0)
    scale = Float(1.0)
    title = String(default="my sin wave")
    input_specs = [
        {
            "widget": TextInput,
            "name": "title",
            "value": "my sin wave"
        },
        {
            "widget": Slider,
            "name": "offset",
            "value": 1.0,
            "start": 0.0,
            "end": 5.0
        },
        {
            "widget": Slider,
            "name": "scale",
            "value": 1.0,
            "start": -5.0,
            "end": 5.0
        },
    ]
Esempio n. 2
0
class XyGlyph(CompositeGlyph):
    """Composite glyph that plots in cartesian coordinates."""
    x = EitherColumn(String, Column(Float), Column(String), Column(Datetime),
                     Column(Bool))
    y = EitherColumn(String, Column(Float), Column(String), Column(Datetime),
                     Column(Bool))
    line_color = String(default=DEFAULT_PALETTE[0])
    line_alpha = Float(default=1.0)

    def build_source(self):
        if self.x is None:
            x = [self.label] * len(self.y)
            data = dict(x_values=x, y_values=self.y)
        elif self.y is None:
            y = [self.label] * len(self.x)
            data = dict(x_values=self.x, y_values=y)
        else:
            data = dict(x_values=self.x, y_values=self.y)
        return ColumnDataSource(data)

    @property
    def x_max(self):
        return max(self.source._data['x_values'])

    @property
    def x_min(self):
        return min(self.source._data['x_values'])

    @property
    def y_max(self):
        return max(self.source._data['y_values'])

    @property
    def y_min(self):
        return min(self.source._data['y_values'])
Esempio n. 3
0
class DatetimeAxis(LinearAxis):
    type = String("datetime_axis")
    axis_label = String("date")
    scale = String("time")
    num_labels = Int(8)
    char_width = Int(10)
    fill_ratio = Float(0.3)
    formats = Dict({"days": ["%m/%d/%Y"]})
Esempio n. 4
0
class Quantile(Stat):
    """Produces the cutpoint that divides the input data by the interval.

    Quartiles are a special case of quartiles that divide a dataset into four
    equal-size groups. (https://en.wikipedia.org/wiki/Quantile)
    """
    interval = Float(default=0.5)

    def calculate(self):
        self.value = self.get_data().quantile(self.interval)
Esempio n. 5
0
    def test_Float(self):
        prop = Float()

        self.assertTrue(prop.is_valid(None))
        # TODO: self.assertFalse(prop.is_valid(False))
        # TODO: self.assertFalse(prop.is_valid(True))
        self.assertTrue(prop.is_valid(0))
        self.assertTrue(prop.is_valid(1))
        self.assertTrue(prop.is_valid(0.0))
        self.assertTrue(prop.is_valid(1.0))
        self.assertFalse(prop.is_valid(1.0 + 1.0j))
        self.assertFalse(prop.is_valid(""))
        self.assertFalse(prop.is_valid(()))
        self.assertFalse(prop.is_valid([]))
        self.assertFalse(prop.is_valid({}))
        self.assertFalse(prop.is_valid(Foo()))
Esempio n. 6
0
class Stat(HasProps):

    column = ColumnLabel()
    source = ColumnDataSource()

    values = EitherColumn(Column(Float),
                          Column(Int),
                          Column(String),
                          Column(Date),
                          Column(Datetime),
                          Column(Bool),
                          default=None)
    value = Float()

    def __init__(self, **properties):
        super(Stat, self).__init__(**properties)
        self._refresh()

    def _refresh(self):
        """Lazy update of properties, used for initial transform init."""
        if self.get_data() is not None:
            self.update()
            self.calculate()

    def set_data(self, data, column=None):
        """Set data properties and update all dependent properties."""
        if isinstance(data, pd.DataFrame):
            self.source = ColumnDataSource(data)
            if column is None:
                raise ValueError(
                    'When providing a table of data, you must also provide a column label'
                )
            else:
                self.column = column
        else:
            self.values = data
        self.update()
        self.calculate()

    def get_data(self):
        """Returns the available columnlabel/source values or column values."""
        if self.source is not None and self.column is not None:
            return self.source._data[self.column]
        elif self.values is not None:
            return self.values
        else:
            return None

    def calculate(self):
        """Return transformed value from column label/source or column-like data."""
        raise NotImplementedError(
            'You must implement the calculate method for each stat type.')

    def update(self):
        pass
Esempio n. 7
0
class Bin(Stat):
    """Represents a single bin of data values and attributes of the bin."""
    label = String()
    start = Float()
    stop = Float()

    start_label = String()
    stop_label = String()

    center = Float()

    stat = Instance(Stat, default=Count())

    def __init__(self, bin_label, values, **properties):
        properties['label'] = bin_label
        properties['start'], properties['stop'] = self.binstr_to_list(
            bin_label)
        properties['center'] = (properties['start'] + properties['stop']) / 2.0
        properties['values'] = values
        super(Bin, self).__init__(**properties)

    @staticmethod
    def binstr_to_list(bins):
        """Produce a consistent display of a bin of data."""
        value_chunks = bins.split(',')
        value_chunks = [
            val.replace('[', '').replace(']', '').replace('(',
                                                          '').replace(')', '')
            for val in value_chunks
        ]
        bin_values = [float(value) for value in value_chunks]
        return bin_values[0], bin_values[1]

    def update(self):
        self.stat.set_data(self.values)

    def calculate(self):
        self.value = self.stat.value
Esempio n. 8
0
class PointGlyph(XyGlyph):
    """A set of glyphs placed in x,y coordinates with the same attributes."""

    fill_color = Color(default=DEFAULT_PALETTE[1])
    fill_alpha = Float(default=0.7)
    marker = String(default='circle')
    size = Float(default=8)

    def __init__(self,
                 x=None,
                 y=None,
                 line_color=None,
                 fill_color=None,
                 marker=None,
                 size=None,
                 **kwargs):
        kwargs['x'] = x
        kwargs['y'] = y
        kwargs['line_color'] = line_color or self.line_color
        kwargs['fill_color'] = fill_color or self.fill_color
        kwargs['marker'] = marker or self.marker
        kwargs['size'] = size or self.size
        super(PointGlyph, self).__init__(**kwargs)
        self.setup()

    def get_glyph(self):
        return marker_types[self.marker]

    def build_renderers(self):
        glyph_type = self.get_glyph()
        glyph = glyph_type(x='x_values',
                           y='y_values',
                           line_color=self.line_color,
                           fill_color=self.fill_color,
                           size=self.size,
                           fill_alpha=self.fill_alpha,
                           line_alpha=self.line_alpha)
        yield GlyphRenderer(glyph=glyph)
Esempio n. 9
0
    def test_Float(self):
        prop = Float()

        self.assertTrue(prop.is_valid(None))
        # TODO: self.assertFalse(prop.is_valid(False))
        # TODO: self.assertFalse(prop.is_valid(True))
        self.assertTrue(prop.is_valid(0))
        self.assertTrue(prop.is_valid(1))
        self.assertTrue(prop.is_valid(0.0))
        self.assertTrue(prop.is_valid(1.0))
        self.assertFalse(prop.is_valid(1.0+1.0j))
        self.assertFalse(prop.is_valid(""))
        self.assertFalse(prop.is_valid(()))
        self.assertFalse(prop.is_valid([]))
        self.assertFalse(prop.is_valid({}))
        self.assertFalse(prop.is_valid(Foo()))
Esempio n. 10
0
class Bins(Stat):
    """A set of many individual Bin stats.

    Bin counts using: https://en.wikipedia.org/wiki/Freedman%E2%80%93Diaconis_rule
    """
    bin_count = Either(Int, Float)
    bin_width = Float(default=None, help='Use Freedman-Diaconis rule if None.')
    bins = List(Instance(Bin))
    q1 = Quantile(interval=0.25)
    q3 = Quantile(interval=0.75)
    labels = List(String)

    def __init__(self, values=None, column=None, bins=None, **properties):
        properties['values'] = values
        properties['column'] = column
        properties['bins'] = bins
        super(Bins, self).__init__(**properties)

    def update(self):
        values = self.get_data()
        self.q1.set_data(values)
        self.q3.set_data(values)
        if self.bin_count is None:
            self.calc_num_bins(values)

    def calculate(self):
        binned, bin_edges = pd.cut(self.get_data(),
                                   self.bin_count,
                                   retbins=True,
                                   precision=0)

        df = pd.DataFrame(dict(values=self.get_data(), bins=binned))
        bins = []
        for name, group in df.groupby('bins'):
            bins.append(Bin(bin_label=name, values=group['values']))
        self.bins = bins

    def calc_num_bins(self, values):
        iqr = self.q3.value - self.q1.value
        self.bin_width = 2 * iqr * (len(values)**-(1. / 3.))
        self.bin_count = np.ceil(
            (self.values.max() - self.values.min()) / self.bin_width)
Esempio n. 11
0
class AggregateGlyph(NestedCompositeGlyph):
    """A base composite glyph for aggregating an array.

    Implements default stacking and dodging behavior that other composite
    glyphs can inherit.
    """

    stack_label = String()
    stack_shift = Float(default=0.0)

    dodge_label = String(
        help="""Where on the scale the glyph should be placed.""")
    dodge_shift = Float(default=None)

    agg = Instance(Stat, default=Sum())

    span = Float(help="""The range of values represented by the aggregate.""")

    def get_dodge_label(self, shift=0.0):
        """Generate the label defining an offset in relation to a position on a scale."""
        if self.dodge_shift is None:
            shift_str = ':' + str(0.5 + shift)
        elif self.dodge_shift is not None:
            shift_str = ':' + str(self.dodge_shift + shift)
        else:
            shift_str = ''
        return str(self.label) + shift_str

    def filter_glyphs(self, glyphs):
        """Return only the glyphs that are of the same class."""
        return [glyph for glyph in glyphs if isinstance(glyph, self.__class__)]

    @staticmethod
    def groupby(glyphs, prop):
        """Returns a dict of `CompositeGlyph`s, grouped by unique values of prop.

        For example, if all glyphs had a value of 'a' or 'b' for glyph.prop, the dict
        would contain two keys, 'a' and 'b', where each value is a list of the glyphs
        that had each of the values.
        """
        grouped = defaultdict(list)
        [grouped[getattr(glyph, prop)].append(glyph) for glyph in glyphs]
        return grouped

    def __stack__(self, glyphs):
        """Apply relative shifts to the composite glyphs for stacking."""
        if self.stack_label is not None:
            filtered_glyphs = self.filter_glyphs(glyphs)
            grouped = self.groupby(filtered_glyphs, 'label')

            for index, group in iteritems(grouped):

                # separate the negative and positive aggregates into separate groups
                neg_group = [glyph for glyph in group if glyph.span < 0]
                pos_group = [glyph for glyph in group if glyph.span >= 0]

                # apply stacking to each group separately
                for group in [neg_group, pos_group]:
                    shift = []
                    for i, glyph in enumerate(group):
                        # save off the top of each rect's height
                        shift.append(glyph.span)
                        if i > 0:
                            glyph.stack_shift = sum(shift[0:i])
                            glyph.refresh()

    def __dodge__(self, glyphs):
        """Apply relative shifts to the composite glyphs for dodging."""
        if self.dodge_label is not None:
            filtered_glyphs = self.filter_glyphs(glyphs)
            grouped = self.groupby(filtered_glyphs, 'dodge_label')

            # calculate transformations
            step = np.linspace(0, 1.0, len(grouped.keys()) + 1, endpoint=False)
            width = min(0.2, (1. / len(grouped.keys()))**1.1)

            # set bar attributes and re-aggregate
            for i, (index, group) in enumerate(iteritems(grouped)):
                for glyph in group:
                    glyph.dodge_shift = step[i + 1]
                    glyph.width = width
                    glyph.refresh()
Esempio n. 12
0
class Quantile(Stat):
    interval = Float(default=0.5)

    def calculate(self):
        self.value = self.get_data().quantile(self.interval)
Esempio n. 13
0
class DataRange1d(DataRange):
    """ Represents a range in a scalar dimension """
    sources = List(ColumnsRef, has_ref=True)
    rangepadding = Float(0.1)
    start = Float
    end = Float
Esempio n. 14
0
class Range1d(PlotObject):
    start = Float()
    end = Float()
Esempio n. 15
0
class HistogramGlyph(AggregateGlyph):
    """Depicts the distribution of values using rectangles created by binning.

    The histogram represents a distribution, so will likely include other
    options for displaying it, such as KDE and cumulative density.
    """

    bins = Instance(Bins)
    bin_count = Float()

    bars = List(Instance(BarGlyph))

    centers = List(Float)

    bin_width = Float()

    def __init__(self,
                 values,
                 label=None,
                 color=None,
                 bin_count=None,
                 **kwargs):
        if label is not None:
            kwargs['label'] = label
        kwargs['values'] = values
        kwargs['bin_count'] = bin_count
        kwargs['color'] = color or self.color

        # remove width, since this is handled automatically
        kwargs.pop('width', None)

        super(HistogramGlyph, self).__init__(**kwargs)

    def _set_sources(self):
        pass

    def build_source(self):
        pass

    def build_renderers(self):
        self.bins = Bins(values=self.values, bin_count=self.bin_count)
        self.centers = [bin.center for bin in self.bins.bins]
        self.bin_width = self.centers[1] - self.centers[0]

        bars = []
        for bin in self.bins.bins:
            bars.append(
                BarGlyph(label=bin.center,
                         values=bin.values,
                         color=self.color,
                         fill_alpha=self.fill_alpha,
                         agg=bin.stat,
                         width=self.bin_width))

        self.bars = bars
        self.children = self.bars

        for comp_glyph in self.bars:
            for renderer in comp_glyph.renderers:
                yield renderer

    @property
    def y_min(self):
        return 0.0
Esempio n. 16
0
class Interval(AggregateGlyph):
    """A rectangle representing aggregated values.

    The interval is a rect glyph where two of the parallel sides represent a
    summary of values. Each of the two sides is derived from a separate aggregation of
    the values provided to the interval.

    .. note::
        A bar is a special case interval where one side is pinned and used to
        communicate a value relative to it.
    """

    width = Float(default=0.8)
    start_agg = Instance(Stat,
                         default=Min(),
                         help="""The stat used to derive the
        starting point of the composite glyph.""")
    end_agg = Instance(Stat,
                       default=Max(),
                       help="""The stat used to derive the end
        point of the composite glyph.""")

    start = Float(default=0.0)
    end = Float()

    label_value = Either(String, Float, Datetime, Bool, default=None)

    def __init__(self, label, values, **kwargs):
        if not isinstance(label, str):
            label_value = label
            label = str(label)
        else:
            label_value = None

        kwargs['label'] = label
        kwargs['label_value'] = label_value
        kwargs['values'] = values

        super(Interval, self).__init__(**kwargs)
        self.setup()

    def get_start(self):
        """Get the value for the start of the glyph."""
        self.start_agg.set_data(self.values)
        return self.start_agg.value

    def get_end(self):
        """Get the value for the end of the glyph."""
        self.end_agg.set_data(self.values)
        return self.end_agg.value

    def get_span(self):
        """The total range between the start and end."""
        return self.end - self.start

    def build_source(self):
        # ToDo: Handle rotation
        self.start = self.get_start()
        self.end = self.get_end()
        self.span = self.get_span()

        width = [self.width]
        if self.dodge_shift is not None:
            x = [self.get_dodge_label()]
        else:
            x = [self.label_value or self.label]
        height = [self.span]
        y = [self.stack_shift + (self.span / 2.0) + self.start]
        color = [self.color]
        fill_alpha = [self.fill_alpha]
        line_color = [self.line_color]
        return ColumnDataSource(
            dict(x=x,
                 y=y,
                 width=width,
                 height=height,
                 color=color,
                 fill_alpha=fill_alpha,
                 line_color=line_color))

    @property
    def x_max(self):
        """The maximum extent of the glyph in x.

        .. note::
            Dodging the glyph can affect the value.
        """
        return (self.dodge_shift or self.label_value) + (self.width / 2.0)

    @property
    def x_min(self):
        """The maximum extent of the glyph in y.

        .. note::
            Dodging the glyph can affect the value.
        """
        return (self.dodge_shift or self.label_value) - (self.width / 2.0)

    @property
    def y_max(self):
        """Maximum extent of all `Glyph`s.

        How much we are stacking + the height of the interval + the base of the interval

        .. note::
            the start and end of the glyph can swap between being associated with the
            min and max when the glyph end represents a negative value.
        """
        return max(self.bottom, self.top)

    @property
    def y_min(self):
        """The minimum extent of all `Glyph`s in y.

        .. note::
            the start and end of the glyph can swap between being associated with the
            min and max when the glyph end represents a negative value.
        """
        return min(self.bottom, self.top)

    @property
    def bottom(self):
        """The value associated with the start of the stacked glyph."""
        return self.stack_shift + self.start

    @property
    def top(self):
        """The value associated with the end of the stacked glyph."""
        return self.stack_shift + self.span + self.start

    def build_renderers(self):
        """Yields a `GlyphRenderer` associated with a `Rect` glyph."""
        glyph = Rect(x='x',
                     y='y',
                     width='width',
                     height='height',
                     fill_color='color',
                     fill_alpha='fill_alpha',
                     line_color='line_color')
        yield GlyphRenderer(glyph=glyph)
Esempio n. 17
0
class BoxGlyph(AggregateGlyph):
    """Summarizes the distribution with a collection of glyphs.

    A box glyph produces one "box" for a given array of vales. The box
    is made up of multiple other child composite glyphs (intervals,
    scatter) and directly produces glyph renderers for the whiskers,
    as well.
    """

    q1 = Float()
    q2 = Float()
    q3 = Float()
    iqr = Float()

    w0 = Float(help='Lower whisker')
    w1 = Float(help='Upper whisker')

    q2_glyph = Instance(QuartileGlyph)
    q3_glyph = Instance(QuartileGlyph)

    whisker_glyph = Instance(GlyphRenderer)

    outliers = Either(Bool, Instance(PointGlyph))

    marker = String(default='circle')
    whisker_width = Float(default=0.3)
    whisker_line_width = Float(default=2)
    whisker_span_line_width = Float(default=2)
    whisker_color = String(default='black')

    outlier_fill_color = String(default='red')
    outlier_line_color = String(default='red')
    outlier_size = Float(default=5)

    bar_color = String(default='DimGrey')

    def __init__(self, label, values, outliers=True, **kwargs):
        width = kwargs.pop('width', None)

        bar_color = kwargs.pop('color', None) or self.bar_color

        kwargs['outliers'] = kwargs.pop('outliers', None) or outliers
        kwargs['label'] = label
        kwargs['values'] = values
        kwargs['q2_glyph'] = QuartileGlyph(label=label,
                                           values=values,
                                           interval1=0.25,
                                           interval2=0.5,
                                           width=width,
                                           color=bar_color)
        kwargs['q3_glyph'] = QuartileGlyph(label=label,
                                           values=values,
                                           interval1=0.5,
                                           interval2=0.75,
                                           width=width,
                                           color=bar_color)
        super(BoxGlyph, self).__init__(**kwargs)

    def build_renderers(self):

        self.calc_quartiles()
        outlier_values = self.values[((self.values < self.w0) |
                                      (self.values > self.w1))]

        self.whisker_glyph = GlyphRenderer(
            glyph=Segment(x0='x0s',
                          y0='y0s',
                          x1='x1s',
                          y1='y1s',
                          line_width=self.whisker_line_width,
                          line_color=self.whisker_color))

        if len(outlier_values) > 0 and self.outliers:
            self.outliers = PointGlyph(y=outlier_values,
                                       label=self.get_dodge_label(),
                                       line_color=self.outlier_line_color,
                                       fill_color=self.outlier_fill_color,
                                       size=self.outlier_size,
                                       marker=self.marker)

        for comp_glyph in self.composite_glyphs:
            for renderer in comp_glyph.renderers:
                yield renderer

        yield self.whisker_glyph

    def calc_quartiles(self):
        self.q1 = self.q2_glyph.start
        self.q2 = self.q2_glyph.end
        self.q3 = self.q3_glyph.end
        self.iqr = self.q3 - self.q1
        self.w0 = self.q1 - (1.5 * self.iqr)
        self.w1 = self.q3 + (1.5 * self.iqr)

    def build_source(self):
        self.calc_quartiles()
        x_label = self.get_dodge_label()
        x_w0_label = self.get_dodge_label(shift=(self.whisker_width / 2.0))
        x_w1_label = self.get_dodge_label(shift=-(self.whisker_width / 2.0))

        # span0, whisker bar0, span1, whisker bar1
        x0s = [x_label, x_w0_label, x_label, x_w0_label]
        y0s = [self.w0, self.w0, self.q3, self.w1]
        x1s = [x_label, x_w1_label, x_label, x_w1_label]
        y1s = [self.q1, self.w0, self.w1, self.w1]

        return ColumnDataSource(dict(x0s=x0s, y0s=y0s, x1s=x1s, y1s=y1s))

    def _set_sources(self):
        self.whisker_glyph.data_source = self.source

    def get_extent(self, func, prop_name):
        return func([
            getattr(renderer, prop_name) for renderer in self.composite_glyphs
        ])

    @property
    def composite_glyphs(self):
        comp_glyphs = [self.q2_glyph, self.q3_glyph]
        if isinstance(self.outliers, PointGlyph):
            comp_glyphs.append(self.outliers)
        return comp_glyphs

    @property
    def x_max(self):
        return self.get_extent(max, 'x_max') + self.right_buffer

    @property
    def x_min(self):
        return self.get_extent(min, 'x_min') - self.left_buffer

    @property
    def y_max(self):
        return max(self.w1, self.get_extent(max, 'y_max')) + self.top_buffer

    @property
    def y_min(self):
        return min(self.w0, self.get_extent(min, 'y_min')) - self.bottom_buffer
Esempio n. 18
0
class Interval(AggregateGlyph):
    """A rectangle representing aggregated values.

    The interval is a rect glyph where two of the parallel
    sides represent a summary of values. Each of the two sides
    is derived from a separate aggregation of the values
    provided to the interval.

    Note: A bar is a special case interval where one side
    is pinned and used to communicate a value relative to
    it.
    """

    width = Float(default=0.8)
    start_agg = Instance(Stat, default=Min())
    end_agg = Instance(Stat, default=Max())

    start = Float(default=0.0)
    end = Float()

    label_value = Either(String, Float, Datetime, Bool, default=None)

    def __init__(self, label, values, **kwargs):
        if not isinstance(label, str):
            label_value = label
            label = str(label)
        else:
            label_value = None

        kwargs['label'] = label
        kwargs['label_value'] = label_value
        kwargs['values'] = values

        super(Interval, self).__init__(**kwargs)

    def get_start(self):
        self.start_agg.set_data(self.values)
        return self.start_agg.value

    def get_end(self):
        self.end_agg.set_data(self.values)
        return self.end_agg.value

    def get_span(self):
        return self.end - self.start

    def build_source(self):
        # ToDo: Handle rotation
        self.start = self.get_start()
        self.end = self.get_end()
        self.span = self.get_span()

        width = [self.width]
        if self.dodge_shift is not None:
            x = [self.get_dodge_label()]
        else:
            x = [self.label_value or self.label]
        height = [self.span]
        y = [self.stack_shift + (self.span / 2.0) + self.start]
        color = [self.color]
        fill_alpha = [self.fill_alpha]
        return ColumnDataSource(
            dict(x=x,
                 y=y,
                 width=width,
                 height=height,
                 color=color,
                 fill_alpha=fill_alpha))

    @property
    def x_max(self):
        return (self.dodge_shift or self.label_value) + (self.width / 2.0)

    @property
    def x_min(self):
        return (self.dodge_shift or self.label_value) - (self.width / 2.0)

    @property
    def y_max(self):
        return self.stack_shift + self.span + self.start

    @property
    def y_min(self):
        return self.stack_shift + self.start

    def build_renderers(self):
        glyph = Rect(x='x',
                     y='y',
                     width='width',
                     height='height',
                     fill_color='color',
                     fill_alpha='fill_alpha')
        yield GlyphRenderer(glyph=glyph)
Esempio n. 19
0
class HistogramGlyph(AggregateGlyph):
    """Depicts the distribution of values using rectangles created by binning.

    The histogram represents a distribution, so will likely include other
    options for displaying it, such as KDE and cumulative density.
    """

    # input properties
    bin_width = Float()
    bin_count = Float(
        help="""Provide a manually specified number of bins to use.""")

    # derived models
    bins = Instance(Bins,
                    help="""A stat used to calculate the bins. The bins stat
        includes attributes about each composite bin.""")
    bars = List(Instance(BarGlyph),
                help="""The histogram is comprised of many
        BarGlyphs that are derived from the values.""")

    def __init__(self,
                 values,
                 label=None,
                 color=None,
                 bin_count=None,
                 **kwargs):
        if label is not None:
            kwargs['label'] = label
        kwargs['values'] = values
        kwargs['bin_count'] = bin_count
        if color is not None:
            kwargs['color'] = color

        # remove width, since this is handled automatically
        kwargs.pop('width', None)

        super(HistogramGlyph, self).__init__(**kwargs)
        self.setup()

    def _set_sources(self):
        # No need to set sources, since composite glyphs handle this
        pass

    def build_source(self):
        # No need to build source, since composite glyphs handle this
        pass

    def build_renderers(self):
        """Yield a bar glyph for each bin."""
        self.bins = Bins(values=self.values, bin_count=self.bin_count)
        centers = [bin.center for bin in self.bins.bins]
        self.bin_width = centers[1] - centers[0]

        bars = []
        for bin in self.bins.bins:
            bars.append(
                BarGlyph(label=bin.center,
                         values=bin.values,
                         color=self.color,
                         fill_alpha=self.fill_alpha,
                         agg=bin.stat,
                         width=self.bin_width))

        # provide access to bars as children for bounds properties
        self.bars = bars
        self.children = self.bars

        for comp_glyph in self.bars:
            for renderer in comp_glyph.renderers:
                yield renderer

    @property
    def y_min(self):
        return 0.0
Esempio n. 20
0
class CompositeGlyph(HasProps):
    """Represents a subset of data.

    A collection of hetero or homogeneous glyph
    renderers which represent a subset of data. The
    purpose of the composite glyph is to abstract
    away the details of constructing glyphs, based on
    the details of a subset of data, from the grouping
    operations that a generalized builders must implement.

    In general, the Builder operates at the full column
    oriented data source level, segmenting and assigning
    attributes from a large selection, while the composite glyphs
    will typically be passed an array-like structures with
    one or more singlular attributes to apply.

    Another way to explain the concept is that the Builder
    operates as the groupby, as in pandas, while the
    CompositeGlyph operates as the function used in the apply.

    What is the responsibility of the Composite Glyph?
        - Produce GlyphRenderers
        - Apply any aggregations
        - Tag the GlyphRenderers with the group label
        - Apply transforms due to chart operations
            - Note: Operations require implementation of special methods
    """

    # composite glyph inputs
    label = String('None', help='Identifies the subset of data.')
    values = Either(Column(Float),
                    Column(String),
                    help="""Array-like values,
        which are used as the input to the composite glyph.""")

    # derived from inputs
    source = Instance(ColumnDataSource,
                      help="""The data source used for the contained
        glyph renderers. Simple glyphs part of the composite glyph might not use the
        column data source.""")
    renderers = List(Instance(GlyphRenderer))

    operations = List(Any,
                      help="""A list of chart operations that can be applied to
        manipulate their visual depiction.""")

    color = Color(default='gray',
                  help="""A high level color. Some glyphs will
        implement more specific color attributes for parts or specific glyphs."""
                  )
    line_color = Color(default='black',
                       help="""A default outline color for contained
        glyphs.""")
    fill_alpha = Float(default=0.8)

    left_buffer = Float(default=0.0)
    right_buffer = Float(default=0.0)
    top_buffer = Float(default=0.0)
    bottom_buffer = Float(default=0.0)

    def __init__(self, **kwargs):
        label = kwargs.pop('label', None)

        if label is not None:
            if not isinstance(label, str):
                label = str(label)
            kwargs['label'] = label

        super(CompositeGlyph, self).__init__(**kwargs)

    def setup(self):
        """Build renderers and data source and set sources on renderers."""
        self.renderers = [renderer for renderer in self.build_renderers()]
        if self.renderers is not None:
            self.refresh()

    def refresh(self):
        """Update the GlyphRenderers.

        .. note:
            this method would be called after data is added.
        """
        if self.renderers is not None:
            self.source = self.build_source()
            self._set_sources()

    def build_renderers(self):
        raise NotImplementedError('You must return list of renderers.')

    def build_source(self):
        raise NotImplementedError('You must return ColumnDataSource.')

    def _set_sources(self):
        """Store reference to source in each GlyphRenderer.

        .. note::
            if the glyphs that are part of the composite glyph differ, you may have to
            override this method and handle the sources manually.
        """
        for renderer in self.renderers:
            renderer.data_source = self.source

    def __stack__(self, glyphs):
        """A special method the `stack` function applies to composite glyphs."""
        pass

    def __jitter__(self, glyphs):
        """A special method the `jitter` function applies to composite glyphs."""
        pass

    def __dodge__(self, glyphs):
        """A special method the `dodge` function applies to composite glyphs."""
        pass

    def __overlay__(self, glyphs):
        """A special method the `overlay` function applies to composite glyphs."""
        pass

    def apply_operations(self):
        pass
Esempio n. 21
0
 class Base(HasProps):
     num = NumberSpec(12)
     not_a_dataspec = Float(10)
Esempio n. 22
0
    def test_Float(self):
        prop = Float()

        self.assertTrue(prop.is_valid(None))
        # TODO: self.assertFalse(prop.is_valid(False))
        # TODO: self.assertFalse(prop.is_valid(True))
        self.assertTrue(prop.is_valid(0))
        self.assertTrue(prop.is_valid(1))
        self.assertTrue(prop.is_valid(0.0))
        self.assertTrue(prop.is_valid(1.0))
        self.assertFalse(prop.is_valid(1.0+1.0j))
        self.assertFalse(prop.is_valid(""))
        self.assertFalse(prop.is_valid(()))
        self.assertFalse(prop.is_valid([]))
        self.assertFalse(prop.is_valid({}))
        self.assertFalse(prop.is_valid(Foo()))

        try:
            import numpy as np
            # TODO: self.assertFalse(prop.is_valid(np.bool8(False)))
            # TODO: self.assertFalse(prop.is_valid(np.bool8(True)))
            self.assertTrue(prop.is_valid(np.int8(0)))
            self.assertTrue(prop.is_valid(np.int8(1)))
            self.assertTrue(prop.is_valid(np.int16(0)))
            self.assertTrue(prop.is_valid(np.int16(1)))
            self.assertTrue(prop.is_valid(np.int32(0)))
            self.assertTrue(prop.is_valid(np.int32(1)))
            self.assertTrue(prop.is_valid(np.int64(0)))
            self.assertTrue(prop.is_valid(np.int64(1)))
            self.assertTrue(prop.is_valid(np.uint8(0)))
            self.assertTrue(prop.is_valid(np.uint8(1)))
            self.assertTrue(prop.is_valid(np.uint16(0)))
            self.assertTrue(prop.is_valid(np.uint16(1)))
            self.assertTrue(prop.is_valid(np.uint32(0)))
            self.assertTrue(prop.is_valid(np.uint32(1)))
            self.assertTrue(prop.is_valid(np.uint64(0)))
            self.assertTrue(prop.is_valid(np.uint64(1)))
            self.assertTrue(prop.is_valid(np.float16(0)))
            self.assertTrue(prop.is_valid(np.float16(1)))
            self.assertTrue(prop.is_valid(np.float32(0)))
            self.assertTrue(prop.is_valid(np.float32(1)))
            self.assertTrue(prop.is_valid(np.float64(0)))
            self.assertTrue(prop.is_valid(np.float64(1)))
            self.assertFalse(prop.is_valid(np.complex64(1.0+1.0j)))
            self.assertFalse(prop.is_valid(np.complex128(1.0+1.0j)))
            self.assertFalse(prop.is_valid(np.complex256(1.0+1.0j)))
        except ImportError:
            pass
Esempio n. 23
0
class CompositeGlyph(HasProps):
    """Represents a subset of data.

    A collection of hetero or homogeneous glyph
    renderers which represent a subset of data. The
    purpose of the composite glyph is to abstract
    away the details of constructing glyphs, based on
    the details of a subset of data, from the grouping
    operations that a generalized builder must implement.

    In general, the Builder operates at the full column
    oriented data source level, segmenting and assigning
    attributes from a large selection, while the composite glyphs
    will typically be passed an array-like structures with
    one or more singlular attributes to apply.

    Another way to explain the concept is that the Builder
    operates as the groupby, as in pandas, while the
    CompositeGlyph operates as the apply.

    What is the responsibility of the Composite Glyph?
        - Produce GlyphRenderers
        - Apply any aggregations
        - Tag the GlyphRenderers with the group label
        - Apply transforms due to chart operations
            - Operations require implementation of special methods
    """

    label = String('All', help='Identifies the subset of data.')
    values = Either(Column(Float), Column(String), help='Array-like values.')
    color = Color(default='gray')
    fill_alpha = Float(default=0.8)

    source = Instance(ColumnDataSource)
    operations = List(Any)
    renderers = List(Instance(GlyphRenderer))

    left_buffer = Float(default=0.0)
    right_buffer = Float(default=0.0)
    top_buffer = Float(default=0.0)
    bottom_buffer = Float(default=0.0)

    def __init__(self, **kwargs):
        label = kwargs.pop('label', None)

        if label is not None:
            if not isinstance(label, str):
                label = str(label)
            kwargs['label'] = label

        super(CompositeGlyph, self).__init__(**kwargs)
        self.setup()

    def setup(self):
        self.renderers = [renderer for renderer in self.build_renderers()]
        if self.renderers is not None:
            self.refresh()

    def refresh(self):
        if self.renderers is not None:
            self.source = self.build_source()
            self._set_sources()

    def build_renderers(self):
        raise NotImplementedError('You must return list of renderers.')

    def build_source(self):
        raise NotImplementedError('You must return ColumnDataSource.')

    def _set_sources(self):
        """Store reference to source in each glyph renderer."""
        for renderer in self.renderers:
            renderer.data_source = self.source

    def __stack__(self, glyphs):
        pass

    def __jitter__(self, glyphs):
        pass

    def __dodge__(self, glyphs):
        pass

    def __overlay__(self, glyphs):
        pass

    def apply_operations(self):
        pass
Esempio n. 24
0
    def test_Float(self):
        prop = Float()

        self.assertTrue(prop.is_valid(None))
        # TODO: self.assertFalse(prop.is_valid(False))
        # TODO: self.assertFalse(prop.is_valid(True))
        self.assertTrue(prop.is_valid(0))
        self.assertTrue(prop.is_valid(1))
        self.assertTrue(prop.is_valid(0.0))
        self.assertTrue(prop.is_valid(1.0))
        self.assertFalse(prop.is_valid(1.0+1.0j))
        self.assertFalse(prop.is_valid(""))
        self.assertFalse(prop.is_valid(()))
        self.assertFalse(prop.is_valid([]))
        self.assertFalse(prop.is_valid({}))
        self.assertFalse(prop.is_valid(Foo()))

        try:
            import numpy as np
            # TODO: self.assertFalse(prop.is_valid(np.bool8(False)))
            # TODO: self.assertFalse(prop.is_valid(np.bool8(True)))
            self.assertTrue(prop.is_valid(np.int8(0)))
            self.assertTrue(prop.is_valid(np.int8(1)))
            self.assertTrue(prop.is_valid(np.int16(0)))
            self.assertTrue(prop.is_valid(np.int16(1)))
            self.assertTrue(prop.is_valid(np.int32(0)))
            self.assertTrue(prop.is_valid(np.int32(1)))
            self.assertTrue(prop.is_valid(np.int64(0)))
            self.assertTrue(prop.is_valid(np.int64(1)))
            self.assertTrue(prop.is_valid(np.uint8(0)))
            self.assertTrue(prop.is_valid(np.uint8(1)))
            self.assertTrue(prop.is_valid(np.uint16(0)))
            self.assertTrue(prop.is_valid(np.uint16(1)))
            self.assertTrue(prop.is_valid(np.uint32(0)))
            self.assertTrue(prop.is_valid(np.uint32(1)))
            self.assertTrue(prop.is_valid(np.uint64(0)))
            self.assertTrue(prop.is_valid(np.uint64(1)))
            self.assertTrue(prop.is_valid(np.float16(0)))
            self.assertTrue(prop.is_valid(np.float16(1)))
            self.assertTrue(prop.is_valid(np.float32(0)))
            self.assertTrue(prop.is_valid(np.float32(1)))
            self.assertTrue(prop.is_valid(np.float64(0)))
            self.assertTrue(prop.is_valid(np.float64(1)))
            self.assertFalse(prop.is_valid(np.complex64(1.0+1.0j)))
            self.assertFalse(prop.is_valid(np.complex128(1.0+1.0j)))
            self.assertFalse(prop.is_valid(np.complex256(1.0+1.0j)))
        except ImportError:
            pass
Esempio n. 25
0
class Stat(HasProps):
    """Represents a statistical operation to summarize a column of data.

    Can be computed from either a ColumnLabel with a ColumnDataSource, *or*, a
    discrete column of data.
    """

    # inputs
    column = ColumnLabel(
        help="""A column to use for the stat calculation. Required
        when providing a ColumnDataSource as input.""")
    source = Instance(ColumnDataSource,
                      help="""One option for providing the data
        source for stat calculation.""")
    values = EitherColumn(Column(Float),
                          Column(Int),
                          Column(String),
                          Column(Date),
                          Column(Datetime),
                          Column(Bool),
                          default=None,
                          help="""
                  Second option for providing values for stat calculation is by
                  passing the actual column of data.""")

    # output
    value = Float(
        help="""The value calculated for the stat. Some stats could use
        multiple properties to provide the calculation if required.""")

    def __init__(self, **properties):
        super(Stat, self).__init__(**properties)
        self._refresh()

    def _refresh(self):
        """Lazy update of properties, used for initial transform init."""
        if self.get_data() is not None:
            self.update()
            self.calculate()

    def set_data(self, data, column=None):
        """Set data properties and update all dependent properties."""
        if isinstance(data, pd.DataFrame):
            self.source = ColumnDataSource(data)
            if column is None:
                raise ValueError('When providing a table of data, '
                                 'you must also provide a column label')
            else:
                self.column = column
        else:
            self.values = data
        self.update()
        self.calculate()

    def get_data(self):
        """Returns the available columnlabel/source values or column values."""
        if self.source is not None and self.column is not None:
            return self.source._data[self.column]
        elif self.values is not None:
            return self.values
        else:
            return None

    def calculate(self):
        """Return transformed value from column label/source or column-like data."""
        raise NotImplementedError('You must implement the calculate method '
                                  'for each stat type.')

    def update(self):
        """Perform any initial work before the actual calculation is performed."""
        pass
Esempio n. 26
0
 class Child(Base):
     z = Float(3.14)
Esempio n. 27
0
class AggregateGlyph(NestedCompositeGlyph):
    """A base composite glyph for aggregating an array.

    Implements default stacking and dodging behavior that other composite
    glyphs can inherit.
    """

    stack_label = String()
    stack_shift = Float(default=0.0)

    dodge_label = String()
    dodge_shift = Float(default=None)

    agg = Instance(Stat, default=Sum())

    span = Float()

    def get_dodge_label(self, shift=0.0):
        if self.dodge_shift is None:
            shift_str = ':' + str(0.5 + shift)
        elif self.dodge_shift is not None:
            shift_str = ':' + str(self.dodge_shift + shift)
        else:
            shift_str = ''
        return str(self.label) + shift_str

    def filter_glyphs(self, glyphs):
        return [glyph for glyph in glyphs if isinstance(glyph, self.__class__)]

    @staticmethod
    def groupby(glyphs, prop):
        grouped = defaultdict(list)
        [grouped[getattr(glyph, prop)].append(glyph) for glyph in glyphs]
        return grouped

    def __stack__(self, glyphs):
        if self.stack_label is not None:
            filtered_glyphs = self.filter_glyphs(glyphs)
            grouped = self.groupby(filtered_glyphs, 'label')

            for index, group in iteritems(grouped):
                group = sorted(group, key=lambda x: x.stack_label)
                shift = []
                for i, glyph in enumerate(group):
                    # save off the top of each rect's height
                    shift.append(glyph.span)
                    if i > 0:
                        glyph.stack_shift = sum(shift[0:i])
                        glyph.refresh()

    def __dodge__(self, glyphs):
        if self.dodge_label is not None:
            filtered_glyphs = self.filter_glyphs(glyphs)
            grouped = self.groupby(filtered_glyphs, 'dodge_label')

            # calculate transformations
            step = np.linspace(0, 1.0, len(grouped.keys()) + 1, endpoint=False)
            width = min(0.2, (1. / len(grouped.keys()))**1.1)

            # set bar attributes and re-aggregate
            for i, (index, group) in enumerate(iteritems(grouped)):
                for glyph in group:
                    glyph.dodge_shift = step[i + 1]
                    glyph.width = width
                    glyph.refresh()