Skip to content

Commit

Permalink
Histogram enhancements (#2812)
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr authored and jlstevens committed Jun 22, 2018
1 parent b5f80c9 commit eda6350
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 11 deletions.
4 changes: 2 additions & 2 deletions holoviews/core/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class Element(ViewableElement, Composable, Overlayable):
group = param.String(default='Element', constant=True)

def hist(self, dimension=None, num_bins=20, bin_range=None,
adjoin=True, individually=True, **kwargs):
adjoin=True, **kwargs):
"""
The hist method generates a histogram to be adjoined to the
Element in an AdjointLayout. By default the histogram is
Expand All @@ -37,7 +37,7 @@ def hist(self, dimension=None, num_bins=20, bin_range=None,
hists = []
for d in dimension[::-1]:
hist = histogram(self, num_bins=num_bins, bin_range=bin_range,
individually=individually, dimension=d, **kwargs)
dimension=d, **kwargs)
hists.append(hist)
if adjoin:
layout = self
Expand Down
5 changes: 2 additions & 3 deletions holoviews/core/spaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,8 +421,7 @@ def hist(self, num_bins=20, bin_range=None, adjoin=True, individually=True, **kw
if issubclass(self.type, (NdOverlay, Overlay)) and 'index' not in kwargs:
kwargs['index'] = 0
for k, v in self.data.items():
hists = v.hist(adjoin=False, bin_range=bin_range,
individually=individually, num_bins=num_bins,
hists = v.hist(adjoin=False, bin_range=bin_range, num_bins=num_bins,
style_prefix=style_prefix, **kwargs)
if isinstance(hists, Layout):
for i, hist in enumerate(hists):
Expand Down Expand Up @@ -1421,7 +1420,7 @@ def overlay(self, dimensions=None, **kwargs):
return self.groupby(dims, group_type=NdOverlay)


def hist(self, num_bins=20, bin_range=None, adjoin=True, individually=True, **kwargs):
def hist(self, num_bins=20, bin_range=None, adjoin=True, **kwargs):
"""
Computes a histogram from the object and adjoins it by
default. By default the histogram is computed for the bottom
Expand Down
25 changes: 19 additions & 6 deletions holoviews/operation/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,12 @@ class histogram(Operation):
bin_range = param.NumericTuple(default=None, length=2, doc="""
Specifies the range within which to compute the bins.""")

bins = param.ClassSelector(default=None, class_=(np.ndarray, list, tuple), doc="""
An explicit set of bin edges.""")

cumulative = param.Boolean(default=False, doc="""
Whether to compute the cumulative histogram""")

dimension = param.String(default=None, doc="""
Along which dimension of the Element to compute the histogram.""")

Expand All @@ -495,9 +501,6 @@ class histogram(Operation):
groupby = param.ClassSelector(default=None, class_=(basestring, Dimension), doc="""
Defines a dimension to group the Histogram returning an NdOverlay of Histograms.""")

individually = param.Boolean(default=True, doc="""
Specifies whether the histogram will be rescaled for each Element in a UniformNdMapping.""")

log = param.Boolean(default=False, doc="""
Whether to use base 10 logarithmic samples for the bin edges.""")

Expand Down Expand Up @@ -555,15 +558,21 @@ def _process(self, view, key=None):
hist_range = (0, 1)

datetimes = False
bins = None if self.p.bins is None else np.asarray(self.p.bins)
steps = self.p.num_bins + 1
start, end = hist_range
if data.dtype.kind == 'M' or (data.dtype.kind == 'O' and isinstance(data[0], datetime_types)):
start, end = dt_to_int(start, 'ns'), dt_to_int(end, 'ns')
datetimes = True
data = data.astype('datetime64[ns]').astype('int64') * 1000.
hist_range = start, end
if bins is not None:
bins = bins.astype('datetime64[ns]').astype('int64') * 1000.
else:
hist_range = start, end

if self.p.log:
if self.p.bins:
edges = bins
elif self.p.log:
bin_min = max([abs(start), data[data>0].min()])
edges = np.logspace(np.log10(bin_min), np.log10(end), steps)
else:
Expand Down Expand Up @@ -601,7 +610,11 @@ def _process(self, view, key=None):
if view.group != view.__class__.__name__:
params['group'] = view.group

return Histogram((hist, edges), kdims=[view.get_dimension(selected_dim)],
if self.p.cumulative:
hist = np.cumsum(hist)
if self.p.normed in (True, 'integral'):
hist *= edges[1]-edges[0]
return Histogram((edges, hist), kdims=[view.get_dimension(selected_dim)],
label=view.label, **params)


Expand Down
29 changes: 29 additions & 0 deletions tests/operation/testoperation.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,35 @@ def test_points_histogram_bin_range(self):
hist = Histogram(([0.25, 0.25, 0.5], [0., 1., 2., 3.]))
self.assertEqual(op_hist, hist)

def test_points_histogram_explicit_bins(self):
points = Points([float(i) for i in range(10)])
op_hist = histogram(points, bins=[0, 1, 3], normed=False)

# Make sure that the name and label are as desired
op_freq_dim = op_hist.get_dimension('x_frequency')
self.assertEqual(op_freq_dim.label, 'x Frequency')

# Because the operation labels are now different from the
# default Element label, change back before comparing.
op_hist = op_hist.redim(x_frequency='Frequency')
hist = Histogram(([0, 1, 3], [1, 3]))
self.assertEqual(op_hist, hist)

def test_points_histogram_cumulative(self):
arr = np.arange(4)
points = Points(arr)
op_hist = histogram(points, cumulative=True, num_bins=3, normed=False)

# Make sure that the name and label are as desired
op_freq_dim = op_hist.get_dimension('x_frequency')
self.assertEqual(op_freq_dim.label, 'x Frequency')

# Because the operation labels are now different from the
# default Element label, change back before comparing.
op_hist = op_hist.redim(x_frequency='Frequency')
hist = Histogram(([0, 1, 2, 3], [1, 2, 4]))
self.assertEqual(op_hist, hist)

def test_points_histogram_not_normed(self):
points = Points([float(i) for i in range(10)])
op_hist = histogram(points, num_bins=3, normed=False)
Expand Down

0 comments on commit eda6350

Please sign in to comment.