Tengo el siguiente df:

     usersidid  clienthostid    LoginDaysSumLastMonth   LoginDaysSumLast7Days LoginDaysSum
0       9            1                50                          7              1728
1       3            1                43                          3              1331
2       6            1                98                          9               216
3       4            1                10                          6                64
4       9            2                64                          32              343
5       12           3                45                          43             1000
6       8            3                87                          76              512
7       9            3                16                          3              1200

Lo que intento hacer es:

Por cada 'clienthostid' busque el 'usersidid' con el 'LoginDaysSum' más alto, verifico si hay un useridid ​​que sea el LoginDaysSum más alto en dos clienthostid diferentes (por ejemplo, usersidid = 9 es el LoginDaysSum más alto en ambos clienthostid 1, 2 y 3 en las filas 0, 4 y 7 según corresponda).

En este caso, quiero elegir el LoginDaysSum más alto (en el ejemplo sería la fila con 1728), llamémoslo maxRT.

Quiero calcular la proporción de LoginDaysSumLast7Days entre maxRT y cada una de las otras filas (en ejemplo, serían las filas índice 7 y 4).

Si la proporción es inferior a 0,8, quiero eliminar la fila:

Index 4- LoginDaysSumLast7Days_ratio = 7/32 <0.8 // ¡la fila caerá!

Índice 7- LoginDaysSumLast7Days_ratio = 7/3> 0.8 // ¡la fila permanecerá!

La misma condición también se aplicará a LoginDaysSumLastMonth.

Entonces, para el ejemplo, el resultado será:

     usersidid  clienthostid    LoginDaysSumLastMonth   LoginDaysSumLast7Days LoginDaysSum
0       9            1                50                          7              1728
1       3            1                43                          3              1331
2       6            1                98                          9               216
3       4            1                10                          6                64
5       12           3                45                          43             1000
6       8            3                87                          76              512
7       9            3                16                          3              1200

Ahora, aquí está el inconveniente: el rendimiento es fundamental. Traté de implementarlo usando .apply pero no solo no pude hacerlo funcionar correctamente, también funcionó demasiado lento :(

Mi código hasta ahora (perdóname si está escrito terriblemente mal, solo comencé a trabajar por primera vez con SQL, Pandas y Python la semana pasada y todo lo que aprendí es de los ejemplos que encontré aquí ^ _ ^):

df_client_Logindayssum_pairs = df.merge(df.groupby(['clienthostid'], as_index=False, sort=False)['LoginDaysSum'].max(),df, how='inner', on=['clienthostid', 'LoginDaysSum'])
    UsersWithMoreThan1client = df_client_Logindayssum_pairs.groupby(['usersidid'], as_index=False, sort=False)['LoginDaysSum'].count().rename(columns={'LoginDaysSum': 'NumOfClientsPerUesr'})
    UsersWithMoreThan1client = UsersWithMoreThan1client[UsersWithMoreThan1client.NumOfClientsPerUesr >= 2]
    UsersWithMoreThan1client = df_client_Logindayssum_pairs[df_client_Logindayssum_pairs.usersidid.isin(UsersWithMoreThan1Device.loc[:, 'usersidid'])].reset_index(drop=True)
    UsersWithMoreThan1client = UsersWithMoreThan1client.sort_values(['clienthostid', 'LoginDaysSum'], ascending=[True, False], inplace=True)
    UsersWithMoreThan1client = ttm.groupby(['clienthostid'], sort=False)['LoginDaysSumLast7Days'].apply(lambda x: x.iloc[0] / x.iloc[1]).reset_index(name='ratio')
    UsersWithMoreThan1client = UsersWithMoreThan1client[UsersWithMoreThan1client.ratio > 0.8]
    UsersWithMoreThan1client = ttm.groupby(['clienthostid'], sort=False)['LoginDaysSumLastMonth'].apply(lambda x: x.iloc[0] / x.iloc[1]).reset_index(name='ratio2')
    UsersWithMoreThan1client = UsersWithMoreThan1client[UsersWithMoreThan1client.ratio2 > 0.8]

Agradecería mucho cualquier sugerencia sobre cómo hacerlo.

Gracias

0
O. San 16 ene. 2017 a las 17:56
¿Puede mostrar su salida deseada con el ejemplo que ha dado y también mostrar su código usando aplicar?
 – 
IanS
16 ene. 2017 a las 18:25
Gracias, edité mi Q en consecuencia
 – 
O. San
16 ene. 2017 a las 18:56

1 respuesta

La mejor respuesta

Creo que esto es lo que necesitas:

# Put the index as a regular column
data = data.reset_index()
# Find greates LoginDaysSum for each clienthostid
agg1 = data.sort_values(by='LoginDaysSum', ascending=False).groupby(['clienthostid']).first()
# Collect greates LoginDaysSum for each usersidid
agg2 = agg1.sort_values(by='LoginDaysSum', ascending=False).groupby('usersidid').first()
# Join both previous aggregations
joined = agg1.set_index('usersidid').join(agg2, rsuffix='_max')
# Compute ratios
joined['LoginDaysSumLast7Days_ratio'] = joined['LoginDaysSumLast7Days_max'] / joined['LoginDaysSumLast7Days']
joined['LoginDaysSumLastMonth_ratio'] = joined['LoginDaysSumLastMonth_max'] / joined['LoginDaysSumLastMonth']
# Select index values that do not meet the required criteria
rem_idx = joined[(joined['LoginDaysSumLast7Days_ratio'] < 0.8) | (joined['LoginDaysSumLastMonth_ratio'] < 0.8)]['index']
# Restore index and remove the selected rows
data = data.set_index('index').drop(rem_idx)

El resultado en data es:

       usersidid  clienthostid  LoginDaysSumLastMonth  LoginDaysSumLast7Days    LoginDaysSum  
index                                                                                         
0              9             1                     50                      7            1728  
1              3             1                     43                      3            1331  
2              6             1                     98                      9             216  
3              4             1                     10                      6              64  
5             12             3                     45                     43            1000  
6              8             3                     87                     76             512  
7              9             3                     16                      3            1200
2
jdehesa 16 ene. 2017 a las 20:25
Esa entrada me ayudó a resolver aún más problemas, ¡gracias!
 – 
O. San
17 ene. 2017 a las 09:01
@ O.San No hay problema :) Si la respuesta resuelve su problema, considere marcarlo como respuesta aceptada, ¡gracias!
 – 
jdehesa
17 ene. 2017 a las 13:01