如何使用 pandas 计算与起始值相比的百分比变化?

How to calculate percent change compared to the beginning value using pandas?

我有一个 DataFrame 并且需要计算公司与年初相比的百分比变化。有什么方法可以使用 pct_change() 或其他方法来执行此任务吗?谢谢!

df 看起来像

security    date        price
IBM         1/1/2016    100
IBM         1/2/2016    102
IBM         1/3/2016    108
AAPL        1/1/2016    1000
AAPL        1/2/2016    980
AAPL        1/3/2016    1050
AAPL        1/4/2016    1070

我想要的结果

security    date        price   change
IBM         1/1/2016    100     NA
IBM         1/2/2016    102     2%
IBM         1/3/2016    108     8%
AAPL        1/1/2016    1000    NA
AAPL        1/2/2016    980     -2%
AAPL        1/3/2016    1050    5%
AAPL        1/4/2016    1070    7%

听起来您正在寻找 pct_change()expanding_window 版本。这不存在开箱即用的 AFAIK,但您可以自己推出:

df.groupby('security')['price'].apply(lambda x: x.div(x.iloc[0]).subtract(1).mul(100))

假设您已经在每个可能的分组中按日期排序,这是可行的。

def pct_change(df):
    df['pct'] = 100 * (1 - df.iloc[0].price / df.price)
    return df

df.groupby('security').apply(pct_change)

我遇到了同样的问题,但是他用他的方式解决了:

(唯一的区别是列是您的公司而不是行。)

对于我的数据框的每一列,我做了:

df[column] = df[column].pct_change().cumsum()

pct_change() 计算现在和上一个值之间的变化,cumcum() 将它们加在一起。

聚会迟到了,但我遇到了类似的问题,我想与您分享我的解决方案,以防对任何人有帮助。

TL;博士

def get_variation(values: pd.Series) -> np.float64:
    base = values.iloc[0]  # first element in window iteration
    current = values.iloc[-1]  # last element in window iteration

    return (current - base) / base if base else 0  # avoid ZeroDivisionError

variations = df.groupby('security')['price'].expanding(min_periods=2).apply(get_variation)
df = df.assign(change=variations.droplevel(0))
security date price change
0 IBM 1/1/2016 100 NaN
1 IBM 1/2/2016 102 0.02
2 IBM 1/3/2016 108 0.08
3 AAPL 1/1/2016 1000 NaN
4 AAPL 1/2/2016 980 -0.02
5 AAPL 1/3/2016 1050 0.05
6 AAPL 1/4/2016 1070 0.07

您可以通过以下方式完成您的要求:

  1. 定义自己的变异函数,
  2. security 列分组 pd.DataFrame
  3. 对生成的 price 列使用 扩展 window 操作,
  4. 在扩展 window 操作上调用 apply 并将您在步骤 1 中定义的自定义变体函数作为参数传递,
  5. 删除结果系列的外部索引,
  6. 正在将之前的结果分配给原始结果 pd.DataFrame

可选地,您可以通过调用 .rolling(window=2, min_periods=2) 将步骤 3 中的扩展 window 操作替换为 rolling window 操作,以获得每个证券价格的 step-by-step 变化。 window=2 定义 window 的大小,以便 return 每次迭代两个元素, min_periods=2 设置计算所需的最小数据(将导致 NaN ,否则的话)。

循序渐进

1。定义自己的变异函数

您的变体函数应接受 pd.Series 作为参数,并使用集合中的第一项和最后一项来计算变体。在这里,我使用金融中使用的标准变化函数来计算利率。最后一行有一个 if/else 语句来避免 ZeroDivisionError.

def get_variation(values: pd.Series) -> np.float64:
    base = values.iloc[0]
    current = values.iloc[-1]

    return (current - base) / base if base else 0

2。按 security

分组 pd.DataFrame

接下来调用.groupby('security')pd.DataFramesecurity列分组,为分组计算准备数据。

grouped_df = df.groupby('security')
security date price change
security
AAPL 3 AAPL 1/1/2016 1000 NaN
4 AAPL 1/2/2016 980 -0.02
5 AAPL 1/3/2016 1050 0.05
6 AAPL 1/4/2016 1070 0.07
IBM 0 IBM 1/1/2016 100 NaN
1 IBM 1/2/2016 102 0.02
2 IBM 1/3/2016 108 0.08

3。对生成的 price

使用扩展 window 操作

接下来,您应该通过在价格组上调用 .expanding(min_periods=2) 来使用扩展 window 操作。这将迭代每个 price 组,并以 pd.Series 的形式获取当前迭代的所有数据。您调用 .expanding(min_periods=n)n 设置为每次迭代 return 值所需的最少观察次数(或者 Nan,否则)。在你的情况下,鉴于你在第一条记录上需要 NaN 因为它没有与其他任何东西进行比较,你应该通过 min_periods=2;如果您宁愿将 0 作为结果,请传递 min_periods=1.

windows = grouped_df['price'].expanding(min_periods=2)

4。在扩展 window 操作上调用 apply 并将步骤 1 中定义的自定义变体函数作为参数传递

调用 .apply(get_variation) 会将您的自定义变化公式应用于每个结果 window 和 return 结果。

grouped_variations = windows.apply(get_variation)
security
AAPL 3 NaN
4 -0.02
5 0.05
6 0.07
IBM 0 NaN
1 0.02
2 0.08

5.删除结果系列的外部索引

正如您在 .4 中看到的那样,数据显示为 multi-index。我们通过调用 .droplevel(0) 摆脱了外部索引级别 ('AAPL', 'IBM'),以准备数据以将其正确合并到原始数据帧中。

variations = grouped_variations.droplevel(0)

6。将之前的结果分配给原始 pd.DataFrame

最后,我们通过调用 df.assign 将价格变化分配到原始数据框中。数据将在其索引上加入目标。

df = df.assign(change=variations)
security date price change
0 IBM 1/1/2016 100 NaN
1 IBM 1/2/2016 102 0.02
2 IBM 1/3/2016 108 0.08
3 AAPL 1/1/2016 1000 NaN
4 AAPL 1/2/2016 980 -0.02
5 AAPL 1/3/2016 1050 0.05
6 AAPL 1/4/2016 1070 0.07