6

有没有一种优雅的方法来平衡不平衡的面板数据集?我想从一个不平衡的面板开始(即一些人丢失了一些数据)并以一个平衡的面板结束(即所有的人都没有丢失任何数据)。下面是一些示例代码。正确的最终结果是保留对“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

4 回答 4

6

所以我不确定它是否满足“优雅”的要求,但这是一个通用功能,您可以使用它来获取平衡数据。

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 进行观察(默认),或者如果您将其设置为“共享”,它只会返回所有 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

仅要求所有 PERSON/YEARS 的“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

现在请注意,Edward 是唯一具有 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 年的数据,因为所有人都有这些年份的数据。

于 2014-09-04T18:18:38.237 回答
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
于 2014-09-04T17:14:46.617 回答
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”变量,然后将其与您的原始数据框合并(并确保删除原始数据框中未找到的案例)您创建的 d 数据框)

df <- df[c("NAME", "YEAR")]
balanced <- merge.data.frame(df, unbal, by = c("NAME", "YEAR"), all.x = TRUE)
于 2017-05-09T19:06:42.677 回答
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")
于 2016-08-20T21:02:54.423 回答