2016-08-29 12 views
6

Chcę przekształcić ramkę danych w inną ramkę danych. Jeśli to możliwe, w mniej komendach, używanie dplyr lub tidyr byłoby świetne.przekształć współrzędne json w json.frame

Aby przeanalizować listę współrzędnych użyłem library(rjson), ta część jest w porządku, ale nie mogę dalej manipulować listą, aby uzyskać mój wynik.

Gdyby można uniknąć stosując dowolny for oświadczenie byłoby świetnie, ale każde rozwiązanie jest dobre, jak długo, jak rozwiązać ten problem :)

Wejście:

df <- data.frame(code = c("12000", "89000"), 
       polygon = c("[[[11,12], [13,14], [15,16]], [[21, 22], [23,24], [25,26]]]", 
          "[[[81,82], [83,84], [85,86]]]")) 
df 

> df 
    code              polygon 
1 12000 [[[11,12], [13,14], [15,16]], [[21, 22], [23,24], [25,26]]] 
2 89000        [[[81,82], [83,84], [85,86]]] 

dane wejściowe Opis:

  • kolumna code zawiera kod pocztowy
  • kolumna polygon zawiera jeden lub więcej wielokątów zdefiniowane przez ich par szerokość-długość geograficzną punktów

Wyjście Poszukiwany

> wanted 
     a lon lat id 
1 12000 11 12 1 
2 12000 13 14 1 
3 12000 15 16 1 
4 12000 21 22 2 
5 12000 23 24 2 
6 12000 25 26 2 
7 89000 81 82 1 
8 89000 83 84 1 
9 89000 85 86 1 

chcę wykreślić chciał data.frame korzystając ggplot.

+2

może można finagle to do pracy: https://cran.r-project.org/web/packages/tidyjson /vignettes/introduction-to-tidyjson.html – MichaelChirico

+0

Nie można znaleźć metody użycia 'tidyjson' z tablicami/właściwościami bez nazwy. Jak w takim przypadku użyjesz 'spread_values'? – Rentrop

Odpowiedz

6

purrr, dplyr i jsonlite rozwiązanie:

df <- data.frame(code = c("12000", "89000"), 
       polygon = c("[[[11,12], [13,14], [15,16]], [[21, 22], [23,24], [25,26]]]", 
          "[[[81,82], [83,84], [85,86]]]"), 
       stringsAsFactors=FALSE) 

library(purrr) 
library(dplyr) 
library(jsonlite) 

make_coords <- function(x) { 
    fromJSON(x$polygon, simplifyMatrix=FALSE) %>% 
    map_df(~map_df(., ~setNames(as.data.frame(as.list(.)), c("lat", "lon"))), .id="id") 
} 

group_by(df, a=code) %>% 
    do(make_coords(.)) %>% 
    ungroup() %>% 
    select(a, lat, lon, id) 
## # A tibble: 9 x 4 
##  a lat lon id 
## <chr> <int> <int> <chr> 
## 1 12000 11 12  1 
## 2 12000 13 14  1 
## 3 12000 15 16  1 
## 4 12000 21 22  2 
## 5 12000 23 24  2 
## 6 12000 25 26  2 
## 7 89000 81 82  1 
## 8 89000 83 84  1 
## 9 89000 85 86  1 

Ma to dodatkową zaletę walidacji dane wielokąta, ponieważ twój przykład ha [ds] nieprawidłowy JSON (musiałem edytować ostateczny ] w początkowym przykładzie).

UWAGI:

  1. group_by mogą być zastąpione przez dplyr::rowwise lub (w niektórych innych zmian w kodzie) przez purrr::by_row
  2. Idiom jest iterację każdego code, konwertować JSON do listy współrzędnych, iteruj po tej liście i ustaw ramkę daty z każdego wielokąta i przypisz do niego identyfikator pozycyjny.
  3. Nazwy kolumn chcesz przypisane są w trzech miejscach: początkowa group_by (aby włączyć code do a), najgłębsza map_df (dla lat & lon) i wreszcie id który jest automatycznie tworzony przez najdalszym map_df.

rowwise wersja:

make_coords2 <- function(x) { 
    fromJSON(x$polygon, simplifyMatrix=FALSE) %>% 
    map_df(~map_df(., ~setNames(as.data.frame(as.list(.)), c("lat", "lon"))), .id="id") %>% 
    mutate(a=x$a) 
} 

select(df, a=code, polygon) %>% 
    rowwise() %>% 
    do(make_coords2(.)) %>% 
    ungroup() %>% 
    select(a, lat, lon, id) 

by_row wersja:

make_coords3 <- function(x) { 
    fromJSON(x$polygon, simplifyMatrix=FALSE) %>% 
    map_df(~map_df(., ~setNames(as.data.frame(as.list(.)), c("lat", "lon"))), .id="id") 
} 

select(df, a=code, polygon) %>% 
    by_row(make_coords3, .collate="rows") %>% 
    select(a, lat, lon, id) 
+0

Wybrałem to rozwiązanie, ponieważ było mi łatwiej używać go w moim kodzie. Dziękuję Ci. :) – Costin

2

To nie jest ładne, ale niektóre połączenia do strsplit, gsub i unnest można zrobić całkiem sporo:

  • Dzielenie przez ]], pozwala nam oddzielne kilku wielokątów.
  • Następnie rozprowadzamy je po osobnych rzędach.
  • Tworzenie kolumna id można łatwo zrobić z row_number ramach każdego code
  • Splitu znowu na ], aby oddzielić par punktów.
  • Ponownie załóż kolejne rzędy.
  • Usuń wszystkie [ i ].
  • Oddziel na ,, aby oddzielić lon i lat.
  • Umieść je w osobnych kolumnach.

.

df %>% 
    mutate(polygon = strsplit(polygon, ']],')) %>% 
    unnest() %>% 
    group_by(code) %>% 
    mutate(id = row_number(), 
     polygon = strsplit(polygon, '],')) %>% 
    unnest() %>% 
    mutate(polygon = gsub(']|\\[', '', polygon), 
     polygon = strsplit(polygon, ','), 
     lon = sapply(polygon, '[', 1), 
     lat = sapply(polygon, '[', 2)) %>% 
    select(-polygon) 
Source: local data frame [9 x 4] 
Groups: code [2] 

    code id lon lat 
    <chr> <int> <chr> <chr> 
1 12000  1 11 12 
2 12000  1 13 14 
3 12000  1 15 16 
4 12000  2 21 22 
5 12000  2 23 24 
6 12000  2 25 26 
7 89000  1 81 82 
8 89000  1 83 84 
9 89000  1 85 86 
+0

Nie wiedziałem, że istnieje "unnest()". Ale używanie go z 'strsplit()' nie jest dla mnie w porządku, nie jestem pewien, że ']],' jest zawsze poprawnym separatorem. Naprawdę podoba mi się to rozwiązanie, a postaram się go jeszcze bardziej dostosować do moich prawdziwych danych. Dziękuję :) – Costin

1

myślę, że jest zbyt dużo nawias zamykający w df $ wielokąta [2].Jeśli tak jest usuwany, można wykonać następujące czynności:

require(jsonlite) 
require(reshape2) 
parse_json <- function(polygon, code){ 
    molten <- melt(fromJSON(polygon)) 
    lat <- molten[which(molten$Var3==1), "value"] 
    lon <- molten[which(molten$Var3==2), "value"] 
    id <- molten[which(molten$Var3==1), "Var1"] 
    data.frame(code, lat, lon, id) 
} 

dat_raw <- mapply(parse_json, df$polygon, df$code, SIMPLIFY = FALSE, USE.NAMES = FALSE) 
do.call(rbind, dat_raw) 

co daje:

code lat lon id 
1 12000 11 12 1 
2 12000 21 22 2 
3 12000 13 14 1 
4 12000 23 24 2 
5 12000 15 16 1 
6 12000 25 26 2 
7 89000 81 82 1 
8 89000 83 84 1 
9 89000 85 86 1 
+0

Dziękuję @ Floo0. Nie użyłem twojego rozwiązania, ponieważ używa ono 'reshape2' i nigdy nie rozumiałem jak działa' melt() '. Ale może jest to dobra okazja, aby zacząć odkrywać pakiet. Przepraszam! :) – Costin

Powiązane problemy