Использование нескольких функций интерполяции в кадре данных - PullRequest
0 голосов
/ 24 апреля 2019

У меня большой массив данных со многими столбцами. Для простоты скажем:

df_sample = pd.DataFrame({'a':np.arange(10)})

Мне нужно определить новый столбец в df_sample (скажем, столбец 'b'), который должен использовать некоторую интерполяционную функцию, аргумент которой должен быть взят из столбца 'a'.

Теперь проблема в том, что функция интерполяции различна для каждой строки. Для каждой строки я интерполирую из другой 1D сетки; поэтому у меня есть разные функции интерполяции для каждой строки. Итак, я заранее сгенерировал эти функции интерполяции и сохранил их в массив. Просто для примера, приведенного ниже кода для генерации образца массива "list_interpfns"

list_interpfns = np.array([None]*10)
for j in range(10):
    list_interpfns[j] = scipy.interpolate.interp1d(np.linspace(0,10*(j+1),10),np.linspace(0,50,10))

Чтобы сгенерировать df_sample.b [j], мне нужно использовать list_interpfns [j] с аргументом df_sample.a [j]. Поскольку я не могу напрямую применить формулу столбца для этой цели, я поместил это в цикл.

df_sample['b'] = 0
for j in range(10):
    df_sample.loc[j,'b'] = list_interpfns[j](df_sample.a[j])

Проблема в том, что эта операция занимает много времени. В этом небольшом примере вычисления могут показаться быстрыми. Но моя настоящая программа намного больше, и когда я сравнивал время, затрачиваемое на все операции, эта конкретная последовательность операций занимала 84% от общего времени; и мне нужно ускорить это.

Если есть какой-то способ избежать цикла for (например, использовать df.apply или что-то в этом роде), то я считаю, что это может сократить время работы. Не могли бы вы дать возможные альтернативы?

1 Ответ

1 голос
/ 24 апреля 2019

Не используйте несколько циклов for и ведите учет инициализации и обновления массивов и рядов, а также передавайте значения столбцов в построение функции и аргумент функции, используя Series.apply():

def interp_(j):
    return scipy.interpolate.interp1d(np.linspace(0,10*(j+1),10), np.linspace(0,50,10))

df_sample['b_'] = df_sample['a'].apply(lambda x: interp_(x)(x))

Результаты повторяют ваш исходный

df_sample
#    a         b        b_
# 0  0  0.000000  0.000000
# 1  1  2.500000  2.500000
# 2  2  3.333333  3.333333
# 3  3  3.750000  3.750000
# 4  4  4.000000  4.000000
# 5  5  4.166667  4.166667
# 6  6  4.285714  4.285714
# 7  7  4.375000  4.375000
# 8  8  4.444444  4.444444
# 9  9  4.500000  4.500000

А тайминги предполагают немного более быструю обработку, хотя Series.apply() все еще является циклом:

def run1():
    list_interpfns = np.array([None]*10)
    for j in range(10):
        list_interpfns[j] = scipy.interpolate.interp1d(np.linspace(0,10*(j+1),10),
                                                       np.linspace(0,50,10))            
    df_sample['b'] = 0
    for j in range(10):
        df_sample.loc[j,'b'] = list_interpfns[j](df_sample.a[j])

def run2():
    def interp_(j):
        return scipy.interpolate.interp1d(np.linspace(0,10*(j+1),10), np.linspace(0,50,10))

    df_sample['b_'] = df_sample['a'].apply(lambda x: interp_(x)(x))

if __name__=='__main__':
    from timeit import Timer

    f1 = Timer("run1()", "from __main__ import run1")
    res1 = f1.repeat(repeat=100, number=1)
    print('LOOP: {}'.format(np.mean(res1)))

    f2 = Timer("run2()", "from __main__ import run2")
    res2 = f2.repeat(repeat=100, number=1)
    print('APPLY: {}'.format(np.mean(res2)))

# LOOP: 0.006322918700000002
# APPLY: 0.0015046094699999867
...