Я пытаюсь создать все возможные комбинации пар игроков, которые можно распределить по 4-мя командам по гольфу на основе гандикапа типа A, B, C или D.
Я пробовал различные методы itertools, такие как комбинации и перестановки, но не могу найти правильный подход.
from itertools import combinations, product, permutations
g = player_df.groupby(by = 'hcp_ABCD')
teams_listoflists = [group[1].index for group in g]
teams_combo_ndx = [player for player in permutations(teams_listoflists, 4)]
Вот мой стол панд:
handicap name hcp_ABCD
0 24 Player1 D
1 21 Player2 D
2 8 Player3 B
3 14 Player4 C
4 20 Player5 D
5 13 Player6 C
6 -1 Player7 A
7 5 Player8 A
8 8 Player9 B
9 6 Player10 B
10 20 Player11 D
11 15 Player12 C
12 0 Player13 A
13 12 Player14 C
14 0 Player15 A
15 10 Player16 B
Я хотел бы, чтобы на выходе были все комбинации (без дубликатов) комбинаций игроков (команд), чтобы каждая команда имела тип A, B, C и D на каждой. Эти выходные данные могут быть похожими на таблицы, сгруппированные по "опциям".
Изменение: добавляю этот выходной пример для ясности.
A Player B Player C Player D Player
option 1 team1 Player7 Player3 Player4 Player1
team2 Player8 Player9 Player6 Player2
team3 Player13 Player10 Player12 Player5
team4 Player15 Player16 Player14 Player11
option 2 team1 Player7 Player16 Player4 Player1
team2 Player8 Player3 Player6 Player2
team3 Player13 Player9 Player12 Player5
team4 Player15 Player10 Player14 Player11
...
A Player B Player C Player D Player
option n team1 Player7 Player3 Player4 Player11
team2 Player8 Player9 Player6 Player1
team3 Player13 Player10 Player12 Player2
team4 Player15 Player16 Player14 Player5
Смысл вышеизложенного состоит в том, что я пытаюсь найти генератор, который циклически просматривает все комбинации игроков в каждой группе гандикапа, чтобы комбинация опций команд была ясной.
Правка № 2 Я определил, что этот код создает комбинацию всех возможных командных комбинаций:
g = df.groupby(by = 'hcp_ABCD')
combinations = [list(group[1].index) for group in g]
Это создает список списков с игроками A в списке [0], игроками B в списке [1] и т.д.
И это получает индекс для всех возможных комбинаций команд:
from itertools import product
options = [option for option in product(*combinations)]
Но то, как я назначаю их в "опции" (см. Пример выше) и не допускаю дублирования, это то, на чем я застрял.
Редактирование # 3 Более простая версия (способ думать об этой проблеме) заключается в использовании следующих наборов:
A = ['A1', 'A2', 'A3', 'A4']
B = ['B1', 'B2', 'B3', 'B4']
C = ['C1', 'C2', 'C3', 'C4']
D= ['D1', 'D2', 'D3', 'D4']
По сути это делает то, что делает groupby выше (группировка по hcp_ABCD), но называет каждого "A Player", "B Player" и т.д.
возможные комбинации команд:
team_combinations = [team for team in product(A, B, C, D)]
затем следующий трюк состоит в том, чтобы назначить их комбинациям из 4 команд без дублирования игроков.
Спасибо за разъяснения по поводу ожидаемого результата. Вот мой ответ, который я проверял. Возможно, это не точный формат вашего ожидаемого результата, но я оставляю вам это исправить.
import pandas as pd
def is_duplicate_team(team, group):
'''check if an option already exists'''
return any(group == t for t in team)
def is_player_exists(group, arr):
'''check if a player exists in a group'''
return any(x in g for g in group for x in arr)
df = [ (24 ,'Player1','D'),
(21 ,'Player2','D'),
(8 ,'Player3','B'),
(14 ,'Player4','C'),
(20 ,'Player5','D'),
(13 ,'Player6','C'),
(-1 ,'Player7','A'),
(5 ,'Player8','A'),
(8 ,'Player9','B'),
(6 ,'Player10','B'),
(20 ,'Player11','D'),
(15 ,'Player12','C'),
(0 ,'Player13','A'),
(12 ,'Player14','C'),
(0 ,'Player15','A'),
(10 ,'Player16','B')]
df = pd.DataFrame(df, columns=['handicap', 'name', 'hcp_ABCD'])
from itertools import product
grouped = df.groupby('hcp_ABCD')['name'].apply(list).reset_index()
df_name = [n for n in grouped.name]
df_comb = [p for p in product(*df_name)]
# below code will get all combinations of groups and for a team having all players
teams=[]
for i in df_comb[:-1]:
group=[i]
for j in df_comb[1:]:
if not is_player_exists(group, j):
group.append(j)
if len(group) == 4:
if not is_duplicate_team(teams, group):
teams.append(group)
continue
# below code will print the output similar to what you expected
i=0
for t in teams:
i+=1
print('option: ', str(i) )
for p in t:
print(p)
Я сделал предложение в комментариях. Вот реализация:
import pandas as pd
from functools import reduce
data = [
(24,'Player1','D'),
(21,'Player2','D'),
(8,'Player3','B'),
(8,'Player4','B'),
(14,'Player5','C'),
(13,'Player6','C'),
(-1,'Player7','A'),
(5,'Player8','A')
]
df = pd.DataFrame(
data,
columns=['handicap', 'name', 'hcp_ABCD']
)
dfs = [
grp_df.drop(columns="hcp_ABCD")
.rename(columns={"name": f"player_{hndcp}",
"handicap": f"handicap_{hndcp}"})
for hndcp, grp_df in df.assign(key=1)
.groupby("hcp_ABCD")
]
result = reduce(
lambda left, right: left.merge(right, how="outer", on="key"),
dfs
).drop(columns="key")
print(result)
Выход:
handicap_A player_A handicap_B player_B handicap_C player_C handicap_D player_D
0 -1 Player7 8 Player3 14 Player5 24 Player1
1 -1 Player7 8 Player3 14 Player5 21 Player2
2 -1 Player7 8 Player3 13 Player6 24 Player1
3 -1 Player7 8 Player3 13 Player6 21 Player2
4 -1 Player7 8 Player4 14 Player5 24 Player1
5 -1 Player7 8 Player4 14 Player5 21 Player2
6 -1 Player7 8 Player4 13 Player6 24 Player1
7 -1 Player7 8 Player4 13 Player6 21 Player2
8 5 Player8 8 Player3 14 Player5 24 Player1
9 5 Player8 8 Player3 14 Player5 21 Player2
10 5 Player8 8 Player3 13 Player6 24 Player1
11 5 Player8 8 Player3 13 Player6 21 Player2
12 5 Player8 8 Player4 14 Player5 24 Player1
13 5 Player8 8 Player4 14 Player5 21 Player2
14 5 Player8 8 Player4 13 Player6 24 Player1
15 5 Player8 8 Player4 13 Player6 21 Player2
Следующий подход использует декартово произведение, а затем группируется дважды, чтобы распределить игроков по командам с уникальными гандикапами.
import pandas as pd
from pandas.compat import StringIO
print(pd.__version__)
pd.options.display.max_rows = 664
csvdata = StringIO("""handicap,name,hcp_ABCD
24,Player1,D
21,Player2,D
8,Player3,B
14,Player4,C
20,Player5,D
13,Player6,C
-1,Player7,A
5,Player8,A
8,Player9,B
6,Player10,B
20,Player11,D
15,Player12,C
0,Player13,A
12,Player14,C
0,Player15,A
10,Player16,B""")
df=pd.read_csv(csvdata)
# Generate all possible teams of unique handicap groups
# https://stackoverflow.com/questions/53699012/performant-cartesian-product-cross-join-with-pandas
def cartesian_product(left, right):
return (left.assign(key=1).merge(right.assign(key=1), on='key').drop('key', 1))
def distribute_players(x):
x['distribute'] = range(0, 4)
return x
df = cartesian_product(df, df.copy())
df = df.groupby(['name_x', 'hcp_ABCD_y']).apply(distribute_players)
df['team'] = df.groupby(['name_x', 'distribute']).ngroup()
print(df[['handicap_y','name_y','hcp_ABCD_y','team']].sort_values(['team']))
Используя pivot
затем stack
yourdf=df.pivot(*df).stack(dropna=False).fillna(0).reset_index()
Вы можете сначала создать список игроков в каждой группе гандикапа. Затем примените комбинации к этому.
grouped = df.groupby('hcp_ABCD')['name'].apply(list).reset_index()
grouped['combinations'] = grouped['name'].apply(lambda x: [player for player in combinations(x,2)]