def batcher(X_, y_=None, w_=None, batch_size=-1):
    """Split data to mini-batches.

    Parameters
    ----------
    X_ : {numpy.array, scipy.sparse.csr_matrix}, shape (n_samples, n_features)
        Training vector, where n_samples in the number of samples and
        n_features is the number of features.

    y_ : np.array or None, shape (n_samples,)
        Target vector relative to X.

    w_ : np.array or None, shape (n_samples,)
        Vector of sample weights.

    batch_size : int
        Size of batches.
        Use -1 for full-size batches

    Yields
    -------
    ret_x : {numpy.array, scipy.sparse.csr_matrix}, shape (batch_size, n_features)
        Same type as input

    ret_y : np.array or None, shape (batch_size,)

    ret_w : np.array or None, shape (batch_size,)
    """
    n_samples = X_.shape[0]
    if batch_size == -1:
        batch_size = n_samples
    if batch_size < 1:
        raise ValueError('Parameter batch_size={} is unsupported'.format(
            batch_size))
    for i in range(0, n_samples, batch_size):
        upper_bound = min(i + batch_size, n_samples)
        ret_x = X_[i:upper_bound]
        ret_y = None
        ret_w = None
        if y_ is not None:
            ret_y = y_[i:i + batch_size]
        if w_ is not None:
            ret_w = w_[i:i + batch_size]
        yield ret_x, ret_y, ret_w
