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

时间:2018-08-07 14:45:46

标签: python pandas numpy

我想在数据框中创建一个新列,该列包含(在每个单元格中)数组中的第一个值,该值小于一个已经存在的列中每个相应单元格中的值。以下是有关此操作方式的简要说明,我确定了3种情况:
1.以5的步长创建a并将其从10排列到75。
2a。如果列c1在一个单元格中具有0到10的值,则新列的结果应为0(我发现这很棘手,没有在数组a上加上零。宁愿不做)。 c1列始终为非负数。
2b。如果c1是11到75,则它应返回a的元素,该元素立即小于单元格的值。
2c。如果c1大于75,则应返回75。(实际上,这只是2b的扩展)

这是我的尝试-它可以完成工作,但是我觉得它很慢。我认为我不能使用np.argmaxnp.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 个答案:

答案 0 :(得分:3)

设置

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)

答案 1 :(得分:1)

类似于@ 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