2017-10-20 17 views
0

J'ai un algorithme assez spécifique que je veux suivre.Définition de la valeur du cadre de données pandas en fonction des conditions de ligne et de colonne

Fondamentalement, j'ai une trame de données comme suit:

 month taken score 
1  1  2  23 
2  1  1  34 
3  1  2  12 
4  1  2  59 
5  2  1  12 
6  2  2  23 
7  2  1  43 
8  2  2  45 
9  3  1  43 
10  3  2  43 
11  4  1  23 
12  4  2  94 

Je veux faire en sorte que la colonne « score » est changé à 100 jours où prendre == 2 en continu jusqu'à la fin de ce mois. Ainsi, toutes les occurrences de prise == 2 ont leur score fixé à 100, le cas échéant jour suivant au cours de ce mois a == pris 1.

donc le résultat que je veux est:

 month taken score 
1  1  2  23 
2  1  1  34 
3  1  2  100 
4  1  2  100 
5  2  1  12 
6  2  2  23 
7  2  1  43 
8  2  2  100 
9  3  1  43 
10  3  2  43 
11  3  1  23 
12  3  2  100 
13  4  1  32 
14  4  2  100 

J'ai écrit ce code que je me sens devrait le faire:

#iterate through months 
for month in range(12): 
    #iterate through scores 
    for score in range(len(df_report.loc[df_report['month'] == month+1])): 
     #starting from the bottom, of that month, if 'taken' == 2... 
     if df_report.loc[df_report.month==month+1, 'taken'].iloc[-score-1] == 2: 
      #then set the score to 100 
      df_report.loc[df_report.month==month+1, 'score'].iloc[-score-2] = 100 
     #if you run into a 'taken' == 1, move on to next month 
     else: break 

Cependant, cela ne semble pas modifier les valeurs, en dépit de ne pas jeter une erreur ... il ne me donne pas une erreur à propos de la définition de valeurs pour une image copiée.

Quelqu'un pourrait-il expliquer ce que je fais mal?

+0

Si je devais deviner, ce serait que vous êtes définir une copie d'une copie avec une nouvelle valeur. Chaining * loc calls n'est pas la meilleure idée. –

+0

Je pense que vous avez raison, mais comment puis-je résoudre ce problème? Aussi, si .loc n'est pas une copie, et .iloc n'est pas une copie, alors pourquoi un .iloc d'un .loc une copie?! – James

Répondre

2

La raison de vos valeurs ne sont pas mises à jour est que l'affectation à iloc met à jour la copie retournée par le précédent loc appel, de sorte que le ne soit pas touché d'origine.


Voici comment j'aborderais cela. D'abord, définissez une fonction foo.

def foo(df): 
    for i in reversed(df.index): 
     if df.loc[i, 'taken'] != 2: 
      break 
     df.loc[i, 'score'] = 100 
     i -= 1 
    return df 

Maintenant, groupbymonth et appelez foo:

df = df.groupby('month').apply(foo) 
print(df) 
    month taken score 
1  1  2  23 
2  1  1  34 
3  1  2 100 
4  1  2 100 
5  2  1  12 
6  2  2  23 
7  2  1  43 
8  2  2 100 
9  3  1  43 
10  3  2 100 
11  4  1  23 
12  4  2 100 

De toute évidence, apply a ses défauts, mais je ne peux pas penser à une approche vectorisé à ce problème.

+0

Moi non plus. Je peux me débarrasser de pour la boucle mais pas l'approche avec appliquer en groupby – Dark

+0

Merci, cela fonctionne parfaitement – James

2

Vous pouvez faire

import numpy as np 
def get_value(x): 
    s = x['taken'] 
    # Get a mask of duplicate sequeence and change values using np.where 
    mask = s.ne(s.shift()).cumsum().duplicated(keep=False) 
    news = np.where(mask,100,x['score']) 

    # if last number is 2 then change the news value to 100 
    if s[s.idxmax()] == 2: news[-1] = 100 
    return pd.Series(news) 

df['score'] = df.groupby('month').apply(get_value).values 

Sortie:

 
    month taken score 
1  1  2  23 
2  1  1  34 
3  1  2 100 
4  1  2 100 
5  2  1  12 
6  2  2  23 
7  2  1  43 
8  2  2 100 
9  3  1  43 
10  3  2 100 
11  4  1  23 
12  4  2 100 

vitesse presque identique, mais @coldspeed est vainqueur

ndf = pd.concat([df]*10000).reset_index(drop=True) 

%%timeit 
ndf['score'] = ndf.groupby('month').apply(foo) 
10 loops, best of 3: 40.8 ms per loop 


%%timeit 
ndf['score'] = ndf.groupby('month').apply(get_value).values 
10 loops, best of 3: 42.6 ms per loop 
+1

Bien sûr, cela vaut mieux qu'une simple itération? –

+0

Je ne sais pas besoin de vérifier la vitesse – Dark

+0

@ cᴏʟᴅsᴘᴇᴇᴅ son très très bizarre. La différence est de 2 millisecondes, peu importe la taille de la dataframe dans mon PC. – Dark