迭代多个变量时如何与 NA 进行比较
How to compare to NA when iterating over multiple variables
我将提供很多并非绝对必要的背景知识(我欢迎提出改进建议),因此如果您只想查看我的主要问题,可以滚动到最后。
背景
我有一个数据集,我想根据一些规则在其中填充多个变量的缺失值。一些示例数据:
library(data.table)
library(tidyverse)
dt <- data.frame(id = c('a', 'a', 'a', 'a', 'a',
'b', 'b', 'b', 'b', 'b'),
var1 = c(1, 1, NA, 1, NA,
NA, 5, 5, NA, 5),
var2 = c('hi', 'hi', NA, 'hi', 'hi',
'bye', NA, 'bye', 'bye', NA),
year = c(2005:2009,
1995:1998, 2002))
请注意,实际数据很大:数以百万计的观察值,至少 30-40 个变量,我将填写大约 10 列,因此采用 data.table
方法。
我目前使用查找超前和滞后并检查我的条件的函数填写它们,如下所示:
fill.in <- function(var, yr, finyr) {
leadv <- lead(var, order_by = yr)
lagv <- lag(var, order_by = yr)
leadyr <- lead(yr, order_by = yr)
lagyr <- lag(yr, order_by = yr)
# ------- build the updated var w/ sequential conditions
# keep the var as it is if not missing
try1 <- ifelse(test = !is.na(var),
yes = var,
no = NA)
# fill in if the lead and lag match and no more than 2 missing years
try2 <- ifelse(test = is.na(try1) & leadv == lagv &
abs(leadyr-lagyr) <= 3 &
!is.na(leadv),
yes = leadv,
no = try1)
# fill in with the lag if it's the final year of observed data
ifelse(test = is.na(try2) & yr == finyr &
abs(yr-lagyr) <= 3 & !is.na(lagv),
yes = lagv,
no = try2)
}
我用 data.table
的 .SD
:
来称呼它
setDT(dt)
dt[, finalyr := max(year), by = id]
# variables to iterate over and the new column names
fill.in.vs <- c('var1', 'var2')
fixed.vnames <- paste0('fixed.', fill.in.vs)
# I don't want factor values, so make strings characters
dt$var2 <- as.character(dt$var2)
dt[, (fixed.vnames) := sapply(.SD,
FUN = fill.in,
yr = year,
finyr = finalyr,
simplify = FALSE, USE.NAMES = FALSE),
by = id, .SDcols = fill.in.vs]
这给了我想要的数据:
id var1 var2 year finalyr fixed.var1 fixed.var2
1: a 1 hi 2005 2009 1 hi
2: a 1 hi 2006 2009 1 hi
3: a NA <NA> 2007 2009 1 hi
4: a 1 hi 2008 2009 1 hi
5: a NA hi 2009 2009 1 hi
6: b NA bye 1995 2002 NA bye
7: b 5 <NA> 1996 2002 5 bye
8: b 5 bye 1997 2002 5 bye
9: b NA bye 1998 2002 NA bye
10: b 5 <NA> 2002 2002 5 <NA>
主要问题
现在回答我的实际问题:我想要一个已更新行的指示器。我不认为我使用 data.table
和 .SD
更新的方式允许制作配对变量(我的意思是在每次迭代中产生两个或三个不同的变量),所以我想我只会标记基本上 original.var != fixed.var
.
的观察结果
这会遇到问题,因为 R 不喜欢将值与 NA
s 进行比较。 (参见 here for example.)
我已经尝试为此实施一些解决方案,但我一直 运行 陷入尝试通过变量列表执行此操作的问题。例如,以下工作可以识别哪些相同或不同:
# this works:
dt$updated.var1 <- mapply(identical, dt$var1, dt$fixed.var1,
SIMPLIFY = FALSE, USE.NAMES = FALSE)
dt[,c('var1', 'fixed.var1', 'updated.var1')]
var1 fixed.var1 updated.var1
1: 1 1 TRUE
2: 1 1 TRUE
3: NA 1 FALSE
4: 1 1 TRUE
5: NA 1 FALSE
6: NA NA TRUE
7: 5 5 TRUE
8: 5 5 TRUE
9: NA NA TRUE
10: 5 5 TRUE
但是当我尝试将它与 data.table
和变量列表一起使用时,它 returns 全部 FALSE
用于两个变量:
# indicate updated observations
update.names <- paste0('updated.', fill.in.vs)
# this doesn't work how I want it to:
dt[, (update.names) := mapply(identical,
.SD,
fixed.vnames,
SIMPLIFY = FALSE, USE.NAMES = FALSE),
.SDcols = fill.in.vs]
dt[,c('var1', 'fixed.var1', 'updated.var1')]
var1 fixed.var1 updated.var1
1: 1 1 FALSE
2: 1 1 FALSE
3: NA 1 FALSE
4: 1 1 FALSE
5: NA 1 FALSE
6: NA NA FALSE
7: 5 5 FALSE
8: 5 5 FALSE
9: NA NA FALSE
10: 5 5 FALSE
我不确定问题出在哪里,但我猜这与我试图让 mapply
将“固定”变量列表识别为要迭代的变量有关。
如有任何建议,我们将不胜感激。
您可以编写一个自定义函数来处理这个问题,并使用 Map
为每一对应用该函数。
library(data.table)
new_cols <- function(x, y) {
z <- x != y | is.na(x) & is.na(y)
z[is.na(z)] <- TRUE
z
}
update.names <- paste0('updated.', fill.in.vs)
dt[, (update.names) := Map(new_cols, dt[, ..fixed.vnames], dt[, ..fill.in.vs])]
dt
# id var1 var2 year finalyr fixed.var1 fixed.var2 updated.var1 updated.var2
# 1: a 1 hi 2005 2009 1 hi FALSE FALSE
# 2: a 1 hi 2006 2009 1 hi FALSE FALSE
# 3: a NA <NA> 2007 2009 1 hi TRUE TRUE
# 4: a 1 hi 2008 2009 1 hi FALSE FALSE
# 5: a NA hi 2009 2009 1 hi TRUE FALSE
# 6: b NA bye 1995 2002 NA bye TRUE FALSE
# 7: b 5 <NA> 1996 2002 5 bye FALSE TRUE
# 8: b 5 bye 1997 2002 5 bye FALSE FALSE
# 9: b NA bye 1998 2002 NA bye TRUE FALSE
#10: b 5 <NA> 2002 2002 5 <NA> FALSE TRUE
您可能会在控制台中收到警告,请暂时忽略它们。关于它有一个悬而未决的问题。 https://github.com/Rdatatable/data.table/issues/2988
我将提供很多并非绝对必要的背景知识(我欢迎提出改进建议),因此如果您只想查看我的主要问题,可以滚动到最后。
背景
我有一个数据集,我想根据一些规则在其中填充多个变量的缺失值。一些示例数据:
library(data.table)
library(tidyverse)
dt <- data.frame(id = c('a', 'a', 'a', 'a', 'a',
'b', 'b', 'b', 'b', 'b'),
var1 = c(1, 1, NA, 1, NA,
NA, 5, 5, NA, 5),
var2 = c('hi', 'hi', NA, 'hi', 'hi',
'bye', NA, 'bye', 'bye', NA),
year = c(2005:2009,
1995:1998, 2002))
请注意,实际数据很大:数以百万计的观察值,至少 30-40 个变量,我将填写大约 10 列,因此采用 data.table
方法。
我目前使用查找超前和滞后并检查我的条件的函数填写它们,如下所示:
fill.in <- function(var, yr, finyr) {
leadv <- lead(var, order_by = yr)
lagv <- lag(var, order_by = yr)
leadyr <- lead(yr, order_by = yr)
lagyr <- lag(yr, order_by = yr)
# ------- build the updated var w/ sequential conditions
# keep the var as it is if not missing
try1 <- ifelse(test = !is.na(var),
yes = var,
no = NA)
# fill in if the lead and lag match and no more than 2 missing years
try2 <- ifelse(test = is.na(try1) & leadv == lagv &
abs(leadyr-lagyr) <= 3 &
!is.na(leadv),
yes = leadv,
no = try1)
# fill in with the lag if it's the final year of observed data
ifelse(test = is.na(try2) & yr == finyr &
abs(yr-lagyr) <= 3 & !is.na(lagv),
yes = lagv,
no = try2)
}
我用 data.table
的 .SD
:
setDT(dt)
dt[, finalyr := max(year), by = id]
# variables to iterate over and the new column names
fill.in.vs <- c('var1', 'var2')
fixed.vnames <- paste0('fixed.', fill.in.vs)
# I don't want factor values, so make strings characters
dt$var2 <- as.character(dt$var2)
dt[, (fixed.vnames) := sapply(.SD,
FUN = fill.in,
yr = year,
finyr = finalyr,
simplify = FALSE, USE.NAMES = FALSE),
by = id, .SDcols = fill.in.vs]
这给了我想要的数据:
id var1 var2 year finalyr fixed.var1 fixed.var2
1: a 1 hi 2005 2009 1 hi
2: a 1 hi 2006 2009 1 hi
3: a NA <NA> 2007 2009 1 hi
4: a 1 hi 2008 2009 1 hi
5: a NA hi 2009 2009 1 hi
6: b NA bye 1995 2002 NA bye
7: b 5 <NA> 1996 2002 5 bye
8: b 5 bye 1997 2002 5 bye
9: b NA bye 1998 2002 NA bye
10: b 5 <NA> 2002 2002 5 <NA>
主要问题
现在回答我的实际问题:我想要一个已更新行的指示器。我不认为我使用 data.table
和 .SD
更新的方式允许制作配对变量(我的意思是在每次迭代中产生两个或三个不同的变量),所以我想我只会标记基本上 original.var != fixed.var
.
这会遇到问题,因为 R 不喜欢将值与 NA
s 进行比较。 (参见 here for example.)
我已经尝试为此实施一些解决方案,但我一直 运行 陷入尝试通过变量列表执行此操作的问题。例如,以下工作可以识别哪些相同或不同:
# this works:
dt$updated.var1 <- mapply(identical, dt$var1, dt$fixed.var1,
SIMPLIFY = FALSE, USE.NAMES = FALSE)
dt[,c('var1', 'fixed.var1', 'updated.var1')]
var1 fixed.var1 updated.var1
1: 1 1 TRUE
2: 1 1 TRUE
3: NA 1 FALSE
4: 1 1 TRUE
5: NA 1 FALSE
6: NA NA TRUE
7: 5 5 TRUE
8: 5 5 TRUE
9: NA NA TRUE
10: 5 5 TRUE
但是当我尝试将它与 data.table
和变量列表一起使用时,它 returns 全部 FALSE
用于两个变量:
# indicate updated observations
update.names <- paste0('updated.', fill.in.vs)
# this doesn't work how I want it to:
dt[, (update.names) := mapply(identical,
.SD,
fixed.vnames,
SIMPLIFY = FALSE, USE.NAMES = FALSE),
.SDcols = fill.in.vs]
dt[,c('var1', 'fixed.var1', 'updated.var1')]
var1 fixed.var1 updated.var1
1: 1 1 FALSE
2: 1 1 FALSE
3: NA 1 FALSE
4: 1 1 FALSE
5: NA 1 FALSE
6: NA NA FALSE
7: 5 5 FALSE
8: 5 5 FALSE
9: NA NA FALSE
10: 5 5 FALSE
我不确定问题出在哪里,但我猜这与我试图让 mapply
将“固定”变量列表识别为要迭代的变量有关。
如有任何建议,我们将不胜感激。
您可以编写一个自定义函数来处理这个问题,并使用 Map
为每一对应用该函数。
library(data.table)
new_cols <- function(x, y) {
z <- x != y | is.na(x) & is.na(y)
z[is.na(z)] <- TRUE
z
}
update.names <- paste0('updated.', fill.in.vs)
dt[, (update.names) := Map(new_cols, dt[, ..fixed.vnames], dt[, ..fill.in.vs])]
dt
# id var1 var2 year finalyr fixed.var1 fixed.var2 updated.var1 updated.var2
# 1: a 1 hi 2005 2009 1 hi FALSE FALSE
# 2: a 1 hi 2006 2009 1 hi FALSE FALSE
# 3: a NA <NA> 2007 2009 1 hi TRUE TRUE
# 4: a 1 hi 2008 2009 1 hi FALSE FALSE
# 5: a NA hi 2009 2009 1 hi TRUE FALSE
# 6: b NA bye 1995 2002 NA bye TRUE FALSE
# 7: b 5 <NA> 1996 2002 5 bye FALSE TRUE
# 8: b 5 bye 1997 2002 5 bye FALSE FALSE
# 9: b NA bye 1998 2002 NA bye TRUE FALSE
#10: b 5 <NA> 2002 2002 5 <NA> FALSE TRUE
您可能会在控制台中收到警告,请暂时忽略它们。关于它有一个悬而未决的问题。 https://github.com/Rdatatable/data.table/issues/2988