代码之家  ›  专栏  ›  技术社区  ›  varnie

熊猫回路优化

  •  1
  • varnie  · 技术社区  · 8 年前

    是否有更好的方法(从性能角度)在pandas中执行以下循环(假设 df 是一个 DataFrame )?

    for i in range(len(df)):
        if df['signal'].iloc[i] == 0:   # if the signal is negative
            if df['position'].iloc[i - 1] - 0.02 < -1:   # if the row above - 0.1 < -1 set the value of current row to -1
                df['position'].iloc[i] = -1
            else:   # if the new col value above -0.1 is > -1 then subtract 0.1 from that value
                df['position'].iloc[i] = df['position'].iloc[i - 1] - 0.02
        elif df['signal'].iloc[i] == 1:     # if the signal is positive
            if df['position'].iloc[i - 1] + 0.02 > 1:     # if the value above + 0.1 > 1 set the current row to 1
                df['position'].iloc[i] = 1
            else:   # if the row above + 0.1 < 1 then add 0.1 to the value of the current row
                df['position'].iloc[i] = df['position'].iloc[i - 1] + 0.02
    

    我会很感激任何建议,因为我刚开始通过熊猫路线,显然,可能会错过一些重要的事情。

    源CSV数据:

    Date,sp500,sp500 MA,UNRATE,UNRATE MA,signal,position
    2000-01-01,,,4.0,4.191666666666665,1,0
    2000-01-02,,,4.0,4.191666666666665,1,0
    2000-01-03,102.93,95.02135,4.0,4.191666666666665,1,0
    2000-01-04,98.91,95.0599,4.0,4.191666666666665,1,0
    2000-01-05,99.08,95.11245000000001,4.0,4.191666666666665,1,0
    2000-01-06,97.49,95.15450000000001,4.0,4.191666666666665,1,0
    2000-01-07,103.15,95.21575000000001,4.0,4.191666666666665,1,0
    2000-01-08,103.15,95.21575000000001,4.0,4.191666666666665,1,0
    2000-01-09,103.15,95.21575000000001,4.0,4.191666666666665,1,0
    

    期望输出:

    Date,sp500,sp500 MA,UNRATE,UNRATE MA,signal,position
    2000-01-01,,,4.0,4.191666666666665,1,0.02
    2000-01-02,,,4.0,4.191666666666665,1,0.04
    2000-01-03,102.93,95.02135,4.0,4.191666666666665,1,0.06
    2000-01-04,98.91,95.0599,4.0,4.191666666666665,1,0.08
    2000-01-05,99.08,95.11245000000001,4.0,4.191666666666665,1,0.1
    2000-01-06,97.49,95.15450000000001,4.0,4.191666666666665,1,0.12
    2000-01-07,103.15,95.21575000000001,4.0,4.191666666666665,1,0.14
    2000-01-08,103.15,95.21575000000001,4.0,4.191666666666665,1,0.16
    2000-01-09,103.15,95.21575000000001,4.0,4.191666666666665,1,0.18
    

    更新 下面的所有答案(在我写这篇文章的时候)都会产生常数 position 0.02,这与我的简单循环方法不同。 换言之,我正在寻找一个能给 0.02 , 0.04 , 0.06 , 0.08 等等 位置 列。

    4 回复  |  直到 8 年前
        1
  •  1
  •   ak_slick    8 年前

    感谢您添加数据和示例输出。首先,我很确定你不能将其矢量化,因为每个计算都依赖于前一个计算的输出。所以这是我所能做的最好的。

    你的方法出现了 0.116999 在我的机器上几秒钟

    这个进来了 0.0039999

    不是矢量化的,但是它得到了一个很好的速度提升,因为使用一个列表来处理这个问题并在最后将它添加回数据帧会更快。

    def myfunc(pos_pre, signal):
        if signal == 0:  # if the signal is negative
            # if the new col value above -0.2 is > -1 then subtract 0.2 from that value
            pos = pos_pre - 0.02
            if pos < -1:  # if the row above - 0.2 < -1 set the value of current row to -1
                pos = -1
    
        elif signal == 1:
            # if the row above + 0.2 < 1 then add 0.2 to the value of the current row
            pos = pos_pre + 0.02
            if pos > 1:  # if the value above + 0.1 > 1 set the current row to 1
                pos = 1
    
        return pos
    
    
    ''' set first position value because you aren't technically calculating it correctly in your method since there is no 
    position minus 1... IE: it will always be 0.02'''
    new_pos = [0.02]
    
    # skip index zero since there is no position 0 minus 1
    for i in range(1, len(df)):
        new_pos.append(myfunc(pos_pre=new_pos[i-1], signal=df['signal'].iloc[i]))
    
    df['position'] = new_pos
    

    输出:

    df.position
    0    0.02
    1    0.04
    2    0.06
    3    0.08
    4    0.10
    5    0.12
    6    0.14
    7    0.16
    8    0.18
    
        2
  •  2
  •   jpp    8 年前

    不要使用循环。熊猫擅长矢量化操作,例如 signal == 0 :

    pos_shift = df['position'].shift() - 0.02
    m1 = df['signal'] == 0
    m2 = pos_shift < -1
    
    df.loc[m1 & m2, 'position'] = -1
    df['position'] = np.where(m1 & ~m2, pos_shift, df['position'])
    

    你可以写一些类似的东西 signal == 1 .

        3
  •  0
  •   Jonas Byström    8 年前

    是的。在寻找性能时,您应该始终在底层numpy阵列上操作:

    signal = df['signal'].values
    position = df['position'].values
    for i in range(len(df)):
        if signal[i] == 0:
            if position[i-1]-0.02 < -1:
                position[i] = -1
            else:
                position[i] = position[i-1]-0.02
        elif signal[i] == 1:
            if position[i-1]+0.02 > 1:
                position[i] = 1
            else:
                position[i] = position[i-1]+0.02
    

    你会惊讶于性能的提高,通常是10倍或更多。

        4
  •  0
  •   Ashish Acharya    8 年前

    很可能有更好的方法,但这个方法也应该奏效:

    df['previous'] = df.signal.shift()
    
    def get_signal_value(row):
        if row.signal == 0:
            compare = row.previous - 0.02
            if compare < -1:
                return -1
            else:
                return compare
        elif row.signal == 1: 
            compare = row.previous + 0.01
            if compare > 1:
                return 1
            else:
                return compare
    
    df['new_signal'] = df.apply(lambda row: get_signal_value(row), axis=1)