如何在R

时间:2018-07-07 15:53:24

标签: r indexing rcpp

我想在rcpp中更改ListMatrix列表的元素,但始终无法做到这一点。请参见以下玩具示例:

library("Rcpp")
cppFunction('
ListMatrix ListMatrixType(ListMatrix x){
            NumericMatrix a = x(0,0);
            a(0,0) = 100;
            return x;
            }
            ')
x = matrix(list(matrix(0,3,2)),2,2)
a = ListMatrixType(x)
a[[1,1]]
a[[2,2]]

我希望仅更改a[[1,1],但是为什么还要更改a[[2,2]]

> a[[1,1]]
     [,1] [,2]
[1,]  100    0
[2,]    0    0
[3,]    0    0
> a[[2,2]]
     [,1] [,2]
[1,]  100    0
[2,]    0    0
[3,]    0    0

我必须误解rcpp中的索引规则。所以我的问题是如何正确更改每个列表的元素?我想每个列表都包含一个矩阵。

2 个答案:

答案 0 :(得分:9)

TL; DR::欢迎使用共享环境以及Rcpp使用指针。

有两种方法可以解决共享环境中 R 对对象的处理,从而避免与该功能相关的多米诺骨牌更新。

  1. 执行对象的深层副本,然后更新矩阵列表位置或
  2. 列表中仅具有唯一元素(例如,没有重复的矩阵)

首先,让我们看一下该共享内存列表的内存分配。为了方便起见并避免使用tracemem(),我们将使用lobstr包来探索共享环境。该软件包目前仅适用于GitHub,可以使用以下方式获取:

install.packages("devtools")
devtools::install_github("r-lib/lobstr")

手握lobstr,让我们看一下 R ...

中矩阵列表的基础内存地址
x = matrix(list(matrix(0,3,2)),2,2)
lobstr::obj_addrs(x)
# [1] "0x7fceb69757c8" "0x7fceb69757c8" "0x7fceb69757c8" "0x7fceb69757c8"
#      ^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^ 
# identical memory addresses for all objects

请注意,所有对象共享相同内存位置。因此, R 将列表中的每个这些元素视为相同。 R 选择此行为是为了减少内存中的数据大小,因为每个元素都属于同一共享环境

这对于 Rcpp 尤其成问题,因为它正在操纵指针,例如内存中显示的存储位置,以及 not 的位置,例如拥有intdouble之类的值的变量。

由于此指针行为,发生了两个操作:

  1. 同时更新矩阵列表中的所有矩阵,并且
  2. 整个对象x无需返回语句即可更新。

如果我们稍加修改您的功能,第二点就更明显了。

修改后的功能以说明指针

注意:该函数不再具有返回类型,但是我们仍然看到 R 的环境中x对象发生了变化。

#include<Rcpp.h>

// [[Rcpp::export]]
void ListMatrixType_pointer(Rcpp::ListMatrix x){
  Rcpp::NumericMatrix a = x(0, 0);
  a(0, 0) = 100;
}

输出

ListMatrixType_pointer(x)
str(x)
# List of 4
# $ : num [1:3, 1:2] 100 0 0 0 0 0
# $ : num [1:3, 1:2] 100 0 0 0 0 0
# $ : num [1:3, 1:2] 100 0 0 0 0 0
# $ : num [1:3, 1:2] 100 0 0 0 0 0
# - attr(*, "dim")= int [1:2] 2 2

注意,由于x是自动更新的,我们不必必须返回。此外,对于每个元素,我们仍然具有相同内存位置,例如

lobstr::obj_addrs(x)
# [1] "0x7fceb69757c8" "0x7fceb69757c8" "0x7fceb69757c8" "0x7fceb69757c8"
#      ^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^ 
# identical memory addresses for all objects

深层矩阵副本

要绕过相同的内存地址,可以深入克隆对象,然后将其保存回ListMatrix中。 clone()函数实例化一个新的内存块来存储值。因此,修改一个元素不再将触发多米诺骨牌更新。此外,将克隆的对象添加回列表时,内存地址仅更改该元素。

使用clone()进行深拷贝

#include<Rcpp.h>

// [[Rcpp::export]]
void ListMatrixType_clone(Rcpp::ListMatrix x){
  Rcpp::NumericMatrix a = x(0, 0);

  // Perform a deep copy of a into b
  // and, thus, changing the memory address
  Rcpp::NumericMatrix b = Rcpp::clone(a);
  b(0, 0) = 100;

  // Update x with b
  x(0, 0) = b;
}
输出量
ListMatrixType_clone(x)
str(x)
# List of 4
# $ : num [1:3, 1:2] 100 0 0 0 0 0
# $ : num [1:3, 1:2] 0 0 0 0 0 0
# $ : num [1:3, 1:2] 0 0 0 0 0 0
# $ : num [1:3, 1:2] 0 0 0 0 0 0
# - attr(*, "dim")= int [1:2] 2 2

lobstr::obj_addrs(x)
# [1] "0x7fceb811a7e8" "0x7fceb69757c8" "0x7fceb69757c8" "0x7fceb69757c8"
#             ^^^^^^^          ^^^^^^^ 
#            different memory addresses

列表中的唯一元素

为强调对唯一元素的需求,请考虑在矩阵列表的第一个位置修改矩阵,仅第一个元素的内存地址将更改;其余地址将保持不变。例如

x[[1, 1]] = matrix(1, 2, 2)
lobstr::obj_addrs(x)
# [1] "0x7fceb811a7e8" "0x7fceb69757c8" "0x7fceb69757c8" "0x7fceb69757c8"
#             ^^^^^^^          ^^^^^^^ 
#            different memory addresses

其他资源

有关此主题的更多信息,请参见下面的任何链接文章,其中强调了与 Rcpp 相关的指针行为(免责声明:是我写的):

关于对象大小的其他说明

注意:Base R中object.size()中的utils提供了对共享环境大小的错误计算。 c.f. ?utils::object.size

  

此函数仅提供一个粗略的指示:例如,它对于原子向量应该是相当准确的,但不能检测列表的元素是否共享。 (考虑了字符向量的元素之间的共享,但不考虑单个对象中字符向量之间的共享。)

lobstr::obj_size(x)
# 424 B
utils::object.size(x)
# 1304 bytes

因此,由于共享环境的原因,列表的对象大小约为1/3。

答案 1 :(得分:1)

我认为您不会误解Rcpp中的索引编制。

不仅a[[1,1]]a[[2,2]]被更改,而且我相信a[[1,2]]a[[2,1]]也被更改了。

如果我们使用另一种方式生成矩阵x

x = matrix(data = c(list(matrix(0,3,2)),
                    list(matrix(0,3,2)),
                    list(matrix(0,3,2)),
                    list(matrix(0,3,2))), 2, 2)

我们将看到a[[2,2]]未被更改。

> x = matrix(data = c(list(matrix(0,3,2)),
+                     list(matrix(0,3,2)),
+                     list(matrix(0,3,2)),
+                     list(matrix(0,3,2))), 2, 2)
> a = ListMatrixType(x)
> a[[1,1]]
     [,1] [,2]
[1,]  100    0
[2,]    0    0
[3,]    0    0
> a[[2,2]]
     [,1] [,2]
[1,]    0    0
[2,]    0    0
[3,]    0    0