【问题标题】:Dividing groups in dataframe by median of separate groups按不同组的中位数划分数据框中的组
【发布时间】:2016-03-01 12:57:15
【问题描述】:

我有一个包含 group_IDclass 列的数据框,以及多个数字特征和一些字符元数据,即:

group_ID  class  var1  var2  var3  metadata
a         foo    1     324   3     cat
a         bar    1.3   34    53    dog
a         baz    31    34    5     elephant
b         foo    34    34    943   dolphin
b         bar    94    51    23    chipmunk
b         baz    985   595   43    badger
c         foo    43    93    23    tapir
c         bar    43    23    23    monkey
c         baz    40    53    512   duck

我想为每个group_ID 计算类foo 的中位数,然后将每一行除以与group_ID 匹配的中位数。

在此示例中,每个foo 只有 1 行,因此中位数将与初始值相同,但实际上每个 classgroup_ID 都有很多行。


有没有简单的方法来做到这一点?到目前为止,我最好的尝试包括为 foo 的中值创建一个单独的数据框,然后按 group_ID 拆分并在一个可怕的循环中扫描,但我最终丢失了元数据列。这似乎是一件很例行的事情,所以我确定我错过了一些东西。

任何帮助将不胜感激。

【问题讨论】:

  • 这对我有用,但您应该添加此示例的预期结果以确保 df %>% group_by(group_ID) %>% mutate_each(funs(./median(.[class == "bar"])), var1:var3)

标签: r


【解决方案1】:

我们可以使用dplyr中的mutate_each来除以条件。

library(dplyr)
df %>% group_by(group_ID) %>%
  mutate_each(funs(./median(.[class == "foo"])), var1:var3)
# Source: local data frame [9 x 6]
# Groups: group_ID
# 
#   group_ID class       var1       var2        var3 metadata
# 1        a   foo  1.0000000  1.0000000  1.00000000      cat
# 2        a   bar  1.3000000  0.1049383 17.66666667      dog
# 3        a   baz 31.0000000  0.1049383  1.66666667 elephant
# 4        b   foo  1.0000000  1.0000000  1.00000000  dolphin
# 5        b   bar  2.7647059  1.5000000  0.02439024 chipmunk
# 6        b   baz 28.9705882 17.5000000  0.04559915   badger
# 7        c   foo  1.0000000  1.0000000  1.00000000    tapir
# 8        c   bar  1.0000000  0.2473118  1.00000000   monkey
# 9        c   baz  0.9302326  0.5698925 22.26086957     duck

如果 OP 想将这些添加为新的/附加列并保持以前的数据不变,您可以将上述方法修改为:

df %>% 
  group_by(group_ID) %>%
  mutate_each(funs(./median(.[class == "foo"])), setNames(var1:var3, paste0("varN", 1:3)))

【讨论】:

  • 谢谢,看起来应该可以解决问题。有一个 dplyr 解决方案并不奇怪。
【解决方案2】:

这是data.table 解决方案。我们将“data.frame”转换为“data.table”(setDT(df)),按“group_ID”分组,循环(使用lapply)以列名“var”开头的列子集(使用grep 我们正在设置子集),将每一列除以与“class”中的“foo”值相对应的该列子集的median。这可以分配 (:=) 作为新列,或者我们可以将其分配回同一列以替换原始列。替换原始列的一个问题是我们应该将原始列的class 与替换的列匹配。如果“var”列的原始类是numeric,那么它将作为median 计算和除法将新列转换为numeric。如果原始列是integer 类,一个可能的选项是将类更改为numeric,然后分配。

library(data.table)
setDT(df)[, paste0("varN", 1:3) := lapply(.SD[, 
     grep("^var", names(.SD)), with=FALSE], 
         function(x) x/median(x[class=="foo"])), group_ID]
df
# group_ID class  var1 var2 var3 metadata      varN1      varN2       varN3
#1:        a   foo   1.0  324    3      cat  1.0000000  1.0000000  1.00000000
#2:        a   bar   1.3   34   53      dog  1.3000000  0.1049383 17.66666667
#3:        a   baz  31.0   34    5 elephant 31.0000000  0.1049383  1.66666667
#4:        b   foo  34.0   34  943  dolphin  1.0000000  1.0000000  1.00000000
#5:        b   bar  94.0   51   23 chipmunk  2.7647059  1.5000000  0.02439024
#6:        b   baz 985.0  595   43   badger 28.9705882 17.5000000  0.04559915
#7:        c   foo  43.0   93   23    tapir  1.0000000  1.0000000  1.00000000
#8:        c   bar  43.0   23   23   monkey  1.0000000  0.2473118  1.00000000
#9:        c   baz  40.0   53  512     duck  0.9302326  0.5698925 22.26086957

【讨论】:

  • @PierreLafortune 我之前忘了用.SDgrep,开会有点忙。
  • 从统计角度考虑。对于您收到的 100 次赞成票,您将获得 1 次反对票。统计意义不大。 :)
  • 谢谢你们的cmets。
【解决方案3】:

1) by 这是一个基本的 R 解决方案:

do.call("rbind", by(DF, DF$group_ID, function(d)
      data.frame(d, sapply(d[3:5], function(x) x / median(x[d$class == "foo"])))
))

给予:

    group_ID class  var1 var2 var3 metadata     var1.1     var2.1      var3.1
a.1        a   foo   1.0  324    3      cat  1.0000000  1.0000000  1.00000000
a.2        a   bar   1.3   34   53      dog  1.3000000  0.1049383 17.66666667
a.3        a   baz  31.0   34    5 elephant 31.0000000  0.1049383  1.66666667
b.4        b   foo  34.0   34  943  dolphin  1.0000000  1.0000000  1.00000000
b.5        b   bar  94.0   51   23 chipmunk  2.7647059  1.5000000  0.02439024
b.6        b   baz 985.0  595   43   badger 28.9705882 17.5000000  0.04559915
c.7        c   foo  43.0   93   23    tapir  1.0000000  1.0000000  1.00000000
c.8        c   bar  43.0   23   23   monkey  1.0000000  0.2473118  1.00000000
c.9        c   baz  40.0   53  512     duck  0.9302326  0.5698925 22.26086957

2) by/sweep 使用sweep 的替代方法,同样,只有基本功能是:

do.call("rbind", by(DF, DF$group_ID, function(d) {
      med <- apply(subset(d, class == "foo")[3:5], 2, median)
      data.frame(d, sweep(as.matrix(d[3:5]), 2, med, "/"))
    }))

3) sapply/ave 另一个基本解决方案是将ave 应用于每个var 列:

data.frame(DF, sapply(names(DF[3:5]), function(j)
    ave(1:nrow(DF), DF$group_ID, FUN = function(i)
        DF[i, j] / median(subset(DF[i, ], class == "foo")[[j]]))
))

注意:输入DF的可重现形式为:

DF <- structure(list(group_ID = structure(c(1L, 1L, 1L, 2L, 2L, 2L, 
3L, 3L, 3L), .Label = c("a", "b", "c"), class = "factor"), class = structure(c(3L, 
1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L), .Label = c("bar", "baz", "foo"
), class = "factor"), var1 = c(1, 1.3, 31, 34, 94, 985, 43, 43, 
40), var2 = c(324L, 34L, 34L, 34L, 51L, 595L, 93L, 23L, 53L), 
    var3 = c(3L, 53L, 5L, 943L, 23L, 43L, 23L, 23L, 512L), metadata = structure(c(2L, 
    4L, 7L, 5L, 3L, 1L, 9L, 8L, 6L), .Label = c("badger", "cat", 
    "chipmunk", "dog", "dolphin", "duck", "elephant", "monkey", 
    "tapir"), class = "factor")), .Names = c("group_ID", "class", 
"var1", "var2", "var3", "metadata"), class = "data.frame", row.names = c(NA, 
-9L))

【讨论】:

    猜你喜欢
    • 2023-03-08
    • 2017-08-14
    • 1970-01-01
    • 2019-07-06
    • 2019-08-30
    • 2019-10-14
    • 2023-02-21
    • 2023-02-24
    • 1970-01-01
    相关资源
    最近更新 更多