繁体   English   中英

Cython:如何按另一个向量对一个向量的内容进行排序?

[英]Cython: How to sort the contents of one vector by another vector?

我正在尝试在 Cython 中对两个 C++ 向量进行排序,一个按其自身的内容排序,另一个按第一个向量的内容排序。

cimport cython
from libcpp.vector cimport vector
from libcpp.algorithm cimport sort as stdsort

def function():

    cdef vector[np.npy_float] distances
    cdef vector[np.npy_intp] indices

    d = [9., 8., 3., 2., 3.]
    for i in range(len(d)):
        distances.push_back(d[i])
        indices.push_back(i)

    stdsort(distances.begin(), distances.end())
    // distances = [2.0, 3.0, 3.0, 8.0, 9.0]
    // Sort indices by distances?

    return distances, indices

我知道在纯 C++ 中,您可以使用包含距离和索引的对象轻松完成此操作,并为该对象提供自定义排序功能,但是在 Cython 中执行此操作的简单方法是什么?

为了在 C++ 中获取排序索引,人们通常不会创建一个带有索引值对的新向量,而是选择一个比较器,它可以在不实际复制内存的情况下实现相同的目标。

将其转换为 Cython 将如下所示:

%%cython -+ -c=-std=c++11
from libcpp.vector cimport vector
cdef extern from *:
    """
    #include <algorithm>
    #include <vector>
    void sort_via_score(std::vector<int>& indices, const std::vector<double>& scores){
        std::sort(indices.begin(), indices.end(),
                  [&scores](int i, int j){return scores.at(i)<scores.at(j);}
                 );
    }
    """
    void sort_via_score(vector[int]& indices, vector[double]& scores)
    
def sort_indices(lst):
    cdef vector[double] scores = lst
    cdef vector[int] indices = range(len(lst))
    sort_via_score(indices, scores)
    return indices

函数sort_indices是一个包装器,它允许我们快速检查实现:

sort_indices([5,4,3,2,1])
# [4, 3, 2, 1, 0] as expected

sort_via_score工作方式类似于 Python 中的以下单行:

def sort_indices_py(scores):
    return sorted(range(len(scores)), key=lambda x: scores[x])

在闭包中使用scores向量来查找索引的分数。 没有创建新的对象将 index 和它的分数放在内存中 - 它们单独由key函数的逻辑组合。


上面的解决方案使用逐字 C 代码,因为使用 C++ 编写 C++ 代码比使用 Cython 容易得多。

如果真的想坚持使用“纯”Cython(我不推荐),那么可以使用以下代码模拟 C++ 闭包:

%%cython -+
from libcpp.vector cimport vector
from libcpp.algorithm cimport sort as stdsort

cdef vector[double]* vec
cdef bint comp_fun(int i, int j):
    return vec.at(i)<vec.at(j)

def sort_indices2(lst):
    cdef vector[double] scores = lst
    cdef vector[int] indices = range(len(lst))
    global vec
    vec = &scores # "global closure"
    stdsort(indices.begin(), indices.end(), comp_fun)
    return indices

我很想采用稍微简单的方法来创建一个包含分数和索引的单个数组,然后对其进行排序。 缺点是它涉及更多复制@ead 的答案。

对于这个std::pair工作得很好(因为它已经定义了你需要的运算符)并且可以很容易地从 Cython 访问。

# distutils: language = c++

from libcpp.vector cimport vector
from libcpp.utility cimport pair
from libcpp.algorithm cimport sort as stdsort

def function():
    cdef vector[pair[float, int]] di
    cdef vector[float] distances
    cdef vector[int] indices
    

    d = [9., 8., 3., 2., 3.]
    for i in range(len(d)):
        di.push_back(pair[float, int](d[i], i))

    stdsort(di.begin(), di.end())
    
    for di_pair in di:
        distances.push_back(di_pair.first)
        indices.push_back(di_pair.second)

    return distances, indices

对于更高级的情况,您可能必须使用自己的比较器定义自定义结构或类,而不是使用std::pair 在这些情况下,我不会太执着于使用 Cython 来处理所有事情——用 C++ 编写 C++ 通常更容易。 但是对于这种情况,您不需要。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM