2012-08-25 12 views
26

Tengo una tabla de datos llamada enc.per.day para encuentros por día. Tiene 2403 filas en las que se especifica una fecha de servicio y la cantidad de pacientes atendidos ese día. Quería ver la cantidad mediana de pacientes vistos en cualquier tipo de día de la semana.¿Por qué triplica la tripulación data.table (integer versus double)?

enc.per.day[,list(patient.encounters=median(n)),by=list(weekdays(DOS))] 

Esa línea da un error

Error en [.data.table (enc.per.day,, lista (patient.encounters = mediana (n)),: columnas de j no evalúan a los tipos consistentes para cada grupo: resultado para el grupo 4 tiene la columna 1 de tipo 'entero', pero tipo esperando 'doble'

La siguiente todo bien trabajo

tapply(enc.per.day$n,weekdays(enc.per.day$DOS),median) 
enc.per.day[,list(patient.encounters=round(median(n))),by=list(weekdays(DOS))] 
enc.per.day[,list(patient.encounters=median(n)+0),by=list(weekdays(DOS))] 

¿Qué está pasando? Me llevó mucho tiempo descubrir por qué mi código no funcionaría.

Por cierto, el vector subyacente enc.per.day $ n es un entero

storage.mode(enc.per.day$n) 

devuelve "número entero". Además, no hay NA en ninguna parte de la tabla de datos.

Respuesta

37

TL; DR envolver median con as.double()

median() 'tropezar' data.table porque --- incluso cuando sólo pasó vectores enteros --- median() veces devuelve un valor entero, y devuelve a veces un doble .

## median of 1:3 is 2, of type "integer" 
typeof(median(1:3)) 
# [1] "integer" 

## median of 1:2 is 1.5, of type "double" 
typeof(median(1:2)) 
# [1] "double" 

La reproducción de su mensaje de error con un ejemplo mínimo:

library(data.table) 
dt <- data.table(patients = c(1:3, 1:2), 
       weekdays = c("Mon", "Mon", "Mon", "Tue", "Tue")) 

dt[,median(patients), by=weekdays] 
# Error in `[.data.table`(dt, , median(patients), by = weekdays) : 
# columns of j don't evaluate to consistent types for each group: 
# result for group 2 has column 1 type 'double' but expecting type 'integer' 

data.table se queja porque, después de inspeccionar el valor del primer grupo para ser procesados, se ha llegado a la conclusión de que, OK, estos resultados van a ser de tipo "entero". Pero luego, de inmediato (o en su caso en el grupo 4), se le pasa un valor de tipo "doble", que no cabe en su vector de resultados "entero".


data.table lugar podría acumular resultados hasta el final de los cálculos de grupos sabia, y luego realizar conversiones de tipo si es necesario, pero que requeriría un montón de sobrecarga adicional rendimiento-degradantes; en su lugar, solo informa lo que sucedió y te permite solucionar el problema. Después de que el primer grupo se haya ejecutado y conozca el tipo de resultado, asigna un vector de resultados de ese tipo siempre que sea el número de grupos y luego lo rellena. Si luego descubre que algunos grupos devuelven más de 1 elemento, crecerá (es decir, reasignará) ese vector resultante según sea necesario. Sin embargo, en la mayoría de los casos, la primera estimación de data.table para el tamaño final del resultado es correcta por primera vez (p. Ej., 1 fila de resultados por grupo) y, por lo tanto, es rápida.

En este caso, usar as.double(median(X)) en lugar de median(X) proporciona una solución adecuada.

(Por cierto, la versión utilizando round() funcionado porque siempre devuelve valores de tipo "doble", como se puede ver escribiendo typeof(round(median(1:2))); typeof(round(median(1:3))).)

+1

@Matthew Dowle - Gracias por añadir esos detalles acerca de cómo * * data.table ** inicializa y asigna espacio para el vector de resultados. –

+0

¿Es posible tener una mediana del mismo tipo que el valor? Por lo tanto, incluso si tuviera valores como = 1,1,1,2,2,2,2, no debería resultar en mediana = 1,5, sino que debería mostrar mediana = 2. – lony

+0

Como un ejemplo de la sugerencia anterior, haga esto DT [, c (como .do (lapply (.SD, mediana)), .N), por = x, .SDcols = c ("x", "y "," z ")] en lugar de DT [, c (lapply (.SD, mediana), .N), por = x, .SDcols = c (" x "," y "," z ")] –

Cuestiones relacionadas