J'ai le dataframe suivant pour lequel je veux créer une colonne nommée 'Value' en utilisant numpy pour une boucle rapide et en même temps faire référence à la valeur de ligne précédente dans la même colonne.
for i in range(len(df)):
if df.loc[i, "Is First?"] == "Yes":
df.loc[i, "Value"] = df.loc[i, "Inbound"] + df.loc[i, "Outbound"]
else:
df.loc[i, "Value"] = df.loc[i, "Value"].shift(-1) + df.loc[i, "Outbound"]
XXX
La formule de la colonne Value dans le pseudocode est:
if ['Is First?'] = 'Yes' then [Value] = [Inbound] + [Outbound] else [Value] = [Previous Value] - [Outbound]
La manière idéale de créer le La colonne de valeur en ce moment est de faire une boucle for et d'utiliser shift pour faire référence à la colonne précédente (que je ne suis pas en mesure de faire fonctionner). Mais comme je vais l'appliquer sur un ensemble de données géant, je souhaite utiliser la méthode de vectorisation numpy dessus.
Product Inbound Outbound Is First? Value 0 A 115 10 Yes 125 1 A 220 20 No 105 2 A 200 24 No 81 3 A 402 52 No 29 4 B 313 40 Yes 353 5 B 434 12 No 341 6 B 321 43 No 298 7 C 343 23 Yes 366 8 C 120 16 No 350
4 Réponses :
Ce n'est pas une tâche anodine, la difficulté réside dans les Non consécutifs. Il est nécessaire de regrouper les non consécutifs, le code ci-dessous devrait faire,
col_sum = df.Inbound+df.Outbound
mask_no = df['Is First?'].eq('No')
mask_yes = df['Is First?'].eq('Yes')
consec_no = mask_yes.cumsum()
result = col_sum.groupby(consec_no).transform('first')-df['Outbound'].where(mask_no,0).groupby(consec_no).cumsum()
Utiliser :
df.loc[df['Is First?'].eq('Yes'),'Value']=df['Inbound']+df['Outbound']
df.loc[~df['Is First?'].eq('Yes'),'Value']=df['Value'].fillna(0).shift().cumsum()-df.loc[~df['Is First?'].eq('Yes'),'Outbound'].cumsum()
C'est faux car le premier cum est calculé sur les groupes "Oui" .
Aller simple :
Vous pouvez utiliser np.subtract.accumulate avec transform
df['value'] = (df.Inbound + df.Outbound).where(df['Is First?'].eq('Yes'))
s = df['Is First?'].eq('Yes').cumsum()
s1 = df.value.ffill() - df.Outbound.shift(-1).groupby(s).cumsum().shift()
df['value'] = df.value.fillna(s1)
Out[1671]:
Product Inbound Outbound Is First? value
0 A 115 10 Yes 125.0
1 A 220 20 No 105.0
2 A 200 24 No 81.0
3 A 402 52 No 29.0
4 B 313 40 Yes 353.0
5 B 434 12 No 341.0
6 B 321 43 No 298.0
7 C 343 23 Yes 366.0
8 C 120 16 No 350.0
Autre moyen :
Attribuez une valeur à Oui . Créez des s groupid à utiliser pour groupby. Groupby et décalez Sortant pour calculer le cumulé, et soustrayez-le de la valeur «Oui» de chaque groupe. Enfin, utilisez-le pour fillna.
s = df['Is First?'].eq('Yes').cumsum()
df['value'] = ((df.Inbound + df.Outbound).where(df['Is First?'].eq('Yes'), df.Outbound)
.groupby(s)
.transform(np.subtract.accumulate))
Out[1749]:
Product Inbound Outbound Is First? value
0 A 115 10 Yes 125
1 A 220 20 No 105
2 A 200 24 No 81
3 A 402 52 No 29
4 B 313 40 Yes 353
5 B 434 12 No 341
6 B 321 43 No 298
7 C 343 23 Yes 366
8 C 120 16 No 350
Pourrait le rendre encore plus court si un nouveau produit correspond toujours à un Oui: df.loc [df ['Is First?']. Eq ('Yes'), 'Value'] = df.Inbound + df.Outbound df.loc [df ['Est le premier?']. eq ('Non'), 'Valeur'] = df.Value.ffill () - df.Outbound.shift (-1) .groupby (df.Product). c umsum (). shift ()
ah, je vois ce que tu veux dire. J'ai envisagé df.Product pour groupby. Cependant, j'ai décidé de ne pas le faire parce que la logique d'OP ne le dit jamais. Sa logique ne mentionne que les valeurs de 'Is First?' , donc je dois créer des s à utiliser pour groupby.
Code numpy annoté:
Product Inbound Outbound Is First? Value 0 A 115 10 Yes 125 1 A 220 20 No 105 2 A 200 24 No 81 3 A 402 52 No 29 4 B 313 40 Yes 353 5 B 434 12 No 341 6 B 321 43 No 298 7 C 343 23 Yes 366 8 C 120 16 No 350
Résultat:
## 1. line up values to sum ob = -df["Outbound"].values # get yes indices fi, = np.where(df["Is First?"].values == "Yes") # insert yes formula at yes positions ob[fi] = df["Inbound"].values[fi] - ob[fi] ## 2. calculate block sums and subtract each from the ## first element of the **next** block ob[fi[1:]] -= np.add.reduceat(ob,fi)[:-1] # now simply taking the cumsum will reset after each block df["Value"] = ob.cumsum()
un «oui» va-t-il toujours avec un autre nom de produit?