Choose the initial centers for K-Means.

    Parameters
    ----------
    X : da.Array (n_samples, n_features)
    n_clusters : int
        Number of clusters to end up with
    init : {'k-means||', 'k-means++', 'random'} or numpy.ndarray
        Initialization method, or pass a NumPy array to use
    random_state : int, optional
    max_iter : int, optional
        Only used for ``init='k-means||'``.
    oversampling_factor : int, optional
        Only used for ``init='k-means||`''. Controls the additional number of
        candidate centers in each iteration.

    Return
    ------
    centers : np.ndarray (n_clusters, n_features)

    Notes
    -----
    The default strategy is ``k-means||``, which tends to be slower than
    ``k-means++`` for small (in-memory) datasets, but works better in a
    distributed setting.

    .. warning::

       Using ``init='k-means++'`` assumes that the entire dataset fits
       in RAM.

def k_init(
    X,
    n_clusters,
    init="k-means||",
    random_state=None,
    max_iter=None,
    oversampling_factor=2,
):
    """Choose the initial centers for K-Means.

    Parameters
    ----------
    X : da.Array (n_samples, n_features)
    n_clusters : int
        Number of clusters to end up with
    init : {'k-means||', 'k-means++', 'random'} or numpy.ndarray
        Initialization method, or pass a NumPy array to use
    random_state : int, optional
    max_iter : int, optional
        Only used for ``init='k-means||'``.
    oversampling_factor : int, optional
        Only used for ``init='k-means||`''. Controls the additional number of
        candidate centers in each iteration.

    Return
    ------
    centers : np.ndarray (n_clusters, n_features)

    Notes
    -----
    The default strategy is ``k-means||``, which tends to be slower than
    ``k-means++`` for small (in-memory) datasets, but works better in a
    distributed setting.

    .. warning::

       Using ``init='k-means++'`` assumes that the entire dataset fits
       in RAM.
    """
    if isinstance(init, np.ndarray):
        K, P = init.shape

        if K != n_clusters:
            msg = (
                "Number of centers in provided 'init' ({}) does "
                "not match 'n_clusters' ({})"
            )
            raise ValueError(msg.format(K, n_clusters))

        if P != X.shape[1]:
            msg = (
                "Number of features in the provided 'init' ({}) do not "
                "match the number of features in 'X'"
            )
            raise ValueError(msg.format(P, X.shape[1]))

        return init

    elif not isinstance(init, str):
        raise TypeError("'init' must be an array or str, got {}".format(type(init)))

    valid = {"k-means||", "k-means++", "random"}

    if isinstance(random_state, Integral) or random_state is None:
        if init == "k-means||":
            random_state = da.random.RandomState(random_state)
        else:
            random_state = np.random.RandomState(random_state)

    if init == "k-means||":
        return init_scalable(X, n_clusters, random_state, max_iter, oversampling_factor)
    elif init == "k-means++":
        return init_pp(X, n_clusters, random_state)

    elif init == "random":
        return init_random(X, n_clusters, random_state)

    else:
        raise ValueError("'init' must be one of {}, got {}".format(valid, init))