在R中,当需要计算每个子集时,避免使用for循环

3
的意思是输出列表中第200到250个元素,其中list为列表名。
structure(list(id = c(10L, 10L, 10L, 10L, 10L, 10L, 12L, 12L, 
12L, 12L, 12L, 12L, 12L, 12L, 12L, 12L, 12L, 12L, 12L, 12L, 12L, 
12L, 12L, 12L, 12L, 12L, 12L, 12L, 12L, 12L, 12L, 14L, 14L, 14L, 
14L, 14L, 14L, 14L, 14L, 14L, 14L, 14L, 14L, 14L, 14L, 14L, 14L, 
14L, 14L, 14L, 14L), group = c(3, 3, 3, 3, 3, 3, 2, 2, 1, 3, 
3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 2, 1, 3, 3, 
2, 2, 2, 3, 1, 3, 2, 2, 3, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 2)), .Names = c("id", 
"group"), row.names = c(3618L, 3218L, 3372L, 3613L, 3217L, 3531L, 
3653L, 3835L, 3748L, 3740L, 4007L, 3769L, 3915L, 3834L, 3813L, 
3927L, 4066L, 4048L, 3978L, 3731L, 4030L, 3721L, 3869L, 4070L, 
4021L, 3789L, 3749L, 3739L, 3964L, 3924L, 4023L, 4284L, 4238L, 
4102L, 4642L, 4309L, 4577L, 4784L, 4125L, 4346L, 4088L, 4406L, 
4785L, 4893L, 4347L, 4498L, 4758L, 4686L, 4469L, 4864L, 4193L
), class = "data.frame")

我的代码:

for (i in 1:(max(list$id))) {
  p <- subset(list,list$id==i)
  h <-0
  for (j in 1:(nrow(p)-1)){ 
    if (p$group[j]!=p$group[(j+1)]) {
      h <- (h+1)
      } 
    }
  list$group_move[list$id==i] <- h
}

错误:

Error in if (p$group[j] != p$group[(j + 1)]) { : 
  missing value where TRUE/FALSE needed
  • 我检查了N/A(is.na(list)),结果为false。

关于我的列表:

我有5000个id,其中可以将“34526”作为id。 我需要计算每个不同id在1、2和3组之间移动的次数。 我知道两个“for”循环效率不高,但我不知道有什么其他方法来区分每个id。

如果您能帮助我理解我的代码问题,那就太好了。 如果您知道一种为每个不同id编写注释的方法,那就更好了(这些注释不是常规函数)。


1
你的示例数据在之间没有移动。请提供一个最小可复现的示例。 - MichaelChirico
@MichaelChirico,请在问题中进行更改。 - anat
1个回答

1
使用 dplyr 并将您的数据命名为 dd
library(dplyr)
dd %>% 
    group_by(id) %>% 
    summarize(changes = sum(lag(group) != group, na.rm = T))
# # A tibble: 3 × 2
#      id changes
#   <int>   <int>
# 1    10       0
# 2    12       7
# 3    14       8

上面总结了每个组的移动次数。为了将此列添加到原始数据框中,我们将summarize替换为mutate:
dd2 = dd %>% 
    group_by(id) %>% 
    mutate(group_move = sum(lag(group) != group, na.rm = T))
dd2
# Source: local data frame [51 x 3]
# Groups: id [3]
# 
#       id group group_move
#    <int> <dbl>      <int>
# 1     10     3          0
# 2     10     3          0
# 3     10     3          0
# 4     10     3          0
# 5     10     3          0
# 6     10     3          0
# 7     12     2          7
# 8     12     2          7
# 9     12     1          7
# 10    12     3          7
# # ... with 41 more rows

我认为你的代码唯一的问题就是可能会访问不存在的id值,这会导致子集为空并出现缺失条件错误。一个简单的解决方法是将 for (i in 1:(max(list$id))) 替换为 for (i in unique(list$id))
list = dd

for (i in unique(list$id)) {
  p <- subset(list,list$id==i)
  h <-0
  for (j in 1:(nrow(p)-1)){ 
    if (p$group[j] != p$group[(j+1)]) {
      h <- (h+1)
    } 
  }
  list$group_move[list$id==i] <- h
}

这个方法有效,但比 dplyr 版本要慢得多。我们可以通过向量化替换内部循环来加快速度:
for (i in unique(list$id)) {
  p <- subset(list,list$id==i)
  h <- sum(p$group[-1] != p$group[-nrow(p)])
  list$group_move[list$id==i] <- h
}

这基本上就是我们在顶部使用所做的- 是一个方便的函数,用于偏移索引,而则负责分别处理每个。

如果我想为每个ID计算标准差和斜率,我该怎么做?这样可以吗?h <- list %>% group_by(id) %>% summarize(new_list = sd(value), na.rm = T) - anat
几乎,你想让 na.rm = T 成为 sd() 的一个参数而不是 summarize() 的参数。 - Gregor Thomas
那么,我需要改变什么? - anat
你有 summarize(new_list = sd(value), na.rm = T) - 你看到 na.rm = T sd 的右括号之外吗? 把它改为放在 sd() 的内部。 所以你应该有 summarize(new_list = sd(value, na.rm = T))。 如果你的数据中没有缺失值(那么你根本不需要 na.rm = T),这就不重要了。 - Gregor Thomas
na.rm = T 只是我的答案所需,因为我的 lag(group) 会创建一个缺失值,它将 1, 2, 3 转换为 NA, 1, 2 - Gregor Thomas
你如何为一个ID找到斜率?只需这样做,然后将其放入 summarize() 中。如果仍有困难,请提出新问题。 - Gregor Thomas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接