r split

¿Cuál es el algoritmo detrás de la función `split ''de R core?



(1)

split es una función especialmente importante en el núcleo R Muchas de las respuestas de Stack Overflow que ofrecen soluciones R-base en la manipulación de datos dependen de ello. Es la rutina workhorse de cualquier operación grupal.

También hay muchas preguntas cuya solución es solo una línea con split . Mucha gente no sabe eso.

  • split.data.frame puede dividir una matriz por fila;
  • split.default puede dividir un cuadro de datos por columna.

Tal vez la documentación de R en split no lo está haciendo muy bien. Menciona el primer uso, pero no menciona el segundo.

Hay cuatro métodos para split en el núcleo R:

methods(split) #[1] split.data.frame split.Date split.default split.POSIXct

Proporcionaré una respuesta explicando en profundidad cómo funcionan split.data.frame , split.default y el nivel .Internal(split(x, f)) . Otras respuestas son bienvenidas en el objeto "Fecha" y "POSIXct".


¿Cómo funciona split.data.frame ?

function (x, f, drop = FALSE, ...) lapply(split(x = seq_len(nrow(x)), f = f, drop = drop, ...), function(ind) x[ind, , drop = FALSE])

Llama a split.default para dividir el vector de índice de la fila seq_len(nrow(x)) , luego usa un bucle lapply para extraer las filas asociadas en una entrada de lista.

Esto no es estrictamente un método "data.frame". Divide cualquier objeto bidimensional por la primera dimensión, incluida la división de una matriz por filas .

¿Cómo funciona split.default ?

function (x, f, drop = FALSE, sep = ".", lex.order = FALSE, ...) { if (!missing(...)) .NotYetUsed(deparse(...), error = FALSE) if (is.list(f)) f <- interaction(f, drop = drop, sep = sep, lex.order = lex.order) else if (!is.factor(f)) f <- as.factor(f) else if (drop) f <- factor(f) storage.mode(f) <- "integer" if (is.null(attr(x, "class"))) return(.Internal(split(x, f))) lf <- levels(f) y <- vector("list", length(lf)) names(y) <- lf ind <- .Internal(split(seq_along(x), f)) for (k in lf) y[[k]] <- x[ind[[k]]] y }

  1. si x no tiene clases (es decir, principalmente un vector atómico), se .Internal(split(x, f)) ;
  2. de lo contrario, usa .Internal(split()) para dividir el índice a lo largo de x , luego usa un bucle for para extraer elementos asociados en una entrada de lista.

Un vector atómico (ver ?vector ) es un vector con el siguiente modo:

  • "lógico", "entero", "numérico", "complejo", "carácter" y "en bruto"
  • "lista"
  • "expresión"

Un objeto con clase ... Er ... hay tantos !! Permítanme darles tres ejemplos:

  • "factor"
  • "marco de datos"
  • "matriz"

En mi opinión, el split.default no está bien escrito. Hay tantos objetos con clases, pero split.default trataría de la misma manera a través de "[" . Esto funciona bien con "factor" y "data.frame" (¡así que dividiremos el marco de datos a lo largo de las columnas!), Pero definitivamente no funciona con una matriz de la forma que esperamos.

A <- matrix(1:9, 3) # [,1] [,2] [,3] #[1,] 1 4 7 #[2,] 2 5 8 #[3,] 3 6 9 split.default(A, c(1, 1, 2)) ## it does not split the matrix by columns! #$`1` #[1] 1 2 4 5 7 8 # #$`2` #[1] 3 6 9

En realidad, la regla de reciclaje se ha aplicado a c(1, 1, 2) y estamos haciendo lo mismo:

split(c(A), rep_len(c(1,1,2), length(A)))

¿Por qué el núcleo R no escribe otra línea para una "matriz", como

for (k in lf) y[[k]] <- x[, ind[[k]], drop = FALSE]

Hasta ahora, la única forma de dividir de forma segura una matriz por columnas es transponerla, luego split.data.frame y luego otra transposición.

lapply(split.data.frame(t(A), c(1, 1, 2)), t)

Otra solución a través de lapply(split.default(data.frame(A), c(1, 1, 2)), as.matrix) tiene errores si A es una matriz de caracteres.

¿Cómo funciona .Internal(split(x, f)) ?

¡Este es realmente el núcleo del núcleo! Tomaré un pequeño ejemplo a continuación para explicación:

set.seed(0) f <- sample(factor(letters[1:3]), 10, TRUE) # [1] c a b b c a c c b b #Levels: a b c x <- 0:9

Básicamente hay 3 pasos. Para mejorar la legibilidad, se proporciona un código R equivalente para cada paso.

paso 1: tabulación (conteo de ocurrencia de cada nivel de factor)

## a factor has integer mode so `tabulate` works tab <- tabulate(f, nbins = nlevels(f)) [1] 2 4 4

paso 2: asignación de almacenamiento de la lista resultante

result <- vector("list", nlevels(f)) for (i in 1:length(tab)) result[[i]] <- vector(mode(x), tab[i]) names(result) <- levels(f)

Anotaría esta lista de la siguiente manera, donde cada línea es un elemento de lista que es un vector en este ejemplo, y cada [ ] es un marcador de posición para una entrada de ese vector.

$a: [ ] [ ] $b: [ ] [ ] [ ] [ ] $c: [ ] [ ] [ ] [ ]

paso 3: asignación de elementos

Ahora es útil para descubrir el modo de entero interno para un factor:

.f <- as.integer(f) #[1] 3 1 2 2 3 1 3 3 2 2

Necesitamos escanear x y .f , rellenando x[i] en la entrada correcta del result[[.f[i]]] , Informado por un vector de búfer acumulador.

ab <- integer(nlevels(f)) ## accumulator buffer for (i in 1:length(.f)) { fi <- .f[i] counter <- ab[fi] + 1L result[[fi]][counter] <- x[i] ab[fi] <- counter }

En la siguiente ilustración, ^ es un puntero a los elementos a los que se accede o actualiza.

## i = 1 x: [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] .f: [3] [1] [2] [2] [3] [1] [3] [3] [2] [2] ^ ab: [0] [0] [0] ## on entry ^ $a: [ ] [ ] $b: [ ] [ ] [ ] [ ] $c: [0] [ ] [ ] [ ] ^ ab: [0] [0] [1] ## on exit ^

## i = 2 x: [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] .f: [3] [1] [2] [2] [3] [1] [3] [3] [2] [2] ^ ab: [0] [0] [1] ## on entry ^ $a: [1] [ ] ^ $b: [ ] [ ] [ ] [ ] $c: [0] [ ] [ ] [ ] ab: [1] [0] [1] ## on exit ^

## i = 3 x: [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] .f: [3] [1] [2] [2] [3] [1] [3] [3] [2] [2] ^ ab: [1] [0] [1] ## on entry ^ $a: [1] [ ] $b: [2] [ ] [ ] [ ] ^ $c: [0] [ ] [ ] [ ] ab: [1] [1] [1] ## on exit ^

## i = 4 x: [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] .f: [3] [1] [2] [2] [3] [1] [3] [3] [2] [2] ^ ab: [1] [1] [1] ## on entry ^ $a: [1] [ ] $b: [2] [3] [ ] [ ] ^ $c: [0] [ ] [ ] [ ] ab: [1] [2] [1] ## on exit ^

## i = 5 x: [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] .f: [3] [1] [2] [2] [3] [1] [3] [3] [2] [2] ^ ab: [1] [2] [1] ## on entry ^ $a: [1] [ ] $b: [2] [3] [ ] [ ] $c: [0] [4] [ ] [ ] ^ ab: [1] [2] [2] ## on exit ^

## i = 6 x: [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] .f: [3] [1] [2] [2] [3] [1] [3] [3] [2] [2] ^ ab: [1] [2] [2] ## on entry ^ $a: [1] [5] ^ $b: [2] [3] [ ] [ ] $c: [0] [4] [ ] [ ] ab: [2] [2] [2] ## on exit ^

## i = 7 x: [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] .f: [3] [1] [2] [2] [3] [1] [3] [3] [2] [2] ^ ab: [2] [2] [2] ## on entry ^ $a: [1] [5] $b: [2] [3] [ ] [ ] $c: [0] [4] [6] [ ] ^ ab: [2] [2] [3] ## on exit ^

## i = 8 x: [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] .f: [3] [1] [2] [2] [3] [1] [3] [3] [2] [2] ^ ab: [2] [2] [3] ## on entry ^ $a: [1] [5] $b: [2] [3] [ ] [ ] $c: [0] [4] [6] [7] ^ ab: [2] [2] [4] ## on exit ^

## i = 9 x: [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] .f: [3] [1] [2] [2] [3] [1] [3] [3] [2] [2] ^ ab: [2] [2] [4] ## on entry ^ $a: [1] [5] $b: [2] [3] [8] [ ] ^ $c: [0] [4] [6] [7] ab: [2] [3] [4] ## on exit ^

## i = 10 x: [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] .f: [3] [1] [2] [2] [3] [1] [3] [3] [2] [2] ^ ab: [2] [3] [4] ## on entry ^ $a: [1] [5] $b: [2] [3] [8] [9] ^ $c: [0] [4] [6] [7] ab: [2] [4] [4] ## on exit ^