2014-09-18 13 views
12

Poszukuję skutecznego sposobu na tworzenie niepowtarzalnych, numerycznych identyfikatorów dla niektórych syntetycznych danych, które generuję.Jak wygenerować auto-inkrementujące ID w R

W tej chwili po prostu mam funkcję, która emituje i zwiększa wartość zmiennej globalnej (patrz kod demo poniżej). Jest to jednak kłopotliwe, ponieważ muszę zainicjować zmienną idCounter i wolałbym nie używać zmiennych globalnych, jeśli to możliwe.

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

Pakiet uuid zapewnia funkcjonalność blisko tego, co chcę, ale muszę identyfikatory być tylko liczbami całkowitymi. Jakieś sugestie? Być może sposób przekonwertować wartość UUID na jakąś wartość liczbową? Oczywiście zdarzają się kolizje, ale prawdopodobnie będzie dobrze. Myślę, że potrzebuję co najwyżej miliarda wartości.

Dzięki za wszelkie sugestie!

-Rob

+2

sprzeczne wymagania: „wyjątkowy”, „Oczywiście niektóre kolizje byłoby wystąpić, ale to pewnie będzie ok” –

Odpowiedz

22

Non-Global wersja licznika wykorzystuje zakres leksykalny do hermetyzacji idCounter z funkcją przyrostu

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

a następnie

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

zabawy Alternatywą jest użycie wzór fabryczny do tworzenia niezależnych liczników. Twoje pytanie sugeruje, że nazwiesz tę funkcję miliardem (hmm, nie wiem, skąd brałem to wrażenie ...) razy, więc może warto wywołać wektoryzację wywołania formatC, tworząc bufor identyfikatorów?

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

, a następnie (emitID1 jest instancją powyższej wersji zmiennej lokalnej).

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

(rozwiązanie proto jest około 3x wolniej niż emitID1, choć szybkość to nie wszystko).

+0

+1 dla 'local'. Nie myśl, że kiedykolwiek widziałem, żeby ktokolwiek kiedyś go używał. –

+0

@BrandonBertelsen Zgodzę się, że nie widziałem tego w tym celu, ale użyłem lokalnego w skryptach startowych. – Dason

+0

Nigdy wcześniej nie widziałem miejscowego zakresu - bardzo fajne! Robi dokładnie to, co próbowałem zrobić. Ponadto opcja fabryczna jest świetna, ponieważ chcę wygenerować kilka niezależnych zestawów identyfikatorów. Bardzo dziękuję za pomoc :) – Rob

5

Chciałbym użyć pakietu proto dla małych programów OO. Pod maską wykorzystuje środowiska w podobny sposób do tego, co zilustrował 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" 
Powiązane problemy