Prostate cancer topics, links and more. Now at 200+ posts!

News: Health Day, Medical News Today, ScienceDaily, Urol Times, Urotoday, Zero Cancer Papers: Pubmed (all), Pubmed (Free only), Amedeo
Journals: Eur Urol, J Urol, JCO, The Prostate Others Pubmed Central Journals (Free): Adv Urol, BMC Urol, J Endourol, Kor J Urol, Rev Urol, Ther Adv Urol, Urol Ann
Reviews: Cochrane Summaries, PC Infolink Newsletters: PCRI, US Too General Medical Reviews: f1000, Health News Review

Thursday, April 26, 2007

PSA Variation

PSA can vary due to a number of systematic factors such as those listed in [PMID: 7554241] (also found on the QA page of this Ontario document and at this Prostate Help page); however, it also varies from day to day and month to month on a purely random basis. Here we discuss the inherent variation in PSA and also analyse an experiment done by a group of cancer patients to investigate this variation.

A group of cancer patients funded Terry Herbert to measure his PSA every day for 28 consecutive days. The experiment is described by Herbert here and here and by Don Cooley here.

As seen on the accompanying graph, Herbert's PSA values (shown in blue) started at 4.9 and ended at 5.2 but during the interim they varied from a low of 4.5 to a high of 6 increasing from 4.5 to 6 one week and plunging back down from 6 to 4.6 the following week.

The mean PSA over the 28 days was 5.1 and the standard deviation was 0.37. The ratio of these two numbers is called the coefficient of variation (CV) and was 0.37 / 5.1 = 7.3%. This value is not that far from the 9.5% that Boddy et al (2003) [PMID: 15049982] found for daily variation. (They also found that the CV did not vary between healthy subjects and cancer patients so CV cannot be used as a biomarker.)

Soletormos et al (2005) [PMID: 15961552] did a review of 12 studies and concluded that sampling at a frequency of less than one month gave inconsistent results but that sampling at one month or greater gave a consistent coefficient of variation in the neighbourhood of 20% (for subjects whose PSA was in the 1-20 range). At the 2007 AUA conference Litwin indicated that he found the coefficient of variation of PSA to be 15% with observations spaced 2 weeks apart.

The 20% figure is also consistent with the breakpoints used in the Partin tables used to make prognoses based on lab results (PSA, Gleason score, stage). See Table 1 of this John Hopkins web page. The PSA breakpoints in that table are 2.5, 4, 6 and 10. With a standard deviation of about 20% of the midpoint this makes the boundary of each range about one standard deviation from the midpoint. For example, the midpoint of the 4-6 range is 5 and 20% of that is 1 and 4 and 6 are each 1 away from 5.

The inconsistent results that Soletormos saw when measuring PSA values less than a month apart are likely due to correlation among PSA values that are measured nearby in time; so, lets examine our PSA data and resample it at a long enough interval to eliminate such correlation. An autocorrelation analysis of the Terry Herbert data indicates that there is autocorrelation at 1 and 4 days (see figure) so we resample every 5 days to place each PSA reading far enough away from the prior readings that it is not significantly influenced by them. This gives us a coefficient of variation of 17%, not far from the 20% that Soletormos et al found.

Program Code Used

For those who wish to play with the data, the R code used to create the graphics and perform the CV calculations is here. The R code reads the data right off of John Fister's site. You can copy the R code below to the clipboard and paste it into the online version of R at Rweb or else download and install R (which is free) and paste the clipboard into a running R session on your computer.

# read web page of John Fistere extracting data into Lines
URL <- ""
Lines.raw <- readLines(URL)
Lines <- grep("Number|<br>#", Lines.raw, value = TRUE)
Lines <- gsub("#|<br>|<p>|.nbsp.|%", "", Lines)

# read Lines into a data frame removing column 1 and converting Date
DF <- read.table(textConnection(Lines), header = TRUE, fill = TRUE)
DF <- DF[,-1]
DF$Date <- as.Date(DF$Date, "%d-%b-%Y")
DF$Free <- DF$fPSA * DF$PSA / 100
DF$Complex <- DF$PSA - DF$Free

# plot with extra wide margins adding text values, mean and sd lines
opar <- par(mar = c(5, 4, 4, 4) + 0.01)
plot((PSA) ~ Date, DF, type = "o", ylab = "", col = "blue")
text(DF$Date, DF$PSA, DF$PSA, pos = 3, cex = 0.7)
abline(h = mean(DF$PSA) + seq(-2, 2) * sd(DF$PSA), lty = c(2, 2, 1, 2, 2))
mtext("PSA (ng/ml)", 2, 3, col = "blue")

# plot Free PSA
par(new = TRUE)
plot(fPSA ~ Date, na.omit(DF), col = "green", type = "l", axes = FALSE,
xlim = range(DF$Date), ylab = "")
points(fPSA ~ Date, DF, col = "green")
text(DF$Date, DF$fPSA, DF$fPSA, pos = 3, cex = 0.7)
axis(4, at = pretty(range(DF$fPSA, na.rm = TRUE)))
mtext("Free PSA", 4, 3, col = "green")

# add title and legends
title(main = "PSA Values Over 28 Consecutive Days")
legend("topright", c("Free PSA", "PSA",
paste("PSA mean:", format(mean(DF$PSA), dig = 2)),
paste("PSA sd:", format(sd(DF$PSA), dig = 2))), lty = c(1, 1, 1, 2),
col = c(3, 4, 1, 1), cex = 0.8)
legend("bottomleft", paste("Source:", URL), cex = 0.7, bty = "n")

# calculate autocorrelation
acf(DF$PSA, main = "Autocorelation at Various Lags", xlab = "Lag in Days",
ylab = "Autocorrelation")
with(DF, sd(PSA)/mean(PSA))

# DF0 is DF sampled every 5 days since autocorrelation
# did not extend past 4 days
DF0 <- DF[seq(1, nrow(DF), 5),]
with(DF0, sd(PSA)/mean(PSA))

No comments: