Maybe there's a better solution, but my immediate thought would be to set up an intermediary variable (say, temp_A) that stores the value of the last value of A (where C != 0). In the code below, I used ifelse() to get the values of A when C != 0 (setting NA if C = 0), then used the na.locf function to carry forward this non-NA value until the next one, then used lag() to offset these values so that the current observation isn't included (e.g. for row 4, you want to subtract 0, not 6, which is the latest non-NA value). Hope that's understandable!
df <- tibble(
ID = c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2),
A = c(0, 2, 4, 6, 42, 46, 48, 60, 66, 70, 0, 1.5, 4.5, 6, 12, 16, 18, 22, 24),
C = c(12, 0, 0, 12, 12, 0, 12, 12, 12, 0, 19, 0, 0, 19, 19, 0, 19, 0, 19)
)
df %>%
group_by(ID) %>%
mutate(
temp_A = ifelse(C != 0, A, NA) %>% zoo::na.locf() %>% lag(n = 1, default = 0),
X = A - temp_A,
)
# A tibble: 19 x 5
# Groups: ID [2]
ID A C temp_A X
<dbl> <dbl> <dbl> <dbl> <dbl>
1 1 0 12 0 0
2 1 2 0 0 2
3 1 4 0 0 4
4 1 6 12 0 6
5 1 42 12 6 36
6 1 46 0 42 4
7 1 48 12 42 6
8 1 60 12 48 12
9 1 66 12 60 6
10 1 70 0 66 4
11 2 0 19 0 0
12 2 1.5 0 0 1.5
13 2 4.5 0 0 4.5
14 2 6 19 0 6
15 2 12 19 6 6
16 2 16 0 12 4
17 2 18 19 12 6
18 2 22 0 18 4
19 2 24 19 18 6