【问题标题】:Adjust function so that it instead of it looping through all rows, it loops only through all rows within groups调整函数,使其不是遍历所有行,而是仅遍历组内的所有行
【发布时间】:2020-08-05 05:15:18
【问题描述】:

考虑下面的玩具数据集和函数: 基本上,它遍历数据集df 的行并根据某些标准查找匹配项。如果有匹配项,则观察结果与其中一个匹配项的行号匹配。

 dataset <- data.frame(id_dom = c(20, 20, 20, 250, 250, 250, 
                                  254, 254, 254),        
                       p201 = c(1, NA, 2, NA, NA, NA, 2, 1, 2), 
                       V2009 = c(63, 42, 64, 26, 5, 4, 69, 30, 68)
                       )
match1 <- function(i, df) {
  j <- 1:nrow(df)
  
  if(!is.na(df$p201[i])){
    l <- df$p201[i]
  } else{
    
    k <-  abs(df$V2009[i] - df$V2009[j]) <= 1
    l <- ifelse(any(k), which(k), i)
  }
  
  return(l)
}

这就是我将如何应用该功能:

dataset2 <- dataset %>%
  group_by(id_dom,
           index = map_dbl(seq(nrow(.)), 
                            ~ .x %>% match1(df = dataset))) %>%
  mutate(p201 = (first(na.omit(V2009)) - 1)*100)

如您所见,我的最终目标是将indexid_dom 的观察结果配对 - 因此,如果i 运行它会更快(而且我认为它也会产生更好的结果)仅通过每个 id_dom 组的行,而不是整个数据集。

我希望得到这样的答案:

i) 不会将id_dom 的分组放在match1 函数中,而是放在管道中。 ii) 这让我可以写出类似map_dbl(seq(nrow(.)), ~ .x %&gt;% match1(df = . )) 的东西——这样如果我之前创建了V2009 变量,我就不需要在运行函数之前断开链。

谢谢!

【问题讨论】:

  • 我认为您的match1 函数可以重写并提高效率。你能解释一下你试图在那里实施的条件吗?
  • @RonakShak 我在示例中对其进行了一些简化。但一般来说,它将具有p201 = NA 的每一行i 与组id_dom 中的其他观察值j 进行比较。如果任意 j 个元素满足比较条件,则函数返回满足条件的最小行索引。如果不是,则返回i
  • 好吧..所以我猜你对改变match1函数不感兴趣,因为这只是实际函数的简化版本。
  • 简化只针对匹配的条件,其他列有多个条件。但是它们都使用相同的跨行操作结构。如果仍然可能的话,我会非常愿意更改功能

标签: r function for-loop dplyr


【解决方案1】:

您可以只传递函数中需要的变量,而不是传递数据框。这是一个简化的函数match2

match2 <- function(x, y, val) {
    if(is.na(x))
      return(which.max(abs(y - val) <= 1))
    else return(x)
} 

这可以用作:

library(dplyr)
library(purrr)
dataset3 <- dataset %>%
              group_by(id_dom, index = map2_dbl(p201, V2009, match2, V2009)) %>%
              mutate(p201 = (first(na.omit(V2009)) - 1)*100)

dataset3
# A tibble: 9 x 4
# Groups:   id_dom, index [6]
#  id_dom  p201 V2009 index
#   <dbl> <dbl> <dbl> <dbl>
#1     20  6200    63     1
#2     20  4100    42     2
#3     20  4100    64     2
#4    250  2500    26     4
#5    250   400     5     5
#6    250   400     4     5
#7    254  6800    69     2
#8    254  2900    30     1
#9    254  6800    68     2

这给出了与dataset2 类似的结果,可以验证:

identical(dataset2, dataset3)
#[1] TRUE

【讨论】:

  • @RonakShak 抱歉,我不确定我是否有直觉,所以我有一个问题:如何扩展它以在其他列的行之间进行多重比较?
  • 您可以将多个参数传递给match2 函数。 match2 &lt;- function(w, x, y, z, val) {........
  • @RonakShak 但这将如何工作?我必须使用pmap 而不是map2 吗?我想知道在我需要abs(df$V2009[i] - df$V2009[j]) &lt;= 1 和其他一些条件df$V1[i] - df$V1[j] == 1 的情况下,例如
  • 是的,如果你想迭代超过 2 个值,你可以使用pmap_dbl。类似pmap_dbl(list(p201, V2009, V1), match2....)
【解决方案2】:

我们可以使用cur_data而不是dataset中的match分组后通过'id_dom'

library(dplyr)
library(purrr)
dataset %>%
     # // grouped by id_dom
     group_by(id_dom) %>%
     # // create new group by looping over the sequence of rows
     # // apply the match1
     group_by(index = map_dbl(seq(n()), ~ 
         match1(.x, df = cur_data())), .add = TRUE) %>%
     # // update the p201
     mutate(p201 = (first(na.omit(V2009)) - 1)*100)

或使用group_split

dataset %>% 
   group_split(id_dom) %>%
   map_dfr(., ~ .x %>%
                group_by(index = map_dbl(row_number(),
                  ~ match1(.x, df = cur_data()))) %>%
                 mutate(p201 = (first(na.omit(V2009)) - 1)*100))

【讨论】:

  • 哦,我现在无法检查,但看起来不错。不知道 cur_data,也不知道 .add 选项。
  • 但由于该函数使用基本 R 语法进行子集化,而不是 dplyr 动词,它会理解例如 df$p201[1] 表示组内的第一个观察吗?
  • @ArthurCarvalhoBrito 您可以使用 group_split(更新)检查输出
猜你喜欢
  • 1970-01-01
  • 2021-11-27
  • 1970-01-01
  • 2019-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-04
相关资源
最近更新 更多