【问题标题】:Assign a new column in a data frame containing sorted groups在包含排序组的数据框中分配一个新列
【发布时间】:2018-05-26 18:46:46
【问题描述】:

显示了一个数据框,其中的行包含一些已排序的分组数据。需要引入一个新列,该列的值取决于某个列的值。

如果第一个值为零,则一个组的所有值都将获得第一个非零值或NA,如果没有这样的值。否则,如果第一个值不为零,则分配一个固定值,例如-1.

输入数据框示例:

df <- data.frame(
 name = c("A", "A", "A", "A", "B", "B", "C", "C"), 
 value = c(0, 0, 6, 3, 0, 0 , 7, 0))

创建了 calc 列的示例输出数据框:

df <- data.frame(
 name = c("A", "A", "A", "A", "B", "B", "C", "C"), 
 value = c(0, 0, 6, 3, 0, 0 , 7, 0),
 calc = c(6, 6, 6, 6, NA, NA, -1, -1))

提前谢谢你。

P.S.: 首选基础 R

【问题讨论】:

    标签: r


    【解决方案1】:

    这是使用 base R 的一种方法:

    df$calc <- unlist(tapply(df$value, df$name, function(x) rep(if(x[1]==0) x[x!=0][1] else -1, length(x))))
    

    ...还有一个更好的方法:

    df$calc <- ave(df$value, df$name, FUN = function(x) if(x[1]==0) x[x!=0][1] else -1)
    

    分两部分比较好理解:

    首先,编写一个与您想要的条件相对应的函数。

     doit <- function(x) if(x[1]==0) x[x!=0][1] else -1
    

    其次,在ave中使用:

     ave(df$value, df$name, FUN=doit)
    

    |编辑|

    如果我想为“calc”列分配另一列的值,应该如何修改函数,例如“value2”,对应于第一个非零“值”?

    这里,ave 将不再为您提供帮助,您需要split 数据框并重新加入它。

    df$value2 <- 101:108
    do.call(rbind, lapply(split(df, df$name), function(x) {
      x $ calc <- with(x, ifelse(value[1]==0, value[value!=0][1], value2[value2!=0][1]))
      x
    }))
    

    注意function(x) 中的第二行...这是返回整个x 而不仅仅是$calc 组件。逻辑顺序是: split --> lapply --> do.call 但由于括号的工作方式,它看起来相反。可以使用来自 magrittr 的管道重写它,以便保留逻辑顺序(管道 LHS %&gt;% RHS 将 LHS 作为第一个参数转发给 RHS,因此需要使用 do.call 的技巧,我们希望它是第二个参数.).

    library(magrittr)
    split(df, df$name) %>%
      lapply(function(x) {
        x $ calc <- with(x, ifelse(value[1]==0, value[value!=0][1], value2[value2!=0][1]))
        x
      }) %>% {do.call(rbind, .)}
    

    【讨论】:

    • 感谢您的回答。这是一个问题......在“值”以零开头的情况下:如果我想为“计算”列分配另一列的值,例如“value2”,对应第一个非零“值”?
    • 这会稍微复杂一些,在基础 R 中,我认为您需要首先按组 split 数据帧,然后进行计算,然后通过 do.call(rbind, ...) 重新加入它(请参阅上面的编辑,我将此添加到我的答案中以获得更好的可读性)
    【解决方案2】:

    这是data.table的选项

    library(data.table)
    setDT(df)[, calc := if(!value[1]) value[value != 0][1] else -1, name]
    df
    #   name value calc
    #1:    A     0    6
    #2:    A     0    6
    #3:    A     6    6
    #4:    A     3    6
    #5:    B     0   NA
    #6:    B     0   NA
    #7:    C     7   -1
    #8:    C     0   -1
    

    【讨论】:

      【解决方案3】:

      您可以将group_bycase_whendplyr 一起使用:

      library(tidyverse)
      
      df %>%
        group_by(name) %>%
        mutate(calc = case_when(
          first(value) != 0 ~ -1., 
          max(value) == 0 ~ NA_real_,
          TRUE ~ value[value != 0][1]))
      

      输出:

      # A tibble: 8 x 3
      # Groups:   name [3]
        name  value  calc
        <fct> <dbl> <dbl>
      1 A        0.    6.
      2 A        0.    6.
      3 A        6.    6.
      4 A        3.    6.
      5 B        0.   NA 
      6 B        0.   NA 
      7 C        7.   -1.
      8 C        0.   -1.
      

      【讨论】:

        【解决方案4】:

        我同意@andrew_reece 的回答。 你甚至可以跳过line max(value) == 0 ~ NA_real_,因为calc 无论如何都会设置为NA,所以ìfelse 可能更短:

        df %>% 
          group_by(name) %>% 
          mutate(calc = ifelse(first(value) == 0, value[value != 0][1], -1))
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-07-02
          • 1970-01-01
          • 2017-02-13
          • 1970-01-01
          • 1970-01-01
          • 2020-08-15
          • 2023-03-16
          相关资源
          最近更新 更多