问题描述
我正在与公司和年份建立一个面板数据库。如果有足够的观测值要进行插值(um(!is.na(var))>1
至少需要两个观测值),我将对这些值进行插值以填充NA,如果变量没有足够的值进行插值(ifelse(sum(!is.na(var))>1,na_interpolation(var),var)
),我将保留先前的值,以免失去价值。
set.seed(1)
data <- data.frame(Output_manufacturing = runif(6),Output_agriculture = runif(6),ID=c(1,1,2,3,3),Date=c(1991,20000,1991,2000,2000))
vars <- ls(data,pattern="Output_*")
for (var in vars) {
data<- data %>%
group_by(ID) %>%
mutate(!!sym(paste0(var,"_interpolated")) := ifelse(sum(!is.na(var))>1,var))
}
I get the error "Error in for (var in vars) { : invalid for() loop sequence"
解决方法
我认为您不需要for
循环。我将使用mtcars
进行演示,并对na_interpolation
的功能做一个幼稚的假设(这实际上与我的观点相切):
library(dplyr)
mt <- select(mtcars,cyl,disp,hp,drat) %>%
group_by(cyl) %>%
slice(1:3) %>%
ungroup()
mt$disp[c(1,7:8)] <- mt$hp[c(2:3)] <- NA
mt
# # A tibble: 9 x 4
# cyl disp hp drat
# <dbl> <dbl> <dbl> <dbl>
# 1 4 NA 93 3.85
# 2 4 147. NA 3.69
# 3 4 141. NA 3.92
# 4 6 160 110 3.9
# 5 6 160 110 3.9
# 6 6 258 110 3.08
# 7 8 NA 175 3.15
# 8 8 NA 245 3.21
# 9 8 276. 180 3.07
na_interpolation <- function(x) mean(x,na.rm = TRUE)
现在是真实的作品:
vars <- c("disp","hp")
mt %>%
group_by(cyl) %>%
mutate_at(vars,list(
interpolated = ~ if (sum(is.na(.)) > 1) na_interpolation(.) else .
)) %>%
ungroup()
# # A tibble: 9 x 6
# cyl disp hp drat disp_interpolated hp_interpolated
# <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1 4 NA 93 3.85 NA 93
# 2 4 147. NA 3.69 147. 93
# 3 4 141. NA 3.92 141. 93
# 4 6 160 110 3.9 160 110
# 5 6 160 110 3.9 160 110
# 6 6 258 110 3.08 258 110
# 7 8 NA 175 3.15 276. 175
# 8 8 NA 245 3.21 276. 245
# 9 8 276. 180 3.07 276. 180
观点:具有摘要统计信息的ifelse
不是正确的工具,出于以下几个原因:
-
base::ifelse
可能很糟糕:它会丢弃类(ifelse(TRUE,Sys.time(),Sys.time())
),并且不会对返回值类型(ifelse(TRUE,1L,"2")
)进行强制执行;都是动机dplyr::if_else
(和data.table::fifelse
); - 声明性:如果您确信条件始终为1,请使用
if
/else
进行声明;如果您看到有关the condition has length > 1
的警告,则您的假设不正确,这意味着关于事物状态的其他一些假设可能不正确; (也许不会在这里发生,但这是一种很好的防御性编程实践)。
这表明您的代码(没有for
循环)可能是:
vars <- grep("^Output_",names(data),value = TRUE)
data %>%
group_by(ID) %>%
mutate_at(vars,list(
interpolated = ~ if (sum(is.na(.)) > 1) na_interpolation(.) else .
)) %>%
ungroup()
# A tibble: 6 x 6
# Output_manufacturing Output_agriculture ID Date Output_manufacturing_interpolated Output_agriculture_interpolated
# <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1 0.266 0.945 1 1991 0.266 0.945
# 2 0.372 0.661 1 20000 0.372 0.661
# 3 0.573 0.629 2 1991 0.573 0.629
# 4 0.908 0.0618 2 2000 0.908 0.0618
# 5 0.202 0.206 3 1991 0.202 0.206
# 6 0.898 0.177 3 2000 0.898 0.177
(由此尚不清楚na_interpolation
是否会做正确的事,因为每个ID
的行数不超过2行,并且您的数据都不是NA
,但我认为这无关紧要。代码仍然可以正常工作。)
已更新,以反映mutate_at
取代了mutate(across(...),...)
。
data %>%
group_by(ID) %>%
mutate(across(vars,list(
interpolated = ~ if (sum(is.na(.)) > 1) na_interpolate(.) else .
))) %>%
ungroup()
# A tibble: 6 x 6
# Output_manufacturing Output_agriculture ID Date Output_manufacturing_interpolated Output_agriculture_interpolated
# <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1 0.266 0.945 1 1991 0.266 0.945
# 2 0.372 0.661 1 20000 0.372 0.661
# 3 0.573 0.629 2 1991 0.573 0.629
# 4 0.908 0.0618 2 2000 0.908 0.0618
# 5 0.202 0.206 3 1991 0.202 0.206
# 6 0.898 0.177 3 2000 0.898 0.177