Подтвердить что ты не робот

Почему медиана срабатывает data.table(целое или двойное)?

У меня есть data.table, называемый enc.per.day для встреч в день. Он имеет 2403 строки, в которых указан дата службы и количество пациентов, замеченных в этот день. Я хотел видеть среднее число пациентов, наблюдаемых в любой день недели.

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

Эта строка дает ошибку

Ошибка в [.data.table (enc.per.day, list (patient.encounters = median (n)),:   столбцы j не оцениваются для согласованных типов для каждой группы: результат для группы 4 имеет тип столбца 1 "целое число", но ожидает тип "double"

Следующие работы хорошо работают

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))]

Что происходит? Мне потребовалось много времени, чтобы понять, почему мой код не работает.

Кстати, базовый вектор enc.per.day $n является целым числом

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

возвращает "integer". Кроме того, в таблице данных нет нигде нет.

4b9b3361

Ответ 1

TL; DR wrap median с as.double()

median() 'trip up' data.table, потому что --- даже когда только переданные целочисленные векторы --- median() иногда возвращает целочисленное значение и иногда возвращает double.

## 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"

Воспроизведение сообщения об ошибке с минимальным примером:

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 жалуется, потому что после проверки значения первой группы, подлежащей обработке, он пришел к выводу, что, OK, эти результаты будут иметь тип "целое число". Но сразу же (или в вашем случае в группе 4) ему передается значение типа "double", которое не будет соответствовать его "целочисленному" вектору результатов.


data.table может вместо этого накапливать результаты до конца групповых вычислений, а затем при необходимости выполнять преобразования типов, но для этого потребуется куча дополнительных служебных данных, снижающих производительность; вместо этого он просто сообщает, что произошло, и позволяет исправить эту проблему. После того, как первая группа запустила и знает тип результата, он выделяет вектор результата этого типа, пока число групп, а затем заполняет его. Если позже выяснится, что некоторые группы возвращают более одного элемента, он будет расти (т.е. Перераспределять) этот вектор результата по мере необходимости. В большинстве случаев, однако, data.table сначала предполагают, что конечный размер результата является первым в первый раз (например, 1 результат строки для каждой группы) и, следовательно, быстро.

В этом случае использование as.double(median(X)) вместо median(X) обеспечивает подходящее исправление.

(Кстати, ваша версия с использованием round() работала, потому что она всегда возвращает значения типа "double", как вы можете видеть, набрав typeof(round(median(1:2))); typeof(round(median(1:3))).)