【问题标题】:Using dplyr to mutate factor variable conditional on numeric variables使用 dplyr 以数值变量为条件改变因子变量
【发布时间】:2017-02-02 19:28:18
【问题描述】:

我有一个运动队的数据,用于跟踪与其他团队的胜负,结构如下:

Game TotalWins TotalLosses Team1Win Team1Loss Team2Win Team2Loss ...
1    1         0           1        NA        NA       NA
2    1         1           NA       NA        NA       1
3    2         1           NA       NA        1        NA
4    2         2           NA       1         NA       NA
5    3         2           NA       NA        1        NA
...

我想创建一个因子变量,其中包括比赛对阵的球队,以便数据看起来像这样:

Game TotalWins TotalLosses Team1Win Team1Loss Team2Win Team2Loss Team
1    1         0           1        NA        NA       NA        Team1
2    1         1           NA       NA        NA       1         Team2
3    2         1           NA       NA        1        NA        Team2
4    2         2           NA       1         NA       NA        Team1
5    3         2           NA       NA        1        NA        Team2
...

我的想法(不可行的代码)本质上是这样的:

if (Team1Win == 1 | Team1Loss == 1), Team = "Team1"
if (Team2Win == 1 | Team2Loss == 1), Team = "Team2"

我真的很纠结如何在 dplyr 中使用 mutate 来做到这一点。我用 ifelse、recode 等尝试了各种方法,但要么不断收到错误,要么得到我不想要的结果。

在 dplyr 中进行这项工作的正确和最有效的方法是什么?

【问题讨论】:

  • mutate(Team = ifelse(is.na(Team1Win) & is.na(Team1Loss), "Team2", "Team1"))
  • 补充@Vlo 的好评:ifelse 函数非常适合这种类型的用例
  • @Vlo,这是我在发布之前尝试过的事情之一(我只是尝试使用非缺失值,因为有很多团队都缺失了,而不仅仅是我的示例中的两个),并且它抛出了这个错误:“mutate_impl(.data,dots) 中的错误:缺少参数“yes”,没有默认值”知道有什么问题吗?
  • @AshLevitt:是的,由于链接 ifelse,可能是语法问题的可怕错误消息。请注意,如果前一个 ifelse 为 FALSE,则第二个 ifelse 是下一个测试。 df <- df %>% mutate(Condition = ifelse(Condition == 'thing1', 'other-thing1', ifelse(Condition == 'thing2', 'other-thing2', 'default-thing')))

标签: r dplyr


【解决方案1】:

与其他答案类似,但有一些有用的变化:

  • na.rm = TRUEgather
  • sub 工作得很好,不需要stringr
  • 根据目标包含完整数据,使用full_join
library(dplyr)
library(tidyr)

df = read.delim(text = 
"Game TotalWins TotalLosses Team1Win Team1Loss Team2Win Team2Loss
1 1 0 1 NA NA NA
2 1 1 NA NA NA 1
3 2 1 NA NA 1 NA
4 2 2 NA 1 NA NA
5 3 2 NA NA 1 NA", sep = " ")

df %>% 
  select(-starts_with("Total")) %>% 
  gather(Team, one, -Game, na.rm = TRUE) %>% 
  select(-one) %>% 
  mutate(Team = sub("Win|Loss", "", Team)) %>% 
  full_join(df, .)

#> Joining, by = "Game"
#>   Game TotalWins TotalLosses Team1Win Team1Loss Team2Win Team2Loss  Team
#> 1    1         1           0        1        NA       NA        NA Team1
#> 2    2         1           1       NA        NA       NA         1 Team2
#> 3    3         2           1       NA        NA        1        NA Team2
#> 4    4         2           2       NA         1       NA        NA Team1
#> 5    5         3           2       NA        NA        1        NA Team2

【讨论】:

    【解决方案2】:

    目前,我对 dplyr 的处理方法很感兴趣,因此我提供了一个使用 dplyr 的解决方案,可以扩展到您可能拥有的许多团队。它还使用了tidyrstringr,正如下面apom 在评论中有用地指出的那样。

    library(dplyr)
    library(tidyr)
    library(stringr)
    
    df = read_delim(
    "Game TotalWins TotalLosses Team1Win Team1Loss Team2Win Team2Loss
    1 1 0 1 NA NA NA
    2 1 1 NA NA NA 1
    3 2 1 NA NA 1 NA
    4 2 2 NA 1 NA NA
    5 3 2 NA NA 1 NA",delim = " ")
    
    df %>% 
      gather("Team",value,contains("Team")) %>% 
      filter(!is.na(value)) %>%
      mutate(Team = str_replace_all(Team,c("Win" = "","Loss" = ""))) %>%
      select(-value)
    

    【讨论】:

    • 你还需要library(tidyr)
    • @apom 很好发现,会修改
    • 谢谢!这很有帮助。
    【解决方案3】:

    你可以用一个简单的循环来做到这一点:

    x = colnames(df)
    df$team<- NA
    
    for (i in 1:nrow(df))
    {
     df$team[i] = x[which(df[i,]==1)]
    }
    

    最后你可以使用以下函数修剪“赢”和“输”:

    df$team<- gsub("win", "",df$team)
    df$team<- gsub("loss", "",df$team)
    

    【讨论】:

      【解决方案4】:

      我很确定您的数据中有两个以上的团队,并且团队名称不是通用的。您要做的是首先将数据重塑为长格式并提取相关的团队名称。因此,您可能希望按照以下方式进行操作。

      library(dplyr)
      library(tidyr)
      new_df <- df %>% 
        gather(team,idx,Team1Win:Team100Loss) %>%
        filter(!is.na(idx)) %>%
        select(-idx) %>%
        mutate(team = gsub("Win|Loss","",team))
      

      如果你想保留那些宽列,那么你可以将新的 DF 加入旧的。

      【讨论】:

      • 我觉得这和我的回答很相似
      • 我放的时候没看到你的。很抱歉。
      【解决方案5】:

      这可能是您正在寻找的。 (并非仅针对 2 个团队进行硬编码)

      # solution 1 :
      paste0("Team",ceiling(apply(df[-c(1:3)], 1, function(x) which(!is.na(x)))/2))
      [1] "Team1" "Team2" "Team2" "Team1" "Team2"
      
      # solution 2: using a apply() {basically a for loop itself]
      apply(df[-c(1:3)], 1, function(x) gsub("(Team\\d)+.*", "\\1", colnames(df[-c(1:3)])[which(!is.na(x))]))
      # [1] "Team1" "Team2" "Team2" "Team1" "Team2"
      
      # solution 3: (long route to dplyr) [ you have indirectly taught me a lot in dplyr through my search for this solution]
      func <- function(x){
        y = which(x == 1)             # get the location of where 1 appears
        z = rep(0, times = length(x)) # create a vector of 0's+location of 1
        z[y] = y                      # i.e. c(0,0,3,0,5) for Team2Win 
        z
      }
      
      df1 = df[-c(1:3)] %>% gather("key", "value", starts_with("Team")) %>% 
                      group_by(key) %>%
                      dplyr::mutate(x = func(value)) %>%
                      filter(x != 0) %>% arrange(x) %>% select(key)
      
      df$newcol = gsub("(Team\\d+).*", "\\1", df1$key)
      

      【讨论】:

      猜你喜欢
      • 2022-12-03
      • 2015-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多