简体   繁体   中英

Convert 'spec' S3 class to new S4 class

In R I want to convert (coerce?) an object returned from stats::spectrum (class 'spec') into a new S4 class. The S3 class 'spec' is essentially a list of various information with mixed formats (I've commented the screen output):

psd3 <- spectrum(rnorm(1e3), plot=FALSE)
summary(psd3)
#           Length Class  Mode     
# freq      500    -none- numeric  
# spec      500    -none- numeric  
# coh         0    -none- NULL     
# phase       0    -none- NULL     
# kernel      0    -none- NULL     
# df          1    -none- numeric  
# bandwidth   1    -none- numeric  
# n.used      1    -none- numeric  
# orig.n      1    -none- numeric  
# series      1    -none- character
# snames      0    -none- NULL     
# method      1    -none- character
# taper       1    -none- numeric  
# pad         1    -none- numeric  
# detrend     1    -none- logical  
# demean      1    -none- logical 

class(unclass(psd3))
# [1] "list"

is.object(psd3) & !isS4(psd3)
# [1] TRUE

Now let's say we define a new, S4 generator for a class named 'specS4', wherein the slot names are the names in the 'spec' object

specS4 <- setClass("specS4",
  representation = representation(freq="numeric", spec="numeric", coh="numeric", phase="numeric", kernel="numeric", df="numeric", bandwidth="numeric", n.used="numeric", orig.n="numeric", series="character", snames="character", method="character", taper="numeric", pad="numeric", detrend="logical", demean="logical"), 
  prototype = prototype(coh=numeric(0), phase=numeric(0), kernel=numeric(0), df=Inf, snames="", detrend=FALSE, demean=FALSE)
)

and generate a new object from it:

psd4 <- specS4()

validObject(psd4)
# [1] TRUE

What would be the best way to assign each component of psd3 to its corresponding slot in psd4 ? A complication is that spectrum may return NULL for a few (known) fields; assigning these values would throw an error in checkSlotAssignment (for the representation given).

A painful solution I have is:

nonull.spec <- function(psd){
  stopifnot(inherits(psd, 'spec', FALSE))
  # as.numeric(NULL) --> numeric(0)
  # spec.pgram/.ar both may return NULL for these:
  psd$coh <- as.numeric(psd$coh)
  psd$phase <- as.numeric(psd$phase)
  psd$kernel <- as.numeric(psd$kernel)
  psd$snames <- as.character(psd$snames)
  return(psd)
}

as.specS4 <- function(psd) UseMethod("as.specS4")
as.specS4.spec <- function(psd){
  stopifnot(inherits(psd, 'spec', FALSE))
  ## deal with possible NULLs
  psd <- nonull.spec(psd)
  ## generate specS4 class
  S4spec <- specS4()
  ## (re)assign from 'spec' list
  S4spec@freq <- psd$freq
  S4spec@spec <- psd$spec
  S4spec@coh <- psd$coh
  S4spec@phase <- psd$phase
  S4spec@kernel <- psd$kernel
  S4spec@snames <- psd$snames
  # [more to assign, obviously]
  # 
  # [run a validity check...]
  #
  return(S4spec)
}

Which works, even though as.specS4.spec is intentionally incomplete.

psd4c <- as.specS4(psd3)

validObject(psd4c)
# [1] TRUE

Is there a better way to achieve what as.specS4.spec does? This solution seems precarious.

I just realized how simple this actually is. Doh!

Match slotNames in the 'specS4' object with names in the 'spec' object, then assign with slot (assumes code in the question has been run):

as.specS4.spec <- function(psd){
  stopifnot(inherits(psd, 'spec', FALSE))
  psd <- nonull.spec(psd)
  S4spec <- specS4() 
  spec_slots <- slotNames(S4spec)
  spec_slots <- spec_slots[match(names(psd), spec_slots)]
  for (what in spec_slots){
    slot(S4spec, what) <- as.vector(unlist(psd[what]))
  }
  return(S4spec)
}

psd4c2 <- as.specS4(psd3)

validObject(psd4c2)
# [1] TRUE
> all.equal(psd4c@freq, psd4c2@freq, psd3$freq)
# [1] TRUE

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM