【问题标题】:Avoiding nested for loops in R, matching across multiple data.frames with multiple conditions避免在 R 中嵌套 for 循环,在多个 data.frames 中匹配多个条件
【发布时间】:2021-05-14 14:18:13
【问题描述】:

这是我正在做的一个例子。 data.frames 通常有数千条记录,而且我经常尝试使用if() 语句来满足更多条件。

library(tidyverse)

# example df 1
coll <- data.frame(id = c("alpha", "alpha", "beta", "beta", "gamma", "delta", "epsilon"),
                   frequency = c("12.340", "23.340", "12.560", "15.670", "56.230", "12.890", "89.430"),
                   start = c("2010-01-01", "2015-01-01", "2011-02-02", "2017-02-02", rep("2019-01-01", 3)),
                   end = c("2011-02-02", NA, "2012-01-01", NA, "2018-02-02", rep(NA, 2))) %>%
  mutate(still.active = ifelse(!is.na(end), still.active <- "No", NA),
         reason = ifelse(!is.na(end), reason <- "Removed", NA)) %>% 
  mutate_all(as.character)

# example df 2
mort <- data.frame(id = c("alpha", "beta", "gamma", "delta", "zeta"),
                   frequency = c("23.340", "15.670", "56.230", "12.890", NA),
                   date = c("2016-01-01", "2018-01-01", rep("2020-01-01", 3)),
                   type = c(rep(1, 2), rep(2, 3))
                   ) %>%
  mutate_all(as.character)

for(i in 1:nrow(coll)){
  for(j in 1:nrow(mort)){
    if(coll$id[i] == mort$id[j] & # if these match
       coll$frequency[i] == mort$frequency[j] & # and these match
       is.na(coll$end[i]) & # and the value I want to fill in is currently blank
       mort$type[j] == "1" # and this other condition is met
    ){
      coll$end[i] <- as.character(mort$date[j]) # then assign these cells these values
      coll$still.active[i] <- "No"
      coll$reason[i] <- "Said so"
    }
  }
}

嵌套的 for 循环正是我所需要的,但在实践中它们变得非常慢,我想学习一种更好的方法。当只需在两个 data.frames 中匹配一列的值时,索引很容易,例如:

df <- data.frame(id = c("one", "two", "three")) %>% arrange(desc(id))

df2 <- data.frame(id = c("one", "two", "three"),
                   frequency = c("23.340", "15.670", "56.230"))

df$freq <- df2[match(df$id, df2$id), "frequency"]

但我不知道在有更多条件时如何到达那里,即使我可以,我认为其他人可能很难阅读并弄清楚发生了什么。我喜欢嵌套 for 循环的一件事是它相当容易阅读。或者也许我只是习惯了他们。 我可以使用嵌套的ifelse() 语句来代替吗?还有哪些其他选择?

【问题讨论】:

  • 不确定,但可能是“变异连接”(例如 dplyr 的 inner_join 是解决方案的一部分。

标签: r performance dataframe for-loop conditional-statements


【解决方案1】:

这可以使用left_joinifelse(或case_when)来完成:

coll %>% left_join(mort, by = "id") %>% 
mutate(tmp = (frequency.x == frequency.y) & is.na(end) & type == "1" ) %>% 
mutate(end = ifelse(tmp, as.character(date), end),
still.active = ifelse(tmp, "No", still.active),
reason = ifelse(tmp,"Said so", reason)) %>% 
select(id, frequency = frequency.x, start, end, still.active,reason)


       id frequency      start        end still.active  reason
1   alpha    12.340 2010-01-01 2011-02-02           No Removed
2   alpha    23.340 2015-01-01 2016-01-01           No Said so
3    beta    12.560 2011-02-02 2012-01-01           No Removed
4    beta    15.670 2017-02-02 2018-01-01           No Said so
5   gamma    56.230 2019-01-01 2018-02-02           No Removed
6   delta    12.890 2019-01-01       <NA>         <NA>    <NA>
7 epsilon    89.430 2019-01-01       <NA>         <NA>    <NA>

【讨论】:

    猜你喜欢
    • 2023-03-15
    • 2021-08-19
    • 1970-01-01
    • 2019-12-26
    • 1970-01-01
    • 2017-01-01
    • 2017-08-13
    • 1970-01-01
    • 2018-05-04
    相关资源
    最近更新 更多