python - 在每行包含一个时间序列的数据框中查找相对和绝对波动的计数
问题描述
我有一个包含金融时间序列表的数据框,每一行都有以下列:
ID
那个时间序列的- 一个
Target
值(我们想要测量的偏差,相对的和绝对的) - 以及不同日期的时间序列值:
1/01, 1/02, 1/03, ...
我们想要计算每行/ID 的时间序列的相对和绝对波动计数。然后我们要找出哪一行/ID的波动/'spikes'最多,如下:
- 首先,我们找到两个时间序列值之间的差异并估计一个阈值。阈值表示在我们声明“波动”或“峰值”之前,两个值之间允许有多少差异。如果差异高于您设置的阈值,则在任何两列的值之间,那么它就是一个尖峰。
- 但是,我们需要确保阈值是通用的,并且适用于任何 row 中任意两个值之间的 % 和绝对值。
- 所以基本上,我们以百分比形式找到一个阈值(做出有根据的预测),因为我们有一个以“%”形式表示的行值。另外,'%' 形式也可以与绝对值一起正常工作。
- 输出应该是
FCount
每行/ID 的相对和绝对的新列波动计数 ( )。
代码:
import pandas as pd
# Create sample dataframe
raw_data = {'ID': ['A1', 'B1', 'C1', 'D1'],
'Domain': ['Finance', 'IT', 'IT', 'Finance'],
'Target': [1, 2, 3, 0.9%],
'Criteria':['<=', '<=', '>=', '>='],
"1/01":[0.9, 1.1, 2.1, 1],
"1/02":[0.4, 0.3, 0.5, 0.9],
"1/03":[1, 1, 4, 1.1],
"1/04":[0.7, 0.7, 0.1, 0.7],
"1/05":[0.7, 0.7, 0.1, 1],
"1/06":[0.9, 1.1, 2.1, 0.6],}
df = pd.DataFrame(raw_data, columns = ['ID', 'Domain', 'Target','Criteria', '1/01',
'1/02','1/03', '1/04','1/05', '1/06'])
ID Domain Target Criteria 1/01 1/02 1/03 1/04 1/05 1/06
0 A1 Finance 1 <= 0.9 0.4 1.0 0.7 0.7 0.9
1 B1 IT 2 <= 1.1 0.3 1.0 0.7 0.7 1.1
2 C1 IT 3 >= 2.1 0.5 4.0 0.1 0.1 2.1
3 D1 Finance 0.9% >= 1.0 0.9 1.1 0.7 1.0 0.6
这是带有波动计数 ( FCount
) 列的预期输出。然后我们可以得到具有最大 FCount 的 ID。
ID Domain Target Criteria 1/01 1/02 1/03 1/04 1/05 1/06 FCount
0 A1 Finance 1 <= 0.9 0.4 1.0 0.7 0.7 0.9 -
1 B1 IT 2 <= 1.1 0.3 1.0 0.7 0.7 1.1 -
2 C1 IT 3 >= 2.1 0.5 4.0 0.1 0.1 2.1 -
3 D1 Finance 0.9% >= 1.0 0.9 1.1 0.7 1.0 0.6 -
解决方案
鉴于,
# importing pandas as pd
import pandas as pd
import numpy as np
# Create sample dataframe
raw_data = {'ID': ['A1', 'B1', 'C1', 'D1'],
'Domain': ['Finance', 'IT', 'IT', 'Finance'],
'Target': [1, 2, 3, '0.9%'],
'Criteria':['<=', '<=', '>=', '>='],
"1/01":[0.9, 1.1, 2.1, 1],
"1/02":[0.4, 0.3, 0.5, 0.9],
"1/03":[1, 1, 4, 1.1],
"1/04":[0.7, 0.7, 0.1, 0.7],
"1/05":[0.7, 0.7, 0.1, 1],
"1/06":[0.9, 1.1, 2.1, 0.6],}
df = pd.DataFrame(raw_data, columns = ['ID', 'Domain', 'Target','Criteria', '1/01',
'1/02','1/03', '1/04','1/05', '1/06'])
通过将其分为两部分(绝对阈值和相对阈值)并在底层 numpy 数组上逐步解决此问题,更容易解决此问题。
编辑:前面的解释很长,跳到最后只是最后的功能
首先,创建日期列列表以仅访问每行中的相关列。
date_columns = ['1/01', '1/02','1/03', '1/04','1/05', '1/06']
df[date_columns].values
#Output:
array([[0.9, 0.4, 1. , 0.7, 0.7, 0.9],
[1.1, 0.3, 1. , 0.7, 0.7, 1.1],
[2.1, 0.5, 4. , 0.1, 0.1, 2.1],
[1. , 0.9, 1.1, 0.7, 1. , 0.6]])
然后我们可以使用np.diff轻松获取底层数组上日期之间的差异。我们也将采用绝对值,因为这是我们感兴趣的。
np.abs(np.diff(df[date_columns].values))
#Output:
array([[0.5, 0.6, 0.3, 0. , 0.2],
[0.8, 0.7, 0.3, 0. , 0.4],
[1.6, 3.5, 3.9, 0. , 2. ],
[0.1, 0.2, 0.4, 0.3, 0.4]])
现在,只需担心绝对阈值,就像检查差异中的值是否大于限制一样简单。
abs_threshold = 0.5
np.abs(np.diff(df[date_columns].values)) > abs_threshold
#Output:
array([[False, True, False, False, False],
[ True, True, False, False, False],
[ True, True, True, False, True],
[False, False, False, False, False]])
我们可以看到,该数组每行的总和将为我们提供所需的结果(布尔数组的总和使用底层 True=1 和 False=0。因此,您实际上是在计算存在多少 True)。对于百分比阈值,我们只需要做一个额外的步骤,在比较之前将所有差异与原始值相除。把它们放在一起。
详细说明:
我们可以看到每一行的总和如何为我们提供跨越绝对阈值的值的计数,如下所示。
abs_fluctuations = np.abs(np.diff(df[date_columns].values)) > abs_threshold
print(abs_fluctuations.sum(-1))
#Output:
[1 2 4 0]
从相对阈值开始,我们可以像以前一样创建差异数组。
dates = df[date_columns].values #same as before, but just assigned
differences = np.abs(np.diff(dates)) #same as before, just assigned
pct_threshold=0.5 #aka 50%
print(differences.shape) #(4, 5) aka 4 rows, 5 columns if you want to think traditional tabular 2D shapes only
print(dates.shape) #(4, 6) 4 rows, 6 columns
现在,请注意,差异数组的列数将减少 1,这也是有道理的。因为对于 6 个日期,将有 5 个“差异”,每个间隔一个。
现在,只关注 1 行,我们看到计算百分比变化很简单。
print(dates[0][:2]) #for first row[0], take the first two dates[:2]
#Output:
array([0.9, 0.4])
print(differences[0][0]) #for first row[0], take the first difference[0]
#Output:
0.5
变化0.9 to 0.4
是0.5
绝对意义上的变化。但就百分比而言,它是0.5/0.9
(差异/原始)* 100 的变化(我省略了乘以 100 以使事情更简单)又名55.555%
或0.5555
..
在这一步要意识到的主要事情是,我们需要针对所有差异的“原始”值进行这种划分,以获得百分比变化。但是,日期数组的一个“列”太多了。所以,我们做一个简单的切片。
dates[:,:-1] #For all rows(:,), take all columns except the last one(:-1).
#Output:
array([[0.9, 0.4, 1. , 0.7, 0.7],
[1.1, 0.3, 1. , 0.7, 0.7],
[2.1, 0.5, 4. , 0.1, 0.1],
[1. , 0.9, 1.1, 0.7, 1. ]])
现在,我可以通过元素除法计算相对或百分比变化
relative_differences = differences / dates[:,:-1]
然后,和以前一样。选择一个门槛,看看它是否被跨越
rel_fluctuations = relative_differences > pct_threshold
#Output:
array([[ True, True, False, False, False],
[ True, True, False, False, True],
[ True, True, True, False, True],
[False, False, False, False, False]])
现在,如果我们想考虑是否超过了绝对阈值或相对阈值之一,我们只需要按位或|
(它甚至在句子中!)然后沿行求和。
把所有这些放在一起,我们可以创建一个可以使用的函数。请注意,函数并没有什么特别之处,它只是一种将代码行组合在一起以便于使用的方式。使用函数就像调用它一样简单,您一直在使用函数/方法而没有意识到它。
date_columns = ['1/01', '1/02','1/03', '1/04','1/05', '1/06'] #if hardcoded.
date_columns = df.columns[5:] #if you wish to assign dynamically, and all dates start from 5th column.
def get_FCount(df, date_columns, abs_threshold=0.5, pct_threshold=0.5):
'''Expects a list of date columns with atleast two values.
returns a 1D array, with FCounts for every row.
pct_threshold: percentage, where 1 means 100%
'''
dates = df[date_columns].values
differences = np.abs(np.diff(dates))
abs_fluctuations = differences > abs_threshold
rel_fluctuations = differences / dates[:,:-1] > pct_threshold
return (abs_fluctuations | rel_fluctuations).sum(-1) #we took a bitwise OR. since we are concerned with values that cross even one of the thresholds.
df['FCount'] = get_FCount(df, date_columns) #call our function, and assign the result array to a new column
print(df['FCount'])
#Output:
0 2
1 3
2 4
3 0
Name: FCount, dtype: int32
推荐阅读
- javascript - 将 Vue 组件绑定到 vue 实例
- c++ - 如何在 Red Hat 上强制 cxx11 ABI?
- nativescript - 从堆栈顶部的视图返回后延迟视图刷新
- visual-studio-code - VS Code 项目代码格式化使用 ESLint
- php - 在 Eclipse 中重新调试 PHP Web 应用程序的概念性问题
- javascript - 为什么尽管 enumerable 设置为 true,但未显示使用 defineProperty 添加的属性?
- ruby - Ruby FFI gem 无法打开库文件
- .net - Hangfire 1.6.22 操作被取消,然后被另一个工人接走
- javascript - URL 和排除扩展标志中多个文件夹名称的正则表达式
- c# - 保持 MenuItem 的 IsChecked 属性与布尔值同步