Reputation: 10033
I have 3 dataframes, each one having a list of players, by position (Forwards, Defenders and Goalkeepers):
FWD_df = pd.DataFrame({'name':['Keno','Pepê','Rhuan','Léo Natel','Pedro Raul','Wesley'],
'team':['Atlético-MG','Grêmio','Botafogo','Corinthians','Botafogo','Palmeiras'],
'adversary':['Fluminense','Botafogo','Grêmio','Athlético-PR','Grêmio','Coritiba'],
'position':['FWD', 'FWD', 'FWD', 'FWD', 'FWD', 'FWD'],
'open_price':[15.75, 14.05, 3.51, 5.83, 5.92, 4.25]})
MID_df = pd.DataFrame({'apelido':['Everton Ribeiro','Thiago Galhardo','Thiago Neves','Mateus Vital','Dodi','Zé Gabriel'],
'clube':['Flamengo','Internacional','Sport','Corinthians','Fluminense', 'Internacional'],
'adversário':['Bragantino','Sport','Internacional','Athlético-PR','Atlético-MG','Sport'],
'posicao':['MID', 'MID', 'MID','MID', 'MID', 'MID'],
'preco_open':[10.82, 6.46, 4.96, 8.20, 5.29, 7.88]}).sort_values('preco_open', ascending=False).reset_index(drop=True).copy()
WING_df = pd.DataFrame({'apelido':['Renê','Abner Vinícius','Marcos Rocha','Victor Luis','Fagner','Ramon'],
'clube':['Flamengo','Athlético-PR','Palmeiras','Botafogo','Corinthians', 'Flamengo'],
'adversário':['Bragantino','Corinthians','Coritiba','Grêmio','Athlético-PR','Bragantino'],
'posicao':['WING', 'WING', 'WING','WING', 'WING', 'WING'],
'preco_open':[10.82, 6.46, 4.96, 8.20, 5.29, 7.88]}).sort_values('preco_open', ascending=False).reset_index(drop=True).copy()
DEF_df = pd.DataFrame({'name':['Pedro Geromel','Felipe Melo','Pedro Henrique','Réver','Thiago Heleno','Lucas Veríssimo'],
'team':['Grêmio','Palmeiras','Athlético-PR','Atlético-MG','Athlético-PR', 'Santos'],
'adversary':['Botafogo','Coritiba','Corinthians','Fluminense','Corinthians','Atlético-GO'],
'position':['DEF', 'DEF', 'DEF','DEF', 'DEF', 'DEF'],
'open_price':[10.82, 6.46, 4.96, 8.20, 5.29, 7.88]})
GKP_df = pd.DataFrame({'name':['Jandrei','Jean','Muriel','Diego Cavalieri','Marcelo Lomba','Luan Polli'],
'team':['Athlético-PR','Atlético-GO','Fluminense','Botafogo','Internacional', 'Sport'],
'adversary':['Corinthians','Santos','Atlético-MG','Grêmio','Sport','Internacional'],
'position':['GKP', 'GKP', 'GKP','GKP', 'GKP', 'GKP'],
'open_price':[8.73, 8.88, 5.66, 5.70, 10.62, 4.00]})
Then I create a pool for those selected players, like so:
dfs = [FWD_df, DEF_df, GKP_df]
pool = pd.concat(dfs)
print (pool)
which prints:
name team adversary position open_price
0 Keno Atlético-MG Fluminense FWD 15.75
1 Pepê Grêmio Botafogo FWD 14.05
2 Rhuan Botafogo Grêmio FWD 3.51
3 Léo Natel Corinthians Athlético-PR FWD 5.83
4 Pedro Raul Botafogo Grêmio FWD 5.92
5 Wesley Palmeiras Coritiba FWD 4.25
0 Pedro Geromel Grêmio Botafogo DEF 10.82
1 Felipe Melo Palmeiras Coritiba DEF 6.46
2 Pedro Henrique Athlético-PR Corinthians DEF 4.96
3 Réver Atlético-MG Fluminense DEF 8.20
4 Thiago Heleno Athlético-PR Corinthians DEF 5.29
5 Lucas Veríssimo Santos Atlético-GO DEF 7.88
0 Jandrei Athlético-PR Corinthians GKP 8.73
1 Jean Atlético-GO Santos GKP 8.88
2 Muriel Fluminense Atlético-MG GKP 5.66
3 Diego Cavalieri Botafogo Grêmio GKP 5.70
4 Marcelo Lomba Internacional Sport GKP 10.62
5 Luan Polli Sport Internacional GKP 4.00
RULE:
The thing is I cannot have, from the top 3 forwards, a player facing any of the top 2 defenders nor the top 1 goalkeeper, ie, if he is his next 'adversary', like so:
name team adversary position open_price
2 Rhuan Botafogo Grêmio FWD 3.51
...
0 Pedro Geromel Grêmio Botafogo DEF 10.82
When this happens I need to keep the most valuable player (highest 'open_price') on its row position and move the cheapest one to its last grouped row index.
In the case above the defender costs more, so he stays where he is and we move the FWD down to last index on its position:
name team adversary position open_price
0 Keno Atlético-MG Fluminense FWD 15.75
1 Pepê Grêmio Botafogo FWD 14.05
2 Léo Natel Corinthians Athlético-PR FWD 5.83
3 Pedro Raul Botafogo Grêmio FWD 5.92
4 Wesley Palmeiras Coritiba FWD 4.25
5 Rhuan Botafogo Grêmio FWD 3.51 <---- was index 2, now 5
0 Pedro Geromel Grêmio Botafogo DEF 10.82
1 Felipe Melo Palmeiras Coritiba DEF 6.46
2 Pedro Henrique Athlético-PR Corinthians DEF 4.96
3 Réver Atlético-MG Fluminense DEF 8.20
4 Thiago Heleno Athlético-PR Corinthians DEF 5.29
5 Lucas Veríssimo Santos Atlético-GO DEF 7.88
0 Jandrei Athlético-PR Corinthians GKP 8.73
1 Jean Atlético-GO Santos GKP 8.88
2 Muriel Fluminense Atlético-MG GKP 5.66
3 Diego Cavalieri Botafogo Grêmio GKP 5.70
4 Marcelo Lomba Internacional Sport GKP 10.62
5 Luan Polli Sport Internacional GKP 4.00
But by doing so, now we have a Forward in the top 3 that will face a goalkeeper, which is also forbidden by the RULE:
name team adversary position open_price
2 Léo Natel Corinthians Athlético-PR FWD 5.83
...
0 Jandrei Athlético-PR Corinthians GKP 8.73
And since the goalkeeper costs more than the forward, he stays where he is and we move the forward down, like so:
name team adversary position open_price
0 Keno Atlético-MG Fluminense FWD 15.75
1 Pepê Grêmio Botafogo FWD 14.05
2 Pedro Raul Botafogo Grêmio FWD 5.92
3 Wesley Palmeiras Coritiba FWD 4.25
4 Rhuan Botafogo Grêmio FWD 3.51 <---- was index 5, now 4
5 Léo Natel Corinthians Athlético-PR FWD 5.83 <---- was index 2, now 5
0 Pedro Geromel Grêmio Botafogo DEF 10.82
1 Felipe Melo Palmeiras Coritiba DEF 6.46
2 Pedro Henrique Athlético-PR Corinthians DEF 4.96
3 Réver Atlético-MG Fluminense DEF 8.20
4 Thiago Heleno Athlético-PR Corinthians DEF 5.29
5 Lucas Veríssimo Santos Atlético-GO DEF 7.88
0 Jandrei Athlético-PR Corinthians GKP 8.73
1 Jean Atlético-GO Santos GKP 8.88
2 Muriel Fluminense Atlético-MG GKP 5.66
3 Diego Cavalieri Botafogo Grêmio GKP 5.70
4 Marcelo Lomba Internacional Sport GKP 10.62
5 Luan Polli Sport Internacional GKP 4.00
And again we will have to do this once more:
name team adversary position open_price
0 Keno Atlético-MG Fluminense FWD 15.75
1 Pepê Grêmio Botafogo FWD 14.05
2 Wesley Palmeiras Coritiba FWD 4.25 <---- was index 3, now 2
3 Rhuan Botafogo Grêmio FWD 3.51 <---- was index 4, now 3
4 Léo Natel Corinthians Athlético-PR FWD 5.83 <---- was index 5, now 4
5 Pedro Raul Botafogo Grêmio FWD 5.92 <---- was index 3, now 5
0 Pedro Geromel Grêmio Botafogo DEF 10.82
1 Felipe Melo Palmeiras Coritiba DEF 6.46
2 Pedro Henrique Athlético-PR Corinthians DEF 4.96
3 Réver Atlético-MG Fluminense DEF 8.20
4 Thiago Heleno Athlético-PR Corinthians DEF 5.29
5 Lucas Veríssimo Santos Atlético-GO DEF 7.88
0 Jandrei Athlético-PR Corinthians GKP 8.73
1 Jean Atlético-GO Santos GKP 8.88
2 Muriel Fluminense Atlético-MG GKP 5.66
3 Diego Cavalieri Botafogo Grêmio GKP 5.70
4 Marcelo Lomba Internacional Sport GKP 10.62
5 Luan Polli Sport Internacional GKP 4.00
Until we end up with the dataframe above, ie the final pool.
How do I do this conditional re ordering of rows? Can anyone point me in the right direction here?
Upvotes: 0
Views: 206
Reputation: 59274
You have a problem in which you need to find an equilibrium based on constraints.
I propose here a solution in which we do not work with pandas
, but rather pure python lists because pandas' overhead would only slow the solution down.
First of all, there's an obvious possiblity of an infinite loop. As such, you have to keep track of past arrangement configurations so as to know when you have made a circle and began a loop.
I have created a custom error InfeasableError
(for readability) which is raised whenever this happens. The rest of the code should be straightforward.
Setup
class InfeasableError(Exception):
pass
# These are the positions on the lists for team, adv and prices.
TEAM_POS = 2
ADVERSARY_POS = 3
PRICE_POS = 5
fwd = FWD_df.reset_index().values.tolist()
dfs = DEF_df.reset_index().values.tolist()
gkp = GKP_df.reset_index().values.tolist()
mid = MID_df.reset_index().values.tolist()
wig = WING_df.reset_index().values.tolist()
Now, the equilibrium-finding function:
def find_equilibrium(fwd, dfs, gkp, mid, wig, configs):
''' Finds an equilibrium for forward, defense and goalkeeper players.
'''
initial_config = get_config(fwd, dfs, gkp) # Initial configuration
# Below is the logic for FWD-DEF equilibria.
for i, fwd_player in enumerate(fwd[:3]):
for j, def_player in enumerate(dfs[:2]):
if fwd_player[ADVERSARY_POS] == def_player[TEAM_POS]:
if fwd_player[PRICE_POS] < def_player[PRICE_POS]:
del fwd[i]
fwd.append(fwd_player)
else:
del dfs[j]
dfs.append(def_player)
# Below is the logic for FWD-GKP equilibria.
gkp_player = gkp[0]
for i, fwd_player in enumerate(fwd[:3]):
if fwd_player[ADVERSARY_POS] == gkp_player[TEAM_POS]:
if fwd_player[PRICE_POS] < gkp_player[PRICE_POS]:
del fwd[i]
fwd.append(fwd_player)
else:
del gkp[0]
gkp.append(gkp_player)
final_config = get_config(fwd, dfs, gkp)
if initial_config == final_config:
# This means nothing changed. Equilibrium found!
return [*fwd, *dfs, *gkp]
if final_config in configs:
raise InfeasableError("There's no solution for an equilibrium.")
configs.append(final_config)
return find_equilibrium(fwd, dfs, gkp, mid, wig, configs)
def get_config(fwd, dfs, gkp):
''' Returns an ordered configuration of integers.
Each integer represents a unique player, and each sequence represents
a specific configuration of how these players are arranged.
'''
return (fwd[0][0], fwd[1][0], fwd[2][0], dfs[0][0], dfs[1][0], gkp[0][0])
solution = find_equilibrium(fwd, dfs, gkp, mid, wig, [])
The solution comes ready to be imported into pandas, if you need it:
>>> pd.DataFrame(solution)
0 1 2 3 4 5
0 0 Keno Atlético-MG Fluminense FWD 15.75
1 1 Pepê Grêmio Botafogo FWD 14.05
2 5 Wesley Palmeiras Coritiba FWD 4.25
3 2 Rhuan Botafogo Grêmio FWD 3.51
4 3 Léo Natel Corinthians Athlético-PR FWD 5.83
5 4 Pedro Raul Botafogo Grêmio FWD 5.92
6 0 Pedro Geromel Grêmio Botafogo DEF 10.82
7 1 Felipe Melo Palmeiras Coritiba DEF 6.46
8 2 Pedro Henrique Athlético-PR Corinthians DEF 4.96
9 3 Réver Atlético-MG Fluminense DEF 8.20
10 4 Thiago Heleno Athlético-PR Corinthians DEF 5.29
11 5 Lucas Veríssimo Santos Atlético-GO DEF 7.88
12 0 Jandrei Athlético-PR Corinthians GKP 8.73
13 1 Jean Atlético-GO Santos GKP 8.88
14 2 Muriel Fluminense Atlético-MG GKP 5.66
15 3 Diego Cavalieri Botafogo Grêmio GKP 5.70
16 4 Marcelo Lomba Internacional Sport GKP 10.62
17 5 Luan Polli Sport Internacional GKP 4.00
Upvotes: 1