如何使用 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
您可以通过以下方式完成您的要求:
- 定义自己的变异函数,
- 按
security
列分组 pd.DataFrame
,
- 对生成的
price
列使用 扩展 window 操作,
- 在扩展 window 操作上调用
apply
并将您在步骤 1 中定义的自定义变体函数作为参数传递,
- 删除结果系列的外部索引,
- 正在将之前的结果分配给原始结果
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.DataFrame
按security
列分组,为分组计算准备数据。
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
我有一个 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 |
您可以通过以下方式完成您的要求:
- 定义自己的变异函数,
- 按
security
列分组pd.DataFrame
, - 对生成的
price
列使用 扩展 window 操作, - 在扩展 window 操作上调用
apply
并将您在步骤 1 中定义的自定义变体函数作为参数传递, - 删除结果系列的外部索引,
- 正在将之前的结果分配给原始结果
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.DataFrame
按security
列分组,为分组计算准备数据。
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 |