Unverified Commit 9d3eb9e6 authored by Bharath Ramsundar's avatar Bharath Ramsundar Committed by GitHub
Browse files

Merge pull request #1346 from rbharath/randomstrat

Fixing bugs in RandomStratifiedSplitter
parents 55d22329 19738581
Loading
Loading
Loading
Loading
+24 −28
Original line number Diff line number Diff line
@@ -274,18 +274,18 @@ class RandomStratifiedSplitter(Splitter):
  """
  RandomStratified Splitter class.

    For sparse multitask datasets, a standard split offers no guarantees that the
    splits will have any activate compounds. This class guarantees that each task
    will have a proportional split of the activates in a split. TO do this, a
    ragged split is performed with different numbers of compounds taken from each
    task. Thus, the length of the split arrays may exceed the split of the
    original array. That said, no datapoint is copied to more than one split, so
    correctness is still ensured.
  For sparse multitask datasets, a standard split offers no guarantees
  that the splits will have any activate compounds. This class guarantees
  that each task will have a proportional split of the activates in a
  split. TO do this, a ragged split is performed with different numbers
  of compounds taken from each task. Thus, the length of the split arrays
  may exceed the split of the original array. That said, no datapoint is
  copied to more than one split, so correctness is still ensured.

  Note that this splitter is only valid for boolean label data.

    TODO(rbharath): This splitter should be refactored to match style of other
    splitter classes.
  TODO(rbharath): This splitter should be refactored to match style of
  other splitter classes.
  """

  def __generate_required_hits(self, w, frac_split):
@@ -319,10 +319,8 @@ class RandomStratifiedSplitter(Splitter):
      split_indices.append(split_index + 1)
    return split_indices

    # TODO(rbharath): Refactor this split method to match API of other splits (or

  # potentially refactor those to match this.

  # TODO(rbharath): Refactor this split method to match API of other
  # splits (or potentially refactor those to match this).
  def split(self, dataset, frac_split, split_dirs=None):
    """
    Method that does bulk of splitting dataset.
@@ -334,7 +332,8 @@ class RandomStratifiedSplitter(Splitter):

    # Handle edge case where frac_split is 1
    if frac_split == 1:
      dataset_1 = NumpyDataset(dataset.X, dataset.y, dataset.w, dataset.ids)
      dataset_1 = DiskDataset.from_numpy(dataset.X, dataset.y, dataset.w,
                                         dataset.ids)
      dataset_2 = None
      return dataset_1, dataset_2
    X, y, w, ids = randomize_arrays((dataset.X, dataset.y, dataset.w,
@@ -351,11 +350,11 @@ class RandomStratifiedSplitter(Splitter):
    # check out if any rows in either w_1 or w_2 are just zeros
    rows_1 = w_1.any(axis=1)
    X_1, y_1, w_1, ids_1 = X[rows_1], y[rows_1], w_1[rows_1], ids[rows_1]
    dataset_1 = NumpyDataset(X_1, y_1, w_1, ids_1)
    dataset_1 = DiskDataset.from_numpy(X_1, y_1, w_1, ids_1)

    rows_2 = w_2.any(axis=1)
    X_2, y_2, w_2, ids_2 = X[rows_2], y[rows_2], w_2[rows_2], ids[rows_2]
    dataset_2 = NumpyDataset(X_2, y_2, w_2, ids_2)
    dataset_2 = DiskDataset.from_numpy(X_2, y_2, w_2, ids_2)

    return dataset_1, dataset_2

@@ -377,9 +376,6 @@ class RandomStratifiedSplitter(Splitter):
      valid_dir = tempfile.mkdtemp()
    if test_dir is None:
      test_dir = tempfile.mkdtemp()
    # Obtain original x, y, and w arrays and shuffle
    X, y, w, ids = randomize_arrays((dataset.X, dataset.y, dataset.w,
                                     dataset.ids))
    rem_dir = tempfile.mkdtemp()
    train_dataset, rem_dataset = self.split(dataset, frac_train,
                                            [train_dir, rem_dir])
@@ -389,8 +385,8 @@ class RandomStratifiedSplitter(Splitter):
      valid_percentage = frac_valid / (frac_valid + frac_test)
    else:
      return train_dataset, None, None
    # split test data into valid and test, treating sub test set also as sparse
    valid_dataset, test_dataset = self.split(dataset, valid_percentage,
    # split remaining data into valid and test, treating sub test set also as sparse
    valid_dataset, test_dataset = self.split(rem_dataset, valid_percentage,
                                             [valid_dir, test_dir])

    return train_dataset, valid_dataset, test_dataset
+27 −0
Original line number Diff line number Diff line
@@ -398,6 +398,33 @@ class TestSplitters(unittest.TestCase):
    y_2 = dataset_2.y
    assert np.count_nonzero(y_2) == n_positives / 2

  def test_singletask_stratified_train_valid_test_split(self):
    """
    Test RandomStratifiedSplitter on a singletask train/valid/test split.
    """
    np.random.seed(2314)
    # Test singletask case.
    n_samples = 100
    n_positives = 10
    n_features = 10
    n_tasks = 1

    X = np.random.rand(n_samples, n_features)
    y = np.zeros((n_samples, n_tasks))
    y[:n_positives] = 1
    w = np.ones((n_samples, n_tasks))
    ids = np.arange(n_samples)
    dataset = dc.data.DiskDataset.from_numpy(X, y, w, ids)

    stratified_splitter = dc.splits.RandomStratifiedSplitter()
    train, valid, test = stratified_splitter.train_valid_test_split(
        dataset, frac_train=.8, frac_valid=.1, frac_test=.1)

    # Should have made an 80/10/10 train/valid/test split of actives.
    self.assertEqual(np.count_nonzero(train.y), 8)
    self.assertEqual(np.count_nonzero(valid.y), 1)
    self.assertEqual(np.count_nonzero(test.y), 1)

  def test_singletask_stratified_k_fold_split(self):
    """
    Test RandomStratifiedSplitter k-fold class.