R优雅的方式来平衡不平衡的面板数据

时间:2014-09-04 17:06:54

标签: r panel balance

是否有一种优雅的方法来平衡不平衡的面板数据集?我想从一个不平衡的小组开始(即,一些人缺少一些数据)并最终得到一个平衡的小组(即,所有个人都没有丢失数据)。下面是一些示例代码。正确的最终结果是对'Frank'和'Edward'的所有观察结果保留,并且对'Tony'的所有观察都要删除,因为他有一些缺失的数据。谢谢。

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

4 个答案:

答案 0 :(得分:6)

平衡面板的一种方法是删除包含不完整数据的个人,另一种方法是为缺失的观察值填写一个值,例如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

答案 1 :(得分:4)

所以我不确定它是否满足&#34;优雅&#34;要求,但这里是一个通用功能,可用于获取平衡数据。

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=)是列名称的字符向量,用于标识每个&#34; person&#34;在数据集中。然后TIME=参数也是一个字符向量,指定每个ID的不同观察时间。最后,您可以选择指定VARS=参数来指定哪些字段必须为NA(默认为除ID或TIME值以外的所有字段)。最后,还有一个名为required的参数,它指出每个ID是否必须对每个TIME(默认)进行观察,或者如果将其设置为&#34; shared&#34;,它将仅返回TIMES所有ID都有非缺失值。

所以例如

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

只需要&#34; X&#34;对于所有人/年都是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的数据,因为所有人都拥有这些年份的数据。

答案 2 :(得分:3)

我使用的解决方案是暂时将数据帧重新整形为宽格式,其中列数和单位为行数,然后按行检查完整的情况。如果您有一个感兴趣的变量,这是最容易做到的 - 如果缺少 - 意味着缺少整个观察。

我使用以下库:

library(data.table)
library(reshape2)

首先,取一个主数据框(unbal)的子集,即ID变量(“NAME”),时间变量(“YEAR”)和感兴趣的变量(“X”或“Y” “)。

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

其次,重塑新数据框以使其格式化。这使得一个数据框中每个“NAME”是一行,每年的“X”是一列。

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

第三,为每一行运行complete.cases。任何缺少数据的NAME都将被完全删除。

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)

第五和第六,仅在您创建的数据框中使用“NAME”和“YEAR”变量,然后将其与原始数据框合并(并确保将原始数据框中的案例放入非原始数据框中在您创建的数据框中找到)

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

答案 3 :(得分:1)

这是我使用的解决方案 - 它利用了data.table包的便捷功能(包括良好的合并功能),并假设您的数据已经是data.table对象。它相对简单,希望很容易理解。它返回一个平衡的面板,其中包含&#34;个人&#34;的每个独特组合的条目。和#34;时间段&#34; - 即在每个时间段内对每个人进行观察的小组。

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")