The OP has requested to fill only NA values which are between other values within each group. This means to skip any sequence of NA values at start or end of each group when applying zoo::na.locf().
With data.table, this can be done by identifying the indices of the rows to be skipped and a kind of anti-join:
library(data.table)
setDT(DT)[!DT[, {
na_grp <- rleid(is.na(Value))
.I[na_grp %in% c(1L, max(na_grp))]
}, by = .(Group_A, GROUP_B)]$V1, Value := zoo::na.locf(Value)][]
Group_A GROUP_B Date Value Expected_Value
1: GROUP_1 Group_1_1 1/1/2017 NA NA
2: GROUP_1 Group_1_1 1/2/2017 NA NA
3: GROUP_1 Group_1_1 1/3/2017 34 34
4: GROUP_1 Group_1_1 1/4/2017 20 20
5: GROUP_1 Group_1_1 1/5/2017 20 20
6: GROUP_1 Group_1_1 1/6/2017 20 20
7: GROUP_1 Group_1_1 1/7/2017 38 38
8: GROUP_1 Group_1_2 1/1/2017 35 35
9: GROUP_1 Group_1_2 1/2/2017 28 28
10: GROUP_1 Group_1_2 1/3/2017 20 28
11: GROUP_1 Group_1_2 1/4/2017 32 32
12: GROUP_1 Group_1_2 1/5/2017 39 39
13: GROUP_1 Group_1_2 1/6/2017 28 28
14: GROUP_1 Group_1_2 1/7/2017 NA NA
15: GROUP_2 Group_1_11 1/1/2017 NA NA
16: GROUP_2 Group_1_11 1/2/2017 NA NA
17: GROUP_2 Group_1_11 1/3/2017 40 40
18: GROUP_2 Group_1_11 1/4/2017 32 32
19: GROUP_2 Group_1_11 1/5/2017 20 20
20: GROUP_2 Group_1_11 1/6/2017 NA NA
21: GROUP_2 Group_1_11 1/7/2017 NA NA
22: GROUP_2 Group_1_21 1/1/2017 NA NA
23: GROUP_2 Group_1_21 1/2/2017 32 32
24: GROUP_2 Group_1_21 1/3/2017 36 36
25: GROUP_2 Group_1_21 1/4/2017 36 36
26: GROUP_2 Group_1_21 1/5/2017 28 28
27: GROUP_2 Group_1_21 1/6/2017 33 33
28: GROUP_2 Group_1_21 1/7/2017 40 40
29: GROUP_3 Group_1_13 1/1/2017 NA NA
30: GROUP_3 Group_1_13 1/2/2017 NA NA
31: GROUP_3 Group_1_13 1/3/2017 NA NA
32: GROUP_3 Group_1_13 1/4/2017 29 29
33: GROUP_3 Group_1_13 1/5/2017 31 31
34: GROUP_3 Group_1_13 1/6/2017 31 31
35: GROUP_3 Group_1_13 1/7/2017 34 34
36: GROUP_3 Group_1_23 1/1/2017 26 26
37: GROUP_3 Group_1_23 1/2/2017 33 33
38: GROUP_3 Group_1_23 1/3/2017 27 27
39: GROUP_3 Group_1_23 1/4/2017 23 23
40: GROUP_3 Group_1_23 1/5/2017 25 25
41: GROUP_3 Group_1_23 1/6/2017 41 41
42: GROUP_3 Group_1_23 1/7/2017 25 25
Group_A GROUP_B Date Value Expected_Value
Explanation
- For each group, the streaks of
NA / non-NA values are numbered
- The first and last of these streaks in each group is picked and the indices are retrieved from the special symbol
.I. (As Value will be updated in place it doesn't matter whether the first or last streak contains NA or not; they will not be updated anyway.)
- The found indices
DT[, {na_grp <- rleid(is.na(Value)); .I[na_grp %in% c(1L, max(na_grp))]}, by = .(Group_A, GROUP_B)]$V1
are excluded so that zoo::na.locf(Value) is only applied to the "inner" streaks of each group.
Data
DT <- structure(list(Group_A = c("GROUP_1", "GROUP_1", "GROUP_1", "GROUP_1",
"GROUP_1", "GROUP_1", "GROUP_1", "GROUP_1", "GROUP_1", "GROUP_1",
"GROUP_1", "GROUP_1", "GROUP_1", "GROUP_1", "GROUP_2", "GROUP_2",
"GROUP_2", "GROUP_2", "GROUP_2", "GROUP_2", "GROUP_2", "GROUP_2",
"GROUP_2", "GROUP_2", "GROUP_2", "GROUP_2", "GROUP_2", "GROUP_2",
"GROUP_3", "GROUP_3", "GROUP_3", "GROUP_3", "GROUP_3", "GROUP_3",
"GROUP_3", "GROUP_3", "GROUP_3", "GROUP_3", "GROUP_3", "GROUP_3",
"GROUP_3", "GROUP_3"), GROUP_B = c("Group_1_1", "Group_1_1",
"Group_1_1", "Group_1_1", "Group_1_1", "Group_1_1", "Group_1_1",
"Group_1_2", "Group_1_2", "Group_1_2", "Group_1_2", "Group_1_2",
"Group_1_2", "Group_1_2", "Group_1_11", "Group_1_11", "Group_1_11",
"Group_1_11", "Group_1_11", "Group_1_11", "Group_1_11", "Group_1_21",
"Group_1_21", "Group_1_21", "Group_1_21", "Group_1_21", "Group_1_21",
"Group_1_21", "Group_1_13", "Group_1_13", "Group_1_13", "Group_1_13",
"Group_1_13", "Group_1_13", "Group_1_13", "Group_1_23", "Group_1_23",
"Group_1_23", "Group_1_23", "Group_1_23", "Group_1_23", "Group_1_23"
), Date = c("1/1/2017", "1/2/2017", "1/3/2017", "1/4/2017", "1/5/2017",
"1/6/2017", "1/7/2017", "1/1/2017", "1/2/2017", "1/3/2017", "1/4/2017",
"1/5/2017", "1/6/2017", "1/7/2017", "1/1/2017", "1/2/2017", "1/3/2017",
"1/4/2017", "1/5/2017", "1/6/2017", "1/7/2017", "1/1/2017", "1/2/2017",
"1/3/2017", "1/4/2017", "1/5/2017", "1/6/2017", "1/7/2017", "1/1/2017",
"1/2/2017", "1/3/2017", "1/4/2017", "1/5/2017", "1/6/2017", "1/7/2017",
"1/1/2017", "1/2/2017", "1/3/2017", "1/4/2017", "1/5/2017", "1/6/2017",
"1/7/2017"), Value = c(NA, NA, 34L, 20L, NA, NA, 38L, 35L, 28L,
NA, 32L, 39L, 28L, NA, NA, NA, 40L, 32L, 20L, NA, NA, NA, 32L,
36L, NA, 28L, 33L, 40L, NA, NA, NA, 29L, 31L, NA, 34L, 26L, 33L,
27L, 23L, 25L, 41L, 25L), Expected_Value = c(NA, NA, 34L, 20L,
20L, 20L, 38L, 35L, 28L, 28L, 32L, 39L, 28L, NA, NA, NA, 40L,
32L, 20L, NA, NA, NA, 32L, 36L, 36L, 28L, 33L, 40L, NA, NA, NA,
29L, 31L, 31L, 34L, 26L, 33L, 27L, 23L, 25L, 41L, 25L)), .Names = c("Group_A",
"GROUP_B", "Date", "Value", "Expected_Value"), row.names = c(NA,
-42L), class = "data.frame")