【问题标题】:R: Grouping two rows into a new row based on factor identityR:根据因子身份将两行分组为新行
【发布时间】:2013-03-04 18:02:28
【问题描述】:

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

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

对于每个物种,我想创建一个新行,它只获取状态“引入”或“地穴”的数据,而忽略“原生”状态中的数据。每个物种要么只有“引进”和“本地”的数据,要么只有“地穴”的数据。

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

> 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 循环是解决此问题的最佳方式,还是有更优雅的方式?任何建议都会非常感谢您的帮助!

【问题讨论】:

  • “Introduced”、“Crypt”或“Native”是否总是只有 1 个值,或者您是否暗示您想在此过程中执行一些聚合?
  • 总是有一个值,不需要聚合。
  • 但是,对于给定的“物种”,是否会有“引入”和“地穴”?
  • 从不;物种 A 要么有“引进”,要么有“地穴”,但绝不会两者兼有。

标签: r


【解决方案1】:

以下使用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 计数,只需取出上面的那一行。

【讨论】:

    【解决方案2】:

    您可以使用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”不感兴趣。此外,我们知道对于一个物种,我们永远不会将“Introduced”和“Crypt”放在一起,并且我们知道“Introduced”或“Crypt”的值永远不会超过一个,因此使用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
    

    【讨论】:

    • 太好了,感谢您的建议!我是 R 新手,现在正在努力使此代码适应多列数据......我将如何扩展它以包含 50 多列值?如何用多列替换值?我试过mydf[,3:53]~Species但没有成功。再次感谢!
    • @user2096647,答案已更新。请查看并让我知道是否需要进一步澄清。
    • 完美!非常感谢您的帮助,非常感谢!
    猜你喜欢
    • 2019-07-30
    • 2019-03-18
    • 2020-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多