Tengo un marco de datos de 50 filas y 4 columnas. Quiero obtener muchos marcos de datos de muestra de 12 filas. Puede ser un millón de ellos y no quiero que mis dos marcos de datos de muestra sean iguales. He usado el siguiente código

    df_l <- list()
    for(i in 1:6000000) {
    set.seed(100+i)
    a <- df[sample(nrow(df),12,replace=T),]
    df_l[[i]] <- a
   rownames(df_l[[i]]) <- 1:12 
   }

Pero mi confusión es que esta podría no ser la forma eficiente de hacerlo y no sé si dos de los marcos de datos de muestra son iguales o no.

2
niranjan poudel 15 may. 2020 a las 10:43

3 respuestas

Sí, esto no es muy eficiente.

1) solo necesita establecer la semilla una vez.

2) R es un lenguaje interpretado, y es muy lento cuando se utilizan funciones básicas de ramificación (por ejemplo, si, para, mientras ...) u operaciones variables (por ejemplo, a <- a + 1). La interpretación cuesta tiempo en particular para funciones que solo toman poco tiempo. Entonces, desea llamar a una función que hace mucho a la vez, porque cuando se indica, es rápida, ya que probablemente se implementa de una manera mucho más rápida (generalmente se compilan).

Simplemente intercambiando el 1: 6000000 con 1:12 y tomando un vector de muestra de 6000000, esto hará que su programa se ejecute mucho más rápido. Solo necesita organizar cómo organizar los datos.

3) intente replicate

3
Stefan 15 may. 2020 a las 13:56

El enfoque que está intentando es bastante lento por un par de razones (descritas a continuación), y también implica una gran cantidad de duplicación de datos, que generalmente no es eficiente.

Primero, está usando un bucle para realizar su muestreo, que a menudo es lento en R. Casi siempre es mejor tratar de 'vectorizar' sus cálculos, lo que significa intentar hacerlos todos con el mismo comando base R. En este caso, podemos hacer el muestreo de filas para todas sus submuestras al mismo tiempo, y luego asignar las filas muestreadas a cada submuestra después.

En segundo lugar, está creando una lista: df_l que contiene una gran cantidad de repeticiones de sus datos originales df. Esto es un desperdicio (no es necesario que almacene potencialmente los mismos datos varias veces), y requiere mucho trabajo. De nuevo, casi siempre es mejor usar índices para acceder a los datos originales.

Entonces, juntando esto, podemos crear un enfoque más rápido que tampoco duplique los datos:

Primero, algunos datos de prueba:

df=data.frame(matrix(sample(1:200),ncol=4))

Ahora, en lugar de replicar los datos en una lista de muchos marcos de datos nuevos, solo creamos una matriz de índices muestreados:

make_index_samples=function(df,n) {
    return(matrix(sample(nrow(df),12*n,replace=T),nrow=n))
}
random_indices=make_index_samples(df,1000)

Entonces, ahora, en lugar de acceder al df muestreado aleatoriamente n usando df_l[[n]] (como en el ejemplo original), solo usamos:

my_random_df=df[random_indices[n,],]

Podemos usar microbenchmark para ver qué tan rápido es esto:

# (almost) original sampling
make_samples_original=function(df,n) {
    df_l=list()
    set.seed(123)
    for(i in seq_len(n)) {
        df_l[[i]]=df[sample(nrow(df),12,replace=T),]
    }
    return(df_l)
}

# compare making list of new dfs to making matrix of indices:
library(microbenchmark)
microbenchmark(make_samples_original(df,1000),make_index_samples(df,1000))
# Unit: microseconds
#                             expr        min          lq        mean      median         uq        max neval
#  make_samples_original(df, 1000) 103515.198 111525.9985 116499.0323 115045.9485 118883.329 200982.370   100
#     make_index_samples(df, 1000)    234.193    246.0805    307.6667    249.3815    300.382    755.873   100

Por lo tanto, muestrear los índices es aproximadamente 300 veces más rápido.

Ahora, con respecto a las muestras idénticas 'repetidas': como lo señaló @ThomasIsCoding, el número de muestras exactamente idénticas de 12 es muy grande (2e20), por lo que es poco probable que obtenga 'colisiones' perfectas.
Sin embargo, si considera que 'lo mismo' también incluye dos muestras con el mismo conjunto de filas, pero en un orden diferente, entonces hay solo 50^12/factorial(12) combinaciones, o 5e11. Esto puede parecer mucho, pero la 'paradoja del cumpleaños' (https://en.wikipedia.org/wiki / Birthday_problem) muestra que solo necesita muestrear unas 7e5 veces para tener al menos una 'colisión'.

Entonces, con 1 millón de aleatorizaciones, es probable que tenga una o dos muestras con el mismo conjunto de filas. Para muchas aplicaciones, es probable que esto no sea un gran problema. Si es para usted, puede verificar cada aleatorización para asegurarse de que no haya ocurrido anteriormente, pero esto podría deshacer la mayoría o todos los beneficios de un muestreo más rápido ...

De todos modos, aquí hay una forma de hacerlo:

Primero, hacemos algunas muestras aleatorias más de las que realmente queremos, para que podamos descartar cualquier duplicado y aún tener suficiente:

set.seed(123)
random_indices=make_index_samples(df,1000100) # 1 million +100 extra

Luego, construimos un nombre para cada muestra aleatoria que identifique de manera única las filas muestreadas dentro de ella, pero (en este caso) sin preocuparse por el orden de las filas:

random_index_names=apply(random_indices,1,function(row) paste(sort(row),collapse="_"))

Podemos verificar si hubo colisiones (que serán reveladas por nombres duplicados) y descartarlas:

sum(duplicated(random_index_names)) # I got 1 duplicate!  
random_indices.no_duplicates=random_indices[-duplicated(random_index_names),][1:1000000,]
0
Dominic van Essen 15 may. 2020 a las 22:00

Puedes probar el siguiente código:

  • sin reemplazo al tomar muestras
n <- nrow(df)
df_1 <- replicate(6000000,df[sample(n,12),],simplify = FALSE)
  • con reemplazo al tomar muestras
n <- nrow(df)
df_1 <- replicate(6000000,df[sample(n,12,replace = TRUE),],simplify = FALSE)

En cuanto a la preocupación de los mismos marcos de datos, depende del tamaño del espacio desde el que está muestreando. Para su caso

  • si no permite el reemplazo, el tamaño de su espacio es choose(50,12)*factorial(12), que es mucho mayor que 6000000. Por lo tanto, la probabilidad de colisión es baja.

  • si permite el reemplazo, el tamaño de su espacio es 50**12*factorial(12), que es incluso mayor que el escenario sin reemplazo. Por lo tanto, la probabilidad de colisión sería mucho menor.

2
ThomasIsCoding 15 may. 2020 a las 08:11