R Language
Codificación de longitud de ejecución
Buscar..
Observaciones
Una carrera es una secuencia consecutiva de valores u observaciones repetidas. Para valores repetidos, la "codificación de longitud de ejecución" de R describe concisamente un vector en términos de sus ejecuciones. Considerar:
dat <- c(1, 2, 2, 2, 3, 1, 4, 4, 1, 1)
Tenemos una longitud de una carrera de 1s; luego una longitud de tres carreras de 2s; luego una carrera de una longitud de 3s; y así. La codificación de longitud de ejecución de R captura todas las longitudes y valores de las ejecuciones de un vector.
Extensiones
Una ejecución también puede referirse a observaciones consecutivas en datos tabulares. Si bien R no tiene una forma natural de codificarlos, se pueden manejar con rleid
desde el paquete data.table (actualmente un enlace sin salida) .
Codificación de longitud de ejecución con `rle`
La codificación de longitud de ejecución captura las longitudes de ejecuciones de elementos consecutivos en un vector. Considere un vector de ejemplo:
dat <- c(1, 2, 2, 2, 3, 1, 4, 4, 1, 1)
La función rle
extrae cada ejecución y su longitud:
r <- rle(dat)
r
# Run Length Encoding
# lengths: int [1:6] 1 3 1 1 2 2
# values : num [1:6] 1 2 3 1 4 1
Los valores para cada ejecución se capturan en r$values
:
r$values
# [1] 1 2 3 1 4 1
Esto captura la primera vez que vimos una serie de 1, luego una serie de 2, luego una serie de 3, luego una serie de 1, y así sucesivamente.
Las longitudes de cada ejecución se capturan en r$lengths
:
r$lengths
# [1] 1 3 1 1 2 2
Vemos que la ejecución inicial de 1 fue de longitud 1, la ejecución de 2 que siguió fue de longitud 3, y así sucesivamente.
Identificación y agrupación por corridas en base R
Uno podría querer agrupar sus datos por las ejecuciones de una variable y realizar algún tipo de análisis. Considere el siguiente conjunto de datos simple:
(dat <- data.frame(x = c(1, 1, 2, 2, 2, 1), y = 1:6))
# x y
# 1 1 1
# 2 1 2
# 3 2 3
# 4 2 4
# 5 2 5
# 6 1 6
La variable x
tiene tres ejecuciones: una corrida de longitud 2 con valor 1, una corrida de longitud 3 con valor 2 y una corrida de longitud 1 con valor 1. Podríamos calcular el valor medio de la variable y
en cada una de las corridas de la variable x
(estos valores medios son 1.5, 4 y 6).
En la base R, primero calcularíamos la codificación de longitud de ejecución de la variable x
usando rle
:
(r <- rle(dat$x))
# Run Length Encoding
# lengths: int [1:3] 2 3 1
# values : num [1:3] 1 2 1
El siguiente paso es calcular el número de ejecución de cada fila de nuestro conjunto de datos. Sabemos que el número total de ejecuciones es la length(r$lengths)
, y la longitud de cada ejecución es r$lengths
, por lo que podemos calcular el número de ejecución de cada una de nuestras carreras con rep
:
(run.id <- rep(seq_along(r$lengths), r$lengths))
# [1] 1 1 2 2 2 3
Ahora podemos usar tapply
para calcular el valor medio y
para cada ejecución agrupando en el id de ejecución:
data.frame(x=r$values, meanY=tapply(dat$y, run.id, mean))
# x meanY
# 1 1 1.5
# 2 2 4.0
# 3 1 6.0
Identificación y agrupación por ejecuciones en data.table.
El paquete data.table proporciona una forma conveniente de agrupar por ejecuciones en datos. Considere los siguientes datos de ejemplo:
library(data.table)
(DT <- data.table(x = c(1, 1, 2, 2, 2, 1), y = 1:6))
# x y
# 1: 1 1
# 2: 1 2
# 3: 2 3
# 4: 2 4
# 5: 2 5
# 6: 1 6
La variable x
tiene tres ejecuciones: una corrida de longitud 2 con valor 1, una corrida de longitud 3 con valor 2 y una corrida de longitud 1 con valor 1. Podríamos calcular el valor medio de la variable y
en cada una de las corridas de la variable x (estos valores medios son 1.5, 4 y 6).
La función data.table rleid
proporciona un id que indica el id de ejecución de cada elemento de un vector:
rleid(DT$x)
# [1] 1 1 2 2 2 3
Entonces, se puede agrupar fácilmente en este ID de ejecución y resumir los datos y
:
DT[,mean(y),by=.(x, rleid(x))]
# x rleid V1
# 1: 1 1 1.5
# 2: 2 2 4.0
# 3: 1 3 6.0
Codificación de longitud de ejecución para comprimir y descomprimir vectores
Los vectores largos con largas ejecuciones del mismo valor pueden comprimirse significativamente almacenándolos en su codificación de longitud de ejecución (el valor de cada ejecución y el número de veces que se repite ese valor). Como ejemplo, considere un vector de longitud 10 millones con un gran número de 1 y solo un pequeño número de 0:
set.seed(144)
dat <- sample(rep(0:1, c(1, 1e5)), 1e7, replace=TRUE)
table(dat)
# 0 1
# 103 9999897
El almacenamiento de 10 millones de entradas requerirá un espacio significativo, pero podemos crear un marco de datos con la codificación de longitud de ejecución de este vector:
rle.df <- with(rle(dat), data.frame(values, lengths))
dim(rle.df)
# [1] 207 2
head(rle.df)
# values lengths
# 1 1 52818
# 2 0 1
# 3 1 219329
# 4 0 1
# 5 1 318306
# 6 0 1
A partir de la codificación de longitud de ejecución, vemos que los primeros 52,818 valores en el vector son 1, seguidos de un solo 0, seguidos de 219,329 1 consecutivos, seguidos de un 0, y así sucesivamente. La codificación de longitud de ejecución solo tiene 207 entradas, lo que nos exige almacenar solo 414 valores en lugar de 10 millones de valores. Como rle.df
es un marco de datos, puede almacenarse utilizando funciones estándar como write.csv
.
La descompresión de un vector en la codificación de longitud de ejecución se puede lograr de dos maneras. El primer método es llamar simplemente rep
, pasando el values
elemento de la codificación por longitud de como primer argumento y la lengths
los elementos de la codificación por longitud de como segundo argumento:
decompressed <- rep(rle.df$values, rle.df$lengths)
Podemos confirmar que nuestros datos descomprimidos son idénticos a nuestros datos originales:
identical(decompressed, dat)
# [1] TRUE
El segundo método es utilizar la función inverse.rle
incorporada de inverse.rle
en el objeto rle
, por ejemplo:
rle.obj <- rle(dat) # create a rle object here
class(rle.obj)
# [1] "rle"
dat.inv <- inverse.rle(rle.obj) # apply the inverse.rle on the rle object
Podemos confirmar de nuevo que esto produce exactamente el dat
original:
identical(dat.inv, dat)
# [1] TRUE