Source code for giant.utilities.random_combination
# Copyright 2021 United States Government as represented by the Administrator of the National Aeronautics and Space
# Administration. No copyright is claimed in the United States under Title 17, U.S. Code. All Other Rights Reserved.
"""
Provides an iterator for generating unique random combinations from a population where order doesn't matter.
This is useful for performing RANSAC analysis as it ensures that the same sample sets are not chosen multiple times.
"""
from typing import Union
from itertools import combinations
from random import sample
try:
from scipy.special import comb
except ImportError:
from scipy.misc import comb
from .._typing import ARRAY_LIKE
[docs]class RandomCombinations:
def __init__(self, population: Union[int, ARRAY_LIKE], combo_length: int, number_of_combos: int):
"""
Iterate over ``number_of_combos`` random combinations of ``combo_length`` from ``population``.
This iterator ensures unique combinations are returned. If more combinations are requested than are possible
then an exhaustive list is returned
:param population: The population to choose from. If specified as an integer then the population will be
range(int).
:param combo_length: The length for each combination as an integer
:param number_of_combos: the number of unique combinations you want as an integer
"""
if isinstance(population, int):
population = range(population)
int_population = population
else:
int_population = range(len(population))
self.population = population
self.int_population = int_population
self.combo_length = combo_length
self.possible_combos = int(comb(len(self.population), self.combo_length))
self.number_of_combos = number_of_combos
def __iter__(self) -> tuple:
if self.number_of_combos >= self.possible_combos:
for combo in combinations(self.population, self.combo_length):
yield combo
else:
used_samples = set()
for _ in range(self.number_of_combos):
new_sample = tuple(sorted(sample(self.int_population, self.combo_length)))
while new_sample in used_samples:
new_sample = tuple(sorted(sample(self.int_population, self.combo_length)))
used_samples.add(new_sample)
yield tuple(self.population[ind] for ind in new_sample)