【问题标题】:Create a dataframe based on character vectors pairs connected by (,) and separated by a space基于由 (,) 连接并由空格分隔的字符向量对创建数据帧
【发布时间】:2021-05-18 07:56:15
【问题描述】:

我有以下data.frame:

b<-structure(list(b = c("47.83006,11.71699 47.83004,11.71691 47.83002,11.7168 47.83001,11.71662", 
"47.83001,11.71662 47.82993,11.71628 47.82991,11.7162 47.82988,11.71614 47.82983,11.71609 47.8295,11.71588 47.82919,11.71566 47.82898,11.71549 47.82845,11.71504 47.82832,11.715 47.82821,11.715 47.82712,11.71531 47.82639,11.71549 47.82606,11.71561 47.8257,11.71567 47.82548,11.71574 47.82433,11.71613", 
"47.82433,11.71613 47.82436,11.7165 47.8244,11.71715 47.82442,11.71742 47.82453,11.71823 47.82459,11.71856 47.82492,11.7199", 
"47.82492,11.7199 47.82495,11.72005 47.82503,11.72034 47.82515,11.72066 47.82526,11.72093 47.82556,11.72172 47.82559,11.72182 47.82561,11.72191 47.82562,11.72201", 
"47.85051,12.11965 47.85092,12.11997", "48.10034,11.75948 48.10021,11.75938"
)), row.names = c(NA, 6L), class = "data.frame")

它由用空格分隔的坐标 lat,lon 对组成。

如何从这种结构中尽可能高效地创建 data.frame 或 data.table,将 lat 和 lon 值放在不同的行中?

Lat       lon
47.83006  11.71699
47.83004  11.71691
47.83002  11.7168
…

更新 感谢您的解决方案。 我会选择@Gki 提案,因为它更快:

Unit: milliseconds
                                                                                                                         expr
 c <- b %>% separate_rows(b, sep = " ") %>% separate(b, into = c("Lat",      "Lon"), sep = ",", convert = T) %>% data.frame()
                                     d <- read.csv(text = unlist(strsplit(b$b, " ", TRUE)), col.names = c("Lat",      "Lon"))
       min        lq      mean    median        uq       max neval
 12.363628 13.031700 14.027860 13.408883 13.703157 28.922909   100
  1.020622  1.050315  1.119533  1.117269  1.170826  1.348833   100

【问题讨论】:

    标签: r dataframe data.table


    【解决方案1】:

    您可以使用strsplit 将值之间的空格分开,然后使用read.csv 得到data.frame

    read.csv(text=unlist(strsplit(b$b, " ", TRUE)), col.names = c("Lat", "Lon"))
    #        Lat      Lon
    #1  47.83004 11.71691
    #2  47.83002 11.71680
    #3  47.83001 11.71662
    #4  47.83001 11.71662
    #5  47.82993 11.71628
    #6  47.82991 11.71620
    #7  47.82988 11.71614
    #...
    

    或使用 Forward Pipe Operator |&gt;function 快捷方式 \()baseR 4.1.0 em>:

    strsplit(b$b, " ", TRUE) |> unlist() |> (\(d) read.csv(text=d, col.names = c("Lat", "Lon")))()
    #        Lat      Lon
    #1  47.83004 11.71691
    #2  47.83002 11.71680
    #3  47.83001 11.71662
    #...
    

    或者使用 bizarro 管道 -&gt;.; 而不是定义函数:

    strsplit(b$b, " ", TRUE) |> unlist() ->.; read.csv(text=., col.names = c("Lat", "Lon"))
    

    当跳过设置列标题时,转换为数字并获得矩阵作为结果的快速方法将是:

    do.call(rbind, strsplit(unlist(strsplit(b$b, " ", TRUE)), ",", TRUE))
    

    或将其转换为数字:

    matrix(as.numeric(unlist(strsplit(unlist(strsplit(b$b, " ", TRUE)), ",", TRUE))), ncol=2, byrow=TRUE)
    

    使用来自@mt1022 的data.table 解决方案进行比较:

    library(data.table)
    microbenchmark::microbenchmark(
      base = do.call(rbind, strsplit(unlist(strsplit(b$b, " ", TRUE)), ",", TRUE))
    , baseNum = matrix(as.numeric(unlist(strsplit(unlist(strsplit(b$b, " ", TRUE)), ",", TRUE))), ncol=2, byrow=TRUE)
    , data.table = as.data.table(tstrsplit(unlist(strsplit(b$b, ' ', T)), ',', T))
    )
    #Unit: microseconds
    #       expr     min       lq      mean   median       uq     max neval cld
    #       base  28.829  30.2965  33.08313  31.5705  33.0475  85.880   100  a 
    #    baseNum  29.832  31.3030  33.51445  32.3635  34.5395  56.851   100  a 
    # data.table 143.745 147.9900 155.41194 150.9960 157.2420 278.190   100   b
    

    【讨论】:

    • 谢谢!遗憾的是,我无法让 Forward Pipe Operator 工作,因为我的 R 版本可能有点旧 4.05,但 csv 方法非常快。
    • 不错的解决方案(从未听说过基本 R 管道运算符),但在微秒上进行基准测试几乎没有意义 IMO
    • @DavidArenburg 基地中的管道自今天 (4.1.0) 以来是新的。是的,benching 是没有意义的,只是显示它,因为它用于选择答案而另一个显示 data.table 更快。
    【解决方案2】:

    如果你可以在tidyr中做到这一点

    • 使用 separate_rowssep = ' ' 参数将额外的一对值分隔到新行中
    • 使用 separatesep = ',' 参数以及 convert = T 将 lat 和 lon 值分隔到不同的列中,这将同时将值转换为数字
    
    b<-structure(list(b = c("47.83006,11.71699 47.83004,11.71691 47.83002,11.7168 47.83001,11.71662", 
                            "47.83001,11.71662 47.82993,11.71628 47.82991,11.7162 47.82988,11.71614 47.82983,11.71609 47.8295,11.71588 47.82919,11.71566 47.82898,11.71549 47.82845,11.71504 47.82832,11.715 47.82821,11.715 47.82712,11.71531 47.82639,11.71549 47.82606,11.71561 47.8257,11.71567 47.82548,11.71574 47.82433,11.71613", 
                            "47.82433,11.71613 47.82436,11.7165 47.8244,11.71715 47.82442,11.71742 47.82453,11.71823 47.82459,11.71856 47.82492,11.7199", 
                            "47.82492,11.7199 47.82495,11.72005 47.82503,11.72034 47.82515,11.72066 47.82526,11.72093 47.82556,11.72172 47.82559,11.72182 47.82561,11.72191 47.82562,11.72201", 
                            "47.85051,12.11965 47.85092,12.11997", "48.10034,11.75948 48.10021,11.75938"
    )), row.names = c(NA, 6L), class = "data.frame")
    
    library(tidyr)
    
    b %>% separate_rows(b, sep = ' ') %>%
      separate(b, into = c('Lat', 'Lon'), sep = ',', convert = T) %>%
      data.frame()
    
    #>         Lat      Lon
    #> 1  47.83006 11.71699
    #> 2  47.83004 11.71691
    #> 3  47.83002 11.71680
    #> 4  47.83001 11.71662
    #> 5  47.83001 11.71662
    #> 6  47.82993 11.71628
    #> 7  47.82991 11.71620
    #> 8  47.82988 11.71614
    #> 9  47.82983 11.71609
    #> 10 47.82950 11.71588
    #> 11 47.82919 11.71566
    #> 12 47.82898 11.71549
    #> 13 47.82845 11.71504
    #> 14 47.82832 11.71500
    #> 15 47.82821 11.71500
    #> 16 47.82712 11.71531
    #> 17 47.82639 11.71549
    #> 18 47.82606 11.71561
    #> 19 47.82570 11.71567
    #> 20 47.82548 11.71574
    #> 21 47.82433 11.71613
    #> 22 47.82433 11.71613
    #> 23 47.82436 11.71650
    #> 24 47.82440 11.71715
    #> 25 47.82442 11.71742
    #> 26 47.82453 11.71823
    #> 27 47.82459 11.71856
    #> 28 47.82492 11.71990
    #> 29 47.82492 11.71990
    #> 30 47.82495 11.72005
    #> 31 47.82503 11.72034
    #> 32 47.82515 11.72066
    #> 33 47.82526 11.72093
    #> 34 47.82556 11.72172
    #> 35 47.82559 11.72182
    #> 36 47.82561 11.72191
    #> 37 47.82562 11.72201
    #> 38 47.85051 12.11965
    #> 39 47.85092 12.11997
    #> 40 48.10034 11.75948
    #> 41 48.10021 11.75938
    

    reprex package (v2.0.0) 于 2021 年 5 月 18 日创建

    【讨论】:

    • 谢谢 AnilGoyal。也学到了很多!点赞!
    【解决方案3】:

    这个解决方案远没有 GKI 和 AnilGoyal 提供的那么好。但它有效。这是separatepivot_longer组合的解决方案

    library(dplyr)
    library(tidyr)
    b %>%  
      separate(b, c("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l","m", "n", "o", "p", "q"), sep=" ",  extra = "drop", fill = "right") %>% 
      pivot_longer(
        cols=everything()
      ) %>% 
      drop_na() %>% 
      separate(value, c("lat", "long"), sep=",") %>% 
      select(-name)
    
    

    输出:

       lat      long    
       <chr>    <chr>   
     1 47.83006 11.71699
     2 47.83004 11.71691
     3 47.83002 11.7168 
     4 47.83001 11.71662
     5 47.83001 11.71662
     6 47.82993 11.71628
     7 47.82991 11.7162 
     8 47.82988 11.71614
     9 47.82983 11.71609
    10 47.8295  11.71588
    

    【讨论】:

      【解决方案4】:

      data.table 的解决方案在这里稍快:

      library(microbenchmark)
      library(data.table)
      microbenchmark(
          base = read.csv(text=unlist(strsplit(b$b, " ", TRUE)), col.names = c("Lat", "Lon")),
          data.table = as.data.table(tstrsplit(unlist(strsplit(b$b, ' ', T)), ',', T))
      )
       # Unit: microseconds
       #       expr     min       lq     mean   median       uq     max neval
       #       base 354.102 360.5485 377.0983 371.2665 380.6985 527.916   100
       # data.table 151.252 161.8555 177.9840 178.1130 184.3945 348.759   100
      

      感谢 GKi 的基本 R 解决方案。

      【讨论】:

      • 它并不比另一个 base 解决方案快:matrix(as.numeric(unlist(strsplit(unlist(strsplit(b$b, " ", TRUE)), ",", TRUE))), ncol=2, byrow=TRUE)
      • 不错的解决方案。我经常使用 data.table 并且有一种错误的印象,即它在各个方面都很有效:(
      猜你喜欢
      • 2017-09-16
      • 1970-01-01
      • 1970-01-01
      • 2019-06-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多