Hi Tina,
I used lm() in my example but you can do the same with glm() since the outcome is continuous.
Yes, the way to plot the regression line and its CI is to create the y.fit and se on the server-side and use the scatterplot function to return back anonymised values that you can then plot using the native R plot function. The anonymised values lie on the same line or curve as the actual values so you get the same line and CI curves as those you expect.
Here is an example on how to do that in DataSHIELD:
ds.dataFrame(c('D$LAB_TRIG','D$PM_BMI_CONTINUOUS'), newobj='Dcompl', completeCases = TRUE)
mod <- ds.glm(formula='LAB_TRIG~PM_BMI_CONTINUOUS', data='Dcompl', family='gaussian')
mod # estimate of intercept=-0.01941156, estimate of x=0.07714569
mod$Nvalid # n=1730
ds.make(toAssign = '-0.01941156+0.07714569*Dcompl$PM_BMI_CONTINUOUS', newobj='y.fit')
ds.mean('Dcompl$PM_BMI_CONTINUOUS') # mean(x)=27.42582
# calculate standard error of regression line using the formula:
# sqrt(sum((y - y.fit)^2) / (n - 2)) * sqrt(1 / n + (x - mean(x))^2 / sum((x - mean(x))^2))
# do the above in parts:
ds.make(toAssign = '(Dcompl$LAB_TRIG-y.fit)^2', newobj='p1')
ds.dataFrame(x = c('p1','p1','p1','p1','p1','p1','p1','p1','p1','p1'), newobj = 'p1df')
ds.rowColCalc(x = 'p1df', operation='colSums', newobj='p2')
ds.mean('p2') # 4047.194
sqrt(4047.194/(1730-2)) # 1.530401
ds.make(toAssign = '(Dcompl$PM_BMI_CONTINUOUS-27.42582)^2', newobj = 'p3')
ds.dataFrame(x = c('p3','p3','p3','p3','p3','p3','p3','p3','p3','p3'), newobj = 'p3df')
ds.rowColCalc(x = 'p3df', operation='colSums', newobj='p4')
ds.mean('p4') # 42465.73
ds.make(toAssign = 'p3/42465.73', newobj = 'p5')
ds.make(toAssign = '(1/1730)+p5', newobj = 'p6')
ds.sqrt('p6', newobj='p7')
ds.make(toAssign = '1.530401*p7', newobj='SE')
# Calculate critical t-value
t.val <- qt(0.975, 1730 - 2) # 1.961338
ds.make(toAssign = 'y.fit + (1.961338 * SE)', newobj = 'upper')
ds.make(toAssign = 'y.fit - (1.961338 * SE)', newobj = 'lower')
upper <- ds.scatterPlot(x='Dcompl$PM_BMI_CONTINUOUS', y='upper', return.coords = TRUE)
lower <- ds.scatterPlot(x='Dcompl$PM_BMI_CONTINUOUS', y='lower', return.coords = TRUE)
fit <- ds.scatterPlot(x='Dcompl$PM_BMI_CONTINUOUS', y='y.fit', return.coords = TRUE)
plot(fit[[1]]$study1[,'x'], fit[[1]]$study1[,'y'], type='l', xlab='x', ylab='y')
lines(sort(lower[[1]]$study1[,'x']), sort(lower[[1]]$study1[,'y']), lty=2)
lines(sort(upper[[1]]$study1[,'x']), sort(upper[[1]]$study1[,'y']), lty=2)
and you get this plot:
In the above code i used only one study but you can do the same if you have multiple studies and also if you run the same model in multiple subgroups.
Let me know if you need to discuss this code in more details or if you need any other help!
Thanks,
Demetris