Reputation: 4229
Here is sample: data and wrong solution:
df <- data.frame(id=c(1,1,2,2,2,3,4,5,5), val=c(NA,1.5,1,NA,NA,5,NA,2,NA), rep=1:9)
Replace last NA
given id
from another column. Here is what I tried (among others):
funrep <- function(x) { is.na(tail(x, 1)) }
df$val[as.logical(with(df, ave(val, id, FUN=funrep)))] <- df$rep[as.logical(with(df, ave(val, id, FUN=funrep)))]
The above replaces figures given id
from another column where there is no NA
.
Desired output:
id val rep val2
1 NA 1 NA
1 1.5 2 1.5
2 1.0 3 1.0
2 NA 4 NA
2 NA 5 5
3 5.0 6 5.0
4 NA 7 7
5 2.0 8 2.0
5 NA 9 9
EDIT : the values in column val2
has been corrected. Also I prefer base R solution. Thanks.
Upvotes: 1
Views: 52
Reputation: 887711
Using data.table
, we convert the 'data.frame' to 'data.table' (setDT(df)
, create "val2" column from "val", get the row index (.I
) based on the logical condition grouped by "ID", extract that column ($V1
). Using that index in the "i", we assign "val2" to those corresponding elements in "rep".
library(data.table)
i1 <- setDT(df)[, val2:= val][, .I[1:.N==.N & is.na(val)] , id]$V1
df[i1, val2:= as.numeric(rep)]
df
# id val rep val2
#1: 1 NA 1 NA
#2: 1 1.5 2 1.5
#3: 2 1.0 3 1.0
#4: 2 NA 4 NA
#5: 2 NA 5 5.0
#6: 3 5.0 6 5.0
#7: 4 NA 7 7.0
#8: 5 2.0 8 2.0
#9: 5 NA 9 9.0
Using base R
, we create a new column with transform
, split
the data.frame to list with split
, use ifelse
to change the values in 'val2' with 'rep' that satisfies the condition, and unsplit
to get the expected output.
unsplit(lapply(split(transform(df, val2=val), df$id),
function(x) transform(x, val2=ifelse(is.na(val2) &
1:nrow(x)==nrow(x), rep, val2))), df$id)
Or using ave
i2 <- with(df, ave(is.na(val), id, FUN=function(x) x &
seq_along(x)==length(x)))
with(df, ifelse(i2, rep, val))
Upvotes: 1