44# Load libraries
55library(admiral )
66library(dplyr )
7+ library(cli )
8+
9+ # Print numeric numbers in regular decimal format
10+ options(scipen = 999 )
711
812# Read input data
913dm_neuro <- pharmaversesdtm :: dm_neuro
1014nv_neuro <- pharmaversesdtm :: nv_neuro
11- adsl_neuro <- admiralneuro :: adsl_neuro
1215
1316# Convert blank to NA
1417dm_neuro <- convert_blanks_to_na(dm_neuro )
18+ nv_neuro <- convert_blanks_to_na(nv_neuro )
1519
1620# Select patients from DM Neuro only (following adsl_neuro.R pattern)
1721# Separate randomized group to identify which subjects don't have planned study days
@@ -40,23 +44,22 @@ visit_schedule <- nv_neuro %>%
4044 ) %> %
4145 distinct()
4246
43- # Get subject characteristics with treatment information from adsl_neuro ----
47+ # Alpha Synuclein data generation
48+
49+ # Get subject characteristics with treatment information from dm_neuro ----
4450subject_chars <- dm_neuro %> %
45- select(STUDYID , USUBJID ) %> %
46- left_join(
47- adsl_neuro %> % select(STUDYID , USUBJID , TRT01P , TRT01PN , TRTSDT ),
48- by = c(" STUDYID" , " USUBJID" )
49- ) %> %
50- mutate(
51- # Create treatment groups for analysis
51+ select(STUDYID , USUBJID , ARM , ARMCD ) %> %
52+ mutate( # Create treatment groups for analysis (pattern as in nv_neuro.R)
5253 TRTGRP = case_when(
53- TRT01P %in% c(" Placebo" , " No Treatment" ) ~ " PLACEBO_CONTROL" ,
54- TRUE ~ " TREATMENT"
54+ ARM == " Placebo" ~ " PLACEBO_CONTROL" ,
55+ ARMCD == " Pbo" ~ " PLACEBO_CONTROL" ,
56+ ARMCD == " Xan_Hi" ~ " TREATMENT" ,
57+ is.na(ARMCD ) ~ " PLACEBO_CONTROL"
5558 )
5659 )
5760
58- # Create LB record for one subject-visit ----
59- create_lb_record <- function (usubjid , visitnum , visit , visitdy , lbdtc , lbdy , trtgrp ) {
61+ # Create Alpha Synuclein Seed Amplification Assay record for one subject-visit ----
62+ create_asyn_record <- function (usubjid , visitnum , visit , visitdy , lbdtc , lbdy , trtgrp ) {
6063 # Define positive rates based on treatment group and visit
6164 pos_rate <- case_when(
6265 trtgrp == " PLACEBO_CONTROL" ~ 0.30 , # 30% positive for placebo/control at all visits
@@ -101,12 +104,12 @@ create_lb_record <- function(usubjid, visitnum, visit, visitdy, lbdtc, lbdy, trt
101104# Set seed for reproducibility
102105set.seed(2774 )
103106
104- # Generate all LB records
105- all_lb_records <- visit_schedule %> %
107+ # Generate Alpha Synuclein LB records
108+ asyn_records <- visit_schedule %> %
106109 left_join(subject_chars , by = c(" USUBJID" )) %> %
107110 rowwise() %> %
108111 do(
109- create_lb_record (
112+ create_asyn_record (
110113 usubjid = . $ USUBJID ,
111114 visitnum = . $ VISITNUM ,
112115 visit = . $ VISIT ,
@@ -118,8 +121,194 @@ all_lb_records <- visit_schedule %>%
118121 ) %> %
119122 ungroup()
120123
124+ # pTau, Amyloid, and Ratio data generation
125+ # Input amyloid data from admiralneuro.adapet - ensure the data is accurate
126+ visit_all <- tribble(
127+ ~ USUBJID , ~ CRIT1FL , ~ CRIT1 , ~ VISITNUM , ~ VISIT , ~ LBDTC , ~ LBDY ,
128+ " 01-701-1015" , " N" , " CENTILOID < 24.1" , 0 , " BASELINE" , " 2014-01-02" , 1 ,
129+ " 01-701-1015" , " N" , " CENTILOID < 24.1" , 12 , " WEEK 12" , " 2014-03-26" , 84 ,
130+ " 01-701-1015" , " N" , " CENTILOID < 24.1" , 26 , " WEEK 26" , " 2014-07-02" , 182 ,
131+ " 01-701-1023" , " N" , " CENTILOID < 24.1" , 0 , " BASELINE" , " 2012-08-05" , 1 ,
132+ " 01-701-1028" , " N" , " CENTILOID < 24.1" , 0 , " BASELINE" , " 2013-07-19" , NA ,
133+ " 01-701-1028" , " N" , " CENTILOID < 24.1" , 12 , " WEEK 12" , " 2013-10-09" , NA ,
134+ " 01-701-1028" , " N" , " CENTILOID < 24.1" , 26 , " WEEK 26" , " 2014-01-14" , NA ,
135+ " 01-701-1034" , " N" , " CENTILOID < 24.1" , 0 , " BASELINE" , " 2014-07-01" , 1 ,
136+ " 01-701-1034" , " N" , " CENTILOID < 24.1" , 12 , " WEEK 12" , " 2014-09-25" , 87 ,
137+ " 01-701-1034" , " N" , " CENTILOID < 24.1" , 26 , " WEEK 26" , " 2014-12-30" , 183 ,
138+ " 01-701-1146" , " N" , " CENTILOID < 24.1" , 0 , " BASELINE" , " 2013-05-20" , 1 ,
139+ " 01-701-1153" , " N" , " CENTILOID < 24.1" , 0 , " BASELINE" , " 2013-09-23" , 1 ,
140+ " 01-701-1153" , " N" , " CENTILOID < 24.1" , 12 , " WEEK 12" , " 2013-12-16" , 85 ,
141+ " 01-701-1153" , " N" , " CENTILOID < 24.1" , 26 , " WEEK 26" , " 2014-04-01" , 191 ,
142+ " 01-701-1181" , " N" , " CENTILOID < 24.1" , 0 , " BASELINE" , " 2013-12-05" , NA ,
143+ " 01-701-1234" , " N" , " CENTILOID < 24.1" , 0 , " BASELINE" , " 2013-03-30" , 1 ,
144+ " 01-701-1234" , " N" , " CENTILOID < 24.1" , 12 , " WEEK 12" , " 2013-07-07" , 100 ,
145+ " 01-701-1234" , " N" , " CENTILOID < 24.1" , 26 , " WEEK 26" , " 2013-09-22" , 177 ,
146+ " 01-701-1275" , " N" , " CENTILOID < 24.1" , 0 , " BASELINE" , " 2014-02-07" , 1 ,
147+ " 01-701-1275" , " N" , " CENTILOID < 24.1" , 12 , " WEEK 12" , " 2014-05-03" , 86 ,
148+ " 01-701-1302" , " N" , " CENTILOID < 24.1" , 0 , " BASELINE" , " 2013-08-29" , 1 ,
149+ " 01-701-1302" , " Y" , " CENTILOID < 24.1" , 12 , " WEEK 12" , " 2013-11-05" , 69 ,
150+ " 01-701-1345" , " N" , " CENTILOID < 24.1" , 0 , " BASELINE" , " 2013-10-08" , NA ,
151+ " 01-701-1345" , " N" , " CENTILOID < 24.1" , 12 , " WEEK 12" , " 2013-12-31" , NA ,
152+ " 01-701-1360" , " N" , " CENTILOID < 24.1" , 0 , " BASELINE" , " 2013-07-31" , NA ,
153+ " 01-701-1383" , " N" , " CENTILOID < 24.1" , 0 , " BASELINE" , " 2013-02-04" , 1 ,
154+ " 01-701-1383" , " N" , " CENTILOID < 24.1" , 12 , " WEEK 12" , " 2013-04-30" , 86 ,
155+ " 01-701-1383" , " Y" , " CENTILOID < 24.1" , 26 , " WEEK 26" , " 2013-08-06" , 184 ,
156+ " 01-701-1392" , " N" , " CENTILOID < 24.1" , 0 , " BASELINE" , " 2012-10-28" , 1 ,
157+ " 01-701-1392" , " N" , " CENTILOID < 24.1" , 12 , " WEEK 12" , " 2013-01-20" , 85 ,
158+ " 01-701-1392" , " N" , " CENTILOID < 24.1" , 26 , " WEEK 26" , " 2013-04-28" , 183 ,
159+ " 01-714-1288" , " N" , " CENTILOID < 24.1" , 0 , " BASELINE" , " 2013-12-04" , NA ,
160+ " 01-714-1288" , " N" , " CENTILOID < 24.1" , 12 , " WEEK 12" , " 2014-03-02" , NA ,
161+ " 01-714-1288" , " N" , " CENTILOID < 24.1" , 26 , " WEEK 26" , " 2014-06-17" , NA
162+ )
163+
164+ # Create LB record for one subject-visit with pTau, Amyloid, and Ratio ----
165+ create_ratio_record <- function (
166+ usubjid ,
167+ visitnum ,
168+ visit ,
169+ visitdy ,
170+ lbdtc ,
171+ lbdy ,
172+ trtgrp ,
173+ crit1fl
174+ ) {
175+ set.seed(as.numeric(gsub(" \\ D" , " " , usubjid )) + visitnum )
176+
177+ # Generate Aβ42 plasma ----
178+ amyloid_value <- runif(1 , 80 , 1200 ) # pg/mL
179+
180+ # Generate pTau conditional on crit1fl status
181+ ptau_value <- case_when(
182+ crit1fl == " Y" ~ runif(1 ,
183+ min = 0.0015 * amyloid_value ,
184+ max = 0.00370 * amyloid_value
185+ ),
186+ crit1fl == " N" ~ runif(1 ,
187+ min = 0.00738 * amyloid_value ,
188+ max = 0.0120 * amyloid_value
189+ ),
190+ TRUE ~ runif(1 ,
191+ min = 0.00371 * amyloid_value ,
192+ max = 0.00737 * amyloid_value
193+ )
194+ )
195+
196+ # Placebo / Untreated stability
197+ if (trtgrp %in% c(" Placebo" , " No Treatment" ) && visit != " BASELINE" ) {
198+ ptau_value <- ptau_value * runif(1 , 0.98 , 1.02 )
199+ amyloid_value <- amyloid_value * runif(1 , 0.98 , 1.02 )
200+ }
201+
202+ # add Intermediate cases
203+ make_intermediate <- runif(1 ) < 0.15 # ~15% of visits
204+
205+ if (make_intermediate ) {
206+ # Force ratio into intermediate band
207+ ratio_target <- runif(1 , 0.00371 , 0.00737 )
208+
209+ # Adjust pTau (Aβ42 unchanged)
210+ ptau_value <- ratio_target * amyloid_value
211+ }
212+
213+ # Derive ratio
214+ ratio_value <- ptau_value / amyloid_value
215+
216+ # Interpretation
217+ ratio_lbnrind <- case_when(
218+ ratio_value < = 0.00370 ~ " Negative" ,
219+ ratio_value > = 0.00738 ~ " Positive" ,
220+ TRUE ~ " Indeterminate"
221+ )
222+
223+ # ---- pTau record ----
224+ ptau_record <- tibble(
225+ STUDYID = " CDISCPILOT01" ,
226+ DOMAIN = " LB" ,
227+ USUBJID = usubjid ,
228+ LBTESTCD = " PTAU217" ,
229+ LBTEST = " Lumipulse G pTau 217 Plasma" ,
230+ LBCAT = " Biomarkers" ,
231+ LBORRES = sprintf(" %.4f" , ptau_value ),
232+ LBORRESU = " pg/mL" ,
233+ LBSTRESC = sprintf(" %.4f" , ptau_value ),
234+ LBSTRESN = ptau_value ,
235+ LBSTRESU = " pg/mL" ,
236+ LBBLFL = ifelse(visit == " BASELINE" , " Y" , NA_character_ ),
237+ VISITNUM = visitnum ,
238+ VISIT = visit ,
239+ VISITDY = visitdy ,
240+ LBDTC = lbdtc ,
241+ LBDY = lbdy
242+ )
243+
244+ # ---- Aβ42 record ----
245+ amyloid_record <- tibble(
246+ STUDYID = " CDISCPILOT01" ,
247+ DOMAIN = " LB" ,
248+ USUBJID = usubjid ,
249+ LBTESTCD = " AMYLB42" ,
250+ LBTEST = " Lumipulse G Beta-Amyloid 1-42-N Plasma" ,
251+ LBCAT = " Biomarkers" ,
252+ LBORRES = sprintf(" %.1f" , amyloid_value ),
253+ LBORRESU = " pg/mL" ,
254+ LBSTRESC = sprintf(" %.1f" , amyloid_value ),
255+ LBSTRESN = amyloid_value ,
256+ LBSTRESU = " pg/mL" ,
257+ LBBLFL = ifelse(visit == " BASELINE" , " Y" , NA_character_ ),
258+ VISITNUM = visitnum ,
259+ VISIT = visit ,
260+ VISITDY = visitdy ,
261+ LBDTC = lbdtc ,
262+ LBDY = lbdy
263+ )
264+
265+ # ---- Ratio record ----
266+ ratio_record <- tibble(
267+ STUDYID = " CDISCPILOT01" ,
268+ DOMAIN = " LB" ,
269+ USUBJID = usubjid ,
270+ LBTESTCD = " PTAB42R" ,
271+ LBTEST = " Lumipulse G pTau 217/Beta-Amyloid 1-42 Plasma Ratio" ,
272+ LBCAT = " Biomarkers" ,
273+ LBORRES = sprintf(" %.5f" , ratio_value ),
274+ LBORNRLO = " 0.00370" ,
275+ LBORNRHI = " 0.00738" ,
276+ LBSTRESC = sprintf(" %.5f" , ratio_value ),
277+ LBSTRESN = ratio_value ,
278+ LBSTNRLO = 0.00370 ,
279+ LBSTNRHI = 0.00738 ,
280+ LBNRIND = ratio_lbnrind ,
281+ LBBLFL = ifelse(visit == " BASELINE" , " Y" , NA_character_ ),
282+ VISITNUM = visitnum ,
283+ VISIT = visit ,
284+ VISITDY = visitdy ,
285+ LBDTC = lbdtc ,
286+ LBDY = lbdy
287+ )
288+
289+ bind_rows(ptau_record , amyloid_record , ratio_record )
290+ }
291+
292+ # Generate all LB pTau, Amyloid and corresponding Ratio's records by joining 'visit_schedule' and 'subject_chars' ----
293+ ratio_records <- visit_all %> %
294+ left_join(subject_chars , by = " USUBJID" ) %> %
295+ rowwise() %> %
296+ do(
297+ create_ratio_record(
298+ usubjid = . $ USUBJID ,
299+ visitnum = . $ VISITNUM ,
300+ visit = . $ VISIT ,
301+ visitdy = . $ LBDY ,
302+ lbdtc = . $ LBDTC ,
303+ lbdy = . $ LBDY ,
304+ trtgrp = . $ TRTGRP ,
305+ crit1fl = . $ CRIT1FL
306+ )
307+ ) %> %
308+ ungroup()
309+
121310# Add sequence numbers and finalize
122- lb_neuro <- all_lb_records %> %
311+ lb_neuro <- bind_rows( asyn_records , ratio_records ) %> %
123312 arrange(USUBJID , VISITNUM ) %> %
124313 group_by(USUBJID ) %> %
125314 mutate(LBSEQ = row_number()) %> %
@@ -132,14 +321,29 @@ lb_neuro <- all_lb_records %>%
132321 )
133322
134323# Validation checks
135- expected_visits <- c(" BASELINE" )
136- stopifnot(all(lb_neuro $ VISIT %in% expected_visits ))
137- stopifnot(all(lb_neuro $ LBORRES %in% c(" Positive" , " Negative" )))
138- stopifnot(all(nchar(lb_neuro $ LBTESTCD ) < = 8 ))
324+ expected_visits <- c(" BASELINE" , " WEEK 12" , " WEEK 26" )
325+
326+ # Check if all VISIT values are in the expected set
327+ if (! all(lb_neuro $ VISIT %in% expected_visits )) {
328+ cli_abort(" The values in 'lb_neuro$VISIT' are not all in the expected 'expected_visits'." )
329+ }
330+
331+ # Check if LBORRES values are either "Positive" or "Negative"
332+ ASYNASAA_data <- lb_neuro [lb_neuro $ LBTESTCD == " ASYNASAA" , ]
333+ if (! all(ASYNASAA_data $ LBORRES %in% c(" Positive" , " Negative" ))) {
334+ cli_abort(
335+ message = " For LBTESTCD = ASYNASAA, the values in 'LBORRES' must be either 'Positive' or 'Negative'."
336+ )
337+ }
338+
339+ # Check if LBTESTCD values have more than 8 characters
340+ if (! all(nchar(lb_neuro $ LBTESTCD ) < = 8 )) {
341+ cli_abort(" The length of values in 'lb_neuro$LBTESTCD' must not exceed 8 characters." )
342+ }
139343
140344# Check for one record per subject per visit
141345visit_counts <- lb_neuro %> %
142- count(USUBJID , VISIT ) %> %
346+ count(USUBJID , LBTESTCD , VISIT ) %> %
143347 filter(n > 1 )
144348
145349if (nrow(visit_counts ) > 0 ) {
0 commit comments