【问题标题】:dplyr mutate with function call returning incorrect valuedplyr 变异,函数调用返回不正确的值
【发布时间】:2016-03-30 05:27:04
【问题描述】:

有人能解释一下为什么下面的 dplyr mutate 调用(我应用一个将一列作为参数来设置新列的值的函数)不起作用吗?它似乎没有在正确的值上调用函数:新的season 列是根据mon 列中的第一个值而不是它自己行中的值设置的。

# Function to return season (winter, summer, or transition) given numerical month
getSeason <- function(m) {
  if(m >= 11 || m <= 3) 
    return(as.factor("Winter"))
  if(m >= 5 && m <= 9) 
    return(as.factor("Summer"))
  return(as.factor("Trans"))
}

getSeason(5) # Works: returns "Summer"

mon <- c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
months <- as.data.frame(mon)

months %>% mutate(season=getSeason(mon))  # doesn't work: all seasons set as "Winter"

我正在使用 R 版本 3.2.4 和 dplyr 的最新开发版本。 (这在 dplyr 的最新版本中也不起作用。)

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    其他答案很好地解释了您遇到问题的原因。

    我认为这是新函数case_when 可以派上用场的情况(目前在开发版本中可用,dplyr_0.4.3.9001)。

    目前您必须使用美元符号表示法才能在 case_when 内使用 mutate

    months %>% mutate(season = case_when(.$mon >= 11 | .$mon <= 3 ~ "Winter",
                                         .$mon >= 5 & .$mon <= 9 ~ "Summer",
                                         TRUE ~ "Trans"))
    
       mon season
    1    1 Winter
    2    2 Winter
    3    3 Winter
    4    4  Trans
    5    5 Summer
    6    6 Summer
    7    7 Summer
    8    8 Summer
    9    9 Summer
    10  10  Trans
    11  11 Winter
    12  12 Winter
    

    您可以使用case_when 而不是ififelse(或新的dplyr 函数if_else)来构建您的函数。对我来说,语法似乎更类似于使用if,而不是嵌套ifelse

    getSeason <- function(m) {
        factor(
            case_when(
                m >= 11 | m <= 3 ~ "Winter",
                m >= 5 & m <= 9 ~ "Summer",
                TRUE ~ "Trans"
                ) 
            )
    }
    
    months %>% mutate(season=getSeason(mon))
    
       mon season
    1    1 Winter
    2    2 Winter
    3    3 Winter
    4    4  Trans
    5    5 Summer
    6    6 Summer
    7    7 Summer
    8    8 Summer
    9    9 Summer
    10  10  Trans
    11  11 Winter
    12  12 Winter
    

    请注意,“其他所有”条件在case_when 中最后完成,您只需将TRUE 放在公式左侧即可使用最终值填写其他所有内容。

    【讨论】:

      【解决方案2】:

      你也可以使用Vectorize:

      # Function to return season (winter, summer, or transition) given numerical month
      getSeason <- function(m) {
        if(m >= 11 || m <= 3) 
          return(as.factor("Winter"))
        if(m >= 5 && m <= 9) 
          return(as.factor("Summer"))
        return(as.factor("Trans"))
      }
      
      
      getSeason <- Vectorize(getSeason)
      
      mon <- c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
      months <- data.frame(mon = mon)
      
      months %>% mutate(season=gs(mon)) 
      

      【讨论】:

        【解决方案3】:

        if 没有矢量化(奇怪),所以它只使用mon 中的第一个值,即1,所以你得到了所有Winter

        为避免这种情况,请使用ifelse,它矢量化的:

        months %>% mutate(season = factor(ifelse(mon >= 11 | mon <=3, 
                                                 'Winter', ifelse(mon >= 5 & mon <= 9, 
                                                                  'Summer', 'Trans'))))
        #    mon season
        # 1    1 Winter
        # 2    2 Winter
        # 3    3 Winter
        # 4    4  Trans
        # 5    5 Summer
        # 6    6 Summer
        # 7    7 Summer
        # 8    8 Summer
        # 9    9 Summer
        # 10  10  Trans
        # 11  11 Winter
        # 12  12 Winter
        

        如果您想添加足够多的级别以使嵌套ifelses 变得讨厌,请改用cut,因为您实际上是在将连续数字数据转换为因子数据,这就是cut 的目的。

        months %>% mutate(season = droplevels(cut(months$mon, c(0, 3, 4, 9, 10, 12), 
                                                  c('Winter', 'Trans', 'Summer', 'Trans', 'Winter'))))
        

        注意droplevels 在这种情况下清理重复的关卡,这会引发警告。

        【讨论】:

        • 非常感谢您的解释。
        猜你喜欢
        • 2021-04-12
        • 1970-01-01
        • 1970-01-01
        • 2015-11-09
        • 2016-03-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多