Case 07 / Time to event
When did the first event occur?
What is the estimated probability of remaining free of a first dermatologic event over treatment follow-up?
Precomputed results · R and Python executed locally · no browser analysis engine
Executed source code
Current-view SVG rendering: TypeScript figure source and display controller. These render existing results; they do not run the R/Python analysis.
Full seven-case analysis pipeline. The exact executed file; use the commands below. This code is read-only.
# Clinical Data Lab: independently executed R analysis of CDISC Pilot XPT.
# Run: Rscript --vanilla analyze.R INPUT_DIRECTORY OUTPUT_DIRECTORY
# Packages must already be installed. No network or installation at runtime.
library(haven)
library(jsonlite)
library(survival)
args <- commandArgs(trailingOnly=TRUE)
input <- args[1]; output <- args[2]
dir.create(output, recursive=TRUE, showWarnings=FALSE)
read <- function(name) {
x <- as.data.frame(read_xpt(file.path(input, paste0(name, '.xpt'))))
x[] <- lapply(x, function(v) {if(is.character(v)) {v[is.na(v)] <- ''; trimws(v)} else v})
x
}
stats <- function(x) {
x <- x[!is.na(x)]; n <- length(x)
m <- if(n) mean(x) else NA_real_
s <- if(n>1) sd(x) else NA_real_
margin <- if(n>1) qt(.975,n-1)*s/sqrt(n) else NA_real_
list(n=n,estimate=m,sd=s,lower=m-margin,upper=m+margin)
}
adsl <- read('adsl'); ae <- read('adae'); q <- read('adqsadas'); lab <- read('adlbc'); tte <- read('adtte')
stopifnot(!anyDuplicated(adsl$USUBJID), !anyDuplicated(tte$USUBJID), all(tte$CNSR %in% c(0,1)))
stopifnot(all((adsl$DISCONFL=='Y')==(adsl$DCDECOD!='COMPLETED')))
q <- subset(q, PARAMCD=='ACTOT' & ANL01FL=='Y' & DTYPE=='')
stopifnot(!anyDuplicated(q[c('USUBJID','AVISIT')]))
lab <- subset(lab, PARAMCD=='ALT' & AVISIT=='End of Treatment')
stopifnot(!anyDuplicated(lab$USUBJID))
cases <- c('baseline','disposition','longitudinal','adverse-events','laboratory','subgroups','time-to-event')
out <- setNames(lapply(cases,function(x) list()),cases)
arms <- c('Placebo','Xanomeline Low Dose','Xanomeline High Dose')
terms <- unique(ae[c('AEBODSYS','AEDECOD')]); terms <- terms[order(terms$AEBODSYS,terms$AEDECOD),]
for(stratum in c('All','F','M')) {
cohort <- if(stratum=='All') adsl else subset(adsl,SEX==stratum)
for(arm in arms) {
subjects <- subset(cohort,TRT01A==arm & SAFFL=='Y'); ids <- subjects$USUBJID; N <- length(ids)
add <- function(case,measure,...) {
out[[case]][[length(out[[case]])+1]] <<- c(list(stratum=stratum,arm=arm,measure=measure),list(...))
}
for(col in c('AGE','WEIGHTBL','BMIBL')) {
label <- c(AGE='Age (years)',WEIGHTBL='Weight (kg)',BMIBL='BMI (kg/m^2)')[[col]]
s <- stats(subjects[[col]])
do.call(add,c(list(case='baseline',measure=label,denominator=N,missing=N-s$n),s))
}
for(col in c('SEX','RACE')) for(level in sort(unique(adsl[[col]]))) {
n <- sum(subjects[[col]]==level)
add('baseline',paste0(col,': ',level),n=n,denominator=N,missing=sum(subjects[[col]]==''),estimate=100*n/N)
}
add('disposition','Analysis cohort',n=N,denominator=N,estimate=100)
for(reason in sort(unique(adsl$DCDECOD))) {
n <- sum(subjects$DCDECOD==reason); add('disposition',reason,n=n,denominator=N,estimate=100*n/N)
}
qi <- subset(q,USUBJID %in% ids)
for(visit in c('Baseline','Week 8','Week 16','Week 24')) for(col in c('AVAL','CHG')) {
s <- stats(qi[qi$AVISIT==visit,col]); label <- if(col=='AVAL') 'ADAS-Cog(11) score' else 'Change from baseline'
time <- c(Baseline=0,'Week 8'=8,'Week 16'=16,'Week 24'=24)[[visit]]
do.call(add,c(list(case='longitudinal',measure=label,visit=visit,time=time,denominator=N,missing=N-s$n),s))
}
ai <- subset(ae,USUBJID %in% ids & TRTEMFL=='Y')
ae_row <- function(d,label,soc,level) add('adverse-events',label,soc=soc,level=level,n=length(unique(d$USUBJID)),events=nrow(d),denominator=N,estimate=100*length(unique(d$USUBJID))/N)
ae_row(ai,'Any treatment-emergent AE','','Any')
for(soc in sort(unique(ae$AEBODSYS))) ae_row(ai[ai$AEBODSYS==soc,],soc,soc,'SOC')
for(i in seq_len(nrow(terms))) {
soc <- terms$AEBODSYS[i]; pt <- terms$AEDECOD[i]
ae_row(ai[ai$AEBODSYS==soc & ai$AEDECOD==pt,],pt,soc,'PT')
}
li <- subset(lab,USUBJID %in% ids & BNRIND %in% c('L','N','H') & ANRIND %in% c('L','N','H'))
for(base in c('L','N','H')) for(follow in c('L','N','H')) {
n <- sum(li$BNRIND==base & li$ANRIND==follow)
add('laboratory',paste0(base,' -> ',follow),n=n,denominator=nrow(li),missing=N-nrow(li),estimate=if(nrow(li)) 100*n/nrow(li) else NA_real_)
}
ti <- subset(tte,USUBJID %in% ids & PARAMCD=='TTDE')
stopifnot(nrow(ti)==N,all(!is.na(ti$AVAL)))
fit <- survfit(Surv(AVAL,1-CNSR)~1,data=ti,conf.type='log-log')
add('time-to-event','Event-free probability',time=0,risk=N,events=0,censored=0,estimate=1,lower=1,upper=1,denominator=N)
for(i in seq_along(fit$time)) add('time-to-event','Event-free probability',time=fit$time[i],risk=fit$n.risk[i],events=fit$n.event[i],censored=fit$n.censor[i],estimate=fit$surv[i],lower=fit$lower[i],upper=fit$upper[i],denominator=N)
}
for(label in c('Overall','Age <65','Age >=65')) {
group <- subset(cohort,ITTFL=='Y')
if(label=='Age <65') group <- subset(group,AGE<65)
if(label=='Age >=65') group <- subset(group,AGE>=65)
end <- subset(q,AVISIT=='Week 24' & USUBJID %in% group$USUBJID)
placebo <- end$CHG[end$USUBJID %in% group$USUBJID[group$TRT01P=='Placebo']]; placebo <- na.omit(placebo)
for(arm in arms[-1]) {
active <- end$CHG[end$USUBJID %in% group$USUBJID[group$TRT01P==arm]]; active <- na.omit(active)
est <- lo <- hi <- NA_real_
if(min(length(active),length(placebo))>=2) {
test <- t.test(active,placebo,var.equal=FALSE,conf.level=.95)
est <- mean(active)-mean(placebo); lo <- test$conf.int[1]; hi <- test$conf.int[2]
}
N <- sum(group$TRT01P==arm)
out[['subgroups']][[length(out[['subgroups']])+1]] <- list(stratum=stratum,arm=arm,measure=label,n=length(active),control_n=length(placebo),denominator=N,missing=N-length(active),estimate=est,lower=lo,upper=hi)
}
}
}
for(case in cases) write_json(out[[case]],file.path(output,paste0(case,'.json')),auto_unbox=TRUE,pretty=TRUE,digits=16,na='null')
f <- stats(c(1,2,3,NA)); stopifnot(f$n==3,f$estimate==2,f$sd==1)
k <- survfit(Surv(c(1,2,2,3),c(1,1,0,1))~1)
stopifnot(abs(k$surv[2]-.5)<1e-12,k$n.risk[2]==3)
packages <- c('haven','jsonlite','survival','ggplot2')
write_json(list(R=as.character(getRversion()),packages=setNames(lapply(packages,function(p) as.character(packageVersion(p))),packages),fixtures='pass',seed='not applicable: deterministic'),file.path(output,'environment.json'),auto_unbox=TRUE,pretty=TRUE)
print(sapply(out,length)); print(sessionInfo())
# Clinical Data Lab: independently executed R analysis of CDISC Pilot XPT.
# Run: Rscript --vanilla analyze.R INPUT_DIRECTORY OUTPUT_DIRECTORY
# Packages must already be installed. No network or installation at runtime.
library(haven)
library(jsonlite)
library(survival)
args <- commandArgs(trailingOnly=TRUE)
input <- args[1]; output <- args[2]
dir.create(output, recursive=TRUE, showWarnings=FALSE)
read <- function(name) {
x <- as.data.frame(read_xpt(file.path(input, paste0(name, '.xpt'))))
x[] <- lapply(x, function(v) {if(is.character(v)) {v[is.na(v)] <- ''; trimws(v)} else v})
x
}
stats <- function(x) {
x <- x[!is.na(x)]; n <- length(x)
m <- if(n) mean(x) else NA_real_
s <- if(n>1) sd(x) else NA_real_
margin <- if(n>1) qt(.975,n-1)*s/sqrt(n) else NA_real_
list(n=n,estimate=m,sd=s,lower=m-margin,upper=m+margin)
}
adsl <- read('adsl'); ae <- read('adae'); q <- read('adqsadas'); lab <- read('adlbc'); tte <- read('adtte')
stopifnot(!anyDuplicated(adsl$USUBJID), !anyDuplicated(tte$USUBJID), all(tte$CNSR %in% c(0,1)))
stopifnot(all((adsl$DISCONFL=='Y')==(adsl$DCDECOD!='COMPLETED')))
q <- subset(q, PARAMCD=='ACTOT' & ANL01FL=='Y' & DTYPE=='')
stopifnot(!anyDuplicated(q[c('USUBJID','AVISIT')]))
lab <- subset(lab, PARAMCD=='ALT' & AVISIT=='End of Treatment')
stopifnot(!anyDuplicated(lab$USUBJID))
cases <- c('baseline','disposition','longitudinal','adverse-events','laboratory','subgroups','time-to-event')
out <- setNames(lapply(cases,function(x) list()),cases)
arms <- c('Placebo','Xanomeline Low Dose','Xanomeline High Dose')
terms <- unique(ae[c('AEBODSYS','AEDECOD')]); terms <- terms[order(terms$AEBODSYS,terms$AEDECOD),]
for(stratum in c('All','F','M')) {
cohort <- if(stratum=='All') adsl else subset(adsl,SEX==stratum)
for(arm in arms) {
subjects <- subset(cohort,TRT01A==arm & SAFFL=='Y'); ids <- subjects$USUBJID; N <- length(ids)
add <- function(case,measure,...) {
out[[case]][[length(out[[case]])+1]] <<- c(list(stratum=stratum,arm=arm,measure=measure),list(...))
}
for(col in c('AGE','WEIGHTBL','BMIBL')) {
label <- c(AGE='Age (years)',WEIGHTBL='Weight (kg)',BMIBL='BMI (kg/m^2)')[[col]]
s <- stats(subjects[[col]])
do.call(add,c(list(case='baseline',measure=label,denominator=N,missing=N-s$n),s))
}
for(col in c('SEX','RACE')) for(level in sort(unique(adsl[[col]]))) {
n <- sum(subjects[[col]]==level)
add('baseline',paste0(col,': ',level),n=n,denominator=N,missing=sum(subjects[[col]]==''),estimate=100*n/N)
}
add('disposition','Analysis cohort',n=N,denominator=N,estimate=100)
for(reason in sort(unique(adsl$DCDECOD))) {
n <- sum(subjects$DCDECOD==reason); add('disposition',reason,n=n,denominator=N,estimate=100*n/N)
}
qi <- subset(q,USUBJID %in% ids)
for(visit in c('Baseline','Week 8','Week 16','Week 24')) for(col in c('AVAL','CHG')) {
s <- stats(qi[qi$AVISIT==visit,col]); label <- if(col=='AVAL') 'ADAS-Cog(11) score' else 'Change from baseline'
time <- c(Baseline=0,'Week 8'=8,'Week 16'=16,'Week 24'=24)[[visit]]
do.call(add,c(list(case='longitudinal',measure=label,visit=visit,time=time,denominator=N,missing=N-s$n),s))
}
ai <- subset(ae,USUBJID %in% ids & TRTEMFL=='Y')
ae_row <- function(d,label,soc,level) add('adverse-events',label,soc=soc,level=level,n=length(unique(d$USUBJID)),events=nrow(d),denominator=N,estimate=100*length(unique(d$USUBJID))/N)
ae_row(ai,'Any treatment-emergent AE','','Any')
for(soc in sort(unique(ae$AEBODSYS))) ae_row(ai[ai$AEBODSYS==soc,],soc,soc,'SOC')
for(i in seq_len(nrow(terms))) {
soc <- terms$AEBODSYS[i]; pt <- terms$AEDECOD[i]
ae_row(ai[ai$AEBODSYS==soc & ai$AEDECOD==pt,],pt,soc,'PT')
}
li <- subset(lab,USUBJID %in% ids & BNRIND %in% c('L','N','H') & ANRIND %in% c('L','N','H'))
for(base in c('L','N','H')) for(follow in c('L','N','H')) {
n <- sum(li$BNRIND==base & li$ANRIND==follow)
add('laboratory',paste0(base,' -> ',follow),n=n,denominator=nrow(li),missing=N-nrow(li),estimate=if(nrow(li)) 100*n/nrow(li) else NA_real_)
}
ti <- subset(tte,USUBJID %in% ids & PARAMCD=='TTDE')
stopifnot(nrow(ti)==N,all(!is.na(ti$AVAL)))
fit <- survfit(Surv(AVAL,1-CNSR)~1,data=ti,conf.type='log-log')
add('time-to-event','Event-free probability',time=0,risk=N,events=0,censored=0,estimate=1,lower=1,upper=1,denominator=N)
for(i in seq_along(fit$time)) add('time-to-event','Event-free probability',time=fit$time[i],risk=fit$n.risk[i],events=fit$n.event[i],censored=fit$n.censor[i],estimate=fit$surv[i],lower=fit$lower[i],upper=fit$upper[i],denominator=N)
}
for(label in c('Overall','Age <65','Age >=65')) {
group <- subset(cohort,ITTFL=='Y')
if(label=='Age <65') group <- subset(group,AGE<65)
if(label=='Age >=65') group <- subset(group,AGE>=65)
end <- subset(q,AVISIT=='Week 24' & USUBJID %in% group$USUBJID)
placebo <- end$CHG[end$USUBJID %in% group$USUBJID[group$TRT01P=='Placebo']]; placebo <- na.omit(placebo)
for(arm in arms[-1]) {
active <- end$CHG[end$USUBJID %in% group$USUBJID[group$TRT01P==arm]]; active <- na.omit(active)
est <- lo <- hi <- NA_real_
if(min(length(active),length(placebo))>=2) {
test <- t.test(active,placebo,var.equal=FALSE,conf.level=.95)
est <- mean(active)-mean(placebo); lo <- test$conf.int[1]; hi <- test$conf.int[2]
}
N <- sum(group$TRT01P==arm)
out[['subgroups']][[length(out[['subgroups']])+1]] <- list(stratum=stratum,arm=arm,measure=label,n=length(active),control_n=length(placebo),denominator=N,missing=N-length(active),estimate=est,lower=lo,upper=hi)
}
}
}
for(case in cases) write_json(out[[case]],file.path(output,paste0(case,'.json')),auto_unbox=TRUE,pretty=TRUE,digits=16,na='null')
f <- stats(c(1,2,3,NA)); stopifnot(f$n==3,f$estimate==2,f$sd==1)
k <- survfit(Surv(c(1,2,2,3),c(1,1,0,1))~1)
stopifnot(abs(k$surv[2]-.5)<1e-12,k$n.risk[2]==3)
packages <- c('haven','jsonlite','survival','ggplot2')
write_json(list(R=as.character(getRversion()),packages=setNames(lapply(packages,function(p) as.character(packageVersion(p))),packages),fixtures='pass',seed='not applicable: deterministic'),file.path(output,'environment.json'),auto_unbox=TRUE,pretty=TRUE)
print(sapply(out,length)); print(sessionInfo())
"""Clinical Data Lab: independent Python analysis of unchanged CDISC Pilot XPT.
Run: python analyze.py INPUT_DIRECTORY OUTPUT_DIRECTORY
No random sampling, imputation, fitting service, or patient upload.
"""
import sys, json, math, platform, importlib.metadata as metadata
from pathlib import Path
import numpy as np
import pandas as pd
from scipy.stats import t
def read(name):
d = pd.read_sas(INPUT / (name + '.xpt'), format='xport', encoding='utf-8')
for c in d.select_dtypes(include=['object','str']).columns:
d[c] = d[c].fillna('').str.strip()
# Legacy IBM XPORT numeric zero can decode as 16**-65 in pandas.
# Normalize only nonzero subnormal XPORT placeholders, never missing values.
for c in d.select_dtypes(include='number').columns:
d.loc[d[c].abs().between(0, 1e-70, inclusive='neither'), c] = 0.0
return d
def summary(values):
x = np.asarray(pd.Series(values).dropna(),dtype=float)
n = len(x)
mean = float(x.mean()) if n else None
sd = float(x.std(ddof=1)) if n > 1 else None
margin = float(t.ppf(.975,n-1)*sd/math.sqrt(n)) if n>1 else None
return dict(n=n,estimate=mean,sd=sd,lower=mean-margin if margin is not None else None,upper=mean+margin if margin is not None else None)
def welch(a,b):
a,b=np.asarray(a.dropna()),np.asarray(b.dropna())
if min(len(a),len(b))<2: return dict(estimate=None,lower=None,upper=None)
va,vb=a.var(ddof=1)/len(a),b.var(ddof=1)/len(b)
se=math.sqrt(va+vb); df=(va+vb)**2/(va**2/(len(a)-1)+vb**2/(len(b)-1))
delta=float(a.mean()-b.mean()); margin=float(t.ppf(.975,df)*se)
return dict(estimate=delta,lower=delta-margin,upper=delta+margin)
def km(times,status):
times,status=np.asarray(times),np.asarray(status)
s=1.; greenwood=0.; rows=[]
for tm in sorted(set(times)):
risk=int((times>=tm).sum()); events=int(((times==tm)&(status==1)).sum()); censored=int(((times==tm)&(status==0)).sum())
s*=1-events/risk
if events and risk>events: greenwood+=events/(risk*(risk-events))
if s==1: lo=hi=1.
elif s==0: lo=hi=None
else:
se=math.sqrt(greenwood)/abs(math.log(s)); z=1.959963984540054
lo=math.exp(-math.exp(math.log(-math.log(s))+z*se)); hi=math.exp(-math.exp(math.log(-math.log(s))-z*se))
rows.append(dict(time=float(tm),risk=risk,events=events,censored=censored,estimate=s,lower=lo,upper=hi))
return rows
def run():
adsl,ae,q,lab,tte=[read(n) for n in ['adsl','adae','adqsadas','adlbc','adtte']]
assert adsl.USUBJID.is_unique
assert set(tte.CNSR)=={0,1} and tte.USUBJID.is_unique
assert (adsl.DISCONFL.eq('Y') == adsl.DCDECOD.ne('COMPLETED')).all()
q=q[(q.PARAMCD=='ACTOT')&(q.ANL01FL=='Y')&(q.DTYPE=='')]
assert not q.duplicated(['USUBJID','AVISIT']).any()
# End of Treatment is an explicit analysis visit; do not reuse ANL01FL as a visit selector.
lab=lab[(lab.PARAMCD=='ALT')&(lab.AVISIT=='End of Treatment')]
assert not lab.duplicated(['USUBJID']).any()
out={k:[] for k in ['baseline','disposition','longitudinal','adverse-events','laboratory','subgroups','time-to-event']}
arms=['Placebo','Xanomeline Low Dose','Xanomeline High Dose']
terms=sorted(set(zip(ae.AEBODSYS,ae.AEDECOD)))
for stratum in ['All','F','M']:
cohort=adsl if stratum=='All' else adsl[adsl.SEX==stratum]
for arm in arms:
subjects=cohort[(cohort.TRT01A==arm)&(cohort.SAFFL=='Y')]
ids=set(subjects.USUBJID); N=len(ids)
def add(case,measure,**kw):
out[case].append(dict(stratum=stratum,arm=arm,measure=measure,**kw))
for col,label in [('AGE','Age (years)'),('WEIGHTBL','Weight (kg)'),('BMIBL','BMI (kg/m^2)')]:
stats=summary(subjects[col]); add('baseline',label,denominator=N,missing=N-stats['n'],**stats)
for col,levels in [('SEX',['F','M']),('RACE',sorted(adsl.RACE.unique()))]:
for level in levels:
n=int((subjects[col]==level).sum()); add('baseline',f'{col}: {level}',n=n,denominator=N,missing=int(subjects[col].eq('').sum()),estimate=100*n/N)
add('disposition','Analysis cohort',n=N,denominator=N,estimate=100.)
for reason in sorted(adsl.DCDECOD.unique()):
n=int((subjects.DCDECOD==reason).sum()); add('disposition',reason,n=n,denominator=N,estimate=100*n/N)
qi=q[q.USUBJID.isin(ids)]
for visit in ['Baseline','Week 8','Week 16','Week 24']:
for col,label in [('AVAL','ADAS-Cog(11) score'),('CHG','Change from baseline')]:
stats=summary(qi.loc[qi.AVISIT==visit,col]); add('longitudinal',label,visit=visit,time={'Baseline':0,'Week 8':8,'Week 16':16,'Week 24':24}[visit],denominator=N,missing=N-stats['n'],**stats)
ai=ae[ae.USUBJID.isin(ids)&(ae.TRTEMFL=='Y')]
for soc,pt in [('', 'Any treatment-emergent AE')]+[(s,'') for s in sorted(ae.AEBODSYS.unique())]+terms:
subset=ai if not soc else ai[(ai.AEBODSYS==soc)&((ai.AEDECOD==pt) if pt else True)]
n=int(subset.USUBJID.nunique()); add('adverse-events',pt or soc,soc=soc,level='Any' if not soc else ('PT' if pt else 'SOC'),n=n,events=len(subset),denominator=N,estimate=100*n/N)
li=lab[lab.USUBJID.isin(ids)]
paired=li[li.BNRIND.isin(['L','N','H'])&li.ANRIND.isin(['L','N','H'])]
for base in ['L','N','H']:
for follow in ['L','N','H']:
n=int(((paired.BNRIND==base)&(paired.ANRIND==follow)).sum()); add('laboratory',f'{base} -> {follow}',n=n,denominator=len(paired),missing=N-len(paired),estimate=100*n/len(paired) if len(paired) else None)
ti=tte[tte.USUBJID.isin(ids)&(tte.PARAMCD=='TTDE')]
assert ti.AVAL.notna().all() and len(ti)==N
add('time-to-event','Event-free probability',time=0.,risk=N,events=0,censored=0,estimate=1.,lower=1.,upper=1.,denominator=N)
for row in km(ti.AVAL,1-ti.CNSR): add('time-to-event','Event-free probability',denominator=N,**row)
for label,mask in [('Overall',pd.Series(True,index=cohort.index)),('Age <65',cohort.AGE<65),('Age >=65',cohort.AGE>=65)]:
group=cohort[mask & cohort.ITTFL.eq('Y')]
end=q[(q.AVISIT=='Week 24')&q.USUBJID.isin(group.USUBJID)]
placebo=end[end.USUBJID.isin(group[group.TRT01P=='Placebo'].USUBJID)].CHG.dropna()
for arm in arms[1:]:
active=end[end.USUBJID.isin(group[group.TRT01P==arm].USUBJID)].CHG.dropna()
out['subgroups'].append(dict(stratum=stratum,arm=arm,measure=label,n=len(active),control_n=len(placebo),denominator=len(group[group.TRT01P==arm]),missing=len(group[group.TRT01P==arm])-len(active),**welch(active,placebo)))
for case,rows in out.items():
(OUTPUT/(case+'.json')).write_text(json.dumps(rows,ensure_ascii=False,allow_nan=False,indent=2)+'\n',encoding='utf8')
fixture=summary([1,2,3,None]); assert fixture['n']==3 and fixture['estimate']==2 and fixture['sd']==1
f=km([1,2,2,3],[1,1,0,1]); assert abs(f[1]['estimate']-.5)<1e-12 and f[1]['risk']==3
(OUTPUT/'environment.json').write_text(json.dumps(dict(python=platform.python_version(),packages={p:metadata.version(p) for p in ['numpy','pandas','scipy']},fixtures='pass',seed='not applicable: deterministic'),indent=2),encoding='utf8')
print(json.dumps({k:len(v) for k,v in out.items()}))
if __name__=='__main__':
INPUT=Path(sys.argv[1]); OUTPUT=Path(sys.argv[2]); OUTPUT.mkdir(parents=True,exist_ok=True); run()
Reproduction instructions ↓Time to dermatologic event
Loading verified results…
Results could not be loaded. Check the local server and try again.
Focus a plotted point to inspect its exact value.
Search is a display filter: it selects matching rows for the chart, detail table and CSV. Cohort context tables are explicitly labeled. CSV includes every matching row across pages; it does not contain participant-level data.
Column definitions
n: observed subjects (continuous), subjects meeting the category, or active-arm observed subjects (subgroups). N: eligible arm/stratum cohort, except laboratory shifts where N is the paired cohort. Missing: eligible subjects excluded from that statistic. SD: sample standard deviation. CI: pointwise 95% interval. Events: records for AE, first events at that day for Kaplan–Meier. Risk: subjects immediately before that day. Null estimates sort last.
Figures rendered by R and Python
Separately rendered SVG exports from each language's own results. These downloads always use all participants and the original single-measure display (baseline: age; AE: SOC); they do not follow browser filters. These original analysis exports retain their original layout and palette. For the redesigned current view, use Download chart above.
Data
The population behind the result
Safety population; ADTTE PARAMCD = TTDE, actual treatment, one row per subject.
- Dataset and variables
- ADTTE: AVAL = ADT − STARTDT + 1 (days), CNSR = 0 event / 1 censored, PARAMCD = TTDE, EVNTDESC. This is not overall survival.
- Source
- CDISC SDTM/ADaM Pilot Project, commit
667511d. Public test data; no employer data or additional synthetic endpoints. Original XPT files are downloaded unchanged and kept outside public assets.
Provenance, dictionary and source terms · Download full case results (JSON, all strata)
Methods
What was estimated—and what was not
Kaplan–Meier product limit with events handled before censoring at ties. 95% pointwise log-log intervals using Greenwood variance. Risk counts are immediately before the stated day; + marks indicate one or more censors. Python independently implements the estimator; R uses survival::survfit.
Missing values
No missing time/status in this input. Analysis fails if times/status are incomplete. Censoring follows the supplied dataset, without rederiving follow-up.
Interpretation limits
Independent censoring is an assumption. The source describes non-events as censored at Study Completion Date. No hazard ratio or survival benefit is claimed; intervals are undefined after survival reaches zero.
Display rounding: figure means/percentages 1 decimal, forest contrasts 2 decimals; detail tables up to 3 decimals; counts are integers. Exports retain unrounded numeric results. Sex filters select separately calculated strata. Treatment toggles only select arm-specific results and never recalculate a pooled result.
Quality control
Evidence you can inspect
Independent R and Python outputs agree for this case, including counts, denominators, estimates and available intervals.
Tool identity / permitted local entry point is pending confirmation. Numerical agreement is not a package risk assessment.
Absolute and relative tolerance: 1e-9. Executed: . Counts are checked without rounding; known-answer mean, SD and tied-event/censor fixtures run in both languages.
Reproduce
From unchanged input to checked output
Download the files below into one directory. Use the recorded R/Python package versions; no package is installed by these scripts. The data download contacts only the public source repository. Both analysis scripts run without network access.
python fetch-data.py
Rscript --vanilla analyze.R inputs outputs/r
python analyze.py inputs outputs/pythonData fetch script · R analysis · Python analysis · Environment and hashes · Input SHA-256 manifest
The full local project also includes tools/clinical-data-lab/verify.py for fail-closed comparison and export. No random seed is needed: all analyses are deterministic.