def estimate_mutual_information(x, y):
    """Estimate the mutual information of two datasets.

    Mutual information is a measure of dependence between
    two datasets and is calculated as:

        $I(x;y) = H(x) + H(y) - H(x,y)$

    Where H(x) is the Shannon entropy of x. For continuous datasets,
    adapts the Kraskov Estimator [1] for mutual information.

    Args:
        x (array-like): An array with shape (n_samples, n_features_x)
        y (array-like): An array with shape (n_samples, n_features_y)

    Returns:
        float: A floating point number representing the mutual
            information of x and y. This calculation is *exact*
            for entirely discrete datasets and *approximate* if
            there are continuous columns present.

    References:

    .. [1] A. Kraskov, H. Stogbauer and P. Grassberger, "Estimating mutual
      information". Phys. Rev. E 69, 2004.
    """
    xy = np.concatenate((x, y), axis=1)
    epsilon = _calculate_epsilon(xy)
    h_x = estimate_entropy(x, epsilon)
    h_y = estimate_entropy(y, epsilon)
    h_xy = estimate_entropy(xy, epsilon)
    return max(0, h_x + h_y - h_xy)