web-dev-qa-db-ja.com

pandas DataFrameの2つの列を乗算し、結果を新しい列に追加したい

pandasデータフレーム(orders_df)の2つの既存の列-価格(終値)と金額(在庫量)を乗算して、「値」という新しい列に計算を追加しようとしています。何らかの理由でこのコードを実行すると、「値」列の下のすべての行が正の数値になり、一部の行は負になります。 DataFrameの[アクション]列の下には、「販売」文字列を持つ7つの行と「購入」文字列を持つ7つの行があります。

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)

私が間違っていることを教えてください!

48
OAK

ヘイデンのソリューションの簡潔さを犠牲にしたい場合、次のようなこともできます。

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

これで、ifステートメントの必要性がなくなりました。 DataFrame.apply()を使用して、forループも廃止します。 Haydenが指摘したように、ベクトル化された操作は常に高速です。

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

このソリューションでは、1行ではなく2行のコードを使用しますが、少し読みやすくなっています。計算コストも似ていると思います。

18
Aman

エレガントな解決策は、 where メソッドを使用することだと思います( 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

さらに、これが最速のソリューションになるはずです。

70
bmu

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)

通常、これらのメソッドを使用する方が、for forループよりも高速です。

27
Andy Hayden

この質問が再び出てきたので、良いクリーンなアプローチは assign を使用していると思います。

コードは非常に表現力豊かで自己記述的です:

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

物事をすっきりさせるために、私はヘイデンのソリューションを取り入れますが、そこから小さな機能を作ります。

def create_value(row):
    if row['Action'] == 'Sell':
        return row['Prices'] * row['Amount']
    else:
        return -row['Prices']*row['Amount']

関数をデータフレームに適用したいときにできるように。

df['Value'] = df.apply(lambda row: create_value(row), axis=1)

...また、変更は小さな関数自体でのみ必要です。

簡潔で読みやすく、きちんと!

0
Carson

私にとって、これは最も明確で直感的なものです。

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

.valuesメソッドはnumpy arrayを返すので、要素単位で簡単に乗算でき、リストに「追加」することで累積的にリストを生成できます。

0

Bmuからの良い解決策。値を括弧の内側と外側の両方に置く方が読みやすいと思います。

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

いくつかのpandas組み込み関数を使用します。

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