web-dev-qa-db-fra.com

Je veux multiplier deux colonnes dans un DataFrame pandas et ajouter le résultat dans une nouvelle colonne

J'essaie de multiplier deux colonnes existantes dans un pandas Dataframe (orders_df) - Prix (prix de clôture) et Amount (quantités en stock), puis d'ajouter le calcul à une nouvelle colonne appelée "Valeur". Pour une raison quelconque, lorsque j'exécute ce code, toutes les lignes de la colonne "Valeur" sont des nombres positifs, tandis que certaines d'entre elles doivent être négatives. Sous la colonne Action du DataFrame, il y a sept lignes avec la chaîne "Vendre" et sept avec la chaîne "Acheter".

for i in orders_df.Action:
 if i  == 'Sell':
  orders_df['Value'] = orders_df.Prices*orders_df.Amount
 Elif i == 'Buy':
  orders_df['Value'] = -orders_df.Prices*orders_df.Amount)

S'il vous plaît laissez-moi savoir ce que je fais mal!

42
OAK

Si nous sommes prêts à sacrifier le caractère succinct de la solution de Hayden, on pourrait également faire quelque chose comme ceci: 

In [22]: orders_df['C'] = orders_df.Action.apply(
               lambda x: (1 if x == 'Sell' else -1))

In [23]: orders_df   # New column C represents the sign of the transaction
Out[23]:
   Prices  Amount Action  C
0       3      57   Sell  1
1      89      42   Sell  1
2      45      70    Buy -1
3       6      43   Sell  1
4      60      47   Sell  1
5      19      16    Buy -1
6      56      89   Sell  1
7       3      28    Buy -1
8      56      69   Sell  1
9      90      49    Buy -1

Nous avons maintenant éliminé la nécessité de la déclaration if. En utilisant DataFrame.apply(), nous supprimons également la boucle for. Comme Hayden l'a noté, les opérations vectorisées sont toujours plus rapides. 

In [24]: orders_df['Value'] = orders_df.Prices * orders_df.Amount * orders_df.C

In [25]: orders_df   # The resulting dataframe
Out[25]:
   Prices  Amount Action  C  Value
0       3      57   Sell  1    171
1      89      42   Sell  1   3738
2      45      70    Buy -1  -3150
3       6      43   Sell  1    258
4      60      47   Sell  1   2820
5      19      16    Buy -1   -304
6      56      89   Sell  1   4984
7       3      28    Buy -1    -84
8      56      69   Sell  1   3864
9      90      49    Buy -1  -4410

Cette solution prend deux lignes de code au lieu d'une, mais est un peu plus facile à lire. Je soupçonne que les coûts de calcul sont similaires. 

15
Aman

Je pense qu'une solution élégante consiste à utiliser la méthode where (voir également le API docs ):

In [37]: values = df.Prices * df.Amount

In [38]: df['Values'] = values.where(df.Action == 'Sell', other=-values)

In [39]: df
Out[39]: 
   Prices  Amount Action  Values
0       3      57   Sell     171
1      89      42   Sell    3738
2      45      70    Buy   -3150
3       6      43   Sell     258
4      60      47   Sell    2820
5      19      16    Buy    -304
6      56      89   Sell    4984
7       3      28    Buy     -84
8      56      69   Sell    3864
9      90      49    Buy   -4410

De plus, cela devrait être la solution la plus rapide.

64
bmu

Vous pouvez utiliser la méthode DataFrame apply :

order_df['Value'] = order_df.apply(lambda row: (row['Prices']*row['Amount']
                                               if row['Action']=='Sell'
                                               else -row['Prices']*row['Amount']),
                                   axis=1)

Il est généralement plus rapide d’utiliser ces méthodes plutôt que d’en finir pour les boucles.

22
Andy Hayden

Depuis que cette question a été posée à nouveau, je pense qu'une bonne approche consiste à utiliser assign .

Le code est assez expressif et auto-descriptif:

df = df.assign(Value = lambda x: x.Prices * x.Amount * x.Action.replace({'Buy' : 1, 'Sell' : -1}))
5
FLab

Pour moi, c'est le plus clair et le plus intuitif:

values = []
for action in ['Sell','Buy']:
    amounts = orders_df['Amounts'][orders_df['Action'==action]].values
    if action == 'Sell':
        prices = orders_df['Prices'][orders_df['Action'==action]].values
    else:
        prices = -1*orders_df['Prices'][orders_df['Action'==action]].values
    values += list(amounts*prices)  
orders_df['Values'] = values

La méthode .values renvoie un numpy array qui vous permet de multiplier facilement les éléments. Vous pouvez ensuite générer de manière cumulative une liste en "ajoutant".

0

Bonne solution de bmu. Je pense qu'il est plus facile de mettre les valeurs entre parenthèses et extérieures. 

    df['Values'] = np.where(df.Action == 'Sell', 
                            df.Prices*df.Amount, 
                           -df.Prices*df.Amount)

Utilisation de quelques pandas intégrés dans des fonctions.

    df['Values'] = np.where(df.Action.eq('Sell'), 
                            df.Prices.mul(df.Amount), 
                           -df.Prices.mul(df.Amount))
0