def block_shuffle_by_attr(x: List[Any], attrorder: List[str], start: int = None, end: int = None) -> None: """ Exactly as for block_shuffle_by_item, but by item attribute rather than item index number. For example: p = list(itertools.product("ABC", "xyz", "123")) q = [AttrDict({'one': x[0], 'two': x[1], 'three': x[2]}) for x in p] block_shuffle_by_attr(q, ['one', 'two', 'three']) """ item_attr_order = attrorder.copy() item_attr = item_attr_order.pop(0) # 1. Take copy sublist = x[start:end] # 2. Sort then shuffle in chunks sublist.sort(key=operator.attrgetter(item_attr)) unique_values = set(tuple(getattr(x, item_attr)) for x in sublist) chunks = [[ i for i, v in enumerate(sublist) if tuple(getattr(v, item_attr)) == value ] for value in unique_values] random.shuffle(chunks) indexes = flatten_list(chunks) sort_list_by_index_list(sublist, indexes) # 3. Call recursively (e.g. at the "xyz" level next) if item_attr_order: # more to do? starts_ends = [(min(chunk), max(chunk) + 1) for chunk in chunks] for s, e in starts_ends: block_shuffle_by_attr(sublist, item_attr_order, s, e) # 4. Write back x[start:end] = sublist
def shuffle_list_chunks(x: List[Any], chunksize: int) -> None: """ Divides a list into chunks and shuffles the chunks themselves (in place). For example: x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] shuffle_list_chunks(x, 4) x might now be [5, 6, 7, 8, 1, 2, 3, 4, 9, 10, 11, 12] ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^ """ starts = list(range(0, len(x), chunksize)) ends = starts[1:] + [len(x)] # Shuffle the indexes rather than the array, then we can write back # in place. chunks = [] for start, end in zip(starts, ends): chunks.append(list(range(start, end))) random.shuffle(chunks) indexes = flatten_list(chunks) sort_list_by_index_list(x, indexes)
def block_shuffle_by_attr(x: List[Any], attrorder: List[str], start: int = None, end: int = None) -> None: """ Exactly as for block_shuffle_by_item, but by item attribute rather than item index number. For example: p = list(itertools.product("ABC", "xyz", "123")) q = [AttrDict({'one': x[0], 'two': x[1], 'three': x[2]}) for x in p] block_shuffle_by_attr(q, ['one', 'two', 'three']) """ item_attr_order = attrorder.copy() item_attr = item_attr_order.pop(0) # 1. Take copy sublist = x[start:end] # 2. Sort then shuffle in chunks sublist.sort(key=operator.attrgetter(item_attr)) unique_values = set(tuple(getattr(x, item_attr)) for x in sublist) chunks = [ [ i for i, v in enumerate(sublist) if tuple(getattr(v, item_attr)) == value ] for value in unique_values ] random.shuffle(chunks) indexes = flatten_list(chunks) sort_list_by_index_list(sublist, indexes) # 3. Call recursively (e.g. at the "xyz" level next) if item_attr_order: # more to do? starts_ends = [(min(chunk), max(chunk) + 1) for chunk in chunks] for s, e in starts_ends: block_shuffle_by_attr(sublist, item_attr_order, s, e) # 4. Write back x[start:end] = sublist
def block_shuffle_by_item(x: List[Any], indexorder: List[int], start: int = None, end: int = None) -> None: """ Shuffles the list x hierarchically, in place. indexorder is a list of indexes of each item of x. The first index varies slowest; the last varies fastest. For example: p = list(itertools.product("ABC", "xyz", "123")) x is now a list of tuples looking like ('A', 'x', '1') block_shuffle_by_item(p, [0, 1, 2]) p might now look like: C z 1 } all values of "123" appear } first "xyz" block C z 3 } once, but randomized } C z 2 } } } C y 2 } next "123" block } C y 1 } } C y 3 } } } C x 3 } C x 2 } C x 1 } A y 3 } second "xyz" block ... } ... """ item_idx_order = indexorder.copy() item_idx = item_idx_order.pop(0) # 1. Take copy sublist = x[start:end] # 2. Sort then shuffle in chunks (e.g. at the "ABC" level) sublist.sort(key=operator.itemgetter(item_idx)) # Note below that we must convert things to tuples to put them into # sets; if you take a set() of lists, you get # TypeError: unhashable type: 'list' unique_values = set(tuple(x[item_idx]) for x in sublist) chunks = [[ i for i, v in enumerate(sublist) if tuple(v[item_idx]) == value ] for value in unique_values] random.shuffle(chunks) indexes = flatten_list(chunks) sort_list_by_index_list(sublist, indexes) # 3. Call recursively (e.g. at the "xyz" level next) if item_idx_order: # more to do? starts_ends = [(min(chunk), max(chunk) + 1) for chunk in chunks] for s, e in starts_ends: block_shuffle_by_item(sublist, item_idx_order, s, e) # 4. Write back x[start:end] = sublist
def block_shuffle_by_item(x: List[Any], indexorder: List[int], start: int = None, end: int = None) -> None: """ Shuffles the list x hierarchically, in place. indexorder is a list of indexes of each item of x. The first index varies slowest; the last varies fastest. For example: p = list(itertools.product("ABC", "xyz", "123")) x is now a list of tuples looking like ('A', 'x', '1') block_shuffle_by_item(p, [0, 1, 2]) p might now look like: C z 1 } all values of "123" appear } first "xyz" block C z 3 } once, but randomized } C z 2 } } } C y 2 } next "123" block } C y 1 } } C y 3 } } } C x 3 } C x 2 } C x 1 } A y 3 } second "xyz" block ... } ... """ item_idx_order = indexorder.copy() item_idx = item_idx_order.pop(0) # 1. Take copy sublist = x[start:end] # 2. Sort then shuffle in chunks (e.g. at the "ABC" level) sublist.sort(key=operator.itemgetter(item_idx)) # Note below that we must convert things to tuples to put them into # sets; if you take a set() of lists, you get # TypeError: unhashable type: 'list' unique_values = set(tuple(x[item_idx]) for x in sublist) chunks = [ [i for i, v in enumerate(sublist) if tuple(v[item_idx]) == value] for value in unique_values ] random.shuffle(chunks) indexes = flatten_list(chunks) sort_list_by_index_list(sublist, indexes) # 3. Call recursively (e.g. at the "xyz" level next) if item_idx_order: # more to do? starts_ends = [(min(chunk), max(chunk) + 1) for chunk in chunks] for s, e in starts_ends: block_shuffle_by_item(sublist, item_idx_order, s, e) # 4. Write back x[start:end] = sublist