listas - tablas en r
AgrupaciĆ³n de dataframe R por valores conectados (4)
Aquí hay otro intento de usar rle
y aggregate
desde la base R:
rl <- rle(df$State)
newdf <- data.frame(ID=df$ID, State=rep(1:length(rl$lengths),rl$lengths))
newdf <- aggregate(ID~State, newdf, FUN = function(x) c(minID=min(x), maxID=max(x)))
newdf$State <- rl$values
# State ID.minID ID.maxID
# 1 A 1 2
# 2 B 3 5
# 3 A 6 8
# 4 C 9 10
datos
df <- structure(list(ID = 1:10, State = c("A", "A", "B", "B", "B",
"A", "A", "A", "C", "C")), .Names = c("ID", "State"), class = "data.frame",
row.names = c(NA,
-10L))
No encontré una solución para este problema de agrupación común en R:
Este es mi conjunto de datos original
ID State
1 A
2 A
3 B
4 B
5 B
6 A
7 A
8 A
9 C
10 C
Este debería ser mi conjunto de datos resultante agrupado
State min(ID) max(ID)
A 1 2
B 3 5
A 6 8
C 9 10
Entonces, la idea es ordenar el conjunto de datos primero por la columna ID (o una columna de marca de tiempo). Luego, todos los estados conectados sin espacios vacíos se deben agrupar y se deben devolver los valores mínimo y máximo de ID. Está relacionado con el método rle, pero esto no permite el cálculo de valores máximos y mínimos para los grupos.
¿Algunas ideas?
Aquí hay un método que usa la función rle
en la base R para el conjunto de datos que proporcionó.
# get the run length encoding
temp <- rle(df$State)
# construct the data.frame
newDF <- data.frame(State=temp$values,
min.ID=c(1, head(cumsum(temp$lengths) + 1, -1)),
max.ID=cumsum(temp$lengths))
que devuelve
newDF
State min.ID max.ID
1 A 1 2
2 B 3 5
3 A 6 8
4 C 9 10
Tenga en cuenta que rle
requiere un vector de caracteres en lugar de un factor, por lo que utilizo el argumento as.is a continuación.
Como @ cryo111 notas en los comentarios a continuación, el conjunto de datos podría ser marcas de tiempo desordenadas que no corresponden a las longitudes calculadas en rle
. Para que este método funcione, primero deberá convertir las marcas de tiempo en un formato de fecha y hora, con una función como as.POSIXct
, use df <- df[order(df$ID),]
y luego emplee una ligera alteración del método anterior:
# get the run length encoding
temp <- rle(df$State)
# construct the data.frame
newDF <- data.frame(State=temp$values,
min.ID=df$ID[c(1, head(cumsum(temp$lengths) + 1, -1))],
max.ID=df$ID[cumsum(temp$lengths)])
datos
df <- read.table(header=TRUE, as.is=TRUE, text="ID State
1 A
2 A
3 B
4 B
5 B
6 A
7 A
8 A
9 C
10 C")
Tu podrías intentar:
library(dplyr)
df %>%
mutate(rleid = cumsum(State != lag(State, default = ""))) %>%
group_by(rleid) %>%
summarise(State = first(State), min = min(ID), max = max(ID)) %>%
select(-rleid)
O como se menciona por @alistaire en los comentarios, puede mutar dentro de group_by()
con la misma sintaxis, combinando los dos primeros pasos. Robar data.table::rleid()
y usar summarise_all()
para simplificar:
df %>%
group_by(State, rleid = data.table::rleid(State)) %>%
summarise_all(funs(min, max)) %>%
select(-rleid)
Lo que da:
## A tibble: 4 × 3
# State min max
# <fctr> <int> <int>
#1 A 1 2
#2 B 3 5
#3 A 6 8
#4 C 9 10
Una idea con data.table
:
require(data.table)
dt <- fread("ID State
1 A
2 A
3 B
4 B
5 B
6 A
7 A
8 A
9 C
10 C")
dt[,rle := rleid(State)]
dt2<-dt[,list(min=min(ID),max=max(ID)),by=c("rle","State")]
lo que da:
rle State min max
1: 1 A 1 2
2: 2 B 3 5
3: 3 A 6 8
4: 4 C 9 10
La idea es identificar secuencias con rleid
y luego obtener el min
y el max
de ID
por la tupla y el State
.
puedes eliminar la columna rle con
dt2[,rle:=NULL]
Encadenado:
dt2<-dt[,list(min=min(ID),max=max(ID)),by=c("rle","State")][,rle:=NULL]
Puede acortar el código anterior aún más utilizando rleid
interior de rleid
:
dt2 <- dt[, .(min=min(ID),max=max(ID)), by=.(State, rleid(State))][, rleid:=NULL]