самый быстрый способ использовать numpy.interp в двумерном массиве

У меня следующая проблема. Я пытаюсь найти самый быстрый способ использовать метод интерполяции numpy для двумерного массива x-координат.

import numpy as np

xp = [0.0, 0.25, 0.5, 0.75, 1.0]

np.random.seed(100)
x = np.random.rand(10)
fp = np.random.rand(10, 5)

Таким образом, в основном, xp будет x-координатами точек данных, x будет массивом, содержащим x-координаты значений, которые я хочу интерполировать, а fp будет двумерным массивом, содержащим y-координаты точек данных.

xp
[0.0, 0.25, 0.5, 0.75, 1.0]

x
array([ 0.54340494,  0.27836939,  0.42451759,  0.84477613,  0.00471886,
        0.12156912,  0.67074908,  0.82585276,  0.13670659,  0.57509333])

fp
array([[ 0.89132195,  0.20920212,  0.18532822,  0.10837689,  0.21969749],
       [ 0.97862378,  0.81168315,  0.17194101,  0.81622475,  0.27407375],
       [ 0.43170418,  0.94002982,  0.81764938,  0.33611195,  0.17541045],
       [ 0.37283205,  0.00568851,  0.25242635,  0.79566251,  0.01525497],
       [ 0.59884338,  0.60380454,  0.10514769,  0.38194344,  0.03647606],
       [ 0.89041156,  0.98092086,  0.05994199,  0.89054594,  0.5769015 ],
       [ 0.74247969,  0.63018394,  0.58184219,  0.02043913,  0.21002658],
       [ 0.54468488,  0.76911517,  0.25069523,  0.28589569,  0.85239509],
       [ 0.97500649,  0.88485329,  0.35950784,  0.59885895,  0.35479561],
       [ 0.34019022,  0.17808099,  0.23769421,  0.04486228,  0.50543143]])

Желаемый результат должен выглядеть следующим образом:

array([ 0.17196795,  0.73908678,  0.85459966,  0.49980648,  0.59893702,
        0.9344241 ,  0.19840596,  0.45777785,  0.92570835,  0.17977264])

Опять же, ищу самый быстрый способ сделать это, потому что это упрощенная версия моей проблемы, длина которой составляет около 1 миллиона против 10.

Спасибо


person Eric B    schedule 04.05.2017    source источник
comment
Как теперь получить желаемый результат? Кажется, вы хотите, чтобы каждый элемент в x интерполировал соответствующую строку в fp, но я могу ошибаться, потому что мне пришлось вычислять путем обратного проектирования ваших значений.   -  person kazemakase    schedule 04.05.2017
comment
Изначально я работал с pandas, но стараюсь быть независимым от pandas, в основном из-за проблем со скоростью. Так у меня появилась идея, как получить ответ с помощью обмана. Но да, ваше понимание было правильным. Я проверяю ваши ответы этим утром, ребята, спасибо. К вашему сведению, вот ссылка на мой вопрос с использованием панд (проблема была немного изменена): [интерполяция значений из фрейма данных на основе значения столбца] [1] [1]: stackoverflow.com/questions/43765796/   -  person Eric B    schedule 05.05.2017


Ответы (2)


Итак, в основном вы хотите, чтобы результат был эквивалентен

np.array([np.interp(x[i], xp, fp[i]) for i in range(x.size)])

Но этот цикл for сделает это довольно медленным для больших x.size

Это должно работать:

def multiInterp(x, xp, fp):
    i, j = np.nonzero(np.diff(np.array(xp)[None,:] < x[:,None]))
    d = (x - xp[j]) / np.diff(xp)[j]
    return fp[i, j] + np.diff(fp)[i, j] * d

EDIT: это работает еще лучше и может обрабатывать большие массивы:

def multiInterp2(x, xp, fp):
    i = np.arange(x.size)
    j = np.searchsorted(xp, x) - 1
    d = (x - xp[j]) / (xp[j + 1] - xp[j])
    return (1 - d) * fp[i, j] + fp[i, j + 1] * d

Тестирование:

multiInterp2(x, xp, fp)
Out: 
array([ 0.17196795,  0.73908678,  0.85459966,  0.49980648,  0.59893702,
        0.9344241 ,  0.19840596,  0.45777785,  0.92570835,  0.17977264])

Временные тесты с исходными данными:

    %timeit multiInterp2(x, xp, fp)
The slowest run took 6.87 times longer than the fastest. This could mean that an intermediate result is being cached.
10000 loops, best of 3: 25.5 µs per loop

%timeit np.concatenate([compiled_interp(x[[i]], xp, fp[i]) for i in range(fp.shape[0])])
The slowest run took 4.03 times longer than the fastest. This could mean that an intermediate result is being cached.
10000 loops, best of 3: 39.3 µs per loop

Кажется, быстрее даже для небольшого размера x

Давайте попробуем что-то гораздо большее:

n = 10000
m = 10000

xp = np.linspace(0, 1, n)
x = np.random.rand(m)
fp = np.random.rand(m, n)

%timeit b()  # kazemakase's above
10 loops, best of 3: 38.4 ms per loop

%timeit multiInterp2(x, xp, fp)
100 loops, best of 3: 2.4 ms per loop

Преимущества масштабируются намного лучше, чем у совместимой версии np.interp.

person Daniel F    schedule 04.05.2017

np.interp в основном представляет собой оболочку вокруг скомпилированного numpy.core.multiarray.interp. Мы можем немного снизить производительность, используя его напрямую:

from numpy.core.multiarray import interp as compiled_interp

def a(x=x, xp=xp, fp=fp):
    return np.array([np.interp(x[i], xp, fp[i]) for i in range(fp.shape[0])])

def b(x=x, xp=xp, fp=fp):
    return np.concatenate([compiled_interp(x[[i]], xp, fp[i]) for i in range(fp.shape[0])])

def multiInterp(x=x, xp=xp, fp=fp):
    i, j = np.nonzero(np.diff(xp[None,:] < x[:,None]))
    d = (x - xp[j]) / np.diff(xp)[j]
    return fp[i, j] + np.diff(fp)[i, j] * d

Временные тесты показывают, что для примеров массивов это соответствует хорошему решению Даниэля Форсмана:

%timeit a()
10000 loops, best of 3: 44.7 µs per loop

%timeit b()
10000 loops, best of 3: 32 µs per loop

%timeit multiInterp()
10000 loops, best of 3: 33.3 µs per loop

Обновить

Для несколько больших массивов слово принадлежит multiInterp:

n = 100
m = 1000

xp = np.linspace(0, 1, n)
x = np.random.rand(m)
fp = np.random.rand(m, n)

%timeit a()
100 loops, best of 3: 4.14 ms per loop

%timeit b()
100 loops, best of 3: 2.97 ms per loop

%timeit multiInterp()
1000 loops, best of 3: 1.42 ms per loop

Но для еще более крупных он отстает:

n = 1000
m = 10000

%timeit a()
10 loops, best of 3: 43.3 ms per loop

%timeit b()
10 loops, best of 3: 32.9 ms per loop

%timeit multiInterp()
10 loops, best of 3: 132 ms per loop

Наконец, для очень больших массивов (у меня 32-битная версия) временные массивы становятся проблемой:

n = 10000
m = 10000

%timeit a()
10 loops, best of 3: 46.2 ms per loop

%timeit b()
10 loops, best of 3: 32.1 ms per loop

%timeit multiInterp()
# MemoryError
person kazemakase    schedule 04.05.2017
comment
Хм, да. Я думаю, многое будет зависеть от того, насколько велик xp. Если он слишком большой, моя логическая матрица даст memerror. Может быть, я смогу найти лучший способ. . . - person Daniel F; 04.05.2017
comment
Посмотрите, сможете ли вы сломать мою новую версию, я думаю, она должна работать даже для больших массивов. - person Daniel F; 04.05.2017
comment
@DanielForsman Хорошее использование searchsorted. Я бы не хотел ломать вашу версию, но, возможно, я помогу ее оптимизировать, если у меня сейчас нет времени :) - person kazemakase; 04.05.2017