R数据帧与c++中的which.min()等价

R data frame and which.min() equivalent in c++

本文关键字:min 等价 which 中的 数据帧 c++      更新时间:2023-10-16

我正在将R代码翻译为c++,我想找到一个等效的(最佳)结构,它将允许与数据帧相同的操作,但在c++中。

操作如下:

  • 添加元素(行)
  • 从索引
  • 中删除元素(行)
  • 获取最低值的索引

。:

a <- data.frame(i = c(4, 9, 3, 1, 8, 2, 7, 10, 6, 6), 
                j = c(8, 8, 8, 4, 3, 9, 1, 4,  8, 9) , 
                v = c(1.9, 18, 1.3, 17, 1.5, 14, 11, 1.4, 18, 2.0), 
                o = c(3, 3, 3, 3, 1, 2, 1, 2, 3, 3))
a[which.min(a$v), c('i', 'j')] # find lowest v value and get i,j value
a <- a[-which.min(a$v)] # remove row from index
a <- cbind(a, data.frame(i = 3, j = 9, v = 2, o = 2)) # add a row

当我使用Rcpp时,Rcpp::DataFrame可能是一个选项(我不知道如何使用它)。但是,我想对于这个任务来说,它是相当慢的,因为这些操作需要重复很多,我不需要把它运回r。

编辑:

的目标。这里的目的是为了获得速度。这是将代码从R翻译成c++的明显原因(可能还有其他原因,这就是我澄清的原因)。然而,维护和易于实现是第二位的。

操作更精确。算法是:向数组(多行)中添加大量数据,然后提取最小值并删除。重复。这就是为什么我不会使用排序向量,而是总是在数组频繁更新(添加)时按需搜索最低的数据。我认为这样更快,但也许我错了。

我认为向量的向量应该做你想做的事。您需要手动实现最小查找(两个嵌套循环),这是在不增加开销的情况下最快的方法。您可以通过跟踪每行中最小元素的位置来加快查找最小值的速度。

这个问题有点过时了,但是我想我应该提供一些关于这类任务的一般观察。

如果您要将行集合保持在有序状态,这可能是您的。最小策略,最难支持的操作是行插入,如果这是一个常见的操作。您很难不使用list<>数据结构,这可能导致。最小值变成了一个线性运算,因为列表不适合平分搜索。

如果您保持一个无序集合,您可以通过将记录从帧的末尾复制到删除空出的行,并从行计数中减去1来处理删除。或者,您可以使用另一个bool向量标记删除,直到删除计数达到阈值(如sqrt(N)),此时执行合并复制。你会得到平摊O(N^2)更好的插入/删除,但是。最小值每次都是对整个向量的线性搜索。

当您需要识别min/max元素作为一个常见操作时,通常要做的事情是使用某种优先级队列,有时会复制索引列的数据。我首先想到的是,将一个数据列上的优先级队列与由于非列表实现中的删除操作而移动的数据帧的行同步是很棘手的。

如果行只是标记为已删除,则优先级队列将保持同步(尽管您必须丢弃从队列中弹出的与随后删除的行对应的元素,直到您得到一个好的行);在合并复制之后,您将重新索引优先级队列,如果您不经常这样做的话,这是相当快的。通常,如果你有足够的内存将结构扩展到更大的尺寸,那么如果结构缩小,你就不会过于紧张地再次将内存归还;如果您的结构倾向于保持在高水位附近的大小,则不需要合并,但要注意优先级队列过期和对同一存储行的新引用的情况,因为您向先前删除的行写入了新数据。为了提高效率,有时您最终会使用辅助列表来跟踪标记为已删除的行,这样您就可以在不到线性时间内为插入的行找到存储空间。

很难从优先级队列的内部提取过时的项目,因为这些项目往往只被设计为在队列的顶部删除;通常情况下,你必须把不新鲜的东西留在那里,如果它们在一段时间后出现,就忽略它们。

当你带着性能目标进入c++时,有很多方法可以让你的皮肤变得更好,你需要更精确地权衡性能,而不是原始R代码所表达的,以获得所有必需操作的良好执行时间。

A data.frame实际上只是一个向量列表。在c++中,我们实际上只有那些向量列表,这使得添加行变得困难。

用于删除行的Idem -由于Rcpp在原始R表示上工作,您总是需要复制所有剩余的值。

至于which.min()等效:我认为在列表中出现一次,您可以使用STL习惯用法做一些简单的事情。我不记得我们在API中有这个。

c++术语中的R 数据帧是对象的容器(R 矩阵可能是向量的向量,但如果你关心效率,你不太可能这样实现它)

那么,用这个类来表示数据帧:

class A{
public:
  int i,j,o;
  double v;
public:
  A(int i_,int j_,int v_,int o_):i(i_),j(j_),v(v_),o(o_){}
}

并准备这个算法参数函数来帮助找到最小值:

bool comp(A &x,A &y){
return x.v<y.v;
}

(在产品代码中,我更可能使用函子对象(参见Meyer的有效STL,第46项),或者boost lambda,或者最好是c++ 0x的lambda)

然后这个代码体:

std::vector<A> a;
a.push_back(A(4,8,1.9,3));
a.push_back(A(9,8,18,3));
a.push_back(A(1,4,1.3,3));
//...
std::vector<A>::iterator lowest=std::min_element(a.begin(),a.end(),comp);
std::cout<< lowest->i << ',' << lowest->j <<"n";
a.erase(lowest);
a.push_back( A(3,9,2,0) );

根据你真正在做的事情,首先对a排序可能更有效,最伟大的优先。然后,如果您希望擦除最低的项,您只需截断向量。如果您实际上要删除所有的数据,而which.min()只是为了举例,那么您可能会发现链表更有效。

数据帧比向量的向量要复杂一些

我可以说,在所有情况下,最简单的速度设计是将每个列存储在类型化向量中,并创建一个列表作为行的标题。然后在它的顶部创建一个侵入式列表