加快 pandas 滚动引用另一个数据框
Speeding up a pandas rolling referencing another dataframe
我想要一些有关如何优化以下 pandas 计算的反馈:
我们有一个固定索引集 I
和一个 lookback
。此外,我们还有 pd.Series index
其回顾中值 index_MEDIAN
和大量 pandas 数据帧。所有 series/dataframes 都以 I 作为索引。每个数据框都有 value
列。让 D
成为这样一个数据框..
对于 D
的每一行,我们在 index_MEDIAN
中取相应的值 m
并对回溯 window 中存在的所有值条目求和,条件是index
系列中的 运行 值大于 m
。换句话说,只要索引值大于回顾的中值,我们就会在 D
.
中对相应的值行求和
为了更清楚地说明,这里是上述实施的草图
def sumvals(x)
S = (D['value'].loc[x.index] >= self.index_median.loc[x.index[-1]])
return sum(S*(x-self.index_median.loc[x.index[-1]]))
D['value'].rolling(lookback).apply(sumvals)
数据帧列表非常庞大,我注意到这种计算数量的方法需要花费过多的时间。我怀疑这个问题与这个实现大量使用 .loc
这一事实有关。因此
Is there another way to express this solution without having to reference an external Series so much?
无论如何,欢迎提出任何优化建议。
编辑。这是一个带有相应计算的示例数据集。
lookback = 3
Index = pd.Series([1,-2,8,-10,3,4,5, 10, -20, 3])
Index_median = Index.rolling(lookback).median
Values = pd.Series([1,2,2,3,0,9,10, 8, 20, 9])
Values 的结果计算应该产生
0 NaN
1 NaN
2 2.0
3 13.0
4 0.0
5 6.0
6 11.0
7 12.0
8 23.0
9 28.0
例如第5行的值是6,为什么?第 5 行中的 Index_median 值是 3。第 5 行中的 3-lookback 是序列 9、0、3。值 >= 是 3 和 9,所以这包括我们对第 5 行的总和 3- 3+9-3 = 6。同样对于最后一行,索引中位数是 3。值中的最后三行都大于 3,总和为 34 - 3*3 = 28。
def sumvals(x)
m = self.index_median.loc[x.index[-1]]
condition = (x.index >= m)
return sum(x[condition]-m)
D['value'].rolling(lookback).apply(sumvals)
当我们对回溯 window 中存在的所有值条目求和时,无需将它们与 self.index 进行比较。另外根据你的描述,如果你在 D 中取值行,那么你可以
return sum(x[condition])
直接代替。
另一个解决方案是你可以将整个操作转换成numpy来加速滚动操作。
检查 numpy_ext 软件包
.loc 慢,apply 慢。
在我看来,您可以在没有 row-by-row-apply 和 loc-lookups.
的情况下使用矢量化函数和对列的操作来实现您想要的效果
如果没有@Manakin 建议的真实数据示例,很难说清楚。
但是我试着re-create你的问题用一个例子,并根据你的描述解决了。
# lookback window
lookback = 3
# Fixed Index
I = [5, 2, 1, 4, 2, 4, 1, 2, 1, 10]
# Dataframe with value column, Index added as column for convenience
df = pd.DataFrame({'I': I,
'value':[6,5,4,3,2,1, 2, 3, 4, 5]},
index=I)
# Median over lookback window
df['I_median'] = df.I.rolling(lookback).median()
产量
| | I | value | I_median
|----|-------|----------|----------|
| 5 | 5 | 6 | NaN |
| 2 | 2 | 5 | NaN |
| 1 | 1 | 4 | 2.0 |
| 4 | 4 | 3 | 2.0 |
| 2 | 2 | 2 | 2.0 |
| 4 | 4 | 1 | 4.0 |
| 1 | 1 | 2 | 2.0 |
| 2 | 2 | 3 | 2.0 |
| 1 | 1 | 4 | 1.0 |
| 10 | 10 | 5 | 2.0 |
# Check if Index is greater than median
df['I_gt'] = df.I > df.I_median
# set all rows to 0 where median is greater than index
df['filtered_val'] = df.value.where(df.I_gt, 0)
| | I | value | I_median | I_gt | filtered_val |
|----|----|-------|----------|-------|--------------|
| 5 | 5 | 6 | NaN | False | 0 |
| 2 | 2 | 5 | NaN | False | 0 |
| 1 | 1 | 4 | 2.0 | False | 0 |
| 4 | 4 | 3 | 2.0 | True | 3 |
| 2 | 2 | 2 | 2.0 | False | 0 |
| 4 | 4 | 1 | 4.0 | False | 0 |
| 1 | 1 | 2 | 2.0 | False | 0 |
| 2 | 2 | 3 | 2.0 | False | 0 |
| 1 | 1 | 4 | 1.0 | False | 0 |
| 10 | 10 | 5 | 2.0 | True | 5 |
然后简单地对过滤后的列进行滚动求和。
df.filtered_val.rolling(lookback).sum()
从您的示例数据开始:
df = pd.DataFrame()
df['I'] = pd.Series([1,-2,8,-10,3,4,5, 10, -20, 3])
df['I_median'] = df['I'].rolling(lookback).median()
df['Values'] = pd.Series([1,2,2,3,0,9,10, 8, 20, 9])
现在为 'Value' 列添加移位列
# add one column for every lookback
for colno in range(lookback):
# shift the column by one and deduct the median
df['n'+ str(colno)] = df['Values'].shift(colno) - df['I_median']
# remove all negative numbers (where value is smaller than median)
df['n'+ str(colno)] = df['n'+ str(colno)].where(df['n'+ str(colno)]> 0, 0)
# sum up across the new columns
df['result'] = df[df.columns[-lookback:]].sum(axis=1)
df.result 包含您的结果并等于
0 0.0
1 0.0
2 2.0
3 13.0
4 0.0
5 6.0
6 11.0
7 12.0
8 23.0
9 28.0
Name: result, dtype: float64
编辑:数据框中没有移动列
df['result'] = 0
for colno in range(lookback):
# shift the column by one and deduct the median
df['temp'] = df['Values'].shift(colno) - df['I_median']
# remove all negative numbers (where value is smaller than median)
df['temp'] = df['temp'].where(df['temp']> 0, 0)
# sum up across the new columns
df['result'] = df['result'] + df['temp']
性能
- 数据框中 1m 行
- 1000 次回顾
lookback = 1000
df = pd.DataFrame()
df['I'] = pd.Series(np.random.randint(0, 10, size=1000000))
df['I_median'] = df['I'].rolling(lookback).median()
df['Values'] = pd.Series(np.random.randint(0, 10, size=1000000))
大约 14 秒后运行。
我想要一些有关如何优化以下 pandas 计算的反馈:
我们有一个固定索引集 I
和一个 lookback
。此外,我们还有 pd.Series index
其回顾中值 index_MEDIAN
和大量 pandas 数据帧。所有 series/dataframes 都以 I 作为索引。每个数据框都有 value
列。让 D
成为这样一个数据框..
对于 D
的每一行,我们在 index_MEDIAN
中取相应的值 m
并对回溯 window 中存在的所有值条目求和,条件是index
系列中的 运行 值大于 m
。换句话说,只要索引值大于回顾的中值,我们就会在 D
.
为了更清楚地说明,这里是上述实施的草图
def sumvals(x)
S = (D['value'].loc[x.index] >= self.index_median.loc[x.index[-1]])
return sum(S*(x-self.index_median.loc[x.index[-1]]))
D['value'].rolling(lookback).apply(sumvals)
数据帧列表非常庞大,我注意到这种计算数量的方法需要花费过多的时间。我怀疑这个问题与这个实现大量使用 .loc
这一事实有关。因此
Is there another way to express this solution without having to reference an external Series so much?
无论如何,欢迎提出任何优化建议。
编辑。这是一个带有相应计算的示例数据集。
lookback = 3
Index = pd.Series([1,-2,8,-10,3,4,5, 10, -20, 3])
Index_median = Index.rolling(lookback).median
Values = pd.Series([1,2,2,3,0,9,10, 8, 20, 9])
Values 的结果计算应该产生
0 NaN
1 NaN
2 2.0
3 13.0
4 0.0
5 6.0
6 11.0
7 12.0
8 23.0
9 28.0
例如第5行的值是6,为什么?第 5 行中的 Index_median 值是 3。第 5 行中的 3-lookback 是序列 9、0、3。值 >= 是 3 和 9,所以这包括我们对第 5 行的总和 3- 3+9-3 = 6。同样对于最后一行,索引中位数是 3。值中的最后三行都大于 3,总和为 34 - 3*3 = 28。
def sumvals(x)
m = self.index_median.loc[x.index[-1]]
condition = (x.index >= m)
return sum(x[condition]-m)
D['value'].rolling(lookback).apply(sumvals)
当我们对回溯 window 中存在的所有值条目求和时,无需将它们与 self.index 进行比较。另外根据你的描述,如果你在 D 中取值行,那么你可以
return sum(x[condition])
直接代替。
另一个解决方案是你可以将整个操作转换成numpy来加速滚动操作。 检查 numpy_ext 软件包
.loc 慢,apply 慢。 在我看来,您可以在没有 row-by-row-apply 和 loc-lookups.
的情况下使用矢量化函数和对列的操作来实现您想要的效果如果没有@Manakin 建议的真实数据示例,很难说清楚。 但是我试着re-create你的问题用一个例子,并根据你的描述解决了。
# lookback window
lookback = 3
# Fixed Index
I = [5, 2, 1, 4, 2, 4, 1, 2, 1, 10]
# Dataframe with value column, Index added as column for convenience
df = pd.DataFrame({'I': I,
'value':[6,5,4,3,2,1, 2, 3, 4, 5]},
index=I)
# Median over lookback window
df['I_median'] = df.I.rolling(lookback).median()
产量
| | I | value | I_median
|----|-------|----------|----------|
| 5 | 5 | 6 | NaN |
| 2 | 2 | 5 | NaN |
| 1 | 1 | 4 | 2.0 |
| 4 | 4 | 3 | 2.0 |
| 2 | 2 | 2 | 2.0 |
| 4 | 4 | 1 | 4.0 |
| 1 | 1 | 2 | 2.0 |
| 2 | 2 | 3 | 2.0 |
| 1 | 1 | 4 | 1.0 |
| 10 | 10 | 5 | 2.0 |
# Check if Index is greater than median
df['I_gt'] = df.I > df.I_median
# set all rows to 0 where median is greater than index
df['filtered_val'] = df.value.where(df.I_gt, 0)
| | I | value | I_median | I_gt | filtered_val |
|----|----|-------|----------|-------|--------------|
| 5 | 5 | 6 | NaN | False | 0 |
| 2 | 2 | 5 | NaN | False | 0 |
| 1 | 1 | 4 | 2.0 | False | 0 |
| 4 | 4 | 3 | 2.0 | True | 3 |
| 2 | 2 | 2 | 2.0 | False | 0 |
| 4 | 4 | 1 | 4.0 | False | 0 |
| 1 | 1 | 2 | 2.0 | False | 0 |
| 2 | 2 | 3 | 2.0 | False | 0 |
| 1 | 1 | 4 | 1.0 | False | 0 |
| 10 | 10 | 5 | 2.0 | True | 5 |
然后简单地对过滤后的列进行滚动求和。
df.filtered_val.rolling(lookback).sum()
从您的示例数据开始:
df = pd.DataFrame()
df['I'] = pd.Series([1,-2,8,-10,3,4,5, 10, -20, 3])
df['I_median'] = df['I'].rolling(lookback).median()
df['Values'] = pd.Series([1,2,2,3,0,9,10, 8, 20, 9])
现在为 'Value' 列添加移位列
# add one column for every lookback
for colno in range(lookback):
# shift the column by one and deduct the median
df['n'+ str(colno)] = df['Values'].shift(colno) - df['I_median']
# remove all negative numbers (where value is smaller than median)
df['n'+ str(colno)] = df['n'+ str(colno)].where(df['n'+ str(colno)]> 0, 0)
# sum up across the new columns
df['result'] = df[df.columns[-lookback:]].sum(axis=1)
df.result 包含您的结果并等于
0 0.0
1 0.0
2 2.0
3 13.0
4 0.0
5 6.0
6 11.0
7 12.0
8 23.0
9 28.0
Name: result, dtype: float64
编辑:数据框中没有移动列
df['result'] = 0
for colno in range(lookback):
# shift the column by one and deduct the median
df['temp'] = df['Values'].shift(colno) - df['I_median']
# remove all negative numbers (where value is smaller than median)
df['temp'] = df['temp'].where(df['temp']> 0, 0)
# sum up across the new columns
df['result'] = df['result'] + df['temp']
性能
- 数据框中 1m 行
- 1000 次回顾
lookback = 1000
df = pd.DataFrame()
df['I'] = pd.Series(np.random.randint(0, 10, size=1000000))
df['I_median'] = df['I'].rolling(lookback).median()
df['Values'] = pd.Series(np.random.randint(0, 10, size=1000000))
大约 14 秒后运行。