75

我正在尝试将熊猫数据框(orders_df)中的两个现有列相乘 - 价格(股票收盘价)和数量(股票数量),并将计算添加到一个名为“价值”的新列中。出于某种原因,当我运行此代码时,“值”列下的所有行都是正数,而某些行应该是负数。在 DataFrame 的 Action 列下,有 7 行带有“Sell”字符串,7 行带有“Buy”字符串。

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)

请让我知道我做错了什么!

4

7 回答 7

87

我认为一个优雅的解决方案是使用该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

此外,这应该是最快的解决方案。

于 2012-12-28T14:47:45.373 回答
34

您可以使用 DataFrameapply方法:

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

使用这些方法通常比使用 for 循环更快。

于 2012-12-27T18:59:12.093 回答
25

如果我们愿意牺牲海登解决方案的简洁性,也可以这样做:

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

这个解决方案需要两行代码而不是一行,但更容易阅读。我怀疑计算成本也相似。

于 2012-12-27T20:05:45.073 回答
9

由于这个问题再次出现,我认为一个很好的干净方法是使用assign

该代码非常具有表现力和自我描述性:

df = df.assign(Value = lambda x: x.Prices * x.Amount * x.Action.replace({'Buy' : 1, 'Sell' : -1}))
于 2017-05-25T14:18:35.017 回答
3

为了使事情变得整洁,我采用了 Hayden 的解决方案,但从中制作了一个小功能。

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)

...并且任何修改只需要在小函数本身中进行。

简洁、易读、整洁!

于 2019-03-06T16:49:07.797 回答
0

对我来说,这是最清晰和最直观的:

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允许您轻松地按元素相乘,然后您可以通过“添加”来累积生成一个列表。

于 2017-05-25T14:06:12.200 回答
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))
于 2019-01-06T13:04:35.077 回答