【问题标题】:Collapsing four data frame columns into two, interleaved columns将四个数据框列折叠成两个交错列
【发布时间】:2019-11-14 12:52:32
【问题描述】:

我正在使用纬度和经度数据在leaflet 地图上画线(见下文)。理想情况下,这些行将存储在数据框中的latlng 列中。在lat 列中,每个起点 lat 值后跟一个终点 lat 值,然后是另一行的起点 lat 值(line_id 列允许区分每一行)。 lng 数据的排列方式类似。理想情况下,数据框应如下所示:

> df.better
  line_id      lat        lng
1     ABC 51.50995 -0.1345093
2     ABC 51.51074 -0.1345093
3     XYZ 51.50991 -0.1345193
4     XYZ 51.51079 -0.1351200

问题是它以这种格式从数据存储中出来:

> df.wide
  line_id start_lat  end_lat  start_lng  end_lng
1     ABC  51.50995 51.51074 -0.1345093 -0.13519
2     XYZ  51.50991 51.51079  0.1351900  0.13512

这看起来有点像经典的“宽到长”数据整理问题,有很多问题和答案,但是标准的“长”格式将 lat 和 lng 数据折叠成一列,我需要两列.我尝试了一个 tidyverse 解决方案如下:

df2 <- df.wide %>% pivot_longer(cols = start_lat:end_lng,
                         names_to="variable",
                         values_to="value")

然后我清理variable 列:

df2$variable <- gsub(".*_lat","lat",df2$variable)
df2$variable <- gsub(".*_lng","lng",df2$variable)

这是结果,至少看起来数据的顺序是正确的:

> df2
A tibble: 8 x 3
  line_id variable      value
  <fct>   <chr>         <dbl>
1 ABC     lat      51.50995  
2 ABC     lat      51.51074  
3 ABC     lng      -0.1345093
4 ABC     lng      -0.13519  
5 XYZ     lat      51.50991  
6 XYZ     lat      51.51079  
7 XYZ     lng       0.13519  
8 XYZ     lng       0.135120 

最后一步似乎涉及再次传播数据,但使用 pivot_wider 会导致抱怨值没有被唯一标识:

df2 %>% pivot_wider(names_from = variable,values_from = value)
    # A tibble: 2 x 3
      line_id         lat         lng
      <fct>   <list<dbl>> <list<dbl>>
    1 ABC             [2]         [2]
    2 XYZ             [2]         [2]
    Warning message:
Values in `value` are not uniquely identified; output will contain list-cols.

我可以(我认为)了解错误发生的原因,但在 variable 中提供唯一标识符只会让我回到开始的地方。我该如何/应该如何处理这个问题?

require(magrittr)
require(tidyr)
require(dplyr)

options(pillar.sigfig = 7)

df.better <- data.frame(
  line_id = c("ABC","ABC","XYZ","XYZ"),
  lat = c(51.509950,51.510736,51.509910,51.510786),
  lng = c(-0.1345093,-0.1345093,-0.1345193,-0.135120)
)

df.wide <- data.frame(
  line_id = c("ABC","XYZ"),
  start_lat = c(51.509950,51.509910),
  end_lat = c(51.510736,51.510786),
  start_lng = c(-0.1345093,0.135190),
  end_lng = c(-0.135190,0.135120)
)

df2 <- df.wide %>% pivot_longer(cols = start_lat:end_lng,
                         names_to="variable",
                         values_to="value")

df2$variable <- gsub(".*_lat","lat",df2$variable)
df2$variable <- gsub(".*_lng","lng",df2$variable)

df2 %>% pivot_wider(names_from = variable,values_from = value)

m <- leaflet() %>% setView(lng = -0.1345093, lat = 51.510090, zoom = 18) %>% addTiles()

for (i in unique(df.better$line_id)) { # HT: https://stackoverflow.com/a/44547502/952708
  m <- m %>%
    addPolylines(data = df.better[df.better$line_id == i, ],
                 lng = ~lng, lat = ~lat, color = "Green",
                 opacity = 0.5, weight = 2, dashArray = 5)
}

m

【问题讨论】:

    标签: r tidyr r-leaflet


    【解决方案1】:

    如果我理解正确,您正在寻找这样的东西:

    df.wide <- data.frame(
      line_id = c("ABC","XYZ"),
      start_lat = c(51.509950,51.509910),
      end_lat = c(51.510736,51.510786),
      start_lng = c(-0.1345093,0.135190),
      end_lng = c(-0.135190,0.135120)
    )
    
    df.wide %>% 
      pivot_longer(-line_id,
                   names_to = c("set", ".value"),
                   names_pattern = "(.+)_(.+)"
      )
    
    #  line_id set        lat        lng
    #  <fct>   <chr>    <dbl>      <dbl>
    #1 ABC     start 51.50995 -0.1345093
    #2 ABC     end   51.51074 -0.13519  
    #3 XYZ     start 51.50991  0.13519  
    #4 XYZ     end   51.51079  0.135120 
    

    【讨论】:

      【解决方案2】:

      类似这样的东西可能会起作用

      library(data.table)
      dt <- data.table::fread("line_id start_lat  end_lat  start_lng  end_lng
           ABC  51.50995 51.51074 -0.1345093 -0.13519
           XYZ  51.50991 51.51079  0.1351900  0.13512")
      
      dt.melt <- melt( dt, 
                       id.vars = "line_id", 
                       measure.vars = patterns( lon = "_lng$", 
                                                lat = "_lat$" ), 
                       variable.name = "point_id" )
      
      #    line_id point_id        lon      lat
      # 1:     ABC        1 -0.1345093 51.50995
      # 2:     XYZ        1  0.1351900 51.50991
      # 3:     ABC        2 -0.1351900 51.51074
      # 4:     XYZ        2  0.1351200 51.51079
      
      library( sf )
      library(dplyr)
      library(leaflet)
      dt.points <- st_as_sf( dt.melt, coords = c("lon", "lat"), crs = 4326)
      
      dt.lines <- dt.points %>%
        group_by( line_id ) %>%
        summarise( geometry = st_combine( geometry ) ) %>%
        st_cast( "LINESTRING" )
      
      leaflet() %>% addTiles() %>% addPolylines( data = dt.lines, popup = ~line_id )
      

      【讨论】:

      • 不是真正的data.table 用户,但感谢完整的答案。
      猜你喜欢
      • 2021-07-12
      • 2016-03-06
      • 2021-08-20
      • 2023-02-06
      • 2021-05-16
      • 1970-01-01
      • 1970-01-01
      • 2023-02-05
      • 1970-01-01
      相关资源
      最近更新 更多