【发布时间】:2026-02-18 04:20:04
【问题描述】:
@user3759195 写了一篇关于 RStudio 与 RCpp 崩溃的帖子 https://*.com/questions/24322356/rstudio-crashes-and-it-does-not-reproduce,但没有给出任何可重现的案例。 @KevinUshey 在 cmets 中提到我们必须在代码中 PROTECT wrap。
我冒昧地发布了两个用 RCpp 编写的 split.data.frame 函数的替代方案:
* 不会导致 RSTUDIO 崩溃的版本 *
//[[Rcpp::export]]
List splitDataFrameCpp(DataFrame x,NumericVector y) {
int nRows=x.nrows();
int nCols=x.size();
std::map<double,vector<double> > z;
for (int i=0;i<nCols;i++) {
std::vector<double> tmp=Rcpp::as<std::vector<double> > (x[i]);
for (int j=0;j<nRows;j++) {
z[y[j]].push_back(tmp[j]);
}
}
std::vector<double> yunq=Rcpp::as<std::vector<double> > (sort_unique(y));
std::map<double, DataFrame> z1;
for (int i=0;i<int(yunq.size());i++) {
NumericVector tmp1=wrap(z[yunq[i]]); // *** DEFINING INSIDE LOOP ***
tmp1.attr("dim")=Dimension(int(tmp1.size())/nCols,nCols);
DataFrame tmp2(wrap(tmp1)); // *** DEFINING INSIDE LOOP ***
tmp2.attr("names")=x.attr("names");
z1[yunq[i]]=tmp2;
}
return wrap(z1);
}
* 导致 RSTUDIO 崩溃的版本 *
//[[Rcpp::export]]
List splitDataFrameCpp(DataFrame x,NumericVector y) {
int nRows=x.nrows();
int nCols=x.size();
std::map<double,vector<double> > z;
for (int i=0;i<nCols;i++) {
std::vector<double> tmp=Rcpp::as<std::vector<double> > (x[i]);
for (int j=0;j<nRows;j++) {
z[y[j]].push_back(tmp[j]);
}
}
std::vector<double> yunq=Rcpp::as<std::vector<double> > (sort_unique(y));
std::map<double, DataFrame> z1;
NumericVector tmp1; // *** DEFINING OUTSIDE LOOP ***
DataFrame tmp2; // *** DEFINING OUTSIDE LOOP ***
for (int i=0;i<int(yunq.size());i++) {
tmp1=wrap(z[yunq[i]]);
tmp1.attr("dim")=Dimension(int(tmp1.size())/nCols,nCols);
tmp2=wrap(tmp1);
tmp2.attr("names")=x.attr("names");
z1[yunq[i]]=tmp2;
}
return wrap(z1);
}
这两个代码之间的主要区别在于,在一种情况下tmp1 和tmp2 是在循环内定义的,而在另一种情况下是在循环外定义的。
谁能解释为什么第二个循环会崩溃(以及可以更改什么以不使 RStudio 崩溃)?我仍然是 C++ 的新手,主要通过查看 SO 或 RCpp 库网站上的示例来编写 RCpp - 所以想进一步了解这种行为。
另外,作为附带的好处,如果有人可以建议更改以使代码更快,那将是很棒的。根据我使用的一些测试用例,目前不会崩溃的代码比 R 的
split.data.frame函数快 2 到 3 倍左右。
测试用例示例:
> testDF
V1 V2 V3 V4 V5 V6
1 1 5 4 1 3 2
2 2 1 5 4 1 3
3 2 2 1 5 4 1
4 3 2 2 1 5 4
5 1 3 2 2 1 5
6 4 1 3 2 2 1
7 1 5 4 1 3 2
8 2 1 5 4 1 3
9 2 2 1 5 4 1
10 3 2 2 1 5 4
11 1 3 2 2 1 5
12 4 1 3 2 2 1
> testSp<-c(1,1,1,2,2,2,3,4,4,3,3,5)
> split(testDF,testSp) OR > splitDataFrameCpp(testDF,testSp)
$`1`
V1 V2 V3 V4 V5 V6
1 1 5 4 1 3 2
2 2 1 5 4 1 3
3 2 2 1 5 4 1
$`2`
V1 V2 V3 V4 V5 V6
4 3 2 2 1 5 4
5 1 3 2 2 1 5
6 4 1 3 2 2 1
$`3`
V1 V2 V3 V4 V5 V6
7 1 5 4 1 3 2
10 3 2 2 1 5 4
11 1 3 2 2 1 5
$`4`
V1 V2 V3 V4 V5 V6
8 2 1 5 4 1 3
9 2 2 1 5 4 1
$`5`
V1 V2 V3 V4 V5 V6
12 4 1 3 2 2 1
此测试用例的microbenchmark 结果:
> microbenchmark(t1<-split(testDF,testSp),t2<-splitDataFrameCpp(testDF,testSp))
Unit: microseconds
expr min lq median uq max neval
t1 <- split(testDF, test2) 343.181 365.562 372.8760 387.9430 1027.786 100
t2 <- splitDataFrameCpp(testDF, test2) 177.881 190.315 200.5545 208.4545 870.093 100
* 编辑 *
添加了sessionInfo:
> sessionInfo()
R version 3.1.0 (2014-04-10)
Platform: x86_64-w64-mingw32/x64 (64-bit)
locale:
[1] LC_COLLATE=English_United States.1252 LC_CTYPE=English_United States.1252
[3] LC_MONETARY=English_United States.1252 LC_NUMERIC=C
[5] LC_TIME=English_United States.1252
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] microbenchmark_1.3-0
loaded via a namespace (and not attached):
[1] Rcpp_0.11.1 tools_3.1.0
另外,testDF 在 R 中创建为 numeric,而不是 integer。
【问题讨论】:
-
感谢您抽出宝贵时间生成一个可重复的示例——您能补充一些缺失的部分吗? 1.
sessionInfo(), 2.testDF是如何构造的(列是整数还是数字?)FWIW,您发布的第二个版本不会在 Mac OSX Mavericks 上使用 Rcpp 0.11.2、R 3.1.0 崩溃, RStudio 0.98.943. -
FWIW 它对我来说也不会崩溃,无论是在 RStudio 中还是在 RStudio 之外;
-
好帖子,好 cmets。一个真正可重现的示例确实需要一个数据集(包括其创建或
dput()调用)和示例调用。 -
@KevinUshey,添加了
sessionInfo和testDF是数字。可能是 Windows 版本的 RStudio 问题? -
我可以用
Rcpp 0.11.1复制你的段错误,所以我的建议是在CRAN上更新到最新版本的Rcpp(现在是(0.11.2)