【问题标题】:dplyr / base R: compute new columns using logic combinations of row indicesdplyr / base R:使用行索引的逻辑组合计算新列
【发布时间】:2020-06-05 10:22:05
【问题描述】:

我分析了一个实验数据集,并想计算每个变量的效应大小。我的数据框由 8 个处理 t(= 行)的多个变量(= 列)组成,t1 - t4 分别是 t5 - t8 的控制(t1 控制 t5,t2 控制 t6,...)。原始数据集要大得多,所以我想解决以下两个任务::

  1. 我想计算一个变量的每个 t5 - t8 的对数(治疗/对照),例如t5 = log(t5/t1) 的影响大小,t6 = log(t6/t2) 的影响大小,...。结果列的名称应该是 variablename_effect 并且新列将只有 4 行而不是 8 行。
    最棘手的部分是,我需要在我的代码中实现特定行的组合,以便为每个处理使用正确的控件。

  2. 我想在一个代码中计算所有变量的效果大小,因此创建多个具有正确名称的新列 (variablename_effect)。

我更愿意在 dplyr 或 base R 中解决问题以保持简单。 到目前为止,我发现的唯一相关问题是/r-dplyr-mutate-refer-new-column-itself(显示多个if else() 的组合)。我将非常感谢解决方案、类似问题的链接或我应该在 cast 中使用哪些包,这在 dplyr / base R 中是不可能的!

样本数据:

df <- data.frame("treatment" = c(1:8), "Var1" = c(9:16), "Var2" = c(17:24))

编辑:这是我希望作为输出收到的 df_effect,感谢@Martin_Gal 的提示!

df_effect <- data.frame("treatment" = c(5:8), "Var1_effect" = c(log(13/9), log(14/10), log(15/11), log(16/12)), "Var2_effect" = c(log(21/17), log(22/18), log(23/19), log(24/20)))

到目前为止我的想法:

  1. 用于计算效果大小:

mutate() 和函数:

# 1st option:
for (i in 5:8) {
dt_effect <- df %>%
  mutate(Var1_effect = log(df[i, "Var1"]/df[i - 4, "Var1"]))
}
#2nd option:
for (i in 5:8){
dt_effect <- df %>%
mutate(Var1_effect = log(df[treatment == i , "Var1"]/df[treatment == i - 4 , "Var1"]))
}

问题:每行都返回 i = 8 的结果!

mutate() 和 ifelse():

df_effect <- df %>%
  mutate(Var1_effect = ifelse(treatment >= 5, log(df[, "Var1"]/df[ , "Var1"]), NA))

似乎工作,但到目前为止我无法实现为控件选择哪一行,因此它返回 NA 为 t1 - t4(正确)和 0 为 t5 - t8 (数学上正确,因为我计算 log(t5/ t5), ...但不是我想要的)。

也许我应该使用 summarise() 而不是 mutate() 因为我创建的行数比原始数据帧中的少?

  1. 让这同时适用于每个变量

我唯一的想法是在一秒钟内为函数索引列并使用 paste() 创建新的列名,但我不知道该怎么做...

【问题讨论】:

  • 基于您的示例 data.frame:您能否展示您的预期输出的样子?
  • 如果您为类型(=处理或控制)和索引(1:4)添加指标变量,然后将数据框与自身连接起来,以便处理和相应的控制在同一行,它会罢工我说你的问题会简单得多。正如@MartinGal 所说,您的预期输出的一些想法会有所帮助。
  • 感谢@MartinGal 的提示 - 我添加了预期的输出!
  • @Limey 我也想到了这一点,但我认为它确实会使我的数据变得凌乱。所有 t1 - t8 都是不同的实验设置,这取决于我正在查看的效果,哪些是对照,哪些是治疗。这就是为什么我可以为上面提到的这个效果这样做,但必须为我看到的每个效果创建一个新的 data.frames(会工作,但感觉有点像作弊)
  • 我评论的初稿以“你的问题是你的数据不整洁”开头!但后来我重读了您的帖子,发现您的数据可能是整洁的。我们处于哲学转移的危险之中。大多数时候,“控制”只是另一种处理方式,所以你的格式是整洁的,而我的建议不是。但是当你想计算一个效果时,我认为将“Control”视为“Not-Treatment”是合理的。毕竟,当你展示你的“效果”时,你会删除“控制”行,不是吗?它们的影响都为零。如果整洁让我的生活变得困难,我就会变得凌乱!

标签: r indexing dplyr


【解决方案1】:

我不知道这是否能解决你的问题,但我想提出一个类似于 Limey 的建议:

library(dplyr)
library(tidyr)

df %>%
  mutate(control = 1 - (treatment-1) %/% (nrow(.)/2),
         group = ifelse(treatment %% (nrow(.)/2) == 0, nrow(.)/2, treatment %% (nrow(.)/2))) %>%
  select(-treatment) %>%
  pivot_wider(names_from = c(control), values_from=c(Var1, Var2)) %>%
  group_by(group) %>%
  mutate(Var1_effect = log(Var1_0/Var1_1))

这会产生

# A tibble: 4 x 6
# Groups:   group [4]
  group Var1_1 Var1_0 Var2_1 Var2_0 Var1_effect
  <dbl>  <int>  <int>  <int>  <int>       <dbl>
1     1      9     13     17     21       0.368
2     2     10     14     18     22       0.336
3     3     11     15     19     23       0.310
4     4     12     16     20     24       0.288

这里发生了什么?

  • 我希望您的 data.frame 的前半部分成为后半部分的控制变量。所以我创建了一个指标变量和一个基于治疗 ID/数字的分组变量。
  • 现在不再使用治疗 ID,所以我放弃了它。
  • 接下来,我使用pivot_wider 创建了一个数据集,其中包含Var1_1(即Var1 用于您的控制变量)和Var1_0(即Var1 用于您的“普通”变量)。
  • 最后我计算了每组Var1_effect

【讨论】:

  • 我认为在真正了解代码的工作原理之前,我必须阅读语法:) 如上所述,这对于上面提到的 t1 - t4 是控件的效果非常有效,但会如果我不能像这里可能的那样直接对控制和治疗进行分类,那就更难了……。这就是为什么我更愿意尽可能使用索引
  • 几乎是我的解决方案,@Martin,虽然我的代码会更长,因为我会在加入 df 之前明确地创建类型和索引。恕我直言,这会更清楚,但会牺牲长度。您的代码紧凑且有效,因此哪个更好取决于个人喜好。两种方法都有效。
  • @Limey 谢谢。不幸的是,这并不能解决 OPs 问题。 :-( ATM 我不知道如何处理它。
  • @Limey 如果不是太多努力,我也会有兴趣查看您的代码 - 这可能有助于理解 MartinGals 优雅版本:)
【解决方案2】:

响应 OP 对@MartinGal 解决方案的评论(这本身就是完美的):

先将输入数据转换成更方便的形式:

# Original input dataset
df <- data.frame("treatment" = c(1:8), "Var1" = c(9:16), "Var2" = c(17:24))
# Revised input dataset
revisedDF <- df %>% 
               select(-treatment) %>% 
               add_column(
                 Treatment=rep(c("Control", "Test"), each=4), 
                 Experiment=rep(1:4, times=2)
               ) %>%
             pivot_longer(
               names_to="Variable", 
               values_to="Value", 
               cols=c(Var1, Var2)
             ) %>% 
             arrange(Experiment, Variable, Treatment)
revisedDF %>% head(6)

给予

# A tibble: 6 x 4
  Treatment Experiment Variable Value
  <chr>          <int> <chr>    <int>
1 Control            1 Var1         9
2 Test               1 Var1        13
3 Control            1 Var2        17
4 Test               1 Var2        21
5 Control            2 Var1        10
6 Test               2 Var1        14

我喜欢这种格式,因为它使分析代码完全独立于变量的数量、实验的数量和治疗的数量。

分析也很简单:

result <- revisedDF %>% pivot_wider(
                          names_from=Treatment,
                          values_from=Value
                        ) %>% 
                        mutate(Effect=log(Test/Control))
result

给予

  Experiment Variable Control  Test Effect
       <int> <chr>      <int> <int>  <dbl>
1          1 Var1           9    13  0.368
2          1 Var2          17    21  0.211
3          2 Var1          10    14  0.336
4          2 Var2          18    22  0.201
5          3 Var1          11    15  0.310
6          3 Var2          19    23  0.191
7          4 Var1          12    16  0.288
8          4 Var2          20    24  0.182

pivot_widerpivot_longer 是相对较新的 dplyr 动词。如果您无法使用最新版本的软件包,spreadgather 执行相同的工作,但参数名称略有不同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-23
    • 2014-01-25
    • 2021-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-01
    • 2020-03-31
    相关资源
    最近更新 更多