首页 文章

dplyr mutate的复杂条件

提问于
浏览
0

样本数据:

library(dplyr)

id <- rep(LETTERS[1:5], each = 10)
x <- round(runif(50, -500, 200), digits = 0)
y <- round(runif(50, -700, 700), digits = 0)
z <- round(runif(50, 250, 300), digits = 0)

df.1 <- data.frame(id = id, x = x, y = y, z = z)
> summary(df.1)
 id           x                y                 z        
 A:10   Min.   :-497.0   Min.   :-665.00   Min.   :251.0  
 B:10   1st Qu.:-283.2   1st Qu.:-349.50   1st Qu.:261.2  
 C:10   Median :-128.0   Median : -33.50   Median :274.5  
 D:10   Mean   :-145.4   Mean   : -39.58   Mean   :275.3  
 E:10   3rd Qu.: -15.0   3rd Qu.: 293.25   3rd Qu.:288.0  
        Max.   : 171.0   Max.   : 696.00   Max.   :299.0

我想要实现的目标是:

  • 将每个id放入自己的数据帧中

  • 创建一个名为"direction"的新列,它将响应下面的条件

a - 在标识的列中标识x,y,z b中具有最宽范围的列,通过下一行值是否大于当前行值来计算方向 - TRUE和FALSE return

即y具有最大范围

id    x    y   z direction
1  A -320   31 251      TRUE
2  A -199 -530 276     FALSE
3  A -228  390 264      TRUE
4  A -158  363 268      TRUE
5  A -308  150 267     FALSE
6  A  -47  345 261        NA

在具有最大范围的列上计算方向非常重要 . 在示例数据中,列y可能始终是具有最大范围的列,但在我的实际数据中,它可以是任何列 .

我想它会涉及mutate和ifelse?!但不知道我该如何去做...我通常会使用广泛的for循环,并且只在上周或者两周开始使用dplyr . 尝试不要再回到凌乱的循环和严重嵌套的代码..

非常感谢你的帮助!谢谢!

for (i in 1:length(unique(id)) {

    x <- 
      df.1 %>% 
      filter(id == unique(id)[i] %>%
      mutate(direction = ifelse())

    assign(unique(id)[i], x)

      }

1 回答

  • 0

    将每个id放入自己的数据帧中

    df_list = split(df.1, df.1$id)
    

    创建一个名为“direction”的新列,它将响应下面的条件,识别所标识列中x,y,zb中最宽范围的列,通过下一行值是否大于当前行值来计算方向TRUE和FALSE返回

    让我们编写一个函数来对一个数据框执行此操作:

    foo = function(df) {
      # identify column with widest range within x, y, z
      sub_df = df[c("x", "y", "z")]
      ranges = sapply(sub_df, max) - sapply(sub_df, min)
      widest = which.max(ranges)
      # see which direction it goes
      direction = diff(sub_df[[widest]]) < 0
      # add this as a column to whole df
      df$direction = c(direction, NA)
      return(df)
    }
    

    然后我们可以将此函数应用于每个数据框:

    df_list = lapply(df_list foo)
    

    在这里完成演示 . 为了保持紧凑,我将数据缩小了一点:

    set.seed(47)
    id <- rep(LETTERS[1:3], each = 6)
    x <- round(runif(18, -500, 200), digits = 0)
    y <- round(runif(18, -700, 700), digits = 0)
    z <- round(runif(18, 250, 300), digits = 0)
    df.1 <- data.frame(id = id, x = x, y = y, z = z)
    
    df_list = split(df.1, df.1$id)
    
    df_list = lapply(df_list, foo)
    df_list
    # $A
    # id    x    y   z direction
    # 1  A  184 -600 262     FALSE
    # 2  A -238  -44 299      TRUE
    # 3  A   33 -451 274     FALSE
    # 4  A   76   80 284      TRUE
    # 5  A  -99   22 253      TRUE
    # 6  A  -16 -513 269        NA
    # 
    # $B
    # id    x    y   z direction
    # 7   B -228  265 280      TRUE
    # 8   B -172 -168 297      TRUE
    # 9   B -120 -653 268     FALSE
    # 10  B  147 -648 260     FALSE
    # 11  B -403   51 283     FALSE
    # 12  B   -9  419 298        NA
    # 
    # $C
    # id    x    y   z direction
    # 13  C -386  348 269      TRUE
    # 14  C  -80 -183 293     FALSE
    # 15  C -146  -45 259      TRUE
    # 16  C  131 -429 289     FALSE
    # 17  C -220  556 253      TRUE
    # 18  C -478  -84 252        NA
    

    我没有用 dplyr . dplyr 擅长许多事情,但是对于列依赖于条件的列做某事很困难 dplyr ,写上面的函数如 foo 更容易 . 也就是说,您可以稍微调整一下这个功能并执行以下操作:

    library(dplyr)
    library(tidyr)
    df.1 %>% group_by(id) %>% 
      do(a = foo(.)) %>%  
      ungroup() %>% 
      unnest()
    

    如果你想将数据帧分开,最后仍然是 split ,但 lapply 非常容易(并且不需要额外的包),而这似乎更复杂,没有任何好处 .

相关问题