Entonces estoy tratando de crear nuevos datos en una serie de tiempo basada en datos pasados. Por ejemplo, aquí tengo datos de jugadores y cada fila son estadísticas acumuladas a una determinada edad. Quiero crear una nueva fila en el marco de datos donde incremento la edad máxima en uno y luego tomo el promedio de las columnas sa y ga de los dos años anteriores.

Aquí están los datos.

import pandas as pd

data = [['Adam Wilcox', 8476330, 25, 14.0, 0.0],
        ['Adin Hill', 8478499, 21, 129.0, 14.0],
        ['Adin Hill', 8478499, 22, 322.0, 32.0],
        ['Adin Hill', 8478499, 23, 343.0, 28.0],
        ['Adin Hill', 8478499, 24, 530.0, 46.0],
        ['Adin Hill', 8478499, 25, 237.0, 26.0],
        ['Al Montoya', 8471219, 24, 120.0, 9.0],
        ['Al Montoya', 8471219, 26, 585.0, 46.0],
        ['Al Montoya', 8471219, 27, 832.0, 89.0],
        ['Al Montoya', 8471219, 28, 168.0, 17.0]]

model_df = pd.DataFrame(data, 
                         columns=['player', 'player_id', 'season_age', 'sa', 'ga'])

Por ejemplo, lo que me gustaría crear es ['Al Montoya', 8471219, 29, 500, 53] (recuerde que los dos últimos valores son el promedio de las columnas sa y ga de las edades de 28 y 27).

Lo logré usando iterrows y creando un nuevo marco de datos y agregando así:

max_ages = model_df.groupby(['player', 'player_id'])[['season_age']].max().reset_index()
added_ages = []
for player in max_ages.iterrows():

    row = [player[1][0],
           player[1][1],
           player[1][2] + 1, 
           (model_df[(model_df['player_id'] == player[1][1]) &
                    (model_df['season_age'] == player[1][2] - 1)]['sa'].sum() +
           model_df[(model_df['player_id'] == player[1][1]) &
                    (model_df['season_age'] == player[1][2] - 2)]['sa'].sum())/2,
           (model_df[(model_df['player_id'] == player[1][1]) &
                    (model_df['season_age'] == player[1][2] - 1)]['ga'].sum() +
           model_df[(model_df['player_id'] == player[1][1]) &
                    (model_df['season_age'] == player[1][2] - 2)]['ga'].sum())/2
          ]
    added_ages.append(row)

added_ages_df = pd.DataFrame(added_ages, 
                             columns=['player', 'player_id', 'season_age', 'sa', 'ga'])
model_df = pd.concat([model_df, added_ages_df])

Obviamente, esta es una solución ad hoc que es muy frágil, mi pregunta es si hay una forma incorporada en pandas de hacer esto sin usar iterrows

El marco de datos esperado se vería así más fácil de representar en forma de lista

data = [['Adam Wilcox', 8476330, 25, 14.0, 0.0],
        ['Adin Hill', 8478499, 21, 129.0, 14.0],
        ['Adin Hill', 8478499, 22, 322.0, 32.0],
        ['Adin Hill', 8478499, 23, 343.0, 28.0],
        ['Adin Hill', 8478499, 24, 530.0, 46.0],
        ['Adin Hill', 8478499, 25, 237.0, 26.0],
        ['Adin Hill', 8478499, 26, 502, 36],
        ['Al Montoya', 8471219, 24, 120.0, 9.0],
        ['Al Montoya', 8471219, 26, 585.0, 46.0],
        ['Al Montoya', 8471219, 27, 832.0, 89.0],
        ['Al Montoya', 8471219, 28, 168.0, 17.0],
        ['Al Montoya', 8471219, 29, 500, 53]]
3
Matthew Barlowe 27 nov. 2021 a las 06:51
¿Sería una solución más fácil obtener sus nombres / identificadores de jugadores únicos, crear un DataFrame de shell con la edad incrementándose para cada jugador desde la edad mínima hasta la edad máxima, lo que sea, luego fusionar su DataFrame inicial y calcular sus promedios? ¿El objetivo es que cada jugador tenga la misma edad mínima que la edad máxima, o el objetivo es que solo desee agregar 2 filas adicionales para cada jugador?
 – 
brb
27 nov. 2021 a las 07:33
Por favor publique su marco de datos de salida esperado
 – 
sammywemmy
27 nov. 2021 a las 07:46
Al corriente
 – 
Matthew Barlowe
27 nov. 2021 a las 08:21

3 respuestas

La mejor respuesta

Puede definir una función llamada add_row y pasarla a un groupby. Asumiré que si no hay dos años de datos para un jugador, querrá que las columnas sa y ga se llenen con NaN:

def add_row(x):
    last_row = x.iloc[-1]
    last_row['season_age'] = last_row['season_age']+1
    if len(x) < 2:
        last_row['sa'], last_row['ga'] = float("nan"), float("nan")
        return x.append(last_row)
    else:
        last_row['sa'], last_row['ga'] = x[['sa','ga']].iloc[-2:].mean()
        return x.append(last_row)

new_model_df = model_df.groupby("player").apply(add_row).reset_index(drop=True)

Salida:

>>> new_model_df
         player  player_id  season_age     sa    ga
0   Adam Wilcox    8476330          25   14.0   0.0
1   Adam Wilcox    8476330          26    NaN   NaN
2     Adin Hill    8478499          21  129.0  14.0
3     Adin Hill    8478499          22  322.0  32.0
4     Adin Hill    8478499          23  343.0  28.0
5     Adin Hill    8478499          24  530.0  46.0
6     Adin Hill    8478499          25  237.0  26.0
7     Adin Hill    8478499          26  383.5  36.0
8    Al Montoya    8471219          24  120.0   9.0
9    Al Montoya    8471219          26  585.0  46.0
10   Al Montoya    8471219          27  832.0  89.0
11   Al Montoya    8471219          28  168.0  17.0
12   Al Montoya    8471219          29  500.0  53.0
3
Derek O 27 nov. 2021 a las 08:34

Puede intentar algo como a continuación.

 df_new = df.shift()
 df_new['season_age'] = df['season_age'].max() +1
 df_new[['sa','ga']] = df[['sa','ga']].rolling(2).mean()
0
Derek O 27 nov. 2021 a las 08:03
Esto no funciona, sobrescribe la columna season_age con un solo valor y no calcula la fila agregada para Addin Hill correctamente
 – 
Matthew Barlowe
27 nov. 2021 a las 08:26

Haga algunos cálculos en el objeto agrupado y combine el resultado con model_df:

grouper = ['player', 'player_id']
grouped = model_df.groupby(grouper, sort = False)
tail = grouped.nth(-1) # get the last row per group
tail = tail.assign(season_age = tail.season_age + 1)
# get the average of the last two columns with rolling
# a second groupby is called here to get single rows per group
sa_ga = (group[['sa', 'ga']]
         .rolling(2)
         .mean()
         .groupby(grouper)
         .nth(-1)
         )

tail = tail.assign(**sa_ga).reset_index()

# final output
(pd.concat([model_df, tail])
   .sort_values(grouper, ignore_index = True)
)

         player  player_id  season_age     sa    ga
0   Adam Wilcox    8476330          25   14.0   0.0
1   Adam Wilcox    8476330          26    NaN   NaN
2     Adin Hill    8478499          21  129.0  14.0
3     Adin Hill    8478499          22  322.0  32.0
4     Adin Hill    8478499          23  343.0  28.0
5     Adin Hill    8478499          24  530.0  46.0
6     Adin Hill    8478499          25  237.0  26.0
7     Adin Hill    8478499          26  383.5  36.0
8    Al Montoya    8471219          24  120.0   9.0
9    Al Montoya    8471219          26  585.0  46.0
10   Al Montoya    8471219          27  832.0  89.0
11   Al Montoya    8471219          28  168.0  17.0
12   Al Montoya    8471219          29  500.0  53.0
0
sammywemmy 27 nov. 2021 a las 09:39