2014-09-04 190 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

的一種方法是去除與不完整的數據的個體,另一種方式是填補的值,爲對缺失觀察如NA0。對於第一種方法,您可以使用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

如果問津倒過來(使不平衡的面板通過在港定居填充平衡),可以使用該功能使' .pm包中的.pbalanced'(需要https://r-forge.r-project.org/R/?group_id=406的最新開發版本) – Helix123 2016-06-28 19:32:11

+0

plm的官方CRAN版本(1.6-4)現在擁有' make.pbalanced '併入(並通過參數'balance.type = c(「fill」,「shared」)'可以選擇是擴展數據還是減少數據。 – Helix123 2016-11-30 08:22:50

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=參數也是一個字符向量,用於指定每個ID的不同觀察時間。最後,您可以選擇指定VARS=參數來指定哪些字段必須是NA(默認爲ID或TIME值以外的所有字段)。最後,還有一個名爲required的最後一個參數,它說明每個ID對每個TIME(默認值)都必須有一個觀察值,還是將其設置爲「shared」,它將只返回所有ID都具有非缺失值的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的數據,因爲所有人都有數據fo那些年。

+0

非常好。我承認,直到我讀到它時,才明白「平衡」的含義。這是一個更好的通用解決方案。 – nograpes 2014-09-05 15:18:17

+0

我試過這種方法,我可以說它非常慢 – Mislav 2017-08-28 19:56:00

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) 

首先,把你的主數據幀的一個子集(UNBAL),這只是中,ID變量( 「NAME」),時間變量( 「YEAR」 )和感興趣的變量(「X」或「Y」)。

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

二,重新塑造新的數據幀,使其成爲寬格式。這使得每個「名稱」是單行的數據框,以及每年的「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默認使用該方法成爲因子變量。如果你的YEAR變量是數字的,你需要相應地改變這個變量。例如:

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

第五和第六,只需要在「名稱」和「年份」變量在您創建的數據幀,然後用你的原始數據幀合併它(一定要下降的情況下在未在您創建的d數據幀中找到原始數據幀)

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