Как отслеживать неупорядоченные пары в фрейме данных `pandas`

#python #pandas #pandas-groupby

#python #pandas #pandas-groupby

Вопрос:

У меня есть a pd.Dataframe со столбцами R_fighter — имя первого бойца, B_fighter — имя второго бойца и Winner столбец. Данные сортируются в хронологическом порядке, и я хотел бы добавить столбец, где, если бойцы ранее встречались, и R боец выиграл, чтобы установить значение -1 , если B боец выиграл — 1 , и 0 в противном случае. Если было гарантировано, что бойцы могут встретиться снова в том же порядке ( R_fighter снова R_fighter , B_fighter снова B_fighter ), тогда можно сделать следующее:

 last_winner_col = np.zeros(df_train.shape[0])

for x in df_train.groupby(['R_fighter', 'B_fighter'])['Winner']:
    last_winner = 0
    for idx, val in zip(x[1].index, x[1].values):
        last_winner_col[idx] = last_winner
        last_winner = 2 * val - 1
  

И добавьте результат pd.Series в набор данных. Однако их роль может измениться в последующей битве. Решение, которое приходит мне на ум, очень длинное и громоздкое. Я был бы признателен, если бы кто-нибудь предложил удобный способ отслеживания предыдущего победителя, чтобы учесть возможность изменения порядка бойцов?

Ответ №1:

Вы можете создать «отсортированную» версию ваших двух комбатантов и использовать ее:

 import pandas as pd

a = list("ABCDEFGH1234")
b = list("12341234ABCD")
win = list("ABCD12341234")

df = pd.DataFrame({"R_fighter":a, "B_fighter":b, "Winner":win})

# make a column with fixed order
df["combatants"] = df[['R_fighter', 'B_fighter']].apply(lambda x: sorted(x), axis=1)

# or simply set the result
df["w"] = df[['R_fighter', 'B_fighter', 'Winner']].apply(lambda x: '-1' 
                                                         if x[2]==x[0] 
                                                         else ('1' if x[2]==x[1] 
                                                               else '0'), axis=1 )
print(df)
  

Вывод:

    R_fighter    B_fighter     Winner    combatants      w
0          A            1          A        [1, A]     -1
1          B            2          B        [2, B]     -1
2          C            3          C        [3, C]     -1
3          D            4          D        [4, D]     -1
4          E            1          1        [1, E]      1
5          F            2          2        [2, F]      1
6          G            3          3        [3, G]      1
7          H            4          4        [4, H]      1
8          1            A          1        [1, A]     -1
9          2            B          2        [2, B]     -1
10         3            C          3        [3, C]     -1
11         4            D          4        [4, D]     -1
  

Чтобы определить победителя на основе 'combatants' (который содержит отсортированные имена), вы можете сделать:

 df["w_combatants"] = df[['combatants', 'Winner']].apply(lambda x: '-1' 
                                                        if x[1]==x[0][0] 
                                                        else ('1' if x[1]==x[0][1] 
                                                                  else '0'), axis=1 )
  

чтобы получить

    R_fighter    B_fighter    Winner    combatants      w    w_combatants
0          A            1         A        [1, A]     -1               1
1          B            2         B        [2, B]     -1               1
2          C            3         C        [3, C]     -1               1
3          D            4         D        [4, D]     -1               1
4          E            1         1        [1, E]      1              -1
5          F            2         2        [2, F]      1              -1
6          G            3         3        [3, G]      1              -1
7          H            4         4        [4, H]      1              -1
8          1            A         1        [1, A]     -1              -1
9          2            B         2        [2, B]     -1              -1
10         3            C         3        [3, C]     -1              -1
11         4            D         4        [4, D]     -1              -1
  

Ответ №2:

На основании ответа @Patrick Artner я предложил следующее решение:

 df_train[['fighters']] = df_train[['R_fighter', 'B_fighter']].apply(lambda x :tuple(sorted(x)), axis = 1)
df_train[['fighter_ord_changed']] = df_train[['R_fighter', 'B_fighter']].apply(lambda x : np.argsort(x)[0], axis = 1)

last_winner_col = np.zeros(df_train.shape[0])

for x in df_train.groupby('fighters')['Winner']:
    last_winner = 0
    for idx, val in zip(x[1].index, x[1].values):
        flag = df_train['fighter_ord_changed'][idx]
        last_winner_col[idx] = -last_winner if flag else last_winner
        last_winner = 2 * (val ^ flag) - 1