Skip to main content
Code Review

Return to Question

Notice removed Draw attention by Community Bot
Bounty Ended with no winning answer by Community Bot
reformulated code --same algorithm
Source Link
from operator import itemgetter
from itertools import (repeat,
 combinations, product, zip_longest, chain, dropwhile)permutations
set_from_index = lambda item:
def set(map(itemgetterpairwise(1)*variables, item)**kwargs):
dual_intersection = lambda item, index: next(dropwhile(lambda x: x[1]num_vars != index, item))
def pw_vals_onlylen(buildervariables):
 """
 Convert a set generated by#Organize the builder algorithm
 to a tuple of testing values"""
  variable states into pairs for comparisons
 resultindexed_vars = tuple(map(itemgetter(0), sorted(builder, key=itemgetter(1))))
 return result
def pairwise(*variables):
 """
 Generate a complete set of pairwise combinations.
 In other words, a minimal spanning set where all
 parameter options are paired up against another option
 at least once."""
 
 #Convert variables to tuples with 2nd element denoting original position
 #For this algorithm, variable sequences must be sorted by length
 r_vars = sorted((list(zip[(varindex, repeat(i))v) for v in vals] for iindex, varvals in enumerate(variables)),
 key=len,
 reverse=True)
 num_vars = len(r_vars)

 #Used to fill in missing elements in the final step of the algorithm
 new_ordervar_combos = dict(zipcombinations(range(num_vars), (i[0][1] for i in r_vars))2)
 #Build the sequence of pairs
 pw_tablevar_products = zip_longest(*(product(r_vars[a]indexed_vars[i], r_vars[b]indexed_vars[j]) for i, j in var_combos)
 vars = chain.from_iterable(zip_longest(*var_products))
 for a, b#Initialize inthe combinations(range(num_vars),builder 2)))list
 pw_chainbuilders = chain.from_iterable(pw_table)
[]
 seen_pairs = set()
 builder_sets#Main =algorithm []
loop
 for pair in pw_chainvars:
 if not pair or pair in seen_pairs:
 continue
 p_set = set(pair)
 p_index_setd_pair = set_from_indexdict(pair)
 #Linear search ofthrough allbuilders ofto thefind "underan construction"empty setsslot
 #to#First seepass: iflook therefor issets freethat slothave intersections
 for index, builder in enumerate(builder_sets)builders:
 intersection = builder.keys() & d_pair.keys()
 index_inters = p_index_set & set_from_indexif len(builderintersection) == 1:
 #See if the set intersectionv is= non-emptyintersection.pop()
 if (len(index_inters)builder[v] == 1)d_pair[v]:
 [t_index] = list builder.update(index_intersd_pair)
 if (dual_intersection(p_set, t_index) == dual_intersection(builder,#Update t_index)):
seen pairs
 if lenseen_pairs.update(combinations(builder) == .items(num_vars -), 12):)
 break
 #every variable is represented in builder setelse:
 #Second Pass
 del builder_sets[index]
 for builder in builders:
 combinedintersection = p_set | builder.keys() & d_pair.keys()
 if yieldnot pw_vals_onlylen(combinedintersection):
 breakbuilder.update(d_pair)
 else:
#Update seen pairs
 seen_pairs.update(combinations(builder.items(), 2))
 #builder set is still missing variables
 break
 builder &= p_setelse:
 #No empty slots/pairs identified. Make combineda =new builder
 builders.append(d_pair)
 break
 else:seen_pairs.add(pair)
 #Fill in remaining values
 #No matchescomplete_set found,= makeset(range(num_vars))
 a new builder
 defaults = {var[0][0]: var[0][1] for var in indexed_vars}
 builder_sets.append(p_set)
 for builder in builders:
 combined = p_set
 if len(builder) == num_vars:
 sorted_vars = sorted(combined, key=lambda x: new_order[x[1]])
 yield tuple(val for index, val in seen_pairs.updatesorted(combinationsbuilder.items(sorted_vars, 2)))
 #fill in remaining builders with default valueselse:
 defaults = {i: variables[i][0] for i in range(num_vars)}
 for key in def_keyscomplete_set =- defaultsbuilder.keys():
 for builder in builder_sets:
 for missingbuilder[key] in= def_keysdefaults[key]
 - set_from_index(builder):
 yield tuple(val for index, val in sorted(builder.add(items(defaults[missing], missing)))
 yield pw_vals_only(builder)
from operator import itemgetter
from itertools import (repeat,
 combinations, product, zip_longest, chain, dropwhile)
set_from_index = lambda item: set(map(itemgetter(1), item))
dual_intersection = lambda item, index: next(dropwhile(lambda x: x[1] != index, item))
def pw_vals_only(builder):
 """
 Convert a set generated by the builder algorithm
 to a tuple of testing values"""
  
 result = tuple(map(itemgetter(0), sorted(builder, key=itemgetter(1))))
 return result
def pairwise(*variables):
 """
 Generate a complete set of pairwise combinations.
 In other words, a minimal spanning set where all
 parameter options are paired up against another option
 at least once."""
 
 #Convert variables to tuples with 2nd element denoting original position
 #For this algorithm, variable sequences must be sorted by length
 r_vars = sorted((list(zip(var, repeat(i))) for i, var in enumerate(variables)),
 key=len,
 reverse=True)
 num_vars = len(r_vars)

 #Used to fill in missing elements in the final step of the algorithm
 new_order = dict(zip(range(num_vars), (i[0][1] for i in r_vars)))
 #Build the sequence of pairs
 pw_table = zip_longest(*(product(r_vars[a], r_vars[b]) for a, b in combinations(range(num_vars), 2)))
 pw_chain = chain.from_iterable(pw_table)

 seen_pairs = set()
 builder_sets = []

 for pair in pw_chain:
 if not pair or pair in seen_pairs:
 continue
 p_set = set(pair)
 p_index_set = set_from_index(pair)
 #Linear search of all of the "under construction" sets
 #to see if there is free slot
 for index, builder in enumerate(builder_sets):
 index_inters = p_index_set & set_from_index(builder)
 #See if the set intersection is non-empty
 if (len(index_inters) == 1):
 [t_index] = list(index_inters)
 if (dual_intersection(p_set, t_index) == dual_intersection(builder, t_index)):
 if len(builder) == (num_vars - 1):
 #every variable is represented in builder set
 del builder_sets[index]
 combined = p_set | builder yield pw_vals_only(combined)
 break
 else:
 #builder set is still missing variables
 builder &= p_set
 combined = builder
 break
 else:
 #No matches found, make a new builder
 builder_sets.append(p_set)
 combined = p_set
 sorted_vars = sorted(combined, key=lambda x: new_order[x[1]])
 seen_pairs.update(combinations(sorted_vars, 2))
 #fill in remaining builders with default values
 defaults = {i: variables[i][0] for i in range(num_vars)}
 def_keys = defaults.keys()
 for builder in builder_sets:
 for missing in def_keys - set_from_index(builder):
 builder.add((defaults[missing], missing))
 yield pw_vals_only(builder)
from operator import itemgetter
from itertools import combinations, product, zip_longest, chain, permutations

def pairwise(*variables, **kwargs):
 num_vars = len(variables)
 #Organize the variable states into pairs for comparisons
 indexed_vars = sorted(
 ([(index, v) for v in vals] for index, vals in enumerate(variables)),
 key=len,
 reverse=True)
 
 var_combos = combinations(range(num_vars), 2)
 var_products = (product(indexed_vars[i], indexed_vars[j]) for i, j in var_combos)
 vars = chain.from_iterable(zip_longest(*var_products))
 #Initialize the builder list
 builders = []
 seen_pairs = set()
 #Main algorithm loop
 for pair in vars:
 if not pair or pair in seen_pairs:
 continue
 d_pair = dict(pair)
 #Linear search through builders to find an empty slot
 #First pass: look for sets that have intersections
 for builder in builders:
 intersection = builder.keys() & d_pair.keys()
 if len(intersection) == 1:
 v = intersection.pop()
 if builder[v] == d_pair[v]:
  builder.update(d_pair)
 #Update seen pairs
 seen_pairs.update(combinations(builder.items(), 2))
 break
 else:
 #Second Pass
 for builder in builders:
 intersection = builder.keys() & d_pair.keys()
 if not len(intersection):
 builder.update(d_pair)
 #Update seen pairs
 seen_pairs.update(combinations(builder.items(), 2))
 break
 else:
 #No empty slots/pairs identified. Make a new builder
 builders.append(d_pair)
 seen_pairs.add(pair)
 #Fill in remaining values
 complete_set = set(range(num_vars))
 defaults = {var[0][0]: var[0][1] for var in indexed_vars}
 
 for builder in builders:
 if len(builder) == num_vars:
 yield tuple(val for index, val in sorted(builder.items()))
 else:
 for key in complete_set - builder.keys():
 builder[key] = defaults[key]
 yield tuple(val for index, val in sorted(builder.items()))
 
Notice added Draw attention by Joel Cornett
Bounty Started worth 50 reputation by Joel Cornett
Tweeted twitter.com/#!/StackCodeReview/status/229806414674153472
Source Link

Pairwise Combinations Generator

Below is an algorithm to generate all pairwise combinations for a set of random variables. See here for an explanation of what constitutes a minimal all-pairs set. The algorithm below works but I feel that it is inefficient. Any suggestions would be welcome.

from operator import itemgetter
from itertools import (repeat,
 combinations,
 product,
 zip_longest,
 chain,
 dropwhile)
set_from_index = lambda item: set(map(itemgetter(1), item))
dual_intersection = lambda item, index: next(dropwhile(lambda x: x[1] != index, item))
def pw_vals_only(builder):
 """
 Convert a set generated by the builder algorithm
 to a tuple of testing values"""
 
 result = tuple(map(itemgetter(0), sorted(builder, key=itemgetter(1))))
 return result
def pairwise(*variables):
 """
 Generate a complete set of pairwise combinations.
 In other words, a minimal spanning set where all
 parameter options are paired up against another option
 at least once."""
 
 #Convert variables to tuples with 2nd element denoting original position
 #For this algorithm, variable sequences must be sorted by length
 r_vars = sorted((list(zip(var, repeat(i)))
 for i, var in enumerate(variables)),
 key=len,
 reverse=True)
 num_vars = len(r_vars)
 #Used to fill in missing elements in the final step of the algorithm
 new_order = dict(zip(range(num_vars), (i[0][1] for i in r_vars)))
 #Build the sequence of pairs
 pw_table = zip_longest(*(product(r_vars[a], r_vars[b])
 for a, b in combinations(range(num_vars), 2)))
 pw_chain = chain.from_iterable(pw_table)
 seen_pairs = set()
 builder_sets = []
 for pair in pw_chain:
 if not pair or pair in seen_pairs:
 continue
 
 p_set = set(pair)
 p_index_set = set_from_index(pair)
 #Linear search of all of the "under construction" sets
 #to see if there is free slot
 for index, builder in enumerate(builder_sets):
 
 index_inters = p_index_set & set_from_index(builder)
 #See if the set intersection is non-empty
 if (len(index_inters) == 1):
 [t_index] = list(index_inters)
 if (dual_intersection(p_set, t_index) == dual_intersection(builder, t_index)):
 if len(builder) == (num_vars - 1):
 #every variable is represented in builder set
 del builder_sets[index]
 combined = p_set | builder
 yield pw_vals_only(combined)
 break
 else:
 #builder set is still missing variables
 builder &= p_set
 combined = builder
 break
 else:
 #No matches found, make a new builder
 builder_sets.append(p_set)
 combined = p_set
 sorted_vars = sorted(combined, key=lambda x: new_order[x[1]])
 seen_pairs.update(combinations(sorted_vars, 2))
 #fill in remaining builders with default values
 defaults = {i: variables[i][0] for i in range(num_vars)}
 def_keys = defaults.keys()
 for builder in builder_sets:
 for missing in def_keys - set_from_index(builder):
 builder.add((defaults[missing], missing))
 yield pw_vals_only(builder)

Usage Example:

u_vars = [
 [1, 2, 3, 4],
 ["A", "B", "C"],
 ["a", "b"],
 ]
result = list(pairwise(*u_vars))
print(result) #prints list of length 12
lang-py

AltStyle によって変換されたページ (->オリジナル) /