Правильний спосіб обмеження просторових полігонівDataFrames з однаковими ідентифікаторами полігону?


22

Яка правильна ідіома R для зв’язування SPDF разом, коли ідентифікатори перетинаються? Зауважте, що тут (як це часто буває) ідентифікатори в основному безглузді, тому це дуже дратує, що я не можу просто змусити rbind ігнорувати їх ....

library(sp)
library(UScensus2000)
library(UScensus2000tract)

data(state) # for state names
states <- gsub( " ", "_", tolower(state.name) )
datanames <- paste(states,"tract", sep=".")
data( list=datanames )
lst <- lapply(datanames,get)

nation <- do.call( rbind, lst )
Error in validObject(res) : 
  invalid class SpatialPolygons object: non-unique Polygons ID slot values

# This non-exported function designed to solve this doesn't seem to work any more.
d <- sp:::makeUniqueIDs( list(arizona.tract,delaware.tract) )
Error in slot(i, "ID") : 
  no slot of name "ID" for this object of class "SpatialPolygonsDataFrame"

Відповіді:


15

Ідентифікатори, слоти та функції застосування. Мої трійки найменш улюблених речей, які абсолютно необхідні для всього, що я роблю. Я думав, що відповім лише для того, щоб створити більше вмісту на цю тему.

Код нижче працює, але він зберігає "марні" значення ідентифікатора. Для кращого коду знадобиться час, щоб проаналізувати речі, щоб кожен тракт мав штат FIPS, округ FIPS та FIPS тракту як свій ідентифікатор. Ще кілька рядків, щоб це сталося, але оскільки ви не переймаєтесь ідентифікаторами, ми покинемо це.

#Your Original Code
library(sp)
library(UScensus2000)
library(UScensus2000tract)

data(state) # for state names
states <- gsub( " ", "_", tolower(state.name) )
datanames <- paste(states,"tract", sep=".")
data( list=datanames )
lst <- lapply(datanames,get)

#All good up to here, but we need to create unique ID's before rbind

#Modified from Roger Bivand's response at:
# https://stat.ethz.ch/pipermail/r-sig-geo/2007-October/002701.html

#For posterity: We can access the ID in two ways:
class(alaska.tract)
getSlots(class(alaska.tract))
class(slot(alaska.tract, "polygons")[[1]])
getSlots(class(slot(alaska.tract, "polygons")[[1]]))

#So to get all ID's
sapply(slot(alaska.tract, "polygons"), function(x) slot(x, "ID"))
#or
rownames(as(alaska.tract, "data.frame"))
#These should be the same, but they are quite different...sigh. Doesn't matter for
#what follows though

#To make them uniform we can write a function using the spChFIDs function from sp:
makeUniform<-function(SPDF){
  pref<-substitute(SPDF)  #just putting the file name in front.
  newSPDF<-spChFIDs(SPDF,as.character(paste(pref,rownames(as(SPDF,"data.frame")),sep="_")))
  return(newSPDF)
}

#now to do this for all of our state files
newIDs<-lapply(lst,function(x) makeUniform(x))

#back to your code...
nation <- do.call( rbind, newIDs )

Спасибі. Я мав намір перевірити це вже кілька днів, але життя втрутилося. Я дуже вражений, що це багато рядків коду. Як ви вважаєте, чи варто було б надіслати патч методом SPDF rbindв spпакеті? Я думав перетворити щось на зразок цього коду в ,deduplicateIDs=TRUEаргумент методу ....
Арі Б. Фрідман

Насправді лише три рядки коду для функції та один, який слід застосувати перед попередньою зв'язкою, але обробка вашої проблеми потребує певного часу. Я завжди вважав, що обробка ідентифікатора в SPDF є проблемою (будь-коли, коли я щось завантажую, наприклад, з rgdal), але Роджер Біванд завжди здається, що може змусити їх вести себе, тому я просто припустив, що це мій власний недолік. Мені подобається ідея патчу, але цікаво, чи отримати доступ до цих слотів може спричинити ускладнення для інших речей.
csfowler

Чудова відповідь. Просто хочу додати слово ради іншим, що коли rbind застрягає в моєму коді, це зазвичай через попередню помилку (в результаті чого дублюючі ідентифікатори). Тож помилка правильна.
Кріс

20

Це ще простіший підхід:

x <- rbind(x1, x2, x3, makeUniqueIDs = TRUE)  

1
Я б хотів, щоб це було зафіксовано на сторінці допомоги rbind. Я маю шукати тут кожен раз, коли не можу згадати правила обсадки, які вони використовували для цього аргументу. Найкраща відповідь напевно. Я не думаю, що це потребує більше контексту, і, безумовно, його не слід видаляти!
JMT2080AD

Документація пропонує "make.row.names = ІСТИНА)" ... яка, здається, не працює. Копіювання-вставка приклад зробив.
Мокс

Я думаю, що причина цього не задокументована у довідці полягає в тому, що ви здійснюєте виклик методу sp, коли ви передаєте об'єкт sp для зміни. Див methods(class = "SpatialLines"). Я не впевнений у цьому, але зараз я найкраще здогадуюсь. Я майже впевнений, що Едзер та співавтор. не підтримують сам rbind, отже, відсутність документації в rbind.
JMT2080AD

Що робити, якщо існує довгий перелік об'єктів для об’єднання ( x1, x2, x3, ..., xn)? Чи існує метод захоплення всього списку, не набираючи їх усіх?
Філ

Працює лише за умови, що кількість стовпців дорівнює.
Денніс

9

Добре, ось моє рішення. Пропозиції вітаються. Я, швидше за все, подаю це як виправлення, spякщо хтось не побачить очевидних упущень.

#' Get sp feature IDs
#' @aliases IDs IDs.default IDs.SpatialPolygonsDataFrame
#' @param x The object to get the IDs from
#' @param \dots Pass-alongs
#' @rdname IDs
IDs <- function(x,...) {
  UseMethod("IDs",x)
}
#' @method IDs default
#' @S3method IDs default
#' @rdname IDs
IDs.default <- function(x,...) {
  stop("Currently only SpatialPolygonsDataFrames are supported.")
}
#' @method IDs SpatialPolygonsDataFrame
#' @S3method IDs SpatialPolygonsDataFrame
#' @rdname IDs
IDs.SpatialPolygonsDataFrame <- function(x,...) {
  vapply(slot(x, "polygons"), function(x) slot(x, "ID"), "")
}

#' Assign sp feature IDs
#' @aliases IDs<- IDs.default<-
#' @param x The object to assign to
#' @param value The character vector to assign to the IDs
#' @rdname IDs<-
"IDs<-" <- function( x, value ) {
  UseMethod("IDs<-",x)
}
#' @method IDs<- SpatialPolygonsDataFrame
#' @S3method IDs<- SpatialPolygonsDataFrame
#' @rdname IDs<-
"IDs<-.SpatialPolygonsDataFrame" <- function( x, value) {
  spChFIDs(x,value)
}

#' rbind SpatialPolygonsDataFrames together, fixing IDs if duplicated
#' @param \dots SpatialPolygonsDataFrame(s) to rbind together
#' @param fix.duplicated.IDs Whether to de-duplicate polygon IDs or not
#' @return SpatialPolygonsDataFrame
#' @author Ari B. Friedman, with key functionality by csfowler on StackExchange
#' @method rbind.SpatialPolygonsDataFrame
#' @export rbind.SpatialPolygonsDataFrame
rbind.SpatialPolygonsDataFrame <- function(..., fix.duplicated.IDs=TRUE) {
  dots <- as.list(substitute(list(...)))[-1L]
  dots_names <- as.character(dots) # store names of objects passed in to ... so that we can use them to create unique IDs later on
  dots <- lapply(dots,eval)
  names(dots) <- NULL
  # Check IDs for duplicates and fix if indicated
  IDs_list <- lapply(dots,IDs)
  dups.sel <- duplicated(unlist(IDs_list))
  if( any(dups.sel) ) {
    if(fix.duplicated.IDs) {
      dups <- unique(unlist(IDs_list)[dups.sel])
      # Function that takes a SPDF, a string to prepend to the badID, and a character vector of bad IDs
      fixIDs <- function( x, prefix, badIDs ) {
        sel <-  IDs(x) %in% badIDs
        IDs(x)[sel] <- paste( prefix, IDs(x)[sel], sep="." )
        x
      }
      dots <- mapply(FUN=fixIDs , dots, dots_names, MoreArgs=list(badIDs=dups) )
    } else {
      stop("There are duplicated IDs, and fix.duplicated.IDs is not TRUE.")
    }
  }
  # One call to bind them all
  pl = do.call("rbind", lapply(dots, function(x) as(x, "SpatialPolygons")))
  df = do.call("rbind", lapply(dots, function(x) x@data))
  SpatialPolygonsDataFrame(pl, df)
}

1

Я оцінив деталізацію інших відповідей тут, і, спираючись на них, однолінійний я знайшов нижче. Як і ОП, я не дуже переймаюся значенням посвідчення особи, але наступне також може бути адаптоване для вбудовування більш інформативного ідентифікатора.

lst <- lapply(1:length(lst), function(i) spChFIDs(lst[[i]], paste0(as.character(i), '.', 1:length(lst[[i]]))))
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.