【问题标题】:mapply of aggregate with named output columns in RR中具有命名输出列的聚合映射
【发布时间】:2014-03-30 23:37:22
【问题描述】:

我有一个数据框,其中每一行都是唯一的用户,并且有一列表示每个用户的选择以及背景变量的列。在实际数据集中,有 800 多个选项,背景变量可以有 2 到 60 多个级别。

我正在做的是根据各种不同的分组组合查找每个选项的总计数。我已经成功地做到了,但我现在想以一种有意义的方式命名它们。

对该问题的第一部分有帮助的来源: How to use aggregate with a list of column names

# sample data
d <- data.frame(user_id = 1:12, choice = rep(c("apple", "banana", "banana"),4), cond1 = c("a1", "a1", "a2", "a1", "a2", "a1", "a2", "a2", "a3", "a3", "a3", "a3"), gender = c(rep(c("F", "M"), 6)))

# list of the different grouping combinations (choice will always be one of these terms)
c1 <- list(c("choice", "cond1"), c("choice", "cond1","gender"))  

# variable to be aggregated over
v1 <- c("user_id")    

varNames <- c("cond1Ct", "cond1_GenderCt")         # for later use

counts <- mapply(FUN= function(z,y, ...) {aggregate(d[ , y], by=d[z], ...)},
       c1, v1, MoreArgs=list(FUN=length) )

# assign each part of the output to its own dataframe for later use
cond1 <- counts[[1]]
cond1_Gender <- counts[[2]]

所以到目前为止这有效。

正如Name columns within aggregate in R 所见,可以为要聚合的变量分配列名。我无法让它在 mapply 函数调用中工作。这是我的主要问题 --- 如何自动命名每个分组组合的列表变量。

我的蛮力做法:

# rename the column labeled 'x' in a meaningful way ... should be possible to do using varnames (defined above)

cond1_Gender$cond1_Gender_Ct <- cond1_Gender$x          
cond1$cond1_Ct <- cond1$x
cond1_Gender$x <- NULL
cond1$x <- NULL

cond1_Gender

# bring back the now meaningfuly named columns into the original dataframe    
dUse <- merge(merge(d, cond1), cond1_Gender)  
     # efficiency of this for large dataset and 8+ aggregated datasets???



dUse_Cond <- dUse[!duplicated(dUse[c("choice", "cond1")]),]   # later: use item from list here!
# has extra columns that just get ignored ... but that could be potentially confusing for later users

# repeat for all other grouping variable combinations

# dUse_CondGender <- dUse[!duplicated(dUse[c1[[2]],])]   # (DOES NOT WORK!)

如何以更自动化的方式完成此命名,以便它可以跨数据文件和多个变量工作?

更大的图景:这只是我想要绘制每个分组组合的最佳选择的频率的更大情况的一部分。我有代码可以确定每个分组组合中评价最高的职业,并为分组类别中的每个级别绘制这些频率。


添加 3-25-14:澄清出现这种情况的背景,尽管可能超出了本问题的范围。

个人保存了一个选择(从一长串列表中)。我想知道(以图形和数字方式),对于背景分组变量的不同组合,哪些选择是“最常见的”(及其相应的频率):即,五年级男性的最频繁选择,或 10 年级学生的最频繁选择状态 X,或针对所有处于治疗状态的高中生。我有代码可以让我完成每个单独的背景分组:制表(如本题中所处理的那样)、查找“最频繁”的选择、计算该组的频率统计数据,以及根据该组中涉及的变量绘制图表。 总体而言,我试图使其更通用和矢量化,以便后续用户更轻松地定义他们想要查看的背景变量组合。 jlhoward 的回答将每个所需组合的计数列添加到原始数据帧中。


谢谢!我希望这是有道理的......我在这里阅读了很多问题和答案,但之前没有尝试过发布。

【问题讨论】:

  • 写下这个问题也让我回到了一个更大的问题,即如何首先构建数据 - 所以我也愿意接受这方面的建议,因为这可能是一种笨拙的方式这样做。我在 (1) 我是否希望将所有这些不同的聚合分组带回我的原始数据帧中,或者 (2) 我是否将它们保存在单独的数据帧中,最终可能会得到一个数据帧列表。这两种情况都涉及分配名称(变量和数据框或数据框)的问题,最好是从列表中。选项 1 是我在上面所做的工作。

标签: r aggregate plyr mapply


【解决方案1】:

您对mapply(...) 的调用比它需要的更复杂:

counts   <- mapply(FUN= function(z,y) aggregate(d[ , y], by=d[z], FUN=length),
                      c1, "user_id")

如果您只想自动将每个数据框中的列 x 重命名为其他名称,这将起作用:

# rename all "x" columns
for (i in 1:length(counts)) 
  colnames(counts[[i]])[ncol(counts[[i]])]<- varNames[i]

为了解决你的核心问题,我需要看一个例子来说明你所说的“绘制每个分组组合的最佳选择的频率。”

编辑(回应 OP 的评论)

如果您的中间目标是将所有内容组合成一个数据框,那么有一种更简单的方法。请注意,这会将名为 x 的聚合列保留到最后。

counts   <- mapply(FUN= function(z,y) aggregate(d[ , y], by=d[z], FUN=length),
                   c1, "user_id")
mrg <- lapply(counts,function(df)merge(d,df)[,c("user_id","x")])
mrg <- do.call(cbind,lapply(mrg,function(df)merge(d,df,by="user_id")$x))
colnames(mrg) <- varNames
result <- cbind(d,mrg)
result
#    user_id choice cond1 gender cond1Ct cond1_GenderCt
# 1        1  apple    a1      F       2              1
# 2        2 banana    a1      M       2              2
# 3        3 banana    a2      F       3              2
# 4        4  apple    a1      M       2              1
# 5        5 banana    a2      F       3              2
# 6        6 banana    a1      M       2              2
# 7        7  apple    a2      F       1              1
# 8        8 banana    a2      M       3              1
# 9        9 banana    a3      F       3              2
# 10      10  apple    a3      M       1              1
# 11      11 banana    a3      F       3              2
# 12      12 banana    a3      M       3              1

lapply(...)的第一次使用

mrg <- lapply(counts,function(df)merge(d,df)[,c("user_id","x")])

创建一个数据帧列表,其中每个将user_id 与适当组合的计数相关联。那么,

mrg <- do.call(cbind,lapply(mrg,function(df)merge(d,df,by="user_id")$x))

将每个列中的x 组合成一个由user_id 正确排序的数据框。最后,

result <- cbind(d,mrg)

将列与原始数据框d 合并,该数据框已在user_id 顺序中。

同样,了解您的最终目标会更好,因为几乎可以肯定有一种方法可以在不经历所有这些的情况下实现它。

【讨论】:

  • 第二步不能是names(counts) &lt;- varNames吗?
  • 感谢 jlhoward ...这也有效。我将@IShouldBuyABoat 标记为答案,因为我目前的方法似乎更直接,但我喜欢你如何将名称放到单独的数据框上并保留该代码。
  • @IShouldBuyABoat 当我尝试您的第二步时,它重命名了每个列表的项目名称(不确定那里的术语),而不是列表中每个数据框中标记为 x 的向量。
  • 我喜欢其他响应如何将它们全部重新组合在一起,但我在试图找出重新排序的一些问题时遇到了困难。您的 for 循环在每个数据帧中很好地命名它(直接问题),我现在将计数中的列表合并到一个数据帧中。这是一个单独的问题,上面还有其他 stackoverflow 帖子,虽然我还不能让它们工作......在 for 循环之后,我使用 d2 &lt;- lapply(counts, function( cts) merge(d, cts)) 然后 Reduce(function(x,y) merge(x,y,by="user_id"),d2) 只需要摆脱重复的列。
  • 行得通!它转移到我的实际数据中。其他人将来使用此代码的几点注意事项:(1)您必须小心 NA 的位置(并且以前处理它们),以便在第二次更改 mrg 时所有列的长度相同,以及(2)每行必须有一个唯一的标识符。 (当我将它应用于我的数据时,这两个都出现了。)耶!前进到下一步,因为到目前为止检查结果似乎给出了预期的行为。 ------ 就大局而言,我在原始帖子的末尾添加了一个部分,但我认为这可能超出了这个问题的范围。
【解决方案2】:

我认为这可以通过使用table 函数(和类)及其as.data.frame 方法来生成适合合并的对象来大大简化:

counts <- lapply(c1, function(cond) { as.data.frame( table(d[cond]))}) 
# That returns two 'Freq' vectors (named in the as.dataframe` step) in a list. 

d[order(d[2],d[3],d[4]), varNames] <- lapply(counts, function( cts) {
               merge(d[order(d[2],d[3],d[4]), ], cts )[['Freq']] })
#Could also have `cbind`-ed it. The `d[names] <-` assigned the names. 
#Could also have used `setNames` on the RHS. 

#------------

> d
   user_id choice cond1 gender cond1Ct cond1_GenderCt
1        1  apple    a1      F       2              1
2        2 banana    a1      M       2              2
3        3 banana    a2      F       3              2
4        4  apple    a1      M       2              1
5        5 banana    a2      F       3              2
6        6 banana    a1      M       2              2
7        7  apple    a2      F       1              1
8        8 banana    a2      M       3              1
9        9 banana    a3      F       3              2
10      10  apple    a3      M       1              1
11      11 banana    a3      F       3              2
12      12 banana    a3      M       3              1

我承认我在试图让ave 函数传递计数向量时遇到了一个死胡同,但它不接受其索引参数的列表参数。我回顾了我之前开发的一个函数,发现table 确实接受了一个列表。我的第二个承认是我没有意识到分配到有序位置不会重新排序原始对象:

> a <- 10:1
> a[order(a)][2] <-100
> a
 [1]  10   9   8   7   6   5   4   3 100   1  # surprised me anyway.

表对象的as.data.frame 方法只是从表条目中创建一个“长”数据框,其中 Freq 列保存计数:

 as.data.frame( table(d[-(1:3)]) )
#-----------------------
   gender cond1Ct cond1_GenderCt Freq
1       F       1              1    1
2       M       1              1    1
3       F       2              1    1
4       M       2              1    1
5       F       3              1    1
6       M       3              1    1
7       F       1              2    0
8       M       1              2    0
9       F       2              2    1
10      M       2              2    1
11      F       3              2    2
12      M       3              2    2
> table(d[-(1:3)]) 
, , cond1_GenderCt = 1

      cond1Ct
gender 1 2 3
     F 1 1 1
     M 1 1 1

, , cond1_GenderCt = 2

      cond1Ct
gender 1 2 3
     F 0 1 2
     M 0 1 2

【讨论】:

  • 哇 - 非常简单,而且效果很好!我将不得不使用 as.data.frame(table(d[cond])) 位来了解它是如何工作的。我能理解死胡同……我在这个项目中遇到了很多很多死胡同,但我一直在学习做某事的新方法。
  • 因此,当我尝试将其与真实数据一起使用时,遇到了不同的问题,并返回以进一步使用您的代码,我发现合并数据帧中的计数不一样应用了变量名。计数本身是正确的。但是,当应用标签时,如您的输出中所见,这些数字不会保留 --- 第 5 行的香蕉 a2 表示组合发生了 2 次;在第 8 行,它说组合发生了 3 次。 last col 也与实际数据不一致。到目前为止,我尝试将命名与合并分开并没有奏效……有什么建议吗?
  • 我看到我没有更正merge 调用的参数中的错误......我以为我已经粘贴了那个更正。看看它现在是否有效。
  • 感谢您仍然提供帮助。我看到您将其切换为递归-您介意解释原因吗? counts 仍然正确。 d_Merged_Sort &lt;- d[with(d, order(choice, cond1, gender)), ] 表明事情还在变得混乱。
  • 我不会调用我所做的递归。这是两个不同的 data.frames 与原始 d 数据帧的串行合并。是否存在您认为不正确匹配的特定类别。
【解决方案3】:

使用包plyr 似乎极大地简化了代码,并处理了具有缺失值的分组变量和一个 id 有多个选择的实例(当我将其带回更大的数据集时,这两种情况都出现了)。

library (plyr)
d2 <- data.frame(user_id = 1:12, choice = rep(c("apple", "banana", "banana"),4), 
                 cond1 = c("a1", "a1", "a2", "a1", "a2", "a1", "a2", "a2", "a3", "a3", "a3", "a3"), 
                 gender = c(rep(c("F", "M"), 6)))

d2$user_id[7] <- 5         # modify the dataset some
d2$gender[10] <- NA

tmp1 <- ddply(d2, ~cond1 + gender + choice, summarize, cond1_GenderCt = length(choice))     
tmp2 <- ddply(d2, ~cond1 + choice, summarize, cond1Ct = length(choice))     
result2 <- merge (tmp2, merge(tmp1, d2))
result2

这将创建一个带有命名变量的数据框,该变量带回了每组分组变量中每个选择的频率。

编辑:所以我显然忘记了我自己问题的要点!处理不同的变量组合。

doddply <- function(df, x){
  ddply(df,x,summarize,nChoice = length(choice))
}

lapply (c2, function (x) {doddply(d2, x)})

这似乎是上面 doddply 函数的一个变体,它接受一个 varNames 列表以及“nChoice”的源并由 mapply 调用会有所帮助,但我无法让它工作。

所以这最终与@jlhoward 的解决方案完全相同...... counts 变量之后的代码仍然是命名和合并所需要的。 (我将把它留在这里,但这只是实现这一点的另一种方式)。

【讨论】:

    猜你喜欢
    • 2013-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-06
    • 2020-02-06
    • 2021-05-16
    相关资源
    最近更新 更多