【问题标题】:Summing or substituting based on conditions根据条件求和或代入
【发布时间】:2019-09-27 10:17:52
【问题描述】:

我有一个编码障碍,我不知道该怎么做。

我的数据集看起来像这样

df <- data.frame("id" = c("id1", "id1", "id1", "id1", "id2", "id2","id2","id2"), 
                 "time" = c(1,2,4,5,2,3,4,6),
                 "Treatmentid" = c("1", "2", "1a","3", "1", "4","4a","3"), 
                 "Val" = c(2,3,5,1,2,7,4,1))

其中“id”表示受试者,“Treatmentid”表示受试者正在接受的治疗,“Val”是治疗的值。

通常,治疗会随着时间的推移而持续存在,因此我想对每个人进行汇总,以计算每个时间点接受的总治疗。

问题在于,一些用“a”表示的处理实际上是替代的,而不是添加到没有 a 的处理中。

因此我希望我的最终数据集看起来像这样

final <- data.frame("id" = c("id1", "id1", "id1", "id1", "id2", "id2","id2","id2"), 
                 "time" = c(1,2,4,5,2,3,4,6),
                 "Treatmentid" = c("1", "2", "1a","3", "1", "4","4a","3"), 
                 "Val" = c(2,3,5,1,2,7,4,1),
                 "totVal" = c(2,5,8,9,2,9,6,7))

因此,对不同的“Treatmentid”(即时间 2 的 id1,totVal= 2+3= 5)求和总计值。 当修改处理时它们被替换(即对于时间 4 的 id1,totVal= 5+3= 8)。此处,“Treatmentid 1a”(5) 的值替换了“Treatmentid 1”(2) 的值。

我希望这很清楚。

提前感谢大家的帮助

【问题讨论】:

  • For Treatmentid 1a for id1 如果是替换,那么 totVal 不应该是 7 (5 + 2) 而不是 8 吗?另外,您如何获得最后 2 行的值 6 和 7?
  • 否,因为处理“1a” (Val=5) 代替了处理“1” (Val=2)。虽然治疗“2”持续存在(Val = 3)。因此 totVal= "1a"+"2"= 5+3= 8.
  • 知道怎么做吗?
  • 对于每个 id 是否只有一种带有“a”的处理?或者可能有多个,如果有很多将如何处理?
  • 不,很遗憾,“a”可以有很多治疗方法。例如,在某些时候,2 也可以变成“2a”。随着时间的推移,多种治疗方法得到修正

标签: r conditional-statements data-manipulation


【解决方案1】:

这是我如何实现你想要的逻辑。这非常复杂,在大型数据集上可能需要很长时间。如果有人可以改进我的解决方案,我很乐意提供帮助。

library(dplyr) # for bind_rows function

首先,我们通过第一列中的id 组将数据框分成两部分。这给出了两个数据帧的列表。然后,我们将遍历此列表以获取总和。

mylist <- split(df, f = df$id)

接下来我们启动 for 循环中需要的变量。我们需要一个列表来跟踪原始 ID,我们需要一个列表来跟踪总和。我们还需要一个用于提取值的索引器,我们还需要另一个用于数据帧列表的索引器,分别为xdf_num

ids <- list()
vals <- list()

x <- 1

df_num <- 1

在下面的代码中,嵌套的 for 循环用于计算 Treatmentid 的滚动和。循环遍历mylist 中的每个元素,然后有一个嵌套的for 循环遍历Treatmentid 列中的每个值。

对于第一行,总和等于值。因此,此处使用 if 测试来检查 for 循环的当前迭代是否等于 Treatmentid 列中的第一个值。如果是,那么第一个总和正好等于my_df$Val 中的第一个值。然后保存 id,for 索引器递增,for 循环进入下一次迭代。

之后,我使用grepl 来检查Treatmentid 中的值是否没有字母。如果测试评估为TRUE,则意味着其中没有“a”,因此没有替换。因此,总和正常进行。如果它是FALSE,那么其中有一个“a”,因此它是一个替换。 'a' 部分被删除,只留下数字,然后我们在 id 列表中搜索数字等于 id 号的位置,我们获取索引并将其保存到a。这将是具有要在原始数据中替换的值的行的索引。我们减去它并添加替换值。

最后,在内部循环之外,我们将值取消列出到名为totval 的新列中。然后这个数据框被保存到mylist

for (my_df in mylist) {

  x <- 1

  for (j in my_df$Treatmentid) {

    if (j == my_df$Treatmentid[1]) {
      vals[[1]] <- my_df$Val[1]
      ids[[x]] <- j
      x <- x + 1
      next
    }
      if (grepl("^[[:digit:]]+$",j)==TRUE) {

      ids[[x]] <- j

      vals[[x]] <- vals[[x-1]] + my_df$Val[x]
      x <- x + 1

    } else {

      a <- which(sapply(ids, function(y) substr(j, 1, nchar(j)-1) %in% y))
      vals[[x]] <- vals[[x-1]] - my_df$Val[a] + my_df$Val[x]
      x <- x + 1

    }
  }

  my_df$totVal <- unlist(vals)
  mylist[[df_num]] <- my_df
  df_num <- df_num + 1
  }

循环完成后,将数据帧列表转换为带有bind_rows()dplyr 的完整数据帧。

Final <- bind_rows(mylist)

我将您想要的输出保存到final,并检查循环中创建的数据帧是否等于您使用identical() 想要的输出,结果是TRUE

identical(Final, final)
[1] TRUE

【讨论】:

  • 非常感谢,这肯定远远超出了我的能力范围。我会在数据上尝试一下,我需要先做一些调整,但我会尽快通知你。这段代码 sintax 是否能够处理丢失的数据?
  • 不客气,告诉我进展如何。该代码不会处理丢失的数据,因为将 NA 添加到数字会返回 NA。您可以添加一个 if 测试以在求和之前检查该值是否为 NA。如果是 NA 则进行下一次迭代。
  • 不确定我应该把if(Val==NA)放在循环的哪个位置。此外,“Treatmentid”可以有不同的长度(1a、111a 或 13a)。关键标识符是“a”,但循环似乎依赖于字符长度。你对改编它有什么建议吗?很抱歉这些非常具体的问题,但我真的不知道如何使用循环。
  • NA应该如何处理?如果您忽略它们,只需使用df &lt;- df[complete.cases(df),] 删除它们。这只会保留在任何地方没有任何 NA 的行,因此将删除 Val 具有 NA 的行的任何观察。顺便说一句,您使用is.na() 而不是== 检查NA。如果您不忽略它们,那么 NA 代表什么?对于你后面的问题,我需要重新考虑一下,但逻辑不应该改变太多。
  • 我在大约 2 小时前编辑了我的答案。我现在不是检查字符的长度,而是检查字符是否只是带有grepl() 的数字。其他一切都一样。
猜你喜欢
  • 1970-01-01
  • 2021-01-25
  • 2016-07-09
  • 1970-01-01
  • 2021-10-12
  • 1970-01-01
  • 1970-01-01
  • 2013-12-03
相关资源
最近更新 更多