如何将字符串视为矩阵/数据框对象/条目而不是简单字符

时间:2016-05-20 01:24:14

标签: r

下面是我的代码,我正在尝试从原始“数据帧”和一些虚拟矩阵创建多个数据帧子集(df)。 然后,根据子集数据帧中的列值,想填充矩阵

# In Here I create some 20 dataframes (df), by subsetting the 'dataframe' and also #create 20 dummy matrices.
for (i in min(dataframe$week):max(dataframe$week)) {

    assign(paste0("df",i), dataframe[dataframe$week==i,] )
    assign(paste0("mat",i), matrix(10, nrow=length(issue.list), ncol=length(category.list)))

    # Now, I fill values for the matrices by evaluating the contents of the 'df' created above
    for (j in 1:length(issue.list)) {
        for (k in 1:length(category.list)) {
            issue=issue.list[j]
            category=category.list[k]
            assign(paste0("mat",i,"[j,k]"), sum(paste0("df",i,"[issue]")==1 &  paste0("df",i,"[category]")==1) )
        }
    }

}

现在,问题是:在上面,paste0("mat",i,"[j,k]")正在评估字符"mati[j,k]"。如何将其评估为ex:mat1[1,2],它指的是在初始for循环中创建的矩阵元素。

我无法使用eval(parse(text=paste0(...))),因为它不符合我的情况。它指的是矩阵元素的值,即10。我想引用元素本身来改变它。

同样,如何将paste0("df",i,"[issue]")==1评估到数据框列(df1[issue] ==1)。

寻找类似于SAS宏语言的功能,&使用

1 个答案:

答案 0 :(得分:2)

使用列表跟踪相关数据要好得多,而不是在全局环境中分配松散变量。

根据您的代码,我认为我们可以通过以下方式完成您所需的任务:

## generate data
set.seed(3L);
N <- 15L; NI <- 3L; NC <- 3L;
dataframe <- cbind(week=sample(2:4,N,T),setNames(nm=paste0('issue',1:NI),as.data.frame(replicate(NI,sample(0:1,N,T)))),setNames(nm=paste0('category',1:NI),as.data.frame(replicate(NC,sample(0:1,N,T)))));
issue.list <- grep(value=T,'^issue',names(dataframe));
category.list <- grep(value=T,'^category',names(dataframe));
dataframe;
##    week issue1 issue2 issue3 category1 category2 category3
## 1     2      1      0      0         1         1         0
## 2     4      0      0      0         0         0         0
## 3     3      1      0      0         1         0         0
## 4     2      1      0      0         0         1         1
## 5     3      0      0      1         1         1         1
## 6     3      0      0      0         0         1         0
## 7     2      0      1      0         1         1         0
## 8     2      0      0      1         1         0         0
## 9     3      0      1      1         0         0         0
## 10    3      0      0      1         0         1         1
## 11    3      1      0      1         1         1         1
## 12    3      1      1      0         1         0         1
## 13    3      1      0      0         1         0         0
## 14    3      1      1      0         1         1         1
## 15    4      1      0      0         1         1         1
issue.list;
## [1] "issue1" "issue2" "issue3"
category.list;
## [1] "category1" "category2" "category3"

解决方案:

## compute dfs
dfs <- split(dataframe,dataframe$week);

## compute mats
cmb <- expand.grid(issue=issue.list,category=category.list);
mats <- lapply(dfs,function(df) matrix(apply(cmb,1L,function(x,il,cl) sum(il[,x['issue']] & cl[,x['category']]),df[issue.list]==1,df[category.list]==1),length(issue.list),dimnames=list(issue.list,category.list)));
dfs;
## $`2`
##   week issue1 issue2 issue3 category1 category2 category3
## 1    2      1      0      0         1         1         0
## 4    2      1      0      0         0         1         1
## 7    2      0      1      0         1         1         0
## 8    2      0      0      1         1         0         0
##
## $`3`
##    week issue1 issue2 issue3 category1 category2 category3
## 3     3      1      0      0         1         0         0
## 5     3      0      0      1         1         1         1
## 6     3      0      0      0         0         1         0
## 9     3      0      1      1         0         0         0
## 10    3      0      0      1         0         1         1
## 11    3      1      0      1         1         1         1
## 12    3      1      1      0         1         0         1
## 13    3      1      0      0         1         0         0
## 14    3      1      1      0         1         1         1
##
## $`4`
##    week issue1 issue2 issue3 category1 category2 category3
## 2     4      0      0      0         0         0         0
## 15    4      1      0      0         1         1         1
##
mats;
## $`2`
##        category1 category2 category3
## issue1         1         2         1
## issue2         1         1         0
## issue3         1         0         0
##
## $`3`
##        category1 category2 category3
## issue1         5         2         3
## issue2         2         1         2
## issue3         2         3         3
##
## $`4`
##        category1 category2 category3
## issue1         1         1         1
## issue2         0         0         0
## issue3         0         0         0
##

最后一行代码

的说明
mats <- lapply(dfs,function(df) ...);

分别处理列表中的每个data.frame,将当前data.frame别名为lambda的参数dflapply()调用的结果将是一个列表,其组件将包含lambda的每个评估的返回值。

apply(cmb,1L,function(x,il,cl) ...,df[issue.list]==1,df[category.list]==1)

在lambda内,我们在cmb上调用apply()。回想一下,cmb是一个包含两列{,1}}和issue的data.frame,其中每一行都包含两个集合中的一个唯一组合,所有可能的组合都在{{1}中表示}}。使用category运行cmb执行另一个lambda(我们可以将其称为&#34;内部lambda&#34;以区别于&#34;外部lambda&#34;)每行一次apply()(实际上是MARGIN=1L内的矩阵实际上被强制转换,但这并不重要)。内部lambda将在其第一个参数(我称之为cmb)中接收当前行作为双元素字符向量。方便地,apply()具有与输入对象相同的名称(特别是在其x属性上),当我们索引x时,我们将在lambda的主体中使用它。

查看names的文档。观察目标对象参数x,边距参数apply()和lambda参数X之后,MARGIN函数接受可选的可变参数,这些参数将直接转发给在FUN内部拨打的apply()来电。我在这里使用这个功能。我有效地预先计算了一个逻辑矩阵,该矩阵表示FUN()的问题列的哪些单元格等于1,并且我对类别列执行相同的操作。这两个逻辑矩阵最终将作为内部lambda调用的两个附加参数传递。这就是我写lambda以获取3个参数的原因:目标对象的当前行apply(),问题逻辑矩阵df和类别逻辑矩阵x。请注意,可变参数仅被评估一次(特别是当它们被il cl FUN()的第一次调用实例化时,由于R's lazy evaluation机制,所以由于对常量表达式的冗余重新评估,这里没有性能损失。另请注意,当您索引data.frame的列的子集(例如apply())时,列名称随子集一起出现,并且当您使用比较操作从data.frame计算逻辑矩阵时(例如df[issue.list])列名再次被带入新矩阵;当我们索引df[issue.list]==1il时,我们将在lambda的主体中使用它。

cl

最后,我们到达内在的lambda的身体。在这里,我们执行您在问题中显示的逻辑。也就是说,对于当前的问题/类别组合,我们在两个问题和类别列中找到sum(il[,x['issue']] & cl[,x['category']]) 的哪些行等于1,并计算该条件所属的行数真。

回想一下,问题(所有)问题和类别列中的哪些单元格等于1的测试已在df调用的可变参数中预先计算,我们将这两个逻辑矩阵作为{{{ 1}}和apply()。但是,我们需要检索与当前问题/类别组合相对应的两个逻辑矩阵的特定列。

首先,我们使用列名il索引cl以将当前问题作为字符串进行索引,然后使用该字符串索引x,因为其列名来自'issue',其具有特定的问题字符串作为列名。这为我们提供了一个逻辑向量,表示此问题列的il行等于1。我们可以对类别执行相同的操作,即列名为dataframe的索引df,然后使用生成的字符串索引x。然后,我们可以针对这两个逻辑向量执行向量化AND运算category,以获得单个逻辑向量,表示两个列中cl的哪些行等于1。取逻辑向量的&有效地计算其元素的数量为TRUE,并且整数计数将是内部lambda的返回值。

通常,调用df的返回值的类型和大小取决于输入对象的维度,边距以及每次lambda评估返回的内容(这很复杂! ),但是对于矩阵输入,行边距和每个lambda评估返回的标量整数的相对简单的情况,sum()调用的返回值将是对应于行的整数向量。输入矩阵。因此,因为apply()中有9行(最终是因为有9个问题/类别组合),我们的apply()调用将返回长度为9的整数向量。评估外部lambda,因为cmbapply()中的所有data.frame都是常量。

cmb

最后,由于您希望将结果作为矩阵,我们必须从向量中构造一个矩阵。这可以通过调用matrix()完成。

现在我们必须考虑,矩阵的尺寸需要什么?有dfs个问题和matrix(...,length(issue.list),dimnames=list(issue.list,category.list)) 个类别。尺寸必须与那些长度相对应。但他们应该走哪条路呢?换句话说,我们应该有length(issue.list)行和length(category.list)列,还是反过来?

回想一下,我们从length(issue.list)收到的向量对应length(category.list)行。这意味着apply()中组合的顺序将决定接收向量的含义。

cmb

观察cmb中的组合如何更快地改变问题&#34;迅速&#34;比类别。换句话说,当您沿着cmb; ## issue category ## 1 issue1 category1 ## 2 issue2 category1 ## 3 issue3 category1 ## 4 issue1 category2 ## 5 issue2 category2 ## 6 issue3 category2 ## 7 issue1 category3 ## 8 issue2 category3 ## 9 issue3 category3 的行前进时,问题首先会循环显示它们的值,然后只进行类别循环。这意味着对于向量的每个cmb元素,我们循环遍历所有问题,并且我们仅覆盖并且完全覆盖一个类别。这意味着cmb长度应该遵循最多填充的尺寸&#34;快速&#34;按length(issues.list)。碰巧,我们可以使用length(issues.list)的{​​{1}}参数来控制此行为。观察:

matrix()

我更喜欢使用默认的byrow填充顺序,这意味着我们需要matrix()行和matrix(1:4,2L); ## default is byrow=F ## [,1] [,2] ## [1,] 1 3 ## [2,] 2 4 matrix(1:4,2L,byrow=T); ## [,1] [,2] ## [1,] 1 2 ## [2,] 3 4 列。

为实现这一目标,我们只需指定byrow=F的{​​{1}}或length(issue.list)个参数之一,我已通过指定length(category.list)来完成此操作。在内部,nrow根据ncol和输入向量matrix()的长度派生所需的列数。

最后,需要将维度名称捕获为与结果矩阵中每个维度的每个索引相对应的问题和类别,这可以通过指定nrow的{​​{1}}参数来实现。 matrix()为行名称,nrow为列名,显然必须与上述data和维度大小选项相对应。

因此,外部lambda将最终返回此矩阵,以用作将从dimnames调用返回并分配给matrix()的列表的相应组件。