代码之家  ›  专栏  ›  技术社区  ›  Tung

长到宽格式:保留行顺序,仅对新创建的列名使用部分行值

  •  2
  • Tung  · 技术社区  · 7 年前

    我的数据:

    > print(DT)
              scenario     hyear          P
     1:  flux_0_P1.0_1 2013-2014 0.14044214
     2:  flux_0_P1.0_1 2014-2015 0.09141671
     3:  flux_0_P1.0_2 2013-2014 0.69610343
     4:  flux_0_P1.0_2 2014-2015 0.52359157
     5:  flux_0_P1.0_3 2013-2014 0.89724457
     6:  flux_0_P1.0_3 2014-2015 0.78003786
     7: flux_0_P1.0_10 2013-2014 0.73752843
     8: flux_0_P1.0_10 2014-2015 0.62216371
     9: flux_0_P1.0_11 2013-2014 0.14259943
    10: flux_0_P1.0_11 2014-2015 0.15309200
    11: flux_0_P1.0_12 2013-2014 0.81472886
    12: flux_0_P1.0_12 2014-2015 0.66015071
    

    我想将长格式更改为宽格式,其中:

    • 保留行顺序 scenario 新创建的宽数据框(data.table)列中的列,例如。 1, 2, 3, 10, 11, 12 不是 1, 10, 11, 12, 2, 3

    • 仅使用中部分行值(匹配和替换模式) 脚本 列作为宽数据框(数据表)中的列名,例如 flux_0_P1.0_1 P_0_P1.0_1 ( P 是原始数据框中值列的名称)

        hyear     P_0_P1.0_1 P_0_P1.0_2 P_0_P1.0_3 P_0_P1.0_10 P_0_P1.0_11 P_0_P1.0_12
      1 2013-2014     0.140       0.696      0.897       0.738       0.143       0.815
      2 2014-2015     0.0914      0.524      0.780       0.622       0.153       0.660
      

    我目前的尝试 :两者 spread dcast 更改了 key

    ### tidyverse
    DT_wide_tidyr <- tidyr::spread(DT, scenario, P)
    DT_wide_tidyr
    
    > DT_wide_tidyr
    # A tibble: 2 x 7
      hyear     flux_0_P1.0_1 flux_0_P1.0_10 flux_0_P1.0_11 flux_0_P1.0_12 flux_0_P1.0_2 flux_0_P1.0_3
      <chr>             <dbl>          <dbl>          <dbl>          <dbl>         <dbl>         <dbl>
    1 2013-2014        0.140           0.738          0.143          0.815         0.696         0.897
    2 2014-2015        0.0914          0.622          0.153          0.660         0.524         0.780
    
    ### data.table
    DT_wide_dcast <- data.table::dcast(DT, hyear ~ scenario, value.var = "P")
    DT_wide_dcast
    
    > DT_wide_dcast
           hyear flux_0_P1.0_1 flux_0_P1.0_10 flux_0_P1.0_11 flux_0_P1.0_12 flux_0_P1.0_2 flux_0_P1.0_3
    1: 2013-2014    0.14044214      0.7375284      0.1425994      0.8147289     0.6961034     0.8972446
    2: 2014-2015    0.09141671      0.6221637      0.1530920      0.6601507     0.5235916     0.7800379
    

    使用的数据

    > dput(as.data.frame(DT))
    structure(list(scenario = c("flux_0_P1.0_1", "flux_0_P1.0_1", 
    "flux_0_P1.0_2", "flux_0_P1.0_2", "flux_0_P1.0_3", "flux_0_P1.0_3", 
    "flux_0_P1.0_10", "flux_0_P1.0_10", "flux_0_P1.0_11", "flux_0_P1.0_11", 
    "flux_0_P1.0_12", "flux_0_P1.0_12"), hyear = c("2013-2014", "2014-2015", 
    "2013-2014", "2014-2015", "2013-2014", "2014-2015", "2013-2014", 
    "2014-2015", "2013-2014", "2014-2015", "2013-2014", "2014-2015"
    ), P = structure(c(0.140442142857143, 0.0914167142857143, 0.696103428571428, 
    0.523591571428571, 0.897244571428571, 0.780037857142857, 0.737528428571428, 
    0.622163714285714, 0.142599428571429, 0.153092, 0.814728857142857, 
    0.660150714285714))), .Names = c("scenario", 
    "hyear", "P"), class = "data.frame", row.names = c(NA, -12L))
    

    欢迎您提出任何建议!谢谢你,新年快乐!

    编辑

    根据@G.Grothendieck提供的解决方案,这就是我最终使用的:

    # Set row order in scenario column
    DT[, scenario := factor(scenario, levels = unique(scenario))]
    
    # tidyr
    DT_wide_tidyr <- tidyr::spread(DT, scenario, P) %>% 
      dplyr::rename_at(vars(contains("flux")), funs(sub("flux", names(DT)[3], .)))
    DT_wide_tidyr
    
    # data.table
    DT_wide_dcast <- data.table::dcast(DT, hyear ~ scenario, value.var = names(DT)[3])
    names(DT_wide_dcast) <- gsub("flux", names(DT)[3], names(DT_wide_dcast))
    DT_wide_dcast
    
    2 回复  |  直到 7 年前
        1
  •  3
  •   G. Grothendieck    7 年前

    如果 scenario 将列更改为按所需顺序给定级别的因子。

    如果 DF 输入是否显示在问题末尾,然后将代码用于 DF2 此处显示:

    DF2 <- transform(DF, scenario = factor(scenario, levels = unique(scenario)))
    

    如果 wide 是您的代码的结果,则这将更改 flux P 在列名中:

    names(wide) <- sub("flux", "P", names(wide))
    
        2
  •  2
  •   duckmayr    7 年前

    解决方案

    DT$scenario <- gsub('flux_', 'P_', DT$scenario)
    DT$scenario <- gsub('(?<=0_)(\\d)$', '0\\1', DT$scenario, perl = TRUE)
    DT <- tidyr::spread(DT, scenario, P)
    

    后果

          hyear P_0_P1.0_01 P_0_P1.0_02 P_0_P1.0_03 P_0_P1.0_10 P_0_P1.0_11
    1 2013-2014  0.14044214   0.6961034   0.8972446   0.7375284   0.1425994
    2 2014-2015  0.09141671   0.5235916   0.7800379   0.6221637   0.1530920
      P_0_P1.0_12
    1   0.8147289
    2   0.6601507
    

    解释

    你的问题是因为按字母顺序排列“1”、“2”、“10”会导致“1”、“10”、“2”。如果你加上前导零,这个问题就会消失。

    使现代化

    您可以使用以下函数来概括这一点:

    custom_spread <- function(data, key, value, strip_name = NULL) {
        if ( !is.null(strip_name) ) {
            data[, key] <- gsub(strip_name, key, data[, key])
        }
        data[, key] <- gsub('(?<=0_)(\\d)$', '0\\1', data[, key], perl = TRUE)
        data <- tidyr::spread(data, key, value)
        colnames(data) <- gsub('(?<=0_)0(\\d)$', '\\1', colnames(data), perl = TRUE)
        return(data)
    }
    

    例如,将其用于您的问题:

    custom_spread(DT, 'scenario', 'P', strip_name = 'flux')
    

    仍然给出相同的结果:

          hyear scenario_0_P1.0_1 scenario_0_P1.0_2 scenario_0_P1.0_3
    1 2013-2014        0.14044214         0.6961034         0.8972446
    2 2014-2015        0.09141671         0.5235916         0.7800379
      scenario_0_P1.0_10 scenario_0_P1.0_11 scenario_0_P1.0_12
    1          0.7375284          0.1425994          0.8147289
    2          0.6221637          0.1530920          0.6601507
    

    但您可以将其用于任何其他值列名称,如“T”、“U”等。这还可以删除添加的前导零,以获得正确的列排序。如果要保留前导零,只需注释掉 return() .