【问题标题】:String as formula字符串作为公式
【发布时间】:2018-09-26 13:36:50
【问题描述】:

我已尝试在论坛中搜索,但无法找到帮助。我对 R 很陌生,并且在加载某些字符串以用作公式方面的成功有限。

我有一个格式如下的 csv:

            Sam, Frank, Dennis, Steven, Robert, Georgia
Region_1    218,    763,    811,    812,    797,    574
Region_2    474,    983,    343,    697,    310,    335
Region_3    700,    813,    133,    212,    906,    680
Region_4    212,    581,    893,    514,    530,    795

并且能够加载它并向我的数据框中添加其他列,以添加特定列(即 Sam + Frank、Dennis + Georgia)。

如果我将团队硬编码到我的代码中,我就可以做到,例如:

temp <- mutate(temp, team_1 = Sam + Robert) 

我的问题是我有另一个包含 2 列列表的 csv,这些是每天更改的团队,例如:

Team 1  Sam + Robert
Team 2  Frank + Georgia
Team 3  Frank + Steven
Team 4  Robert + Dennis
Team 5  Frank + Sam

有没有办法可以将此 csv 列表加载到 R 中并将这些团队成员列添加在一起。

但我看不到要从 csv 文件中添加此列表,并有一个动态包含团队成员总数的新数据框。

有人能帮我解决这个问题吗?

我希望得到这样的结果(将团队成员的总和(第二个 csv 文件中的团队 1、团队 2 等)作为附加列:

         Sam,   Frank,  Dennis, Steven, Robert, Georgia,    Sam + Robert,   Frank + Georgia,    Frank + Steven, Robert + Dennis,    Frank + Sam
 Region_1   218,    763,    811,    812,    797,    574,    981,    1337,   1575,   1608,   981
 Region_2   474,    983,    343,    697,    310,    335,    1457,   1318,   1680,   653,    1457
 Region_3   700,    813,    133,    212,    906,    680,    1513,   1493,   1025,   1039,   1513
 Region_4   212,    581,    893,    514,    530,    795,    793,    1376,   1095,   1423,   793

非常感谢。

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    将代码作为字符串处理有点复杂。在这里,将第二个数据帧转换为一个查找表更简单,该查找表可以连接到第一个重新整形为长格式的表格。然后可以将结果汇总并重新塑造成您想要的任何形式。

    library(tidyverse)
    
    people <- data.frame(
        region = c("Region_1", "Region_2", "Region_3", "Region_4"),
        Sam = c(218L, 474L, 700L, 212L),
        Frank = c(763L, 983L, 813L, 581L),
        Dennis = c(811L, 343L, 133L, 893L),
        Steven = c(812L, 697L, 212L, 514L),
        Robert = c(797L, 310L, 906L, 530L),
        Georgia = c(574L, 335L, 680L, 795L), 
        stringsAsFactors = FALSE
    )
    
    teams <- data.frame(
        team = c("Team 1", "Team 2", "Team 3", "Team 4", "Team 5"),
        members = c("Sam + Robert", "Frank + Georgia", "Frank + Steven", "Robert + Dennis", "Frank + Sam"), 
        stringsAsFactors = FALSE
    )
    
    teams_long <- teams %>% 
        mutate(team = members) %>%    # set team name
        separate_rows(members)    # separate teams to names
    
    people %>% 
        gather(member, value, -region) %>%    # reshape to long form
        left_join(teams_long, c('member' = 'members')) %>%    # join by names
        group_by(region, team) %>% 
        summarise(value = sum(value)) %>%    # aggregate
        spread(team, value) %>%    # reshape to wide form
        left_join(people, .)    # join back to original data
    #>     region Sam Frank Dennis Steven Robert Georgia Frank + Georgia
    #> 1 Region_1 218   763    811    812    797     574            1337
    #> 2 Region_2 474   983    343    697    310     335            1318
    #> 3 Region_3 700   813    133    212    906     680            1493
    #> 4 Region_4 212   581    893    514    530     795            1376
    #>   Frank + Sam Frank + Steven Robert + Dennis Sam + Robert
    #> 1         981           1575            1608         1015
    #> 2        1457           1680             653          784
    #> 3        1513           1025            1039         1606
    #> 4         793           1095            1423          742
    

    【讨论】:

    • 谢谢 - 这真的很有帮助。非常感谢。
    【解决方案2】:

    这是一个使用rlang::parse_expr 直接解析第二个data.frame 中的表达式的选项

    df1 %>% bind_cols(map_dfc(df2$V2, ~df1 %>%
        transmute(!!as.character(.x) := !!rlang::parse_expr(as.character(.x)))))
    #  Sam Frank Dennis Steven Robert Georgia Sam + Robert Frank + Georgia
    #1 218   763    811    812    797     574         1015            1337
    #2 474   983    343    697    310     335          784            1318
    #3 700   813    133    212    906     680         1606            1493
    #4 212   581    893    514    530     795          742            1376
    #  Frank + Steven Robert + Dennis Frank + Sam
    #1           1575            1608         981
    #2           1680             653        1457
    #3           1025            1039        1513
    #4           1095            1423         793
    

    或者您可以根据团队命名新列:

    df1 %>% bind_cols(map2_dfc(df2$V1, df2$V2, ~df1 %>%
        transmute(!!as.character(.x) := !!rlang::parse_expr(as.character(.y)))))
    #  Sam Frank Dennis Steven Robert Georgia Team 1 Team 2 Team 3 Team 4 Team 5
    #1 218   763    811    812    797     574   1015   1337   1575   1608    981
    #2 474   983    343    697    310     335    784   1318   1680    653   1457
    #3 700   813    133    212    906     680   1606   1493   1025   1039   1513
    #4 212   581    893    514    530     795    742   1376   1095   1423    793
    

    我们的想法是我们使用map(或map2)在df1transmute 上下文中应用存储在df2 中的“公式”。表达式使用!!parse_expr(...) 进行评估。


    样本数据

    df1 <- read.table(text =
        "Sam Frank Dennis Steven Robert Georgia
    Region_1    218    763    811    812    797    574
    Region_2    474    983    343    697    310    335
    Region_3    700    813    133    212    906    680
    Region_4    212    581    893    514    530    795", header = T)
    
    
    df2 <- read.table(text =
        "'Team 1'  'Sam + Robert'
    'Team 2'  'Frank + Georgia'
    'Team 3'  'Frank + Steven'
    'Team 4'  'Robert + Dennis'
    'Team 5'  'Frank + Sam'", header = F)
    

    【讨论】:

    • 你太棒了!这正是我一直在尝试做的。
    【解决方案3】:

    背景:您可以使用people[, x]访问存储在向量x中的data.frame列,您可以使用strsplit获取您需要的列的向量。

    回答

    # function to get the sum of two columns from 'people'
    get_sum <- function(m) {
      members <- unlist(strsplit(m, ' + ', fixed = T))
      rowSums(people[, members])
    }
    
    # "Sam + Robert"
    get_sum(teams$members[1])
    # [1] 1015  784 1606  742
    
    # apply this for every team, and add results to 'people'
    cbind(people,
          sapply(teams$members, get_sum))
    
    #     region Sam Frank Dennis Steven Robert Georgia Sam + Robert Frank + Georgia Frank + Steven Robert + Dennis Frank + Sam
    # 1 Region_1 218   763    811    812    797     574         1015            1337           1575            1608         981
    # 2 Region_2 474   983    343    697    310     335          784            1318           1680             653        1457
    # 3 Region_3 700   813    133    212    906     680         1606            1493           1025            1039        1513
    # 4 Region_4 212   581    893    514    530     795          742            1376           1095            1423         793
    

    【讨论】:

    • 我应该补充一下,我从@alistaire 的回答中借用了代码来创建peopleteams 对象——谢谢
    【解决方案4】:

    (感谢@alistaire 以方便的格式编码数据)

    这可以通过简单的 for 循环中的基本 R 函数 (eval + parse) 轻松完成:

    for(i in teams$members){
        people[[i]] <- eval(parse(text = i), envir = people)
    }
    
    # > people
    #     region Sam Frank Dennis Steven Robert Georgia Sam + Robert Frank + Georgia
    # 1 Region_1 218   763    811    812    797     574         1015            1337
    # 2 Region_2 474   983    343    697    310     335          784            1318
    # 3 Region_3 700   813    133    212    906     680         1606            1493
    # 4 Region_4 212   581    893    514    530     795          742            1376
    # Frank + Steven Robert + Dennis Frank + Sam
    # 1           1575            1608         981
    # 2           1680             653        1457
    # 3           1025            1039        1513
    # 4           1095            1423         793
    

    【讨论】:

    • people[teams$members] &lt;- lapply(teams$members, function(i) eval(parse(text = i), envir = people)) 这样您就不会增加列表
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-13
    • 2017-01-01
    • 2011-04-15
    • 1970-01-01
    相关资源
    最近更新 更多