Probability Distribution function from values
    Arguments:
      probability (bool): whether the values should be min/max scaled to lie on the range [0, 1]
    Like `hist` but smoother, more accurate/useful
    Double-Normalization:
      The x values are min/max normalized to lie in the range 0-1 inclusive
      The pdf is normalized to integrate/sum to 1.0
    >>> h = dyno_hist(np.arange(100), window=5)
    >>> abs(sum(np.diff(h.index.values) * h.values[1:]) - 1.) < 0.00001
    True
    >>> h = dyno_hist(np.arange(50), window=12)
    >>> abs(sum(np.diff(h.index.values) * h.values[1:]) - 1.) < 0.00001
    True
    >>> h = dyno_hist(np.random.randn(1000), window=42)
    >>> abs(sum(np.diff(h.index.values) * h.values[1:]) - 1.0) < 0.04
    True

def dyno_hist(x, window=None, probability=True, edge_weight=1.):
    """ Probability Distribution function from values
    Arguments:
      probability (bool): whether the values should be min/max scaled to lie on the range [0, 1]
    Like `hist` but smoother, more accurate/useful
    Double-Normalization:
      The x values are min/max normalized to lie in the range 0-1 inclusive
      The pdf is normalized to integrate/sum to 1.0
    >>> h = dyno_hist(np.arange(100), window=5)
    >>> abs(sum(np.diff(h.index.values) * h.values[1:]) - 1.) < 0.00001
    True
    >>> h = dyno_hist(np.arange(50), window=12)
    >>> abs(sum(np.diff(h.index.values) * h.values[1:]) - 1.) < 0.00001
    True
    >>> h = dyno_hist(np.random.randn(1000), window=42)
    >>> abs(sum(np.diff(h.index.values) * h.values[1:]) - 1.0) < 0.04
    True
    """
    x = np.sort(x)
    if probability:
        # normalize x first
        x = x - x[0]
        x = x / float(x[-1] or 1)
    window = window or 1
    window = min(max(window, 1), int(len(x) / 1.5))
    window += 1
    # Empirical Densitites (PDF) based on diff of sorted values
    delta = x[(window - 1):] - x[:(1 - window)]
    densities = float(window - 1) / (len(delta) + window - 2) / delta
    h = pd.Series(densities, index=x[window // 2:][:len(delta)])
    if probability:
        if h.index[0] > 0:
            h = pd.Series(edge_weight * densities[0], index=[0]).append(h)
        if h.index[-1] < 1:
            h = h.append(pd.Series(edge_weight * densities[-1], index=[1.]))
    return h