2014-09-04 276 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)