Заполнение отсутствующих (пробелов) в таблице данных, для каждой категории - назад и вперед
Я работаю с большим набором данных платежных записей для моей клинической практики в течение 11 лет. Достаточно нескольких строк не хватает лечащего врача. Однако, используя некоторые правила, я могу их легко заполнить, но не знаю, как реализовать его в data.table под R. Я знаю, что в пакете zoo есть такие вещи, как na.locf
и самокалиброванное объединение данных. таблица. Примеры, которые я видел, слишком упрощены и не помогают мне.
Вот некоторые фиктивные данные, чтобы сориентировать вас (как текстовое представление dput ASCII)
structure(list(patient.first.name = structure(c(1L, 1L, 1L, 1L,
1L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L), .Label = c("John", "Kathy",
"Timothy"), class = "factor"), patient.last.name = structure(c(3L,
3L, 3L, 3L, 3L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L), .Label = c("Jones",
"Martinez", "Squeal"), class = "factor"), medical.record.nr = c(4563455,
4563455, 4563455, 4563455, 4563455, 2663775, 2663775, 2663775,
2663775, 2663775, 3330956, 3330956, 3330956, 3330956), date.of.service = c(39087,
39112, 39112, 39130, 39228, 39234, 39244, 39244, 39262, 39360,
39184, 39194, 39198, 39216), procedure.code = c(44750, 38995,
40125, 44720, 44729, 44750, 38995, 40125, 44720, 44729, 44750,
44729, 44729, 44729), diagnosis.code.1 = c(456.87, 456.87, 456.87,
456.87, 456.87, 521.37, 521.37, 521.37, 521.37, 356.36, 456.87,
456.87, 456.87, 456.87), diagnosis.code.2 = c(413, 413, 413,
413, 413, 532.23, NA, NA, NA, NA, NA, NA, NA, NA), referring.doctor.first = structure(c(1L,
1L, 1L, 1L, 1L, 2L, 2L, 2L, NA, NA, NA, 1L, 1L, NA), .Label = c("Abe",
"Mark"), class = "factor"), referring.doctor.last = structure(c(1L,
1L, 1L, 1L, 1L, 2L, 2L, 2L, NA, NA, NA, 1L, 1L, NA), .Label = c("Newstead",
"Wydell"), class = "factor"), referring.docotor.zip = c(15209,
15209, 15209, 15209, 15209, 15222, 15222, 15222, NA, NA, NA,
15209, 15209, NA), some.other.stuff = structure(c(1L, 1L, 1L,
NA, 3L, NA, NA, 4L, NA, 6L, NA, 2L, 5L, NA), .Label = c("alkjkdkdio",
"cheerios", "ddddd", "dddddd", "dogs", "lkjljkkkkk"), class = "factor")), .Names = c("patient.first.name",
"patient.last.name", "medical.record.nr", "date.of.service",
"procedure.code", "diagnosis.code.1", "diagnosis.code.2", "referring.doctor.first",
"referring.doctor.last", "referring.docotor.zip", "some.other.stuff"
), row.names = c(NA, 14L), class = "data.frame")
Очевидным решением является использование какого-то алгоритма последнего наблюдения, перенесенного вперед (LOCF) на referring.doctor.last и referring.doctor.first. Однако он должен остановиться, когда он попадает к новому пациенту. Другими словами, LOCF должен применяться только к одному пациенту, который идентифицируется комбинацией пациента. Первое имя, patient.last.name, medical.record.nr. Также обратите внимание на то, как некоторые пациенты упускают врача-референта при первом же посещении, поэтому это означает, что некоторые наблюдения должны быть перенесены назад. Чтобы усложнить ситуацию, некоторые пациенты меняют врачей первичной медико-санитарной помощи, поэтому раньше может быть один врач-референт, а другой - позже. Следовательно, alogorithm должен знать порядок дат строк с отсутствующими значениями.
В зоопарке na.locf
Я не вижу простого способа группировать LOCF на пациента. Примеры скользящего соединения, которые я видел, не будут работать здесь, потому что я не могу просто вынуть строки с отсутствующей информацией referring.doctor, так как тогда я потерял бы дату. Of.service и procedure.code и т.д. Мне бы очень хотелось узнать, как R может заполнить мои недостающие данные.
Ответы
Ответ 1
@MatthewDowle предоставил нам замечательную отправную точку, и мы остановимся на этом.
Вкратце, используйте зоопарк na.locf
. Проблема не поддается катящимся соединениям.
setDT(bill)
bill[,referring.doctor.last:=na.locf(referring.doctor.last,na.rm=FALSE),
by=list(patient.last.name, patient.first.name, medical.record.nr)]
bill[,referring.doctor.last:=na.locf(referring.doctor.last,na.rm=FALSE,fromLast=TRUE),
by=list(patient.last.name, patient.first.name, medical.record.nr)]
Затем сделайте что-то подобное для referring.doctor.first
Несколько указателей:
-
Оператор by
гарантирует, что последнее наблюдение, перенесенное вперед, ограничено одним и тем же пациентом, так что перенос не "истекает" в следующего пациента в списке.
-
Необходимо использовать аргумент na.rm=FALSE
. Если нет, то пациент, у которого отсутствует информация для лечащего врача при первом же посещении, удалит NA
, а вектор новых значений (существующий + переносимый вперед) будет одним из элементов, меньшим количества строк. Укороченный вектор перерабатывается, и все перемещается вверх, а последняя строка получает первый элемент вектора, поскольку он перерабатывается. Другими словами, большой беспорядок. И хуже всего вы будете видеть это иногда.
-
Используйте fromLast=TRUE
для повторного запуска столбца. Это заполняет NA, которая предшествует любым данным. Вместо последнего наблюдения (LOCF) зоопарк использует следующее наблюдение, перенесенное назад (NOCB). Счастье - теперь вы заполнили недостающие данные таким образом, чтобы это было правильно для большинства обстоятельств.
-
Вы можете передать несколько :=
в строке, например. DT[,`:=`(new=1L,new2=2L,...)]
Ответ 2
Более кратким примером было бы легче ответить. Например, вы включили довольно много столбцов, которые кажутся избыточными. Действительно ли это должно быть по имени и фамилии, или мы можем использовать номер пациента?
Поскольку у вас уже есть NA
в данных, которые вы хотите заполнить, это действительно не roll
в data.table
. Скользящее соединение больше для тех случаев, когда ваши данные не имеют NA
, но у вас есть другой временной ряд (например), который соединяется с позициями между данными. (Одним из преимуществ эффективности является тот факт, что вы сначала не создаете NA
, который затем должен заполнить второй шаг.) Или, другими словами, в вашем вопросе у вас есть только один набор данных; вы не присоединяетесь к двум.
Итак, вам нужно na.locf
, как предложил @Joshua. Я не знаю о функции, которая заполняет NA
вперед, а затем первое значение назад.
В data.table
, чтобы использовать na.locf
по группе, просто:
require(data.table)
require(zoo)
DT[,doctor:=na.locf(doctor),by=patient]
который имеет преимущества эффективности быстрой агрегации и обновления по ссылке. Вам нужно написать новую небольшую функцию поверх na.locf
, чтобы катить первый не NA
назад.
Убедитесь, что данные сначала отсортированы пациентом, а затем - датой. Затем вышеупомянутое будет справляться с изменениями в докторе с течением времени, поскольку by
поддерживает порядок строк в каждой группе.
Надеюсь, что вы получите несколько советов.