All combinations of set of dictionaries into K N-sized groups
我认为这很简单,但不幸的是,事实并非如此。
我正在尝试构建一个函数来获取一个可检索的字典(即唯一字典列表),并返回一个字典的唯一分组列表。
如果我有CMSDK的这个问题和答案集是我能找到的最接近解决方案的东西。在将它从处理字母串到字典的过程中,我发现我的python技能不足。
我正在调整的原始功能来自第二个答案:
1 2 3 4 5 6 7 8 | import itertools as it def unique_group(iterable, k, n): """Return an iterator, comprising groups of size `k` with combinations of size `n`.""" # Build separate combinations of `n` characters groups = ("".join(i) for i in it.combinations(iterable, n)) # 'AB', 'AC', 'AD', ... # Build unique groups of `k` by keeping the longest sets of characters return (i for i in it.product(groups, repeat=k) if len(set("".join(i))) == sum((map(len, i)))) # ('AB', 'CD'), ('AB', 'CE'), ... |
我目前的适应(由于调用
1 2 3 4 | def unique_group(iterable, k, n): groups = [] groups.append((i for i in it.combinations(iterable, n))) return ( i for i in it.product(groups, repeat=k) if len(set(i)) == sum((map(len, i))) ) |
号
有一点背景:我正试图以程序化的方式将一组球员分成小组,根据他们的技能进行圣诞琐事。字典列表是由一个类似于
1 2 3 4 5 6 7 8 | - name: Patricia skill: 4 - name: Christopher skill: 6 - name: Nicholas skill: 7 - name: Bianca skill: 4 |
在
1 2 | players = [{'name':'Patricia', 'skill':4},{'name':'Christopher','skill':6}, {'name':'Nicholas','skill':7},{'name':'Bianca','skill':4}] |
。
所以我希望输出看起来像一个列表(其中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | ( # Team assignment grouping 1 ( # Team 1 ( {'name': 'Patricia', 'skill': 4}, {'name': 'Christopher', 'skill': 6} ), # Team 2 ( {'name': 'Nicholas', 'skill': 7}, {'name': 'Bianca', 'skill': 4} ) ), # Team assignment grouping 2 ( # Team 1 ( {'name': 'Patricia', 'skill': 4}, {'name': 'Bianca', 'skill': 4} ), # Team 2 ( {'name': 'Nicholas', 'skill': 7}, {'name': 'Christopher', 'skill': 6} ) ), ..., # More unique lists ) |
每个团队分配分组需要在各个团队中具有唯一的参与者(即,一个团队分配分组中的多个团队中不能有同一个参与者),并且每个团队分配分组需要是唯一的。
一旦我有了团队任务组合列表,我将总结每个小组的技能,取最高和最低技能之间的差异,并选择最高和最低技能之间差异最小的分组(有差异)。
我承认我不完全理解这个代码。我理解创建字符串中所有字母组合列表的第一个分配,以及在产品不包含不同组中相同字母的情况下查找产品的返回语句。
我最初的尝试是简单地使用
提前谢谢,圣诞快乐!
更新:经过大量的修改,我已经适应了这一点:
这个不行
1 2 3 4 5 6 | def unique_group(iterable, k, n): groups = [] groups.append((i for i in it.combinations(iterable, n))) return (i for i in it.product(groups, repeat=k)\ if len(list({v['name']:v for v in it.chain.from_iterable(i)}.values())) ==\ len(list([x for x in it.chain.from_iterable(i)]))) |
。
我有个虫子
1 2 3 4 5 6 7 8 | Traceback (most recent call last): File"./optimize.py", line 65, in <module> for grouping in unique_group(players, team_size, number_of_teams): File"./optimize.py", line 32, in <genexpr> v in it.chain.from_iterable(i)})) == len(list([x for x in File"./optimize.py", line 32, in <dictcomp> v in it.chain.from_iterable(i)})) == len(list([x for x in TypeError: tuple indices must be integers or slices, not str |
。
这让我很困惑,也让我明白我不知道我的代码在做什么。在IPython中,我获取了这个示例输出:
1 2 3 4 | assignment = ( ({'name': 'Patricia', 'skill': 4}, {'name': 'Bianca', 'skill': 4}), ({'name': 'Patricia', 'skill': 4}, {'name': 'Bianca', 'skill': 4}) ) |
这显然是不可取的,并制定了以下测试:
1 | len(list({v['name']:v for v in it.chain.from_iterable(assignment)})) == len([v for v in it.chain.from_iterable(assignment)]) |
。
正确响应
我理解
1 2 3 4 5 6 | >>> L=[ ... {'id':1,'name':'john', 'age':34}, ... {'id':1,'name':'john', 'age':34}, ... {'id':2,'name':'hanna', 'age':30}, ... ] >>> list({v['id']:v for v in L}.values()) |
在这种情况下,适应起来并不像我想象的那么容易,我意识到我真的不知道
在这里,我将使用集合来利用新的数据类。您可以通过在decorator中设置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | from dataclasses import dataclass from itertools import combinations from typing import FrozenSet import yaml @dataclass(order=True, frozen=True) class Player: name: str skill: int @dataclass(order=True, frozen=True) class Team: members: FrozenSet[Player] def total_skill(self): return sum(p.skill for p in self.members) def is_valid(grouping): players = set() for team in grouping: for player in team.members: if player in players: return False players.add(player) return True def max_team_disparity(grouping): return max( abs(t1.total_skill() - t2.total_skill()) for t1, t2 in combinations(grouping, 2) ) def best_team_matchups(player_file, k, n): with open(player_file) as f: players = set(Player(p['name'], p['skill']) for p in yaml.load(f)) player_combs = combinations(players, n) unique_teams = set(Team(frozenset(team)) for team in player_combs) valid_groupings = set(g for g in combinations(unique_teams, k) if is_valid(g)) for g in sorted(valid_groupings, key=max_team_disparity): print(g) best_team_matchups('test.yaml', k=2, n=4) |
示例输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | ( Team(members=frozenset({ Player(name='Chr', skill=6), Player(name='Christopher', skill=6), Player(name='Nicholas', skill=7), Player(name='Patricia', skill=4) })), Team(members=frozenset({ Player(name='Bia', skill=4), Player(name='Bianca', skill=4), Player(name='Danny', skill=8), Player(name='Nicho', skill=7) })) ) |
号
口述列表并不是一个很好的数据结构,它可以映射出你真正想要重新排列的内容、玩家的名字、他们各自的属性、技能等级。您应该先将dict列表转换为name-to-skill-mapping dict:
1 2 | player_skills = {player['name']: player['skill'] for player in players} # player_skills becomes {'Patricia': 4, 'Christopher': 6, 'Nicholas': 7, 'Blanca': 4} |
这样,您就可以从玩家库中递归地扣除
1 2 3 4 5 6 7 8 | from itertools import combinations def unique_group(iterable, k, n, groups=0): if groups == k: yield [] pool = set(iterable) for combination in combinations(pool, n): for rest in unique_group(pool.difference(combination), k, n, groups + 1): yield [combination, *rest] |
。
通过您的示例输入,
1 2 3 4 5 6 | [[('Blanca', 'Christopher'), ('Nicholas', 'Patricia')], [('Blanca', 'Nicholas'), ('Christopher', 'Patricia')], [('Blanca', 'Patricia'), ('Christopher', 'Nicholas')], [('Christopher', 'Nicholas'), ('Blanca', 'Patricia')], [('Christopher', 'Patricia'), ('Blanca', 'Nicholas')], [('Nicholas', 'Patricia'), ('Blanca', 'Christopher')]] |
您可以使用
1 2 3 | def variance(groups): total_skills = [sum(player_skills[player] for player in group) for group in groups] return max(total_skills) - min(total_skills) |
。
因此,
1 | [('Blanca', 'Nicholas'), ('Christopher', 'Patricia')] |
。