【问题标题】:Create new column based on regular expression match根据正则表达式匹配创建新列
【发布时间】:2020-05-26 21:14:05
【问题描述】:

问题

我想使用以下公式为相对标准差创建一个新列:stdev * 100 / abs(mean)。我有 40 多个变量,每个变量都有自己的 stdevmean(所以 80 列)。我想做的是使用正则表达式根据前面的名称计算两列(stdevmean)的相对标准偏差。例如,对于列 AceticAcid.stdevAceticAcid.mean,计算相对标准偏差以自动创建新列 AcetiAcid.rsd。等式是:AceticAcid.stdev * 100 / abs(AceticAcid.mean)


示例数据框

print(df)

  AceticAcid.mean AceticAcid.stdev Glucose.mean Glucose.stdev Propanol.mean Propanol.stdev
1        28.75775         0.911130     48.27333     4.4991249      144.4770       38.34122
2        78.83051        10.562110     28.13337     1.2304387      134.6402       31.76264
3        40.89769        17.848381     37.10283     0.2102977      132.0253       33.76568
4        88.30174        11.028700     32.90534     1.6396036      149.7135       21.56639
5        94.04673         9.132295     14.11699     4.7725182      132.7853       15.88455

期望的输出(不关心新列的顺序)

print(df_rsd)

  AceticAcid.mean AceticAcid.stdev Glucose.mean Glucose.stdev Propanol.mean Propanol.stdev AceticAcid.rsd Glucose.rsd Propanol.rsd
1        28.75775         0.911130     48.27333     4.4991249      144.4770       38.34122       3.168294   9.3201039     26.53795
2        78.83051        10.562110     28.13337     1.2304387      134.6402       31.76264      13.398504   4.3735921     23.59076
3        40.89769        17.848381     37.10283     0.2102977      132.0253       33.76568      43.641536   0.5667969     25.57515
4        88.30174        11.028700     32.90534     1.6396036      149.7135       21.56639      12.489788   4.9827894     14.40511
5        94.04673         9.132295     14.11699     4.7725182      132.7853       15.88455       9.710380  33.8069175     11.96258

重复尝试... 我不想把这些写出 40 次(必须有一个很好的正则表达式来实现这一点):

df_rsd <- df %>% mutate(AceticAcid.rsd = AceticAcid.stdev * 100 / abs(AceticAcid.mean),
                        Glucose.rsd = Glucose.stdev * 100 / abs(Glucose.mean),
                        Propanol.rsd = Propanol.stdev * 100 / abs(Propanol.mean))

可重现的数据

structure(list(AceticAcid.mean = c(28.7577520124614, 78.8305135443807, 
40.89769218117, 88.3017404004931, 94.0467284293845), AceticAcid.stdev = c(0.911129987798631, 
10.5621097609401, 17.8483808878809, 11.0287002893165, 9.13229470606893
), Glucose.mean = c(48.2733338139951, 28.1333662476391, 37.1028254181147, 
32.9053360782564, 14.1169873066247), Glucose.stdev = c(4.49912485200912, 
1.2304386717733, 0.210297667654231, 1.63960359641351, 4.77251824573614
), Propanol.mean = c(144.476965803187, 134.64017030783, 132.025340688415, 
149.713488831185, 132.785289955791), Propanol.stdev = c(38.3412187267095, 
31.7626409884542, 33.7656808178872, 21.5663894917816, 15.884545892477
)), class = "data.frame", row.names = c(NA, -5L))

【问题讨论】:

    标签: r regex dplyr


    【解决方案1】:

    我们可以使用split.default将数据集拆分为data.frame列的list,基于删除列名的后缀部分,然后用lapply循环list,进行计算和将其分配给“df”中的新列

    out <- lapply(split.default(df, sub("\\..*", "", names(df))), 
              function(x) x[[2]]* 100/abs(x[[1]]))
    df[paste0(names(out), ".rsd")] <- out
    
    df
    #  AceticAcid.mean AceticAcid.stdev Glucose.mean Glucose.stdev Propanol.mean Propanol.stdev AceticAcid.rsd Glucose.rsd Propanol.rsd
    #1        28.75775         0.911130     48.27333     4.4991249      144.4770       38.34122       3.168294   9.3201039     26.53795
    #2        78.83051        10.562110     28.13337     1.2304387      134.6402       31.76264      13.398504   4.3735921     23.59076
    #3        40.89769        17.848381     37.10283     0.2102977      132.0253       33.76568      43.641536   0.5667969     25.57515
    #4        88.30174        11.028700     32.90534     1.6396036      149.7135       21.56639      12.489788   4.9827894     14.40511
    #5        94.04673         9.132295     14.11699     4.7725182      132.7853       15.88455       9.710380  33.8069175     11.96258
    

    或者tidyverse

    library(purrr)
    library(dplyr)
    library(stringr)
    df %>% 
      split.default(str_remove(names(.), "\\..*")) %>%
      map_dfc(~ .x[[2]] * 100/abs(.x[[1]])) %>% 
      rename_all(~ str_c(., '.rsd')) %>% 
      bind_cols(df, .)
    

    【讨论】:

    • 效果很好!我还有其他描述性列,例如 ExperimentDatetime,我必须通过添加 df %&gt;% select(-c(Experiment, Datetime)) 从分析中排除,所以总共:df %&gt;% select(-c(Experiment, Datetime)) %&gt;% split.default(str_remove(names(.), "\\..*")) %&gt;% map_dfc(~ .x[[2]] * 100/abs(.x[[1]])) %&gt;% rename_all(~ str_c(., '.rsd')) %&gt;% bind_cols(df, .)
    【解决方案2】:

    另类,也与 tidyverse。

    library(tidyverse)
    
    df_long <- df %>% 
      mutate(measurement_number=row_number(), .before=1) %>% 
      pivot_longer(cols=-measurement_number, names_to="var", values_to="value") %>% 
      separate(var, into=c("var", "indicator")) %>% 
      pivot_wider(id_cols=c("measurement_number", "var"), names_from = indicator, values_from=value) %>% 
      mutate(rsd=stdev * 100 / abs(mean)) %>% 
      arrange(var, measurement_number)
    
    df_long
    #> # A tibble: 15 x 5
    #>    measurement_number var         mean  stdev    rsd
    #>                 <int> <chr>      <dbl>  <dbl>  <dbl>
    #>  1                  1 AceticAcid  28.8  0.911  3.17 
    #>  2                  2 AceticAcid  78.8 10.6   13.4  
    #>  3                  3 AceticAcid  40.9 17.8   43.6  
    #>  4                  4 AceticAcid  88.3 11.0   12.5  
    #>  5                  5 AceticAcid  94.0  9.13   9.71 
    #>  6                  1 Glucose     48.3  4.50   9.32 
    #>  7                  2 Glucose     28.1  1.23   4.37 
    #>  8                  3 Glucose     37.1  0.210  0.567
    #>  9                  4 Glucose     32.9  1.64   4.98 
    #> 10                  5 Glucose     14.1  4.77  33.8  
    #> 11                  1 Propanol   144.  38.3   26.5  
    #> 12                  2 Propanol   135.  31.8   23.6  
    #> 13                  3 Propanol   132.  33.8   25.6  
    #> 14                  4 Propanol   150.  21.6   14.4  
    #> 15                  5 Propanol   133.  15.9   12.0
    
    df_wide <- df_long %>% 
      pivot_wider(id_cols=c("measurement_number"), 
                  names_from = c(var), 
                  values_from = c(mean, stdev, rsd),
                  names_sep = ".")
    df_wide
    #> # A tibble: 5 x 10
    #>   measurement_num~ mean.AceticAcid mean.Glucose mean.Propanol stdev.AceticAcid
    #>              <int>           <dbl>        <dbl>         <dbl>            <dbl>
    #> 1                1            28.8         48.3          144.            0.911
    #> 2                2            78.8         28.1          135.           10.6  
    #> 3                3            40.9         37.1          132.           17.8  
    #> 4                4            88.3         32.9          150.           11.0  
    #> 5                5            94.0         14.1          133.            9.13 
    #> # ... with 5 more variables: stdev.Glucose <dbl>, stdev.Propanol <dbl>,
    #> #   rsd.AceticAcid <dbl>, rsd.Glucose <dbl>, rsd.Propanol <dbl>
    

    reprex package (v0.3.0) 于 2020 年 5 月 26 日创建

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-26
      • 1970-01-01
      • 1970-01-01
      • 2018-01-08
      相关资源
      最近更新 更多