def fill_sampling(slice_list, N):
    """Given a list of slices, draw N samples such that each slice contributes as much as possible

    Parameters
    --------------------------
    slice_list : list of Slice
        List of slices
    N : int
        Number of samples to draw
    """
    A = [len(s.inliers) for s in slice_list]
    N_max = np.sum(A)
    if N > N_max:
        raise ValueError("Tried to draw {:d} samples from a pool of only {:d} items".format(N, N_max))
    
    samples_from = np.zeros((len(A),), dtype='int') # Number of samples to draw from each group

    remaining = N
    while remaining > 0:
        remaining_groups = np.flatnonzero(samples_from - np.array(A))
        
        if remaining < len(remaining_groups):
            np.random.shuffle(remaining_groups)
            for g in remaining_groups[:remaining]:
                samples_from[g] += 1
        else:
            # Give each group the allowed number of samples. Constrain to their max size.
            to_each = max(1, int(remaining / len(remaining_groups)))
            samples_from = np.min(np.vstack((samples_from + to_each, A)), axis=0)
        
        # Update remaining count
        remaining = int(N - np.sum(samples_from))
    if not remaining == 0:
        raise ValueError("Still {:d} samples left! This is an error in the selection.")

    # Construct index list of selected samples
    samples = []
    for s, a, n in zip(slice_list, A, samples_from):
        if a == n:
            samples.append(np.array(s.inliers)) # all
        elif a == 0:
            samples.append(np.arange([]))
        else:
            chosen = np.random.choice(s.inliers, n, replace=False)
            samples.append(np.array(chosen))
    return samples
Given a list of slices, draw N samples such that each slice contributes as much as possible

    Parameters
    --------------------------
    slice_list : list of Slice
        List of slices
    N : int
        Number of samples to draw