【问题标题】:How to create columns from a list in a for loop using mutate如何使用 mutate 从 for 循环中的列表创建列
【发布时间】:2020-04-28 01:24:49
【问题描述】:

我想知道是否有一种方法可以使用 for 循环中的 mutate() 函数从 R 中的列表创建多个列。

这是我的意思的一个例子:

问题:

我有一个数据框df,它有 2 列:类别和评级。我想为df$category 的每个元素添加一列,并且在该列中,如果类别列与迭代器匹配,我想要一个 1。

library(dplyr)

df <- tibble(
  category = c("Art","Technology","Finance"),
  rating = c(100,95,50)
)

手动做,我可以做:

df <-
  df %>% 
  mutate(art = ifelse(category == "Art", 1,0))

但是,当我有 50 个类别时会发生什么? (这与我原来的问题很接近。这需要很长时间!)

我尝试了什么:

category_names <- df$category

for(name in category_names){

  df <-
    df %>% 
    mutate(name = ifelse(category == name, 1,0))

}

不幸的是,它似乎不起作用。

我会很感激任何关于这个主题的信息!

完整代码:

library(dplyr)

#Creates tibble
df <- tibble(
  category = c("Art","Technology","Finance"),
  rating = c(100,95,50)
)

#Showcases the operation I would like to loop over df
df <-
  df %>% 
  mutate(art = ifelse(category == "Art", 1,0))

#Creates a variable for clarity
category_names <- df$category

#For loop I tried
for(name in category_names){

  df <-
    df %>% 
    mutate(name = ifelse(category == name, 1,0))

}

我知道我实际上在做的是model.matrix() 的一种形式;然而,在我发现这个功能之前,我仍然很困惑为什么我之前做的事情不起作用。

【问题讨论】:

    标签: r list for-loop dplyr


    【解决方案1】:

    我们可以在创建序列列后使用pivot_wider

    library(dplyr)
    library(tidyr)
    df %>% 
        mutate(rn = row_number(), n = 1) %>% 
        pivot_wider(names_from = category, values_from = n, 
                 values_fill = list(n = 0)) %>%
        select(-rn)
    # A tibble: 3 x 4
    #  rating   Art Technology Finance
    #   <dbl> <dbl>      <dbl>   <dbl>
    #1    100     1          0       0
    #2     95     0          1       0
    #3     50     0          0       1
    

    或者另一个选项是map

    library(purrr)
    map_dfc(unique(df$category),  ~  df %>%
                                     transmute(!! .x := +(category == .x))) %>% 
         bind_cols(df, .)
    # A tibble: 3 x 5
    #  category   rating   Art Technology Finance
    #* <chr>       <dbl> <int>      <int>   <int>
    #1 Art           100     1          0       0
    #2 Technology     95     0          1       0
    #3 Finance        50     0          0       1
    

    如果我们需要for 循环

    for(name in category_names) df <- df %>% mutate(!! name := +(category == name))
    

    或者在base Rtable

    cbind(df, as.data.frame.matrix(table(seq_len(nrow(df)), df$category)))
    #    category rating Art Finance Technology
    #1        Art    100   1       0          0
    #2 Technology     95   0       0          1
    #3    Finance     50   0       1          0
    

    【讨论】:

    • 我的第一个想法是使用pivot_wider(),但无法真正理解它;但是,由于最初的问题是使用 mutate 和 for-loops,这似乎是一种解决方法。我很想在这个问题的范围内看到答案。感谢您的快速回复!
    • 我编辑了我之前的评论以反映推理。看到pivot_wider的回复让我非常兴奋,并匆忙决定接受。对此我深表歉意。
    • @spookywagons 不清楚它在哪里不起作用。我没有在您的帖子中看到任何新数据
    • @spookywagons 如果您在使用 := 而不是 == 时遇到问题,请告诉我
    • @spookywagons 没关系。通常人们在使用 tidyverse 时希望避免 for 循环。所以,我正在考虑回答其他选项而不是 for 循环
    【解决方案2】:

    想为任何偶然发现这个问题的人提供一些东西。 OP 中的问题是“名称”列名称在循环的每次迭代中都被重复使用:当你真正想要三个(或 50 个)时,你最终只得到一个新列。我一直发现自己想在循环中创建多个新列,最近我发现 mutate 现在可以采用类似“胶水”的输入来执行此操作。下面的代码现在也解决了原来的问题:

    for(name in category_names){
      df <-
        df %>%
        mutate("{name}" := ifelse(category == name, 1, 0))
    }
    

    这相当于 akrun 使用 for 循环的答案,但不涉及 !!操作员。请注意,您仍然需要“海象”:= 运算符,并且列名需要是一个字符串(我认为因为它在后台使用“胶水”)。我想有些人可能会觉得这种格式更容易理解。

    参考:https://www.tidyverse.org/blog/2020/02/glue-strings-and-tidy-eval/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-14
      • 1970-01-01
      • 2022-01-11
      • 2022-01-09
      • 2017-03-24
      • 1970-01-01
      相关资源
      最近更新 更多