【问题标题】:Create multiple columns from a single column and clean up results从单列创建多列并清理结果
【发布时间】:2016-05-14 04:04:33
【问题描述】:

我有一个这样的数据框:

foo=data.frame(Point.Type = c("Zero Start","Zero Start", "Zero Start", "3000rpm_10%_13barG_Sdsdsa_1.0_ss_Pww","3000rpm_10%_13barG_Sdsdsa_1.0_ss_Pww","3000rpm_10%_13barG_Sdsdsa_1.0_ss_Pww","Zero Stop","Zero Start"),
               Point.Value = c(NA,NA,NA,rnorm(3),NA,NA))

我想添加三列,用分隔符_分割第一列,只保留分割后得到的数值。对于第一列不包含任何_ 的行,三个新列应为NA。我使用separate 有点接近,但这还不够:

> library(tidyr) 
> bar = separate(foo,Point.Type, c("rpm_nom", "GVF_nom", "p0in_nom"), sep="_", remove = FALSE, extra="drop", fill="right")
> bar
                            Point.Type    rpm_nom GVF_nom p0in_nom Point.Value
1                           Zero Start Zero Start    <NA>     <NA>          NA
2                           Zero Start Zero Start    <NA>     <NA>          NA
3                           Zero Start Zero Start    <NA>     <NA>          NA
4 3000rpm_10%_13barG_Sdsdsa_1.0_ss_Pww    3000rpm     10%   13barG   -1.468033
5 3000rpm_10%_13barG_Sdsdsa_1.0_ss_Pww    3000rpm     10%   13barG    1.280868
6 3000rpm_10%_13barG_Sdsdsa_1.0_ss_Pww    3000rpm     10%   13barG    0.270126
7                            Zero Stop  Zero Stop    <NA>     <NA>          NA
8                           Zero Start Zero Start    <NA>     <NA>          NA

我不确定为什么我的数据框现在包含两种明显不同的NA,但is.na 似乎都喜欢它们,所以我可以忍受。但是,我有两种问题:

  1. 新列应至少为numeric,并且可能为integer。相反,它们是character,因为尾随rpm%barG。我该如何摆脱这些?
  2. Point.Type不能被拆分时,rpm_nom应该是NA,而是变成Zero StartZero Stop。更改fill= 选项只会更改哪一列获得Zero Start/Zero Stop。相反,我希望他们三个都是NA。我该怎么做?

注意:我使用的是tidyr,但如果您认为有更好的方法,您当然不需要这样做。

【问题讨论】:

    标签: r split tidyr


    【解决方案1】:

    您可以使用 dplyr 对列进行后处理:

    library(dplyr)
    foo <- foo %>%
      separate(Point.Type, c("rpm_nom", "GVF_nom", "p0in_nom"), 
               sep="_", remove = FALSE, extra="drop", fill="right") %>%
      mutate_each(funs(as.numeric(gsub("[^0-9]","",.))), rpm_nom, GVF_nom, p0in_nom)
    

    gsub("[^0-9]","",.)-部分删除所有非数字字符。如果您想防止删除小数点,您可以使用[^0-9.] 而不是[^0-9](就像他的回答中使用的@PierreLafortune),但请注意,这也将包括以下点不是小数点。通过将其包装在as.numeric 中,您可以将它们转换为数值,同时将空单元格转换为NA。这给出了以下结果:

    > foo
                                Point.Type rpm_nom GVF_nom p0in_nom Point.Value
    1                           Zero Start      NA      NA       NA          NA
    2                           Zero Start      NA      NA       NA          NA
    3                           Zero Start      NA      NA       NA          NA
    4 3000rpm_10%_13barG_Sdsdsa_1.0_ss_Pww    3000      10       13  -1.2361145
    5 3000rpm_10%_13barG_Sdsdsa_1.0_ss_Pww    3000      10       13  -0.8727960
    6 3000rpm_10%_13barG_Sdsdsa_1.0_ss_Pww    3000      10       13   0.9685555
    7                            Zero Stop      NA      NA       NA          NA
    8                           Zero Start      NA      NA       NA          NA
    

    或使用 data.table(由@DavidArenburg 在 cmets 中提供):

    library(data.table)
    setDT(foo)[, c("rpm_nom","GVF_nom","p0in_nom") := 
                 lapply(tstrsplit(Point.Type, "_", fixed = TRUE)[1:3],
                        function(x) as.numeric(gsub("[^0-9]","",x)))
               ]
    

    会给出类似的结果:

    > foo
                                 Point.Type Point.Value rpm_nom GVF_nom p0in_nom
    1:                           Zero Start          NA      NA      NA       NA
    2:                           Zero Start          NA      NA      NA       NA
    3:                           Zero Start          NA      NA      NA       NA
    4: 3000rpm_10%_13barG_Sdsdsa_1.0_ss_Pww -0.09255445    3000      10       13
    5: 3000rpm_10%_13barG_Sdsdsa_1.0_ss_Pww  1.18581340    3000      10       13
    6: 3000rpm_10%_13barG_Sdsdsa_1.0_ss_Pww  2.14475950    3000      10       13
    7:                            Zero Stop          NA      NA      NA       NA
    8:                           Zero Start          NA      NA      NA       NA
    

    这样做的好处是foo 是通过引用更新的。由于这更快,内存效率更高,这对于使用大型数据集特别有价值。

    【讨论】:

    • @Jaap 听起来很棒!是否可以通过使用%&gt;%来避免创建bar
    • @DavidArenburg 您的解决方案看起来也不错,但data.table 是什么?我所有的代码都适用于 data.frames。我担心切换到这些数据表可能会影响我的其余代码。另外,我不知道函数tstrsplit,但我想这在data.tablepackage 中有记录。
    • @DeltaIV 更新了答案。 data.tabledata.frame 的增强形式。请参阅this webpage 了解更多信息。
    • 有趣!我的数据框并不是很大,但比示例大得多(它或多或少 2000 行乘 1200 列)。也许数据表会很好:我会研究它们。我有点遗憾放弃 Hadley Wickham 的软件包,因为我喜欢它们,但如果性能提升很大,那可能是值得的!
    • 顺便说一句,您可以使用type.convert,而不是as.numeric,这样它会自行正确转换为integersnumeric
    【解决方案2】:

    使用base R,我们可以首先在必要时强制NA 值并强制使用类numeric

    bar[-1] <- lapply(bar[-1], function(x) {
      is.na(x) <- grepl("Zero", x)
      as.numeric(gsub("[^0-9.]", "", x))})
    #                             Point.Type rpm_nom GVF_nom p0in_nom Point.Value
    # 1                           Zero Start      NA      NA       NA          NA
    # 2                           Zero Start      NA      NA       NA          NA
    # 3                           Zero Start      NA      NA       NA          NA
    # 4 3000rpm_10%_13barG_Sdsdsa_1.0_ss_Pww    3000      10       13   0.3558397
    # 5 3000rpm_10%_13barG_Sdsdsa_1.0_ss_Pww    3000      10       13   1.1454829
    # 6 3000rpm_10%_13barG_Sdsdsa_1.0_ss_Pww    3000      10       13   0.2958815
    # 7                            Zero Stop      NA      NA       NA          NA
    # 8                           Zero Start      NA      NA       NA          NA
    

    减少到一行(@Jaap):

    bar[-1] <- lapply(bar[-1], function(x) as.numeric(gsub("[^0-9.]", "", x)))
    

    【讨论】:

    • 我不确定你为什么使用bar[-1]。另外,@Jaap gsub("[^0-9]","",x) 和你的 gsub("[[:alpha:]]|[[:punct:]]", "", x) 有什么区别?最后,为什么在你的最后一列中你会得到大约 10^14 的Point.Values? rnorm(3) 应该产生更接近于 0 的数字。
    • bar[-1] 删除我们不想操作的第一列。两种正则表达式模式之间的区别可以在这里看到cheatography.com/davechild/cheat-sheets/regular-expressions。在最后一列中,小数点被删除了。
    猜你喜欢
    • 1970-01-01
    • 2023-03-10
    • 2016-03-27
    • 1970-01-01
    • 1970-01-01
    • 2021-11-20
    • 1970-01-01
    • 1970-01-01
    • 2015-10-13
    相关资源
    最近更新 更多