2014-09-18 9 views
12

Sto cercando un modo efficiente per creare ID numerici univoci per alcuni dati sintetici che sto generando.Come generare ID autoincrementante in R

In questo momento, ho semplicemente una funzione che emette e incrementa un valore da una variabile globale (vedere il codice demo di seguito). Tuttavia, questo è disordinato perché devo iniziare la variabile idCounter e preferisco non utilizzare le variabili globali, se possibile.

# Emit SSN 
idCounter = 0 
emitID = function(){ 
    # Turn into a formatted string 
    id = formatC(idCounter,width=9,flag=0,format="d") 

    # Increment id counter 
    idCounter <<- idCounter+1 

    return(id) 
} 
record$id = emitID() 

Il pacchetto fornisce funzionalità uuid vicino a quello che voglio, ma ho bisogno gli ID di essere solo numeri interi. Eventuali suggerimenti? Forse un modo per convertire il valore UUID in un valore numerico di qualche tipo? Ovviamente alcune collisioni si verificherebbero ma probabilmente sarebbe ok. Penso che, al massimo, avrei bisogno di 1 miliardo di valori.

Grazie per eventuali suggerimenti!

-Rob

+2

esigenze contraddittorie: "unico", "Ovviamente alcune collisioni si sarebbe verificato, ma che probabilmente sarebbe ok" –

risposta

22

Una versione non globale del contatore utilizza scope lessicale per incapsulare idCounter con la funzione di incremento

emitID <- local({ 
    idCounter <- -1L 
    function(){ 
     idCounter <<- idCounter + 1L      # increment 
     formatC(idCounter, width=9, flag=0, format="d") # format & return 
    } 
}) 

e poi

> emitID() 
[1] "000000000" 
> emitID1() 
[1] "000000001" 
> idCounter <- 123 ## global variable, not locally scoped idCounter 
> emitID() 
[1] "000000002" 

Un divertente alternativa è quella di utilizzare un modello 'factory' per creare contatori indipendenti. La tua domanda implica che chiamerai questa funzione un miliardo (hmm, non sono sicuro di dove ho avuto quell'impressione ...) volte, quindi forse ha senso convalidare la chiamata a formatC creando un buffer di id?

idFactory <- function(buf_n=1000000) { 
    curr <- 0L 
    last <- -1L 
    val <- NULL 
    function() { 
     if ((curr %% buf_n) == 0L) { 
      val <<- formatC(last + seq_len(buf_n), width=9, flag=0, format="d") 
      last <<- last + buf_n 
      curr <<- 0L 
     } 
     val[curr <<- curr + 1L] 
    } 
} 
emitID2 <- idFactory() 

e poi (emitID1 è un'istanza della versione variabile locale sopra).

> library(microbenchmark) 
> microbenchmark(emitID1(), emitID2(), times=100000) 
Unit: microseconds 
     expr min  lq median  uq  max neval 
emitID1() 66.363 70.614 72.310 73.603 13753.96 1e+05 
emitID2() 2.240 2.982 4.138 4.676 49593.03 1e+05 
> emitID1() 
[1] "000100000" 
> emitID2() 
[1] "000100000" 

(la soluzione proto è circa 3 volte più lento di emitID1, anche se la velocità non è tutto).

+0

+1 per 'local'. Non credo di aver mai visto qualcuno che lo abbia mai usato prima. –

+0

@BrandonBertelsen Sono d'accordo Non l'ho visto per questo scopo, ma ho usato locale negli script di avvio. – Dason

+0

Non ho mai visto lo scope locale prima - molto bello! Fa esattamente quello che stavo cercando di fare. Inoltre, l'opzione factory è ottima, poiché desidero generare diversi set di ID indipendenti. Grazie mille per il tuo aiuto :) – Rob

5

Mi piace utilizzare il pacchetto proto per la piccola programmazione OO. Sotto il cofano, utilizza gli ambienti in modo simile a quello illustrato da Martin Morgan.

# this defines your class 
library(proto) 
Counter <- proto(idCounter = 0L) 
Counter$emitID <- function(self = .) { 
    id <- formatC(self$idCounter, width = 9, flag = 0, format = "d") 
    self$idCounter <- self$idCounter + 1L 
    return(id) 
} 

# This creates an instance (or you can use `Counter` directly as a singleton) 
mycounter <- Counter$proto() 

# use it: 
mycounter$emitID() 
# [1] "000000000" 
mycounter$emitID() 
# [1] "000000001" 
Problemi correlati