Tengo el siguiente conjunto de datos y me gustaría encontrar el máximo por subconjunto
Conjunto de datos

StudentID Indicator Value  
100 N 30  
100 N 35  
100 N 28  
100 Y 20  
100 N 29  
100 N 60  
200 N 40  
200 N 35  
200 Y 20  
200 N 24  
200 N 35  

Me gustaría que el resultado sea el siguiente:
Resultado

StudentID Indicator Value Max  
100 N 30 35  
100 N 35 35  
100 N 28 35  
100 N 29 60   
100 N 60 60  
200 N 40 40  
200 N 35 40  
200 N 24 35  
200 N 35 35  

Entonces, esencialmente, cada vez que el indicador cambia de N a Y, necesito considerar las filas por studentID e IndicatorID como un bloque y calcular el máximo de ese bloque y pasar a la siguiente iteración.

-3
Vinay 27 feb. 2018 a las 05:10

4 respuestas

La mejor respuesta

Una solución que utiliza en R.

library(dplyr)

dat2 <- dat %>%
  group_by(StudentID) %>%
  mutate(Group = cumsum(Indicator %in% "Y")) %>%
  filter(!Indicator %in% "Y") %>%
  group_by(StudentID, Group) %>%
  mutate(Max = max(Value)) %>%
  ungroup() %>%
  select(-Group) %>%
  as.data.frame(stringsAsFactors = FALSE)
dat2
#   StudentID Indicator Value Max
# 1       100         N    30  35
# 2       100         N    35  35
# 3       100         N    28  35
# 4       100         N    29  60
# 5       100         N    60  60
# 6       200         N    40  40
# 7       200         N    35  40
# 8       200         N    24  35
# 9       200         N    35  35

DATOS

dat <- read.table(text = "StudentID Indicator Value  
100 N 30  
                  100 N 35  
                  100 N 28  
                  100 Y 20  
                  100 N 29  
                  100 N 60  
                  200 N 40  
                  200 N 35  
                  200 Y 20  
                  200 N 24  
                  200 N 35  ",
                  header = TRUE, stringsAsFactors = FALSE)
1
www 27 feb. 2018 a las 02:22

Aquí hay una opción usando pandas de python. Creamos una variable de agrupación al obtener la suma acumulativa de la salida lógica (dat.Indicator == "Y", luego subconjunto las filas eliminando las filas que 'Indicador' como "Y", agrupadas por 'StudentID', 'Grupo', obtienen el max de 'Valor' con transform, asígnelo a 'Valor' y drop las columnas que no se necesitan

dat['Group'] = (dat.Indicator == "Y").cumsum()

datS = dat[dat.Indicator != "Y"]
datS1 = datS.copy()
datS1['Value'] = datS.groupby(['StudentID', 'Group'])['Value'].transform('max')
datS1.drop('Group', axis = 1, inplace = True)
datS1

-salida

enter image description here


Una opción base R sería ave

dat$Value <- with(dat, ave(Value, cumsum(Indicator == "Y"), FUN = max))
subset(dat, Indicator != "Y")
#    StudentID Indicator Value
#1        100         N    35
#2        100         N    35
#3        100         N    35
#5        100         N    60
#6        100         N    60
#7        200         N    60
#8        200         N    60
#10       200         N    35
#11       200         N    35

Datos

import pandas as pd
dat = pd.DataFrame({'StudentID': [100, 100, 100, 100, 100, 100, 200, 200, 200, 200, 200],
               'Indicator':[ "N", "N", "N", "Y", "N", "N", "N", "N", "Y", "N", "N"],
               'Value':[30, 35, 28, 20, 29, 60, 40, 35, 20, 24, 35]})

#R
dat <-structure(list(StudentID = c(100L, 100L, 100L, 100L, 100L, 100L, 
 200L, 200L, 200L, 200L, 200L), Indicator = c("N", "N", "N", "Y", 
"N", "N", "N", "N", "Y", "N", "N"), Value = c(35L, 35L, 35L, 
60L, 60L, 60L, 60L, 60L, 35L, 35L, 35L)), .Names = c("StudentID", 
 "Indicator", "Value"), row.names = c(NA, -11L), class = "data.frame")
1
akrun 27 feb. 2018 a las 05:15

Te falta una variable para indicar los grupos. Puede hacerlo fácilmente en SAS utilizando la opción notsorted en la declaración by.

data grouped ;
  retain group 0;
  set have ;
  by studentid indicator notsorted;
  group + first.indicator;
run;

Hay varias maneras de generar ahora el valor medio por grupo ahora que están definidos. PROC SQL lo hace fácil al volver a combinar automáticamente los valores agregados en las líneas de detalle.

proc sql ;
 select *,max(value) as max
   from grouped
   group by group
 ;
quit;

Resultados:

group  StudentID  Indicator     Value       max

   1        100  N                35        35
   1        100  N                30        35
   1        100  N                28        35
   2        100  Y                20        20
   3        100  N                60        60
   3        100  N                29        60
   4        200  N                40        40
   4        200  N                35        40
   5        200  Y                20        20
   6        200  N                35        35
   6        200  N                24        35

No estoy seguro de por qué su salida de muestra ha eliminado los grupos que tienen INDICADOR = 'Y', pero podría agregar una cláusula where para eliminarlos.

0
Tom 27 feb. 2018 a las 03:54

Una solución que utiliza ddply del paquete plyr.

Suponiendo que esto se almacena como un marco de datos llamado df

output_df <- plyr::ddply(df, c("StudentID", "Indicator"), numcolwise(max))

ddply aplica una función a un subconjunto de datos. El primer argumento son los datos a los que queremos aplicarlos, el segundo son los nombres de columna por los que queremos agruparlos. La final es la función que queremos aplicar. Como lo estamos aplicando a una columna del subconjunto, necesitamos ajustar max en numcolwise.

El resultado puede tener un nombre divertido para la última columna, pero puede configurarlo con names(output_df)[3] <- "Max".

Ahora solo necesitamos fusionar eso con el marco original.

df$Max <- rep(output_df$Max, times = table(df$StudentID))

El argumento times de rep dice cuántas veces repetir cada elemento del primer argumento. El uso de la función table calcula cuántas veces ocurre cada ID de estudiante. Esto solo funcionará si las identificaciones de los estudiantes están en orden. Si ese no es el caso, se necesitará un enfoque diferente.

En total, eso hace 3 líneas de código

output_df <- plyr::ddply(df, c("StudentID", "Indicator"), numcolwise(max))
names(output_df)[3] <- "Max"
df$Max <- rep(output_df$Max, times = table(df$StudentID))
-1
LachlanO 27 feb. 2018 a las 02:35