NLS analysis in R with timeseries data to remove known effect I have a timeseries dataset collected by sensors. The data is crack displacement (disp) and temperature (temp). Thermal expansion is linear with temperature, so it causes a diurnal signature in my displacement data. I would like to remove the effect from temperature. I'm an engineer, not a statistician, so I am uncertain that I am doing it correctly.
I've used R's nls function to remove the known effect of temperature. I used a sin wave with 24-hour period. I also used a shift parameter (phi) in the function to shift the wave (because the temp sensor responds faster than the disp sensor which is mounted on concrete that takes awhile to heat and cool).
My questions are:  Am I doing this correctly? Is there a better way? 
Note that I do not want to predict crack displacement, I want to remove effects of temperature and see the underlying 'unexplained' displacement.
I've developed a pseudo time series that somewhat mimics my data.
Here is my code (requires package lubridate):
# create 1 hour timeseries 1 week long
dat <- data.frame(TIMESTAMP = seq.POSIXt(from = as.POSIXct('2020-03-01'), 
                                          to = as.POSIXct('2020-03-08'),
                                          by = '1 hour'))

# Add displacement data and temperature data.
# Use a sinusoidal pattern with random element
set.seed(1)
dat$disp <- sin(as.numeric(dat$TIMESTAMP)) + runif(n = nrow(dat), min = -0.2, max = 0.2)
dat$temp <- sin(as.numeric(dat$TIMESTAMP)) * runif(n = nrow(dat), min = 0, max = 5) + 20
plot(dat$TIMESTAMP, dat$disp)
plot(dat$TIMESTAMP, dat$temp)

# Add a trend to displacement - this is the unexplained displacement that I am looking for
dat$disp <- dat$disp + seq(from = 0, to = 1, length.out = nrow(dat))*rnorm(1)

# Equation for nls:
# D = b0 + b1*T + b2*sin(omega*t + phi)
    # D = predicted displacement
    # T = temperature
    # omega = angular frequency = 2*pi / 24
    # t = time of day (1...24)
    # phi = time shift between temperature and displacement
    # b0, b1, b2 are fitting parameters

# Provide starting parameters for nls()
b0 <- 1.0
b1 <- 1.0
b2 <- 1.0
phi <- 20

# create time of day (tod) series
library(lubridate)
dat$tod <- hour(dat$TIMESTAMP)

# model
fit <- nls(disp ~ (b0 + b1 * temp + b2 * sin(2 * pi * tod / 24 + phi)), 
           start = list(b0 = b0, b1 = b1, b2 = b2, phi = phi),
           data = dat)

# assign modeling results (residual and fitted points) to dat
dat$res <- resid(fit)
dat$fit <- fitted(fit)

# regression on residuals to get the slope
fit2 <- lm(dat$res ~ dat$TIMESTAMP)

# plot original data
plot(dat$TIMESTAMP, dat$disp)

# plot residuals (the 'unexplained' part of displacement)
points(dat$TIMESTAMP, dat$res, col = 'red')

# plot the fitted values (the 'thermally explained' part of displacement)
points(dat$TIMESTAMP, dat$fit, col = 'green')

# add slope line through the residuals (movement not caused by thermal effects)
abline(a = fit2$coefficients[1], b = fit2$coefficients[2])
```

 A: Your approach doesn't look like a valid one, because you "assume the conclusion" by specifying the sin time dependency. You can try one of 2 options instead:

*

*Use Generalized Additive Model with cyclical splines from mgcv package.

*Get the displacement due to temperature expansion from physics theory (temperature at time with appropriate shift) * coefficient of thermal expansion for the relevant material (concrete?) and just subtract it from actual observation

If the time lag between the temp and disp censors is non-negligible, you need to account for it with a model like disp_t = b0 + b1 * lag(temp_t, temp_disp_lag) + .... If this lag is unknown, then this is a really big problem and I am afraid there is no good solution.
Explanation:
The displacement linearly depends on temperature and on "something else":
disp_t = b0 + b1 * temp_t + u_t
You want to plot "something else" (displacement unexplained by temperature). If you somehow estimate b0 and b1, this is simply:
u_t = disp_t - b0 - b1 * temp_t
The effect that interests you is linear, so you don't need NLS. As a first idea you could try running a simple OLS:
m = lm(disp ~ temp, data = dat)
dat$dispOther = dat$disp - predict(m, newdata = dat)

However, the problem here is that u_t is autocorrelated and not iid (if it were iid, why would you want to plot it?) and even worse, it is correlated with time (that's why you want to plot it) and via time with temperature, so you have endogeneity and the OLS estimates are inconsistent. If you knew the functional form for the u_t dependency on time (u_t = f(t) + e_t, where e_t is stationary noise uncorrelated with temp_t and t), you could model and estimate b0, b1 and f parameters with an equation like this:
disp_t = b0 + b1 * temp_t + f(t) + e_t
If you assume that f(t) = sin wave with 24 hour period, you can estimate everything with nls like you did, but the result would be as good as this assumption. The assumption is likely wrong. Do you really know a priori that whatever process you are trying to measure with displacement sensors has exactly sinusoidal dependency on time besides the temperature dependency?
You can get a non-parametric estimate for the time dependency with mgcv package.
library(mgcv)
m = gam(disp ~ temp + s(t, bs="cp",...), data = dat)
dat$dispOther = dat$disp - coef(m)['(Intercept)'] - coef(m)['temp']

This would approximate the dependency of displacement on time with a smooth cyclical function (with a bit of magic). I don't remember right out of my head how to specify cyclical splines in mgcv, but you can check it here
A completely different approach would be to avoid any statistical estimations at all and get the displacement change due to temperature from physics and some reference table for relevant materials. This could be trivial or impossible depending on your specific case.
