Что эквивалентно функции nth_element в Python?

Я хочу реализовать дерево точек выигрыша в Python, но оно использует std :: nth_element в C ++.

Итак, я хочу найти эквивалентную функцию nth_element в Python или в numpy.

Обратите внимание, что nth_element будет только частично упорядочивать массив, и это O (N).

int the_array[10] = {4,5,7,3,6,0,1,2,9,8};
std::vector<int> the_v(the_array,the_array+10);
std::nth_element (the_v.begin()+0, the_v.begin()+5, the_v.begin()+10);

И теперь вектор может быть:

3,0,2,1,4,5,6,7,9,8

И я не только хочу получить n-й элемент, но также хочу переупорядочить две части списка, [3,0,2,1,4] и [6,7,9,8].

Кроме того, поддержка nth_element принимает функцию, которая может сравнивать два элемента, например, как показано ниже, вектор является вектором op DataPoint, а функция DistanceComparator будет сравнивать расстояние между двумя точками с the_v.begin ():

vector<DataPoint> the_v;
for(int n = 0; n < N; n++) the_v[n] = DataPoint(D, n, X + n * D);
std::nth_element (the_v.begin()+0, the_v.begin()+5, the_v.begin()+10,
    DistanceComparator(the_v.begin()));

РЕДАКТИРОВАТЬ:

Я использовал ответ bhuvan-venkatesh и написал код для тестирования.

partition_timer = timeit.Timer("numpy.partition(a, 10000)",
    "import numpy;numpy.random.seed(2);"+
    "a = numpy.random.rand(10000000)")
print(partition_timer.timeit(10))

sort_timer = timeit.Timer("numpy.sort(a)",
    "import numpy;numpy.random.seed(2);"+
    "a = numpy.random.rand(10000000)")
print(sort_timer.timeit(10))

sorted_timer = timeit.Timer("sorted(a)",
    "import numpy;numpy.random.seed(2);"+
    "a = numpy.random.rand(10000000)")
print(sorted_timer.timeit(10))

и результат:

2.2217168808
17.0386350155
281.301710844

А затем я проведу еще несколько тестов с использованием кода C ++.

Но есть проблема: при использовании numpy он всегда будет возвращать новый массив, он будет тратить много памяти, когда мой массив огромен. Как я могу с этим справиться. Или мне просто нужно написать расширение C ++ для python.

РЕДАКТИРОВАТЬ2:

@ bhuvan-venkatesh Спасибо за рекомендацию функции разделения.

Я использую раздел, как показано ниже:

import numpy

@profile
def for_numpy():
    numpy.random.seed(2)
    a = numpy.random.rand(1e7)
    for i in range(100):
        a.partition(numpy.random.randint(1e6))

if __name__ == '__main__':
    for_numpy()

и запустил профилировщик, например:

python -m memory_profiler profiler_test.py

и результат:

Line #    Mem usage    Increment   Line Contents
================================================
    25   23.613 MiB    0.000 MiB   @profile
    26                             def for_numpy():
    27   23.613 MiB    0.000 MiB       numpy.random.seed(2)
    28   99.934 MiB   76.320 MiB       a = numpy.random.rand(1e7)
    29  100.004 MiB    0.070 MiB       for i in range(100):
    30  100.004 MiB    0.000 MiB           a.partition(numpy.random.randint(1e6))

И он не будет копировать весь массив, например: numpy.partition (a, 3)

Вывод: я хочу найти numpy.ndarray.partition.


person Colin Ji    schedule 04.08.2016    source источник
comment
Вы читали это?   -  person Paul Rooney    schedule 04.08.2016
comment
Я читаю это. Но я хочу не только получить n-й элемент, но и упорядочить список.   -  person Colin Ji    schedule 04.08.2016
comment
Инструменты в ответе тоже это делают.   -  person user2357112 supports Monica    schedule 04.08.2016
comment
Привет, user2357112, вы имеете в виду ответ бхуван-венкатеш, не так ли? где инструменты?   -  person Colin Ji    schedule 04.08.2016
comment
На самом деле я говорил о том, что в ссылке Пола, но numpy.partition - хорошая реализация этих инструментов.   -  person user2357112 supports Monica    schedule 04.08.2016


Ответы (1)


http://docs.scipy.org/doc/numpy/reference/generated/numpy.partition.html

Просто убедитесь, что раздел numpy создаст два новых массива, а это означает, что вы быстро создадите много новых массивов. Они более эффективны, чем списки Python, но не будут делать то же самое, что и в C ++.

Если вам нужен точный элемент, вы можете выполнить поиск по фильтру, который по-прежнему будет O (n)

array = np.array(...)
partition = np.partition(array, 5) # O(n)
element = np.where(partition==array[5]) # O(n)
left, right = partition[:element], partition[element+1:] # O(n)

Итак, ваш новый код работает медленнее, но это питонский способ сделать это.

ИЗМЕНИТЬ:

Итак, вам нужен компаратор? Помимо написания собственной небольшой функции, нет никакого способа - в чистом numpy в качестве ключевого слова - потому что каждая операция numpy реализована в высоко оптимизированном c-коде, что означает, что передача функции python или лямбда-выражения python заставит numpy каждый раз переходить на уровень объекта и оценивать.

numpy.vectorize переходит на уровень объекта, но в конце вам придется написать свой собственный код; Rosetta code требует применения, если вы хотите создать более «оптимизированный алгоритм». (Я заключил это в кавычки, потому что с объектами python вы все равно будете намного медленнее, чем код c или numpy, из-за доступа на уровне объекта). Если вас действительно беспокоит скорость, но вы хотите, чтобы читаемость Python была удобна, рассмотрите возможность расширения с помощью cython.

person bhuvy    schedule 04.08.2016
comment
Почему ты делаешь это с where? Это не выглядит правильным; кажется, вы выбрали не тот элемент, и where возвращает кортеж. Разве это не должно быть просто left, right = partition[:5], partition[6:]? - person user2357112 supports Monica; 04.08.2016
comment
Привет, мне нравится ваш ответ, но я просто добавлю несколько слов по своему вопросу. Это означает, что я также хочу, чтобы nth_element мог принимать компаратор. - person Colin Ji; 04.08.2016
comment
@ user2357112 Нет гарантии, что пятый элемент останется на пятой позиции после раздела, поэтому вам нужно, где; Вы правы, когда вам нужно распаковать кортеж (который я забыл), потому что элемент может повторяться. - person bhuvy; 04.08.2016
comment
@ bhuvan-venkatesh: Но мы ищем пятый элемент в отсортированном порядке, а не пятый элемент в исходном порядке. Нам не нужно ничего особенного, чтобы найти пятый элемент в исходном порядке. - person user2357112 supports Monica; 04.08.2016