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

优化-返回数组中小于单元格值的第一个值(每行)

  •  0
  • BogdanC  · 技术社区  · 8 年前

    我想在数据框架中创建一个新列,它包含(在每个单元格中)数组中的第一个值,该值小于已存在列中每个相应单元格中的值。下面是如何工作的快速解释,我已经确定了3个场景:
    1。创建和排列 a 从10到75,步幅为5。
    2A如果IF列 c1 单元格中的值为0到10,新列中的结果应为 0 (我发现如果不在数组中添加零,这会很棘手 我宁愿不做)。柱 C1 总是非负的。
    2b.如果 C1 是11到75,它应该返回 立即小于单元格值。
    2C如果 C1 大于75,则返回75。(实际上,这只是2b的扩展)

    这是我的尝试-它做的工作,但我觉得它相当慢。我想我不能用 np.argmax np.argmin 因为这两个都不符合上面的2a/b/c点。希望有更快的解决方案。

    import numpy as np
    import pandas as pd
    
    np.random.seed(42)
    N = 10**6  #number of rows in df, change this to lower values for testing
    df = pd.DataFrame({'c1': np.random.randint(1,100,N)}) 
    
    a = np.arange(10,80,5)
    
    def first_lower(value, arr):
        if len(arr[arr < value]) > 0:
            return arr[arr < value][-1]  
        else:
            return 0
    
    def do_stuff(input_df):
        df = input_df.copy()
        df['NewCol'] = df['c1'].apply(lambda x: first_lower(x, a))
        return df
    
    %timeit do_stuff(df)
    # 11.4 s ± 881 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    2 回复  |  直到 8 年前
        1
  •  1
  •   javidcf    8 年前

    类似于@user3483203,但更一般:

    import numpy as np
    import pandas as pd
    
    np.random.seed(42)
    N = 20  # Just 20 for testing
    df = pd.DataFrame({'c1': np.random.randint(1,100,N)}) 
    a = np.arange(10,80,5)
    idx = np.searchsorted(a, df.c1)
    newcol = a[idx - 1]
    newcol[idx == 0] = 0
    df['newcol'] = newcol
    print(df)
    

    输出:

        c1  newcol
    0   52      50
    1   93      75
    2   15      10
    3   72      70
    4   61      60
    5   21      20
    6   83      75
    7   87      75
    8   75      70
    9   75      70
    10  88      75
    11  24      20
    12   3       0
    13  22      20
    14  53      50
    15   2       0
    16  88      75
    17  30      25
    18  38      35
    19   2       0
    
        2
  •  3
  •   user3483203    8 年前

    安装程序

    np.random.seed(1995)
    df = pd.DataFrame({'c1': np.random.randint(1, 100, 10)})
    a = np.arange(10,80,5)
    

    选项1
    你可以使用 np.select :

    c1 = df.c1.isin(range(0, 11))
    c2 = df.c1.isin(range(11,76))
    
    r1 = 0
    r2 = a[np.searchsorted(a, df.c1, side='left')-1]
    
    np.select([c1, c2], [r1, r2], 75)
    

    输出:

    array([35, 75, 35, 50, 65, 25, 75, 50, 25, 65])
    

    选项2
    使用 np.clip :

    s = np.clip(df.c1, 0, 75)
    s[s.isin(range(11,75))] = a[np.searchsorted(a, df.c1)-1]
    

    计时 :

    df = pd.DataFrame({'c1': np.random.randint(1,100,10**6)})
    %%timeit
    c1 = df.c1.isin(range(0, 11))
    c2 = df.c1.isin(range(11,76))
    r1 = 0
    r2 = a[np.searchsorted(a, df.c1, side='left')-1]    
    np.select([c1, c2], [r1, r2], 75)
    
    # 104 ms ± 214 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
    
    %%timeit
    s = np.clip(df.c1, 0, 75)
    s[s.isin(range(11,75))] = a[np.searchsorted(a, df.c1)-1]
    
    # 96 ms ± 1.05 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)