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)
私が間違っていることを教えてください!
ヘイデンのソリューションの簡潔さを犠牲にしたい場合、次のようなこともできます。
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行のコードを使用しますが、少し読みやすくなっています。計算コストも似ていると思います。
エレガントな解決策は、 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
さらに、これが最速のソリューションになるはずです。
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ループよりも高速です。
この質問が再び出てきたので、良いクリーンなアプローチは assign を使用していると思います。
コードは非常に表現力豊かで自己記述的です:
df = df.assign(Value = lambda x: x.Prices * x.Amount * x.Action.replace({'Buy' : 1, 'Sell' : -1}))
物事をすっきりさせるために、私はヘイデンのソリューションを取り入れますが、そこから小さな機能を作ります。
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)
...また、変更は小さな関数自体でのみ必要です。
簡潔で読みやすく、きちんと!
私にとって、これは最も明確で直感的なものです。
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
を返すので、要素単位で簡単に乗算でき、リストに「追加」することで累積的にリストを生成できます。
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))