def generate_data_clusters(n_train=1000, n_test=500, n_clusters=2,
                           n_features=2, contamination=0.1, size='same',
                           density='same', dist=0.25, random_state=None,
                           return_in_clusters=False):
    """Utility function to generate synthesized data in clusters.
       Generated data can involve the low density pattern problem and global
       outliers which are considered as difficult tasks for outliers detection
       algorithms.

    Parameters
    ----------
    n_train : int, (default=1000)
        The number of training points to generate.

    n_test : int, (default=500)
        The number of test points to generate.

    n_clusters : int, optional (default=2)
       The number of centers (i.e. clusters) to generate.

    n_features : int, optional (default=2)
       The number of features for each sample.

    contamination : float in (0., 0.5), optional (default=0.1)
       The amount of contamination of the data set, i.e.
       the proportion of outliers in the data set.

    size : str, optional (default='same')
       Size of each cluster: 'same' generates clusters with same size,
       'different' generate clusters with different sizes.

    density : str, optional (default='same')
       Density of each cluster: 'same' generates clusters with same density,
       'different' generate clusters with different densities.

    dist: float, optional (default=0.25)
       Distance between clusters. Should be between 0. and 1.0
       It is used to avoid clusters overlapping as much as possible.
       However, if number of samples and number of clusters are too high,
       it is unlikely to separate them fully even if ``dist`` set to 1.0

    random_state : int, RandomState instance or None, optional (default=None)
        If int, random_state is the seed used by the random number generator;
        If RandomState instance, random_state is the random number generator;
        If None, the random number generator is the RandomState instance used
        by `np.random`.

    return_in_clusters : bool, optional (default=False)
        If True, the function returns x_train, y_train, x_test, y_test each as
        a list of numpy arrays where each index represents a cluster.
        If False, it returns x_train, y_train, x_test, y_test each as numpy
        array after joining the sequence of clusters arrays,

    Returns
    -------
    X_train : numpy array of shape (n_train, n_features)
        Training data.

    y_train : numpy array of shape (n_train,)
        Training ground truth.

    X_test : numpy array of shape (n_test, n_features)
        Test data.

    y_test : numpy array of shape (n_test,)
        Test ground truth.
    """
    # initialize a random state and seeds for the instance
    random_state = check_random_state(random_state)

    if isinstance(n_clusters, int):
        check_parameter(n_clusters, low=1, param_name='n_clusters')
    else:
        raise ValueError("n_clusters should be int, got %s" % n_clusters)

    if isinstance(n_features, int):
        check_parameter(n_features, low=1, param_name='n_features')
    else:
        raise ValueError("n_features should be int, got %s" % n_features)

    if isinstance(contamination, (float, int)):
        check_parameter(contamination, low=0, high=0.5,
                        param_name='contamination')
    else:
        raise ValueError(
            "contamination should be float, got %s" % contamination)

    if isinstance(dist, float):
        check_parameter(dist, low=0, high=1.0, param_name='dist')
    else:
        raise ValueError("dist should be float, got %s" % dist)

    if not isinstance(return_in_clusters, bool):
        raise ValueError("return_in_clusters should be of type bool, "
                         "got %s" % return_in_clusters)

    # find the required number of outliers and inliers
    n_samples = n_train + n_test
    n_outliers = int(n_samples * contamination)
    n_inliers = n_samples - n_outliers

    if size == 'same':
        a_ = [int(n_inliers / n_clusters)] * (n_clusters - 1)
        clusters_size = a_ + [int(n_inliers - sum(a_))]
    elif size == 'different':
        if (n_clusters * 10) > n_samples:
            raise ValueError('number of samples should be at least 10 times of'
                             'the number of clusters')
        if (n_clusters * 10) > n_inliers:
            raise ValueError('contamination ratio is too high, try to increase'
                             ' number of samples or decrease the contamination')
        _r = 1. / n_clusters
        _offset = random_state.uniform(_r * 0.2, _r * 0.4,
                                       size=(int(n_clusters / 2),)).tolist()
        _offset += [i * -1. for i in _offset]
        clusters_size = np.round(
            np.multiply(n_inliers, np.add(_r, _offset))).astype(int)
        if n_clusters % 2 == 0:  # if it is even number
            clusters_size[n_clusters - 1] += n_inliers - sum(clusters_size)
        else:
            clusters_size = np.append(clusters_size,
                                      n_inliers - sum(clusters_size))
    else:
        raise ValueError(
            'size should be a string of value \'same\' or \'different\'')

    # check for clusters densities and apply split accordingly
    if density == 'same':
        clusters_density = random_state.uniform(low=0.1, high=0.5, size=(
            1,)).tolist() * n_clusters
    elif density == 'different':
        clusters_density = random_state.uniform(low=0.1, high=0.5,
                                                size=(n_clusters,))
    else:
        raise ValueError(
            'density should be a string of value \'same\' or \'different\'')

    # calculate number of outliers for every cluster
    n_outliers_ = []
    for i in range(n_clusters):
        n_outliers_.append(int(round(clusters_size[i] * contamination)))
    _diff = int((n_outliers - sum(n_outliers_)) / n_clusters)
    for i in range(n_clusters - 1):
        n_outliers_[i] += _diff
    n_outliers_[n_clusters - 1] += n_outliers - sum(n_outliers_)
    random_state.shuffle(n_outliers_)

    # generate data
    X_clusters, y_clusters = [], []
    X, y = np.zeros([n_samples, n_features]), np.zeros([n_samples, ])

    center_box = list(filter(lambda a: a != 0, np.linspace(
        -np.power(n_samples * n_clusters, dist),
        np.power(n_samples * n_clusters, dist),
        n_clusters + 2)))

    # index tracker for value assignment
    tracker_idx = 0

    for i in range(n_clusters):
        inliers, outliers = [], []
        _blob, _y = make_blobs(n_samples=clusters_size[i], centers=1,
                               cluster_std=clusters_density[i],
                               center_box=(center_box[i], center_box[i + 1]),
                               n_features=n_features,
                               random_state=random_state)

        inliers.append(_blob)

        center_box_l = center_box[i] * (1.2 + dist + clusters_density[i])
        center_box_r = center_box[i + 1] * (1.2 + dist + clusters_density[i])

        outliers.append(make_blobs(n_samples=n_outliers_[i], centers=1,
                                   cluster_std=random_state.uniform(
                                       clusters_density[i] * 3.5,
                                       clusters_density[i] * 4.,
                                       size=(1,)[0]),
                                   center_box=(center_box_l, center_box_r),
                                   n_features=n_features,
                                   random_state=random_state)[0])
        _y = np.append(_y, [1] * int(n_outliers_[i]))

        # generate X
        if np.array(outliers).ravel().shape[0] > 0:
            stacked_X_temp = np.vstack(
                (np.concatenate(inliers), np.concatenate(outliers)))
            X_clusters.append(stacked_X_temp)
            tracker_idx_new = tracker_idx + stacked_X_temp.shape[0]
            X[tracker_idx:tracker_idx_new, :] = stacked_X_temp
        else:
            X_clusters.append(np.concatenate(inliers))

        # generate Y
        y_clusters.append(_y)
        y[tracker_idx:tracker_idx_new, ] = _y

        tracker_idx = tracker_idx_new

    if return_in_clusters:
        return X_clusters, y_clusters

    # return X_train, X_test, y_train, y_test
    else:
        return train_test_split(X, y, test_size=n_test,
                                random_state=random_state)
Utility function to generate synthesized data in clusters.
       Generated data can involve the low density pattern problem and global
       outliers which are considered as difficult tasks for outliers detection
       algorithms.

    Parameters
    ----------
    n_train : int, (default=1000)
        The number of training points to generate.

    n_test : int, (default=500)
        The number of test points to generate.

    n_clusters : int, optional (default=2)
       The number of centers (i.e. clusters) to generate.

    n_features : int, optional (default=2)
       The number of features for each sample.

    contamination : float in (0., 0.5), optional (default=0.1)
       The amount of contamination of the data set, i.e.
       the proportion of outliers in the data set.

    size : str, optional (default='same')
       Size of each cluster: 'same' generates clusters with same size,
       'different' generate clusters with different sizes.

    density : str, optional (default='same')
       Density of each cluster: 'same' generates clusters with same density,
       'different' generate clusters with different densities.

    dist: float, optional (default=0.25)
       Distance between clusters. Should be between 0. and 1.0
       It is used to avoid clusters overlapping as much as possible.
       However, if number of samples and number of clusters are too high,
       it is unlikely to separate them fully even if ``dist`` set to 1.0

    random_state : int, RandomState instance or None, optional (default=None)
        If int, random_state is the seed used by the random number generator;
        If RandomState instance, random_state is the random number generator;
        If None, the random number generator is the RandomState instance used
        by `np.random`.

    return_in_clusters : bool, optional (default=False)
        If True, the function returns x_train, y_train, x_test, y_test each as
        a list of numpy arrays where each index represents a cluster.
        If False, it returns x_train, y_train, x_test, y_test each as numpy
        array after joining the sequence of clusters arrays,

    Returns
    -------
    X_train : numpy array of shape (n_train, n_features)
        Training data.

    y_train : numpy array of shape (n_train,)
        Training ground truth.

    X_test : numpy array of shape (n_test, n_features)
        Test data.

    y_test : numpy array of shape (n_test,)
        Test ground truth.
    
pyod/utils/data.py