Ricerca…


Ambienti come mappe di hash

Nota: nei passaggi successivi, i termini hash map e hash table sono usati in modo intercambiabile e si riferiscono allo stesso concetto , vale a dire una struttura dati che fornisce una efficiente ricerca di chiavi attraverso l'uso di una funzione hash interna.

introduzione

Sebbene R non fornisca una struttura di tabella hash nativa, è possibile ottenere funzionalità simili sfruttando il fatto che l'oggetto environment restituito da new.env (per impostazione predefinita) fornisce ricerche di chiavi con hash. Le seguenti due istruzioni sono equivalenti, poiché il parametro hash impostato su TRUE :

H <- new.env(hash = TRUE)
H <- new.env() 

Inoltre, si può specificare che la tabella hash interna sia pre-allocata con una dimensione particolare tramite il parametro size , che ha un valore predefinito di 29. Come tutti gli altri oggetti R, l' environment s gestisce la propria memoria e aumenterà di capacità come necessario quindi, anche se non è necessario richiedere un valore non predefinito per le size , potrebbe esserci un leggero vantaggio in termini di prestazioni se l'oggetto contiene (eventualmente) un numero molto elevato di elementi. Vale la pena notare che l'allocazione di spazio aggiuntivo tramite le size non comporta, di per sé, un oggetto con un ingombro di memoria maggiore:

object.size(new.env())
# 56 bytes

object.size(new.env(size = 10e4))
# 56 bytes 

Inserimento

L'inserimento di elementi può essere fatto utilizzando uno dei [[<- o $<- metodi forniti per la classe environment , ma non usando l'assegnazione "parentesi quadra" ( [<- ) :

H <- new.env()

H[["key"]] <- rnorm(1)

key2 <- "xyz"
H[[key2]] <- data.frame(x = 1:3, y = letters[1:3])

H$another_key <- matrix(rbinom(9, 1, 0.5) > 0, nrow = 3)

H["error"] <- 42
#Error in H["error"] <- 42 : 
#  object of type 'environment' is not subsettable 

Come altri aspetti di R, il primo metodo ( object[[key]] <- value ) è generalmente preferito al secondo ( object$key <- value ) perché nel primo caso, una variabile può essere usata al posto di un valore letterale (ad es. key2 nell'esempio sopra).

Come generalmente accade con le implementazioni delle mappe hash, l'oggetto environment non memorizza le chiavi duplicate. Il tentativo di inserire una coppia chiave-valore per una chiave esistente sostituirà il valore memorizzato in precedenza:

H[["key3"]] <- "original value"

H[["key3"]] <- "new value"

H[["key3"]]
#[1] "new value"

Ricerca chiave

Allo stesso modo, è possibile accedere agli elementi con [[ o $ , ma non con [ :

H[["key"]]
#[1] 1.630631
 
H[[key2]]   ## assuming key2 <- "xyz"
#   x y
# 1 1 a
# 2 2 b
# 3 3 c

H$another_key
#       [,1]  [,2]  [,3]
# [1,]  TRUE  TRUE  TRUE
# [2,] FALSE FALSE FALSE
# [3,]  TRUE  TRUE  TRUE

H[1]
#Error in H[1] : object of type 'environment' is not subsettable

Ispezione della mappa hash

Essendo solo un environment ordinario, la mappa hash può essere ispezionata con i mezzi tipici:

names(H)
#[1] "another_key" "xyz"         "key"         "key3"       

ls(H)
#[1] "another_key" "key"         "key3"        "xyz"        
 
str(H)
#<environment: 0x7828228> 
 
ls.str(H)
# another_key :  logi [1:3, 1:3] TRUE FALSE TRUE TRUE FALSE TRUE ...
# key :  num 1.63
# key3 :  chr "new value"
# xyz : 'data.frame':    3 obs. of  2 variables:
#  $ x: int  1 2 3
#  $ y: chr  "a" "b" "c"

Gli elementi possono essere rimossi usando rm :

rm(list = c("key", "key3"), envir = H)

ls.str(H)
# another_key :  logi [1:3, 1:3] TRUE FALSE TRUE TRUE FALSE TRUE ...
# xyz : 'data.frame':    3 obs. of  2 variables:
#  $ x: int  1 2 3
#  $ y: chr  "a" "b" "c"

Flessibilità

Uno dei principali vantaggi dell'utilizzo di oggetti di environment come tabelle hash è la loro capacità di memorizzare virtualmente qualsiasi tipo di oggetto, anche altri environment :

H2 <- new.env()

H2[["a"]] <- LETTERS
H2[["b"]] <- as.list(x = 1:5, y = matrix(rnorm(10), 2))
H2[["c"]] <- head(mtcars, 3)
H2[["d"]] <- Sys.Date()
H2[["e"]] <- Sys.time()
H2[["f"]] <- (function() {
    H3 <- new.env()
    for (i in seq_along(names(H2))) {
        H3[[names(H2)[i]]] <- H2[[names(H2)[i]]]
    }
    H3
})()

ls.str(H2)
# a :  chr [1:26] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" ...
# b : List of 5
#  $ : int 1
#  $ : int 2
#  $ : int 3
#  $ : int 4
#  $ : int 5
# c : 'data.frame':    3 obs. of  11 variables:
#  $ mpg : num  21 21 22.8
#  $ cyl : num  6 6 4
#  $ disp: num  160 160 108
#  $ hp  : num  110 110 93
#  $ drat: num  3.9 3.9 3.85
#  $ wt  : num  2.62 2.88 2.32
#  $ qsec: num  16.5 17 18.6
#  $ vs  : num  0 0 1
#  $ am  : num  1 1 1
#  $ gear: num  4 4 4
#  $ carb: num  4 4 1
# d :  Date[1:1], format: "2016-08-03"
# e :  POSIXct[1:1], format: "2016-08-03 19:25:14"
# f : <environment: 0x91a7cb8> 

ls.str(H2$f)
# a :  chr [1:26] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" ...
# b : List of 5
#  $ : int 1
#  $ : int 2
#  $ : int 3
#  $ : int 4
#  $ : int 5
# c : 'data.frame':    3 obs. of  11 variables:
#  $ mpg : num  21 21 22.8
#  $ cyl : num  6 6 4
#  $ disp: num  160 160 108
#  $ hp  : num  110 110 93
#  $ drat: num  3.9 3.9 3.85
#  $ wt  : num  2.62 2.88 2.32
#  $ qsec: num  16.5 17 18.6
#  $ vs  : num  0 0 1
#  $ am  : num  1 1 1
#  $ gear: num  4 4 4
#  $ carb: num  4 4 1
# d :  Date[1:1], format: "2016-08-03"
# e :  POSIXct[1:1], format: "2016-08-03 19:25:14"

limitazioni

Uno dei principali limiti dell'utilizzo di oggetti di environment come mappe hash è che, a differenza di molti aspetti di R, la vettorizzazione non è supportata per la ricerca / inserimento di elementi:

names(H2)
#[1] "a" "b" "c" "d" "e" "f"

H2[[c("a", "b")]]
#Error in H2[[c("a", "b")]] : 
#  wrong arguments for subsetting an environment
 
Keys <- c("a", "b")
H2[[Keys]]
#Error in H2[[Keys]] : wrong arguments for subsetting an environment

A seconda della natura dei dati memorizzati nell'oggetto, può essere possibile utilizzare vapply o list2env per assegnare più elementi contemporaneamente:

E1 <- new.env()
invisible({
    vapply(letters, function(x) {
        E1[[x]] <- rnorm(1)
        logical(0)
    }, FUN.VALUE = logical(0))
})

all.equal(sort(names(E1)), letters)
#[1] TRUE

Keys <- letters
E2 <- list2env(
    setNames(
        as.list(rnorm(26)),
        nm = Keys), 
    envir = NULL,
    hash = TRUE
)

all.equal(sort(names(E2)), letters)
#[1] TRUE

Nessuno dei precedenti è particolarmente conciso, ma può essere preferibile utilizzare un ciclo for , ecc. Quando il numero di coppie chiave-valore è elevato.

Pacchetto: hash

Il pacchetto hash offre una struttura hash in R. Tuttavia, i termini di sincronizzazione per entrambi gli inserimenti e le letture si confrontano sfavorevolmente con gli ambienti di utilizzo come hash. Questa documentazione riconosce semplicemente la sua esistenza e fornisce un codice temporale di esempio per i motivi sopra indicati. Non esiste un caso identificato in cui l'hash è una soluzione appropriata nel codice R oggi.

Tenere conto:

# Generic unique string generator
unique_strings <- function(n){
    string_i <- 1
    string_len <- 1
    ans <- character(n)
    chars <- c(letters,LETTERS)
    new_strings <- function(len,pfx){
    for(i in 1:length(chars)){
        if (len == 1){
        ans[string_i] <<- paste(pfx,chars[i],sep='')
        string_i <<- string_i + 1
        } else {
        new_strings(len-1,pfx=paste(pfx,chars[i],sep=''))
        }
        if (string_i > n) return ()
    }
    }
    while(string_i <= n){
    new_strings(string_len,'')
    string_len <- string_len + 1
    }
    sample(ans)
}

# Generate timings using an enviornment
timingsEnv <- plyr::adply(2^(10:15),.mar=1,.fun=function(i){
    strings <- unique_strings(i)
    ht1 <- new.env(hash=TRUE)
    lapply(strings, function(s){ ht1[[s]] <<- 0L})
    data.frame(
    size=c(i,i),
    seconds=c(
        system.time(for (j in 1:i) ht1[[strings[j]]]==0L)[3]),
    type = c('1_hashedEnv')
    )
})

timingsHash <- plyr::adply(2^(10:15),.mar=1,.fun=function(i){
    strings <- unique_strings(i)
    ht <- hash::hash()
    lapply(strings, function(s) ht[[s]] <<- 0L)
    data.frame(
    size=c(i,i),
    seconds=c(
        system.time(for (j in 1:i) ht[[strings[j]]]==0L)[3]),
    type = c('3_stringHash')
    )
})

Pacchetto: listenv

Sebbene package:listenv implementa un'interfaccia list-like per gli ambienti, le sue prestazioni relative agli ambienti per scopi hash sono scarse per il recupero hash . Tuttavia, se gli indici sono numerici, può essere abbastanza veloce durante il recupero. Tuttavia, hanno altri vantaggi, ad esempio la compatibilità con il package:future . Coprire questo pacchetto per questo scopo va oltre lo scopo del tema attuale. Tuttavia, il codice temporale fornito qui può essere utilizzato insieme all'esempio per il pacchetto: hash per i tempi di scrittura.

timingsListEnv <- plyr::adply(2^(10:15),.mar=1,.fun=function(i){
    strings <- unique_strings(i)
    le <- listenv::listenv()
    lapply(strings, function(s) le[[s]] <<- 0L)
    data.frame(
    size=c(i,i),
    seconds=c(
        system.time(for (k in 1:i) le[[k]]==0L)[3]),
    type = c('2_numericListEnv')
    )
})


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow