如何对使用其自身输出的滞后值的函数进行矢量化?

Posted

技术标签:

【中文标题】如何对使用其自身输出的滞后值的函数进行矢量化?【英文标题】:How can I vectorize a function that uses lagged values of its own output? 【发布时间】:2017-11-11 08:02:41 【问题描述】:

我很抱歉这个问题的措辞很糟糕,但这是我能做的最好的了。 我确切地知道我想要什么,但不知道如何要求它。

下面是一个例子演示的逻辑:

取值为 1 或 0 的两个条件触发一个也取值为 1 或 0 的信号。条件 A 触发信号(如果 A = 1,则信号 = 1,否则信号 = 0)无论如何。条件 B 不触发信号,但如果条件 B 保持等于 1,则信号保持触发状态 在先前的信号被条件 A 触发之后。 只有在 A 和 B 都回 0 后,信号才会回 0。

1.输入:

2。期望的输出 (signal_d) 并确认 for 循环可以解决它 (signal_l):

3.我使用 numpy.where() 的尝试:

4.可重现的 sn-p:

    # Settings
    import numpy as np
    import pandas as pd
    import datetime

    # Data frame with input and desired output i column signal_d
    df = pd.DataFrame('condition_A':list('00001100000110'),
                       'condition_B':list('01110011111000'),
                       'signal_d':list('00001111111110'))

    colnames = list(df)
    df[colnames] = df[colnames].apply(pd.to_numeric)
    datelist = pd.date_range(pd.datetime.today().strftime('%Y-%m-%d'), periods=14).tolist()
    df['dates'] = datelist
    df = df.set_index(['dates']) 

    # Solution using a for loop with nested ifs in column signal_l
    df['signal_l'] = df['condition_A'].copy(deep = True)
    i=0
    for observations in df['signal_l']:
        if df.ix[i,'condition_A'] == 1:
            df.ix[i,'signal_l'] = 1
        else:
            # Signal previously triggered by condition_A
            # AND kept "alive" by condition_B:                
            if df.ix[i - 1,'signal_l'] & df.ix[i,'condition_B'] == 1:
                 df.ix[i,'signal_l'] = 1
            else:
                df.ix[i,'signal_l'] = 0          
        i = i + 1



    # My attempt with np.where in column signal_v1
    df['Signal_v1'] = df['condition_A'].copy()
    df['Signal_v1'] = np.where(df.condition_A == 1, 1, np.where( (df.shift(1).Signal_v1 == 1) & (df.condition_B == 1), 1, 0))

    print(df)

使用带有滞后值和嵌套 if 语句的 for 循环非常简单,但我无法使用像 numpy.where() 这样的矢量化函数来解决这个问题。而且我知道这对于更大的数据帧会更快。

感谢您的任何建议!

【问题讨论】:

您能否澄清一下,为什么 2017-06-13 的 signal_d 应该是 1? 信号由条件 A = 1 触发,因此 signal_d 应为 1。 【参考方案1】:

我认为没有一种方法可以比 Python 循环更快地对该操作进行矢量化。 (至少,如果您只想坚持使用 Python、pandas 和 numpy,则不会。)

但是,您可以通过简化代码来提高此操作的性能。您的实现使用 if 语句和大量 DataFrame 索引。这些都是相对昂贵的操作。

这是对脚本的修改,其中包括两个函数:add_signal_l(df)add_lagged(df)。第一个是您的代码,只是包装在一个函数中。第二个使用更简单的函数来实现相同的结果——仍然是一个 Python 循环,但它使用 numpy 数组和按位运算符。

import numpy as np
import pandas as pd
import datetime

#-----------------------------------------------------------------------
# Create the test DataFrame

# Data frame with input and desired output i column signal_d
df = pd.DataFrame('condition_A':list('00001100000110'),
                   'condition_B':list('01110011111000'),
                   'signal_d':list('00001111111110'))

colnames = list(df)
df[colnames] = df[colnames].apply(pd.to_numeric)
datelist = pd.date_range(pd.datetime.today().strftime('%Y-%m-%d'), periods=14).tolist()
df['dates'] = datelist
df = df.set_index(['dates']) 
#-----------------------------------------------------------------------

def add_signal_l(df):
    # Solution using a for loop with nested ifs in column signal_l
    df['signal_l'] = df['condition_A'].copy(deep = True)
    i=0
    for observations in df['signal_l']:
        if df.ix[i,'condition_A'] == 1:
            df.ix[i,'signal_l'] = 1
        else:
            # Signal previously triggered by condition_A
            # AND kept "alive" by condition_B:                
            if df.ix[i - 1,'signal_l'] & df.ix[i,'condition_B'] == 1:
                 df.ix[i,'signal_l'] = 1
            else:
                df.ix[i,'signal_l'] = 0          
        i = i + 1

def compute_lagged_signal(a, b):
    x = np.empty_like(a)
    x[0] = a[0]
    for i in range(1, len(a)):
        x[i] = a[i] | (x[i-1] & b[i])
    return x

def add_lagged(df):
    df['lagged'] = compute_lagged_signal(df['condition_A'].values, df['condition_B'].values)

这是两个函数的时序比较,在 IPython 会话中运行:

In [85]: df
Out[85]: 
            condition_A  condition_B  signal_d
dates                                         
2017-06-09            0            0         0
2017-06-10            0            1         0
2017-06-11            0            1         0
2017-06-12            0            1         0
2017-06-13            1            0         1
2017-06-14            1            0         1
2017-06-15            0            1         1
2017-06-16            0            1         1
2017-06-17            0            1         1
2017-06-18            0            1         1
2017-06-19            0            1         1
2017-06-20            1            0         1
2017-06-21            1            0         1
2017-06-22            0            0         0

In [86]: %timeit add_signal_l(df)
8.45 ms ± 177 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [87]: %timeit add_lagged(df)
137 µs ± 581 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

如您所见,add_lagged(df) 更快。

【讨论】:

感谢您的回答!这太棒了!

以上是关于如何对使用其自身输出的滞后值的函数进行矢量化?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 ARM Neon 内在函数对 IF 块进行矢量化?

如何在R中的一列中添加具有不同值的新行

Spark Dataframe - 窗口函数 - 插入和更新输出的滞后和领先

具有返回值的递归

获取最后一个不同值的滞后函数(红移)

滞后环节对系统的影响