2014-09-04 8 views
6

Есть ли элегантный способ сбалансировать несбалансированный набор данных панели? Я хотел бы начать с несбалансированной панели (т. Е. Некоторым людям не хватает некоторых данных) и в итоге получается сбалансированная панель (т. Е. Все люди не имеют данных). Ниже приведен пример кода. Правильный конечный результат - для всех наблюдений над «Фрэнком» и «Эдвардом», чтобы оставаться и для всех наблюдений «Тони», которые нужно удалить, так как у него есть некоторые недостающие данные. Спасибо.R элегантный способ балансировки данных несбалансированной панели

unbal <- data.frame(PERSON=c(rep('Frank',5),rep('Tony',5),rep('Edward',5)), YEAR=c(2001,2002,2003,2004,2005,2001,2002,2003,2004,2005,2001,2002,2003,2004,2005), Y=c(21,22,23,24,25,5,6,NA,7,8,31,32,33,34,35), X=c(1:15)) 
unbal 

ответ

5

Один из способов сбалансировать панель, чтобы удалить людей с неполными данными, другой способ, чтобы заполнить значения, такие как NA или 0 для отсутствующих наблюдений. Для первого подхода вы можете использовать complete.cases, чтобы найти строки, в которых нет NA. Затем вы можете найти все PERSON с хотя бы одним отсутствующим случаем.

missing.at.least.one <- unique(unbal$PERSON[!complete.cases(unbal)]) 
unbal[!(unbal$PERSON %in% missing.at.least.one),] 
# PERSON YEAR Y X 
# 1 Frank 2001 21 1 
# 2 Frank 2002 22 2 
# 3 Frank 2003 23 3 
# 4 Frank 2004 24 4 
# 5 Frank 2005 25 5 
# 11 Edward 2001 31 11 
# 12 Edward 2002 32 12 
# 13 Edward 2003 33 13 
# 14 Edward 2004 34 14 
# 15 Edward 2005 35 15 
+2

Если кто-то заинтересованы в наоборот (сделать несбалансированную панель сбалансирована, заполнив НСБ), можно использовать функцию 'сделать .pbalanced' из пакета 'plm' (последняя версия разработки требуется от https://r-forge.r-project.org/R/?group_id=406) – Helix123

+0

Официальный релиз CRAN (1.6-4) из plm теперь имеет' make.pbalanced 'объединены (и по аргументу' balance.type = c ("fill", "shared") 'можно выбрать, следует ли расширять данные или уменьшать их. – Helix123

3

Так я не уверен, что это удовлетворяет «элегантное» требование, но вот функция общего назначения вы можете использовать, чтобы получить сбалансированные данные.

balanced<-function(data, ID, TIME, VARS, required=c("all","shared")) { 
    if(is.character(ID)) { 
     ID <- match(ID, names(data)) 
    } 
    if(is.character(TIME)) { 
     TIME <- match(TIME, names(data)) 
    } 
    if(missing(VARS)) { 
     VARS <- setdiff(1:ncol(data), c(ID,TIME)) 
    } else if (is.character(VARS)) { 
     VARS <- match(VARS, names(data)) 
    } 
    required <- match.arg(required) 
    idf <- do.call(interaction, c(data[, ID, drop=FALSE], drop=TRUE)) 
    timef <- do.call(interaction, c(data[, TIME, drop=FALSE], drop=TRUE)) 
    complete <- complete.cases(data[, VARS]) 
    tbl <- table(idf[complete], timef[complete]) 
    if (required=="all") { 
     keep <- which(rowSums(tbl==1)==ncol(tbl)) 
     idx <- as.numeric(idf) %in% keep 
    } else if (required=="shared") { 
     keep <- which(colSums(tbl==1)==nrow(tbl)) 
     idx <- as.numeric(timef) %in% keep 
    } 
    data[idx, ] 
} 

Вы можете получить желаемый результат с

balanced(unbal, "PERSON","YEAR") 

# PERSON YEAR Y X 
# 1 Frank 2001 21 1 
# 2 Frank 2002 22 2 
# 3 Frank 2003 23 3 
# 4 Frank 2004 24 4 
# 5 Frank 2005 25 5 
# 11 Edward 2001 31 11 
# 12 Edward 2002 32 12 
# 13 Edward 2003 33 13 
# 14 Edward 2004 34 14 
# 15 Edward 2005 35 15 

Первый параметр является data.frame вы хотите подмножество. Второй параметр (ID=) является символьным вектором имен столбцов, которые идентифицируют каждого «человека» в наборе данных. Затем параметр TIME= также является символьным вектором, определяющим разные времена наблюдения для каждого идентификатора. Наконец, вы можете указать аргумент VARS=, чтобы указать, какие поля должны быть NA (по умолчанию все значения, отличные от ID или TIME). Наконец, есть один последний параметр с именем required, в котором указано, должен ли каждый идентификатор иметь наблюдение за каждым ВРЕМЕНИ (по умолчанию), или если вы установили его в «общий», он вернет только TIMES, для которых все идентификаторы имеют не пропущенные значения.

Так, например

balanced(unbal, "PERSON","YEAR", "X") 

# PERSON YEAR Y X 
# 1 Frank 2001 21 1 
# 2 Frank 2002 22 2 
# 3 Frank 2003 23 3 
# 4 Frank 2004 24 4 
# 5 Frank 2005 25 5 
# 6 Tony 2001 5 6 
# 7 Tony 2002 6 7 
# 8 Tony 2003 NA 8 
# 9 Tony 2004 7 9 
# 10 Tony 2005 8 10 
# 11 Edward 2001 31 11 
# 12 Edward 2002 32 12 
# 13 Edward 2003 33 13 
# 14 Edward 2004 34 14 
# 15 Edward 2005 35 15 

требует только то, что «X» быть NA для всех ЛИЦА/лет и так как это верно для всех записей, никаких настроек суба не происходит.

Если вы

balanced(unbal, "PERSON","YEAR", required="shared") 

# PERSON YEAR Y X 
# 1 Frank 2001 21 1 
# 2 Frank 2002 22 2 
# 4 Frank 2004 24 4 
# 5 Frank 2005 25 5 
# 6 Tony 2001 5 6 
# 7 Tony 2002 6 7 
# 9 Tony 2004 7 9 
# 10 Tony 2005 8 10 
# 11 Edward 2001 31 11 
# 12 Edward 2002 32 12 
# 14 Edward 2004 34 14 
# 15 Edward 2005 35 15 

тогда вы получите данные за 2001, 2002, 2004, 2005 для всех лиц, так как все они имеют данные за эти годы.

Теперь использование создать несколько различные выборочные данные установлен

unbal2 <- unbal 
unbal2[15, 2] <- 2006 
tail(unbal2) 

# PERSON YEAR Y X 
# 10 Tony 2005 8 10 
# 11 Edward 2001 31 11 
# 12 Edward 2002 32 12 
# 13 Edward 2003 33 13 
# 14 Edward 2004 34 14 
# 15 Edward 2006 35 15 

Теперь обратите внимание, что Эдвард является единственным человеком, который имеет значение для 2006 г. Это означает, что

balanced(unbal2, "PERSON","YEAR") 
# [1] PERSON YEAR Y  X  
# <0 rows> (or 0-length row.names) 

Теперь не возвращает ничего, кроме

balanced(unbal2, "PERSON","YEAR", required="shared") 

# PERSON YEAR Y X 
# 1 Frank 2001 21 1 
# 2 Frank 2002 22 2 
# 4 Frank 2004 24 4 
# 6 Tony 2001 5 6 
# 7 Tony 2002 6 7 
# 9 Tony 2004 7 9 
# 11 Edward 2001 31 11 
# 12 Edward 2002 32 12 
# 14 Edward 2004 34 14 

будет возвращать данные за 2001,2002, 2004 г., поскольку все лица имеют данные для в те годы.

+0

Очень приятно. Я признаю, что не понял, что подразумевается под «сбалансированным», пока я не прочитал об этом. Это гораздо более приятное общее решение. – nograpes

+0

Я пробовал этот метод, и могу сказать, что он очень медленный – Mislav

1

Это решение, которое я использую - оно использует удобные функции (включая приятные возможности слияния) пакета data.table и предполагает, что ваши данные уже являются объектами data.table. Это относительно просто и, надеюсь, легко следовать. Он возвращает сбалансированную панель с записями для каждой уникальной комбинации «индивидуумов» и «периодов времени», т. Е. Панели, где есть наблюдение за каждым человеком за каждый период времени.

library(data.table) 
Balance_Panel = function(Data, Indiv_ColName, Time_ColName){ 
    Individuals = unique(Data[, get(Indiv_ColName)]) 
    Times = unique(Data[, get(Time_ColName)]) 

    Full_Panel = data.table(expand.grid(Individuals, Times)) 
    setnames(Full_Panel, c(Indiv_ColName, Time_ColName)) 
    setkeyv(Full_Panel, c(Indiv_ColName, Time_ColName)) 
    setkeyv(Data, c(Indiv_ColName, Time_ColName)) 
    return(Data[Full_Panel]) 
} 

Пример использования:

Balanced_Data = Balance_Panel(Data, "SubjectID", "ObservationTime") 
2

Решение Я использовал это, чтобы временно изменить кадр данных в широком формате с годами, как колонны и единиц, таких как строки, а затем проверить для полных случаев по ряду , Это проще всего сделать, если у вас есть одна переменная, представляющая интерес, которая, если отсутствует, означает, что все наблюдение отсутствует.

Я использую следующие библиотеки:

library(data.table) 
library(reshape2) 

Во-первых, принять подмножество основного кадра данных (несимметричный), который как раз, переменная ID («NAME»), переменную времени («YEAR») и переменную, представляющую интерес («X» или «Y»).

df<- unbal[c("NAME", "YEAR", "X")] 

Во-вторых, измените новый фрейм данных, чтобы сделать его широкоформатным. Это создает кадр данных, в котором каждое «NAME» представляет собой одну строку, а «X» для каждого года - столбец.

df <- dcast(df, NAME ~ YEAR, value.var = "X") 

В-третьих, запустите complete.cases для каждой строки. Любое ИМЯ с отсутствующими данными будет полностью удалено.

df <- df[complete.cases(df),] 

В-четвертых, изменить форму кадра данных обратно в длинном формате (по умолчанию, это дает переменные общие имена, так что вы можете изменить имена обратно к тому, что они были раньше).

df <- melt(df, id.vars = "ID") 
setnames(df, "variable", "YEAR") 

ПРИМЕЧАНИЕ: ГОД становится фактор-переменной по умолчанию с использованием подхода. Если переменная YEAR является числовой, вам необходимо соответствующим образом изменить переменную. Например:

test4$year <- as.character(test4$year) 
test4$year <- as.numeric(test4$year) 

пятой и шестой, принимать только «NAME» и переменные «год» в кадре данных, которую вы создали, а затем объединить его с исходного кадра данных (и обязательно падение случаев в Оригинальный кадр данных, которые не найдены в кадре d данных вы создали)

df <- df[c("NAME", "YEAR")] 
balanced <- merge.data.frame(df, unbal, by = c("NAME", "YEAR"), all.x = TRUE)