在R中使用mapply()对行,对列

时间:2012-08-08 21:26:55

标签: r plyr

我在工作中处理了大量的调查数据等,而且我经常需要制作各种评分程序来逐行处理数据。例如,我正在处理一个表,其中包含12个列,其中包含来自心理测量仪器的子量表分数。这些将使用乐器创建者提供的表格转换为标准化分数。到目前为止似乎很简单。

但是,有四个表格 - 根据性别和年龄范围对乐器进行不同的评分。因此,例如,一名14岁的女性和一名10岁的男性获得不同的标准化表格。所有归一化数据都存储在R数据帧中。

我想要做的是编写一个可以应用于行的函数,它返回从规范化数据中查找的向量。所以,有点像这样:

converter <- function(rawscores,gender,age) {
    if(gender=="Male") {
        if(8 <= age & age <= 11) {convertvec <- c(1:12)}
        if(12 <= age & age <= 14) {convertvec <- c(13:24)}
    }
    else if(gender=="Female") {
        if(8 <= age & age <= 11) {convertvec <- c(25:36)}
        if(12 <= age & age <= 14) {convertvec <- c(37:48)}
    }

    converted_scores <- rep(0,12)
    for(z in 1:12) {
        converted_scores[z] <- conversion_table[(unlist(rawscores)+1)[z],
                                                convertvec[z]]
    }
    rm(z)
    return(converted_scores)
}

编辑:我用昨天实际工作的代码更新了这个。此版本返回带有分数的简单向量。这是我如何实现它。

mydata[,21:32] <- 0
for(x in 1:dim(mydata)[1]) {
    tscc_scores[x,21:32] <- converter(mydata[x,7:18],
                                      mydata[x,"gender"],
                                      mydata[x,"age"])
}

这有效,但就像我说的那样,我明白这是不好的做法?

旁注:rawscores + 1的原因是数据框在第一个索引中得分为零。

从根本上说,这个函数看起来并不复杂,而且我知道我可以使用一个循环实现它,我会做(x in 1:number_of_records),但我的理解是这样做是不好的做法。我本来希望简单地使用apply()来做到这一点,如下所示:

apply(X=mydata[,1:12],MARGIN=1,
      FUN=converter,gender=mydata[,"gender"],age=mydata[,"age"])

不幸的是,R似乎并不赞同这种方法,因为它不会迭代传递给后续参数的向量,而是试图将它们作为整体的参数。解决方案似乎是mapply(),但我无法弄清楚是否有一种方法可以在行上使用mapply()而不是列。

所以,我想我的问题有三个。一,有没有办法在行上使用mapply()?二,有没有办法让apply()迭代参数?三,那里有更好的选择吗?我已经看到并听到了很多关于plyr包的内容,但在我完全调查Base R中的选项之前,我不想跳到那个。

2 个答案:

答案 0 :(得分:1)

您可以重写“转换器”,以便它采用性别,年龄和行索引的向量,然后使用转换数组和数字分数列的数据数组对convert_scores进行查找和分配。使用apply还有一个问题,因为它会将所有x参数转换为“character”类,因为性别类是“character”。目前尚不清楚您的代码normdf[ rawscores+1, convertvec]是应该是数组提取还是函数调用。

在没有工作示例的情况下未经测试(使用normdfmydata):

 converted_scores <- matrix(NA, nrow=NROW(rawscores), ncol=12) 
 converter <- function(idx,gender,age) {
     gidx <- match(gender, c("Male", "Female") )
     aidx <- findInterval(age, c(8,12,15) ) 
     ag.idx <- gidx + 2*aidx -1  
          # the aidx factor needs to be the same number of valid age categories
     cvt <- cvt.arr[ ag.idx, ]

     converted_scores[idx] <- normdf[rawscores+1,convertvec]
     return(converted_scores)
 }
 cvt.arr <- matrix(1:48, nrow=4, byrow=TRUE)[1,3,2,4] # the genders alternate
 cvt.scores <- mapply(converter, 1:NROW(mydata), mydata$gender, mydata$age)

答案 1 :(得分:1)

我建议不要逐行应用这些东西,而是宁愿按列应用。原因是只有12列,但可能有很多行。

以下代码对我有用。可能有更好的方法,但对你来说可能会很有趣。

offset <- with(mydata, 24*(gender == "Female") + 12*(age >= 12))
idxs <- expand.grid(row = 1:nrow(mydata), col = 1:12)
idxs$off <- idxs$col + offset
idxs$val <- as.numeric(mydata[as.matrix(idxs[c("row", "col")])]) + 1
idxs$norm <- normdf[as.matrix(idxs[c("val", "off")])]
converted <- mydata
converted[,1:12] <- as.matrix(idxs$norm, ncol=12)

这里棘手的部分是这个idxs数据框,它结合了所有其余部分。它有以下列:

  • 行和列:在原始数据中的位置
  • {li> off: normdf中的列,基于性别和年龄
  • val:normdf中的行,基于原始值+ 1
  • norm:相应的标准化值

我将在这里首先考虑这个问题,看看我是否可以根据jorans评论或使用normdf的三维或四维数组得出更好的答案。还不确定。