【问题标题】:Create new variable in dataframe based on condition in one column, pulling from other column? (dplyr)根据一列中的条件在数据框中创建新变量,从另一列中提取? (dplyr)
【发布时间】:2018-05-16 15:09:27
【问题描述】:

我有以下数据框:

    df <- structure(list(country = c("Ghana", "Eritrea", "Ethiopia", "Ethiopia", 
"Congo - Kinshasa", "Ethiopia", "Ethiopia", "Ghana", "Botswana", 
"Nigeria"), CommodRank = c(1L, 2L, 3L, 1L, 3L, 1L, 1L, 1L, 1L, 
1L), topCommodInCountry = c(TRUE, FALSE, FALSE, TRUE, FALSE, 
TRUE, TRUE, TRUE, TRUE, TRUE), Main_Commod = c("Gold", "Copper", 
"Nickel", "Gold", "Gold", "Gold", "Gold", "Gold", "Diamonds", 
"Iron Ore")), row.names = c(NA, -10L), class = c("grouped_df", 
"tbl_df", "tbl", "data.frame"), vars = "country", drop = TRUE, indices = list(
    8L, 4L, 1L, c(2L, 3L, 5L, 6L), c(0L, 7L), 9L), group_sizes = c(1L, 
1L, 1L, 4L, 2L, 1L), biggest_group_size = 4L, labels = structure(list(
    country = c("Botswana", "Congo - Kinshasa", "Eritrea", "Ethiopia", 
    "Ghana", "Nigeria")), row.names = c(NA, -6L), class = "data.frame", vars = "country", drop = TRUE, .Names = "country"), .Names = c("country", 
"CommodRank", "topCommodInCountry", "Main_Commod"))

df

            country CommodRank topCommodInCountry Main_Commod
1             Ghana          1               TRUE        Gold
2           Eritrea          2              FALSE      Copper
3          Ethiopia          3              FALSE      Nickel
4          Ethiopia          1               TRUE        Gold
5  Congo - Kinshasa          3              FALSE        Gold
6          Ethiopia          1               TRUE        Gold
7          Ethiopia          1               TRUE        Gold
8             Ghana          1               TRUE        Gold
9          Botswana          1               TRUE    Diamonds
10          Nigeria          1               TRUE    Iron Ore  

我正在尝试添加另一列,显示此数据集中每个国家/地区的顶级商品(顶级 CommodRank),但我不确定如何。我可以用 CommodRank == 1 的“Main_Commod”标记“topcommod”,但我想将此相同的值复制到 CommodRank != 1 的情况。如下所示,第 3 行和第 4 行的埃塞俄比亚值都应为“黄金'。

df %>% mutate(topcommod = ifelse(CommodRank == 1, Main_Commod, 'unknown'))


            country CommodRank topCommodInCountry Main_Commod topcommod
1             Ghana          1               TRUE        Gold      Gold
2           Eritrea          2              FALSE      Copper   unknown
3          Ethiopia          3              FALSE      Nickel   unknown
4          Ethiopia          1               TRUE        Gold      Gold
5  Congo - Kinshasa          3              FALSE        Gold   unknown
6          Ethiopia          1               TRUE        Gold      Gold
7          Ethiopia          1               TRUE        Gold      Gold
8             Ghana          1               TRUE        Gold      Gold
9          Botswana          1               TRUE    Diamonds  Diamonds
10          Nigeria          1               TRUE    Iron Ore  Iron Ore

理想情况下,我正在寻找可以添加到现有长系列管道 %>% 函数调用的 dplyr 解决方案,但任何解决方案都会有所帮助。

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    IIUC,有多种方法可以做到这一点,例如:

    df %>% mutate(topCom = if(!any(topCommodInCountry)) "unknown" 
                           else Main_Commod[which.max(topCommodInCountry)])
    
    # A tibble: 10 x 5
    # Groups:   country [6]
       country          CommodRank topCommodInCountry Main_Commod topCom  
       <chr>                 <int> <lgl>              <chr>       <chr>   
     1 Ghana                     1 TRUE               Gold        Gold    
     2 Eritrea                   2 FALSE              Copper      unknown 
     3 Ethiopia                  3 FALSE              Nickel      Gold    
     4 Ethiopia                  1 TRUE               Gold        Gold    
     5 Congo - Kinshasa          3 FALSE              Gold        unknown 
     6 Ethiopia                  1 TRUE               Gold        Gold    
     7 Ethiopia                  1 TRUE               Gold        Gold    
     8 Ghana                     1 TRUE               Gold        Gold    
     9 Botswana                  1 TRUE               Diamonds    Diamonds
    10 Nigeria                   1 TRUE               Iron Ore    Iron Ore
    

    关于 OP 在评论中的问题如何处理多个顶级商品的关系,您可以执行以下操作:

    df %>% 
      mutate(topCom = if(!any(topCommodInCountry)) "unknown" 
                  else paste(unique(Main_Commod[topCommodInCountry]), collapse = "/"))
    

    如果一个国家有多个独特的顶级商品,它们将被粘贴到一个字符串中,以/分隔。

    【讨论】:

    • 非常感谢!在您的脑海中,是否有一种明显的方法可以在此处拆分和标记领带,以便将 topCom 分配给诸如“Gold / Diamonds / ...”之类的东西? (假设有 2 个或更多 Main_Commods,CommodRank == 1)
    • 没关系,您只需使用 'which' 而不是 'which.max' 来获取所有行索引,然后您可以访问并粘贴唯一名称:df %&gt;% mutate(topCom = Main_Commod[which(topCommodInCountry == max(topCommodInCountry))]) %&gt;% unique %&gt;% paste (sep = '', collapse = '/'))
    【解决方案2】:

    dplyr 的另一种模式...

    df %>% arrange(CommodRank) %>%
        mutate(topCommod = Main_Commod[1])
    

    【讨论】:

    • 对整个数据框(组)进行排序会比获取单个列(组)的最大值要慢得多
    • 除了@Ryan 评论,如果你没有正确安排你的数据集,做'Main_Commod[1]' 可能是非常危险/错误的
    【解决方案3】:

    这不是答案,而是从@docendo discimus 答案中学到了很多东西,我花了一秒钟来理解“如果是否定的”(!any(topCommodInCountry)),我想知道是只有我一个人还是我的电脑需要一秒钟也可以这样做:)

    使用相同的数据集,我研究了使if else 为正的想法。首先,我在两个解决方案之间测试了identical

    identical(
      #Negative
      df %>% 
        mutate(topCom = if(!any(topCommodInCountry)) "unknown" 
               else Main_Commod[which.max(topCommodInCountry)]), 
      #Positive
      df %>% 
        mutate(topCom = if(any(topCommodInCountry)) Main_Commod[which.max(topCommodInCountry)] 
               else "unknown"))
    
    [1] TRUE
    

    接下来,我测试了两者的基准:

    require(rbenchmark)
    
    benchmark("Negative" = {
      df %>% 
        mutate(topCom = if(!any(topCommodInCountry)) "unknown" 
               else Main_Commod[which.max(topCommodInCountry)])
    },
    "Positive" = {
      df %>% 
        mutate(topCom = if(any(topCommodInCountry)) Main_Commod[which.max(topCommodInCountry)] 
               else  "unknown")
    },
    replications = 10000,
    columns = c("test", "replications", "elapsed",
                "relative", "user.self", "sys.self"))
    

    差异并不大,但我假设使用更大的数据集它会增加。

          test replications elapsed relative user.self sys.self
    1 Negative        10000   12.59    1.015     12.44        0
    2 Positive        10000   12.41    1.000     12.30        0 
    

    【讨论】:

    • 在我用了四年的笔记本电脑上,运行 for(i in 1:1e6) !TRUE 大约需要 1/10 秒。不值得担心。
    • 为了可读性,删除不必要的! 可能是值得的,但对于它的价值,我认为如果! 被读作“不是”,即“如果不是任何topCommodInCountry”,那将非常直观跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-04
    • 1970-01-01
    • 1970-01-01
    • 2018-04-04
    • 2012-07-23
    • 1970-01-01
    相关资源
    最近更新 更多