R:根据因子标识将两行分组到一个新行中

时间:2013-03-04 18:02:29

标签: r

在一个大型数据框架中,我正在尝试根据另一个因素的标识创建一个新行,该行根据其他行对特定数据进行分组。以下是一些示例数据:

> Species    Status    Value
> A         Introduced   10
> A          Native      3
> B          Crypt       6
> C         Introduced   19
> C          Native      4

对于每个物种,我想创建一个新行,它只获取Status“Introduced”或“Crypt”的数据,并忽略“Native”状态中的数据。每个物种的数据都只有“引入”和“原生”或只有“地穴”。

因此,我想要的输出将如下所示:

> Species    Status    Value
> A         Introduced   10
> A          Native      3
> A         IC.Total     10
> B          Crypt       6
> B         IC.Total     6
> C         Introduced   19
> C          Native      4
> C         IC.Total     19

for循环是最好的解决方法,还是有更优雅的方式?任何建议都会很棒 - 谢谢你的帮助!

2 个答案:

答案 0 :(得分:2)

以下使用data.table包 假设您的原始data.frame被称为myDat

library(data.table)
myDT <- data.table(myDat, key="Species")

# Creates a new DT, of only the Speices column
myDT2 <- setkey(unique(myDT[, list(Species)]), "Species")

# Add IC.Total values
myDT2[myDT[Status=="Introduced"], c("Status", "ValueC") := list("IC.Total", Value)]

# Add Crypt values
myDT2[myDT[Status=="Crypt"], c("Status", "ValueC") := list("Crypt", Value)]

# fix the column name
setnames(myDT2, "ValueC", "Value")

# combine and sort by speicies
myDT <- setkey(rbind(myDT, myDT2), "Species")

myDT
#    Species     Status Value
# 1:       A Introduced    10
# 2:       A     Native     3
# 3:       A   IC.Total    10
# 4:       B      Crypt     6
# 5:       B      Crypt     6
# 6:       C Introduced    19
# 7:       C     Native     4
# 8:       C   IC.Total    19

注意,如果您不想复制crypt计数,只需取出上面的那一行即可。

答案 1 :(得分:1)

您可以使用mergeaggregate(即使没有要聚合的内容):

merge(mydf, 
      cbind(aggregate(Value ~ Species, mydf, sum, 
                      subset = c(Status != "Native")), 
            Status = "IC.Total"),
      all = TRUE)
#   Species     Status Value
# 1       A Introduced    10
# 2       A     Native     3
# 3       A   IC.Total    10
# 4       B      Crypt     6
# 5       B   IC.Total     6
# 6       C Introduced    19
# 7       C     Native     4
# 8       C   IC.Total    19

我使用了aggregate因为它有一个方便的参数,可以让您对数据进行子集化。在这种情况下,我们对“Native”不感兴趣。此外,我们知道我们永远不会为一个物种“引入”和“地穴”,我们知道“引入”或“地穴”永远不会有多个值,所以使用sum作为我们的聚合函数不会改变任何东西。


更新

这个解决方案背后的概念即使你有多个“价值”变量也可以使用,正如你在评论中指出的那样,但需要做一些细微的修改,如下所示。

首先,让我们编写一些数据:

mydf <- data.frame(
  Species = c("A", "A", "B", "C", "C"),
  Status = c("Introduced", "Native", "Crypt", "Introduced", "Native"),
  Value1 = c(10, 3, 6, 19, 4),
  Value2 = c(6, 8, 12, 19, 5),
  Value3 = c(18, 19, 14, 13, 2))
mydf
#   Species     Status Value1 Value2 Value3
# 1       A Introduced     10      6     18
# 2       A     Native      3      8     19
# 3       B      Crypt      6     12     14
# 4       C Introduced     19     19     13
# 5       C     Native      4      5      2

其次,像以前一样使用aggregatemerge,但请注意细微差别。首先,我们不能像以前那样使用subset,所以不是聚合整个数据集,而是仅聚合我们感兴趣的行。其次,我们将“状态”添加为分组变量,它不会对您的结果产生任何影响,与您描述的数据当前结构有关。第三,在我们聚合之后,我们需要删除“状态”列并添加一个新状态列(这就是[-2]代码正在执行的操作 - 删除第二列。)

在这里,一个整齐的包装:

merge(mydf, 
      cbind(aggregate(. ~ Species + Status, 
                      mydf[mydf$Status != "Native", ], sum)[-2], 
            Status = "IC.Total"),
      all = TRUE)
#   Species     Status Value1 Value2 Value3
# 1       A Introduced     10      6     18
# 2       A     Native      3      8     19
# 3       A   IC.Total     10      6     18
# 4       B      Crypt      6     12     14
# 5       B   IC.Total      6     12     14
# 6       C Introduced     19     19     13
# 7       C     Native      4      5      2
# 8       C   IC.Total     19     19     13