I have a data frame like this:
> head(df1)
iso year var1 var2 var3
1 XXX 2005 165 29 2151
2 XXX 2006 160 21 2139
3 XXX 2007 NA NA NA
4 XXX 2008 184 9 3640
5 XXX 2009 NA NA NA
6 YYY 2005 206 461 8049
I want to replace the NA
's of intermittent years based on the years around it and the NA
's in years at the beginning and end of the range by carrying backward and forward the outer most non-NA observation.
My code to do this for one column is:
df1 %>%
group_by(iso) %>%
mutate(var1 = na.approx(var1, na.rm = FALSE, rule = 1)) %>%
mutate(var1 = na.locf(var1, na.rm = FALSE)) %>%
mutate(var1 = na.locf(var1, na.rm = FALSE, fromLast = TRUE))
This works, so now I want to do this for all columns in one go (there are more than 3 and they are not numbered like in my example). This I pieced together from the answers to this question. I omitted the two calls to na.locf
.
columnnames <- c("var1, "var2", "var3")
df1 %>%
group_by(iso) %>%
mutate_at(.vars = vars(columnnames), .funs = funs(na.approx(., na.rm = FALSE, rule = 1)))
This throws me an error and a warning:
Error in approx(x[!na], y[!na], xout, ...) : need at least two non-NA values to interpolate In addition: Warning message: In xy.coords(x, y, setLab = FALSE) : NAs introduced by coercion
I think I understand the error, but I did not get it when I used the first piece of code on var1
. The warning I don't follow. How cal I apply my code to all columns in my data frame? I also tried putting evertything in a loop, looping over columnnames
but that didn't work either (and it it probably not the best way to go about this).
The dplyr package [v>= 1.0.0] is required. We’ll use the function across () to make computation across multiple columns. .cols: Columns you want to operate on. You can pick columns by position, name, function of name, type, or any combination thereof using Boolean operators. .fns: Function or list of functions to apply to each column. ...:
I’ll begin by loading the dplyr package and then using the group by function. > library (dplyr) > mtcars %>% group_by (gear) This will override the existing groups and create new ones, and we can use the group by function in R to group by any of the characteristics.
library(dplyr) left_join (df1, df2, by=c ('x1'='x2', 'y1'='y2')) This particular syntax will perform a left join where the following conditions are true: The value in the x1 column of df1 matches the value in the x2 column of df2. The value in the y1 column of df1 matches the value in the y2 column of df2.
Update: To interpolate multiple columns, we can use mutate_at. Here's an example with two value columns. We use mutate_at to run na.approx on all columns that include "Value" in the column name. list (interp=na.approx) tells mutate_at to generate new column names by running na.approx and adding interp as a suffix to generate the new column names:
Use na.approx
with method = "constant"
(same as na.locf
) and rule = 2
(means extend nearest value to leading and trailing NAs). If you want the NAs to be linearly interpolated instead remove the method="constant"
argument.
df1 %>%
group_by(iso) %>%
mutate_at(vars(-iso), funs(na.approx(., method = "constant", rule = 2))) %>%
ungroup
giving:
# A tibble: 6 x 5
iso year var1 var2 var3
<fct> <dbl> <dbl> <dbl> <dbl>
1 XXX 2005 165 29 2151
2 XXX 2006 160 21 2139
3 XXX 2007 160 21 2139
4 XXX 2008 184 9 3640
5 XXX 2009 184 9 3640
6 YYY 2005 206 461 8049
df1
in reproducible form is:
df1 <-
structure(list(iso = structure(c(1L, 1L, 1L, 1L, 1L, 2L), .Label = c("XXX",
"YYY"), class = "factor"), year = c(2005L, 2006L, 2007L, 2008L,
2009L, 2005L), var1 = c(165L, 160L, NA, 184L, NA, 206L), var2 = c(29L,
21L, NA, 9L, NA, 461L), var3 = c(2151L, 2139L, NA, 3640L, NA,
8049L)), class = "data.frame", row.names = c("1", "2", "3", "4",
"5", "6"))
You can re-write your code using mutate_at
so that conversion can be done in one go as:
library(dplyr)
library(zoo)
df %>%
group_by(iso) %>%
mutate_at(vars(starts_with("var")),
funs(na.locf(na.locf(na.approx(., na.rm = FALSE, rule = 1),na.rm=FALSE),
fromLast=TRUE)))
# # A tibble: 6 x 5
# # Groups: iso [2]
# iso year var1 var2 var3
# <chr> <int> <dbl> <dbl> <dbl>
# 1 XXX 2005 165 29.0 2151
# 2 XXX 2006 160 21.0 2139
# 3 XXX 2007 172 15.0 2890
# 4 XXX 2008 184 9.00 3640
# 5 XXX 2009 184 9.00 3640
# 6 YYY 2005 206 461 8049
#
Data:
df <- read.table(text=
"iso year var1 var2 var3
1 XXX 2005 165 29 2151
2 XXX 2006 160 21 2139
3 XXX 2007 NA NA NA
4 XXX 2008 184 9 3640
5 XXX 2009 NA NA NA
6 YYY 2005 206 461 8049",
header = TRUE, stringsAsFactors = FALSE)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With