Заполнение пропусков (пробелов) в таблице данных, по категориям - назад и вперед

Я работаю с большим набором данных счетов для моей клинической практики за 11 лет. В нескольких строках отсутствует лечащий врач. Однако, используя некоторые правила, я могу довольно легко их заполнить, но не знаю, как реализовать их в data.table под R. Я знаю, что есть такие вещи, как na.locf в пакете zoo и самообслуживаемое объединение в пакете data.table. Примеры, которые я видел, слишком упрощены и не помогают мне.

Вот некоторые вымышленные данные, чтобы ориентировать вас (как текстовое представление 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.first.name, PatientLast.Name, medical.record.nr. Также обратите внимание, что некоторые пациенты пропускают направляющего врача при первом же посещении, поэтому некоторые наблюдения следует перенести в обратном направлении. Чтобы усложнить ситуацию, некоторые пациенты меняют врачей первичной медицинской помощи, поэтому может быть один направляющий врач ранее, а другой - позже. Алгоритм, следовательно, должен знать порядок дат строк с пропущенными значениями.

В зоопарке na.locf Я не вижу простого способа сгруппировать LOCF для каждого пациента. Скользящие примеры объединения, которые я видел, не сработали бы здесь, потому что я не могу просто извлечь строки с отсутствующей информацией referring.doctor, так как тогда я потеряю date.of.service и method.code и так далее. Мне бы очень хотелось, чтобы вы узнали, как R может заполнить мои недостающие данные.

2 ответа

Решение

@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

Несколько указателей:

  1. by Это утверждение гарантирует, что последнее перенесенное наблюдение ограничено одним и тем же пациентом, чтобы переноска не "перетекла" в следующего пациента в списке.

  2. Надо использовать na.rm=FALSE аргумент. Если этого не сделать, то пациент, у которого при первом посещении отсутствует информация для направляющего врача, получит NA удаляется, и вектор новых значений (существующих + перенесенных) будет на один элемент меньше количества строк. Укороченный вектор перерабатывается, и все смещается вверх, и последний ряд получает первый элемент вектора при его переработке. Другими словами, большой беспорядок. И что хуже всего, вы увидите это только иногда.

  3. использование fromLast=TRUE пробежать через колонку снова. Это заполняет NA, который предшествовал любым данным. Вместо последнего наблюдения, перенесенного вперед (LOCF), зоопарк использует следующее наблюдение, перенесенное назад (NOCB). Счастье - вы заполнили недостающие данные способом, который подходит для большинства обстоятельств.

  4. Вы можете передать несколько := на строку, например DT[,`:=`(new=1L,new2=2L,...)]

На более сжатый пример было бы легче ответить. Например, вы включили довольно много столбцов, которые кажутся избыточными. Нужно ли указывать имя и фамилию или мы можем использовать номер пациента?

Так как у вас уже есть NAв данных, которые вы хотите заполнить, это не roll в data.table действительно. Скользящее соединение больше для случаев, когда ваши данные не имеют NA но у вас есть другой временной ряд (например), который соединяется с позициями между данными. (Одним из преимуществ эффективности является тот факт, что вы не создаете NA первый, который затем необходимо заполнить во втором шаге.) Или, другими словами, в вашем вопросе у вас есть только один набор данных; Вы не присоединяетесь к двум.

Так что вам нужно na.locf как предложил Джошуа. Я не знаю о функции, которая заполняет NA вперед, а затем первое значение назад, хотя.

В data.table, использовать na.locf по группе это просто:

require(data.table)
require(zoo)
DT[,doctor:=na.locf(doctor),by=patient]

которая имеет преимущества эффективности быстрого агрегирования и обновления по ссылке. Вы должны написать новую маленькую функцию поверх na.locf бросить первый не NA в обратном направлении.

Убедитесь, что данные отсортированы по пациенту, а затем по дате. Тогда с течением времени вышесказанное справится с изменениями в докторе, так как by поддерживает порядок строк в каждой группе.

Надеюсь, что это дает вам несколько советов.

Другие вопросы по тегам