Выберите один из каждого в группе панд

1

Я пытаюсь создать все возможные комбинации пар игроков, которые можно распределить по 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 команд без дублирования игроков.

  • 0
    Теперь я думаю, что itertools.product (g) может быть ключом к этому. это создает исчерпывающий список команд ABCD. Но он не назначает их в опции без дубликатов игроков, как указано выше.
  • 0
    @PMende, нет, это не то же самое. Я не пытаюсь попробовать. Я пытаюсь получить исчерпывающий список. Вопрос, на который вы указываете, заключается в получении 30% выборки.
Показать ещё 3 комментария
Теги:
pandas

5 ответов

1
Лучший ответ

Спасибо за разъяснения по поводу ожидаемого результата. Вот мой ответ, который я проверял. Возможно, это не точный формат вашего ожидаемого результата, но я оставляю вам это исправить.

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)
  • 0
    Я думаю, что это включает исчерпывающую опцию для всех конфигураций команды. Но он не распределяет команды на 4 команды так, чтобы отображалась каждая комбинация опций команды. Я собираюсь обновить мой вопрос, чтобы быть более ясным по этому вопросу.
  • 0
    Ну черт, ты прав. Это действительно дает результат. Я надеялся получить это в многоиндексный фрейм данных, но я согласен, это похоже на это.
Показать ещё 2 комментария
1

Я сделал предложение в комментариях. Вот реализация:

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
  • 0
    Спасибо за разъяснение @PMende. Но, если я правильно понял, ваш фрейм данных dfs включает в себя 4 фрейма данных из 4 игроков. Казалось бы, мой список опций приведенный выше пример будет длиться до 4! (факториал). Если в каждой команде по 4 игрока, а в каждой команде по 1 из каждой группы с гандикапом, у вас будет потенциал для гораздо большего количества комбинаций, чем то, что показывает ваша команда. Я не уверен, что общее количество комбинаций, но что-то вроде 4 варианта для игрока B, 4 варианта для игрока C и 4 варианта игрока D для каждого игрока.
  • 0
    @leeprevost Я показал только первые 5 строк, потому что их 256.
Показать ещё 2 комментария
0

Следующий подход использует декартово произведение, а затем группируется дважды, чтобы распределить игроков по командам с уникальными гандикапами.

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']))

  • 0
    Не ищу образец. Ищите исчерпывающую комбинацию вариантов конфигурации команд, чтобы каждая команда была «сбалансированной», то есть по 1 A, 1B, 1C и 1D игрока на каждого.
  • 0
    Обновлен, чтобы отражать создание исчерпывающей комбинации сбалансированных команд, используя краткий подход, включая точный формат вывода.
Показать ещё 1 комментарий
0

Используя pivot затем stack

yourdf=df.pivot(*df).stack(dropna=False).fillna(0).reset_index()
-2

Вы можете сначала создать список игроков в каждой группе гандикапа. Затем примените комбинации к этому.

grouped = df.groupby('hcp_ABCD')['name'].apply(list).reset_index()
grouped['combinations'] = grouped['name'].apply(lambda x: [player for player in combinations(x,2)]

Ещё вопросы

Сообщество Overcoder
Наверх
Меню