如何在不排序的情况下使用ddply()函数?

8
我用以下代码来总结我的数据,按照化合物、重复和质量分组。
summaryDataFrame <- ddply(reviewDataFrame, .(Compound, Replicate, Mass), 
  .fun = calculate_T60_Over_T0_Ratio)

一种不幸的副作用是生成的数据框按这些字段排序。我想保持Compound、Replicate和Mass与原始数据框中的顺序相同。有什么建议吗?我尝试在原始数据中添加一个“Sorting”列,其中包含连续的整数,但当然我不能将其包括在.variables中,因为我不想按此分组,所以它不会出现在summaryDataFrame中。
感谢您的帮助。

这与 write.table 没有任何关系;标题应该更改。 - Brian Diggs
2个回答

11

一段时间以前,在 plyr 邮件列表上出现了这个问题(由@kohske提出),Peter Meilstrup 提供了一个解决方案,适用于特定情况:

#Peter's version used a function gensym to
# create the col name, but I couldn't track down
# what package it was in.
keeping.order <- function(data, fn, ...) { 
  col <- ".sortColumn"
  data[,col] <- 1:nrow(data) 
  out <- fn(data, ...) 
  if (!col %in% colnames(out)) stop("Ordering column not preserved by function") 
  out <- out[order(out[,col]),] 
  out[,col] <- NULL 
  out 
} 

#Some sample data 
d <- structure(list(g = c(2L, 2L, 1L, 1L, 2L, 2L), v = c(-1.90127112738315, 
-1.20862680183042, -1.13913266070505, 0.14899803094742, -0.69427656843677, 
0.872558638137971)), .Names = c("g", "v"), row.names = c(NA, 
-6L), class = "data.frame") 

#This one resorts
ddply(d, .(g), mutate, v=scale(v)) #does not preserve order of d 

#This one does not
keeping.order(d, ddply, .(g), mutate, v=scale(v)) #preserves order of d 

请阅读这篇文章,了解Hadley关于为什么这个功能可能不够通用以将其纳入ddply的注释,特别是在您的情况下,每个部分返回的行数可能较少。如果ddply输出的内容排序方式不符合您的要求,您基本上有两个选择:预先使用有序因子指定拆分变量的所需排序顺序,或在事后手动对输出进行排序。例如,考虑以下数据:
d <- data.frame(x1 = rep(letters[1:3],each = 5), 
                x2 = rep(letters[4:6],5),
                x3 = 1:15,stringsAsFactors = FALSE)

目前使用字符串。 ddply将对输出进行排序,在这种情况下,将采用默认的词法顺序:

> ddply(d,.(x1,x2),summarise, val = sum(x3))
  x1 x2 val
1  a  d   5
2  a  e   7
3  a  f   3
4  b  d  17
5  b  e   8
6  b  f  15
7  c  d  13
8  c  e  25
9  c  f  27


> ddply(d[sample(1:15,15),],.(x1,x2),summarise, val = sum(x3))
  x1 x2 val
1  a  d   5
2  a  e   7
3  a  f   3
4  b  d  17
5  b  e   8
6  b  f  15
7  c  d  13
8  c  e  25
9  c  f  27

如果最终的数据框不是按照“正确”的顺序排列,那很可能是因为你希望其中一些变量成为有序因子。假设我们真的希望将 x1x2 排序如下:

d$x1 <- factor(d$x1, levels = c('b','a','c'),ordered = TRUE)
d$x2 <- factor(d$x2, levels = c('d','f','e'), ordered = TRUE)

现在当我们使用ddply时,结果的排序将如我们所愿:
> ddply(d,.(x1,x2),summarise, val = sum(x3))
  x1 x2 val
1  b  d  17
2  b  f  15
3  b  e   8
4  a  d   5
5  a  f   3
6  a  e   7
7  c  d  13
8  c  f  27
9  c  e  25

这个故事的道理是,如果ddply输出的顺序不是你想要的,那么你应该使用有序因子来拆分变量。

谢谢。这似乎对我“几乎”有效。如何在函数返回的数据中保留.sortColumncalculate_T60_Over_T0_Ratio <- function(df) {`## 检查确保使用正确的时间点进行比率计算` `t60Value = df[which(df[,"Time"] == "t=60"),"Result"]` `t0Value = df[which(df[,"Time"] == "t=0"),"Result"]` `if (t0Value == 0){` `print("错误--除以零!")` `return ("NA")` `} else {` `return (t60Value / t0Value) ` `}`} - James
@James 如果你想在结果中保留.sortColumn,你可能可以从keeping.order中省略这一行 out[,col] <- NULL - joran
抱歉,我的表述可能不够清晰。我之所以从 keeping.order 中得到错误是因为我的函数没有返回 .sortColumn(请参见上文)。 - James
1
@James - 抱歉,我误解了。请记住,在我的答案中我指出,如果您的函数返回的行数较少,则此策略将无法奏效,而这似乎是您的情况。类似的事情可能是可行的,但它必须根据您的数据和函数进行特定的调整,因此除非您编辑问题并包含一些示例数据,否则我(或其他任何人)都无法提供帮助。但我现在可以告诉您,它只需要调用ddply,然后在重新排序数据之后即可完成。 - joran

1

最终我在原始数据框中添加了一个'索引'列。它由两个使用sep="_"粘贴的列组成。然后我又创建了另一个仅包含'索引'列中唯一成员和计数器1:length(df)的数据框。我对数据进行了ddply()处理,返回了一个排序后的数据框。为了将结果返回到原始顺序,我使用merge()将结果数据框和索引数据框合并(确保列名相同会使此过程更容易)。最后,我使用order命令并删除了多余的列。

这不是一种优雅的解决方案,但它能够正常工作。

感谢您的帮助,让我朝着正确的方向思考。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接