2013-01-02 3 views
1

In R Je souhaite convertir (coercer?) Un objet renvoyé de stats::spectrum (classe 'spec') en une nouvelle classe S4. La « spécification » de la classe S3 est essentiellement une liste de différentes informations avec les formats mixtes (je l'ai commenté la sortie de l'écran):Conversion de la classe S3 'spec' en nouvelle classe S4

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 

Maintenant, disons que nous définissons un nouveau générateur S4 pour une classe nommée « specS4 » , dans lequel les noms de sous sont les noms dans l'objet « spec »

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) 
) 

et générer un nouvel objet de celui-ci:

psd4 <- specS4() 

validObject(psd4) 
# [1] TRUE 

Quelle serait la meilleure façon d'attribuer à chaque composante de psd3 à son emplacement correspondant dans psd4? Une complication est que spectrum peut renvoyer NULL pour quelques champs (connus); l'affectation de ces valeurs entraînerait une erreur dans checkSlotAssignment (pour la représentation donnée).

Une solution douloureuse que j'ai est:

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 
    [email protected] <- psd$freq 
    [email protected] <- psd$spec 
    [email protected] <- psd$coh 
    [email protected] <- psd$phase 
    [email protected] <- psd$kernel 
    [email protected] <- psd$snames 
    # [more to assign, obviously] 
    # 
    # [run a validity check...] 
    # 
    return(S4spec) 
} 

qui fonctionne, même si as.specS4.spec est volontairement incomplète.

psd4c <- as.specS4(psd3) 

validObject(psd4c) 
# [1] TRUE 

Y at-il une meilleure façon d'atteindre ce as.specS4.spec fait? Cette solution semble précaire.

+0

Parce qu'il était si utile pour moi , voici la description de Hadley du système S4: https://github.com/hadley/devtools/wiki/S4 –

+0

Ma compréhension est que vous auriez besoin de définir «nouveau» et «comme» S4methods. Construire une classe S4 implique plus que de définir une structure et de créer une fonction 'as.name'. Pour le moment, je ne pense pas que vous ayez une méthode S4 'as'. Vous avez besoin de 'setMethod' pour créer une méthode S4. –

+0

Vous avez raison, mais 'as' et' setAs' sont pour les classes S4. Donc cela fonctionne: 'setClass ('spec', prototype = specS4()); SETA ("spec", "specS4", la fonction (de, à) { nouveau (à, fréq = de fréq $, spec = de $ spec, coh = as.numeric (de coh de $), phase de = as.numérique (à partir de $ phase), kernel = as.numérique (à partir de $ kernel), snames = en tant que caractère (à partir de $ snames)) }); as (psd3, 'specS4') 'mais comme vous pouvez le remarquer, j'ai encore besoin d'assigner manuellement des slots. À moins que je ne manque quelque chose, cela ne me sauve vraiment rien. –

Répondre

1

Je viens de me rendre compte à quel point c'est simple. Doh!

match slotNames dans l'objet « specS4 » avec names dans l'objet « spec », puis attribuer à slot (en supposant que le code dans la question a été exécuté):

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([email protected], [email protected], psd3$freq) 
# [1] TRUE 
Questions connexes