Based on page 164-165 of [Collett:Modelling Survival data in medical research (Third edition-2014)] the Schoenfeld for individual test and for global test can be obtained as follows[1].
However, computing the Chi-square values using own code gives different answers as using cox.zph(res.cox). Survival package version used is survival_3.1-12
rm(list=ls()); set.seed(1234)
library("survival")
hmohiv<-read.table("https://stats.idre.ucla.edu/stat/r/examples/asa/hmohiv.csv", sep=",", header = TRUE)
#______Schoenfeld Residuals from coxph()__________
res.cox<-coxph( Surv(time, censor)~drug+age, data=hmohiv,ties="breslow")
sresid <- residuals(res.cox, "schoenfeld")
#______________Own code__________
beta<-as.matrix( c (res.cox$coefficients[1], res.cox$coefficients[2]) )
OrderedData <- hmohiv[order(hmohiv$time), ] #order the data according to the event times
x_ordered<-as.matrix(OrderedData[ ,4:3]) #Consider x1 and x2.
n<-nrow(OrderedData)
Numerator1<-numeric(n);Numerator2<- numeric(n);Denominator<-numeric(n)
Num1<- numeric(n);Num2<- numeric(n);Den<-numeric(n)
for(j in 1:n) {
Numerator1[j] <- x_ordered[j,1]*exp( x_ordered[j,]%*%beta)
Numerator2[j] <- x_ordered[j,2]*exp( x_ordered[j,]%*%beta)
Denominator[j]<-exp( x_ordered[j,] %*%beta)
}
for(j in 1:n) {
thistime<-OrderedData$time[j]
riskset <- which(thistime<=OrderedData$time)
Num1[j] <- sum(Numerator1[riskset])
Num2[j]<- sum(Numerator2[riskset])
Den[j] <- sum(Denominator[riskset])
}
Schoenfeld<-as.matrix(cbind( (x_ordered[,1]-Num1/Den), (x_ordered[,2]-Num2/Den)) )
Schoenfeldr<- Schoenfeld[OrderedData$censor==1, ]
> OrderedData.Cens<- OrderedData[OrderedData$censor==1, ]
> ObservedTime<- OrderedData.Cens$time
> sch1 <- residuals(res.cox,"scaledsch")
> xx <- ObservedTime - mean(ObservedTime)
> #__________Schoefeld Individual test__________#
> temp1 <- xx %*% sch1 #sch1 : Scaled Schoenfeld residual
> temp2 <- res.cox$var # Variance covariance matrix
> test1.Value <- temp1[1] %*% t(temp1)[1] /
( length(ObservedTime) *temp2[1,1]* sum(xx^2) )
> test2.Value <- temp1[2] %*% t(temp1)[2] /
( length(ObservedTime) *temp2[2,2]* sum(xx^2) )
> #_________Global test_________________#
> temp1 <- xx %*% Schoenfeldr # Unscaled Schoenfeld Residuals
> test.Global<- temp1 %*% temp2 %*% t(temp1) /
( sum(xx^2) / length(ObservedTime) )
> data.frame(test1.Value ,test2.Value,test.Global )
test1.Value test2.Value test.Global
0.2113885 0.01205375 0.2502792
> test.ph <- cox.zph(res.cox)
> test.ph
chisq df p
drug 4.98e-06 1 1.00
age 8.05e-02 1 0.78
GLOBAL 8.64e-02 2 0.96