A clinic adds new patient records to its dataset every week. If the data were reshuffled and split again each week, records that were test examples last week could become training examples this week, and the test set would slowly leak into training. Instead every record decides its side by its own id, a whole number:
- compute
bucket = (id * 2654435761) % 2**32 % 100, a number from 0 to 99 that looks random but is always the same for the same id; - the record goes to the test set if
bucket < test_pct, otherwise to the training set.
Write stable_split(ids, test_pct) that returns a tuple (train_ids, test_ids), two lists that keep the order of ids. An id can appear more than once (several visits of one patient); every copy follows the same rule, so they all land on the same side.
Examples
Input: ids = [1, 2, 3, 4, 5, 6, 7, 8], test_pct = 30
Output: ([1, 3, 4, 6, 7], [2, 5, 8])
Explanation: the buckets are 61, 26, 87, 52, 17, 78, 43 and 4; ids 2, 5 and 8 have buckets below 30.
Adding ids 9 and 10 next week (buckets 69 and 34) sends both to training
and moves nobody else.
Input: ids = [104, 7, 104, 12], test_pct = 50
Output: ([12], [104, 7, 104])
Constraints
0 <= len(ids) <= 10**5,0 <= id <= 10**120 <= test_pct <= 100
Goals
- Split a dataset into training and test examples with a fixed rule
- Make every example's side depend only on its own id
- See why such a split keeps old test examples in the test set when new data arrives