使用R语言生成CDISC SDTM.AE domain

写在前面

  • 使用的是Rstudio

  • 其实R已经有生成sdtm相关的package,以下代码仅作为练习R语言的语法,不是高效生成sdtm的方法

  • 代码中没有解决的问题包括:EPOCH相关的逻辑没有考虑partial date的情况;在使用arrange() function做-SEQ排序时,关于大小写英文字母的排序机制似乎与SAS语言的sort function有所不同,导致使用相同的排序变量,通过R和SAS排序后,record的顺序会有不同;输出xpt结果是乱码,暂时输出到csv文件中。

  • 还没有写生成SUPPAE的代码

  • 代码参考了以下材料

Generating .xpt files with SAS, R and Python

https://www.pharmasug.org/proceedings/2021/EP/PharmaSUG-2021-EP-057.pdf

Yotube @mycsg

mycsg TASKS-SDTMGEN

以下是R代码

setwd('C://R_software')

library(haven)

library(dplyr)

library(tidyverse)

library(sas7bdat)

library(SASxport)

library(Hmisc)

import source data

raw_ae_001 <- read_sas('C://rawdata/ae_001.sas7bdat')

raw_meddrathsaurus <- read_sas('C://rawdata/meddrathesaurus.sas7bdat')

sdtm_dm <- read_sas('C://sdtmdata/SDTM/DM.sas7bdat')

sdtm_se <- read_sas('C://rawdata/SE.sas7bdat')

Update the variable name to uppercase, because var name is case sensitive in R

names(raw_ae_001) <- toupper(names(raw_ae_001))

names(raw_meddrathsaurus) <- toupper(names(raw_meddrathsaurus))

Filter ae raw data with AETERM not missing, and keep necessary variables

ae <- raw_ae_001 %>%

select(SUBJECT,RECORDPOSITION,AETERM,AESTDAT_RAW,AESTTIM,AEENDAT_RAW,AEENTIM,

AESEV_STD,AESER_STD,AEACN_STD,AEREL_STD,AEREL_WD_STD,AEPATT_STD,AEOUT_STD,AESCONG_STD,

AESDISAB_STD,AESDTH_STD,AESHOSP_STD,AESLIFE_STD,AESMIE_STD,AEONGO) %>%

filter(AETERM != "")

Update AETERM value to uppercase in order to merge with source MedDRA coding data

aeAETERM \<- toupper(aeAETERM)

Filter MedDRA source data with AE pannel only

meddra <- raw_meddrathsaurus %>%

filter(PANEL=="AE")

Merge AE and MedDRA data (left join) by AETERM, create AESTDTC/AEENDTC

ae1 <- merge (ae, meddra, by.x = c("AETERM"), by.y = c("VERBATIM"), all.x = T) %>%

create AESTDTC

mutate(

stdayn = suppressWarnings(as.numeric(word(AESTDAT_RAW,1))), ### as.numeric>>input, word>>scan

stday = if_else(!is.na(stdayn), str_pad(stdayn, width = 2, pad = "0"), "-"), ### is.na>>not missing, !>>not, str_pad>>put xx.

stmonthc = str_to_upper(word(AESTDAT_RAW, 2)), ### str_to_upper>>uppercase

stmonth = case_when(

stmonthc == "JAN" ~ "01",

stmonthc == "FEB" ~ "02",

stmonthc == "MAR" ~ "03",

stmonthc == "APR" ~ "04",

stmonthc == "MAY" ~ "05",

stmonthc == "JUN" ~ "06",

stmonthc == "JUL" ~ "07",

stmonthc == "AUG" ~ "08",

stmonthc == "SEP" ~ "09",

stmonthc == "OCT" ~ "10",

stmonthc == "NOV" ~ "11",

stmonthc == "DEC" ~ "12",

TRUE ~ "-"

),

styear = word(AESTDAT_RAW,3),

styear1 = if_else((styear == "UNK") | (is.na(styear)), "-", styear), ### | >> or

aestdate = str_c(styear1, stmonth, stday, sep = "-"), ### str_c >> catx

AESTDTC = if_else(AESTTIM != "", str_c(aestdate, str_pad(AESTTIM, width = 5, pad = "0"), sep = "T"), aestdate),

AESTDTC = if_else(str_sub(AESTDTC, -5) == "-----", "", AESTDTC),

AESTDTC = if_else(str_sub(AESTDTC, -4) == "----", str_sub(AESTDTC,end=-5), AESTDTC),

AESTDTC = if_else(str_sub(AESTDTC, -2) == "--", str_sub(AESTDTC,end=-3), AESTDTC)

) %>%

create AEENDTC

mutate(

endayn = suppressWarnings(as.numeric(word(AEENDAT_RAW,1))), ### as.numeric>>input, word>>scan

enday = if_else(!is.na(endayn), str_pad(endayn, width = 2, pad = "0"), "-"), ### is.na>>not missing, !>>not, str_pad>>put xx.

enmonthc = str_to_upper(word(AEENDAT_RAW, 2)), ### str_to_upper>>uppercase

enmonth = case_when(

enmonthc == "JAN" ~ "01",

enmonthc == "FEB" ~ "02",

enmonthc == "MAR" ~ "03",

enmonthc == "APR" ~ "04",

enmonthc == "MAY" ~ "05",

enmonthc == "JUN" ~ "06",

enmonthc == "JUL" ~ "07",

enmonthc == "AUG" ~ "08",

enmonthc == "SEP" ~ "09",

enmonthc == "OCT" ~ "10",

enmonthc == "NOV" ~ "11",

enmonthc == "DEC" ~ "12",

TRUE ~ "-"

),

enyear = word(AEENDAT_RAW,3),

enyear1 = if_else((enyear == "UNK") | (is.na(enyear)), "-", enyear), ### | >> or

aeendate = str_c(enyear1, enmonth, enday, sep = "-"), ### str_c >> catx

AEENDTC = if_else(AEENTIM != "", str_c(aeendate, str_pad(AEENTIM, width = 5, pad = "0"), sep = "T"), aeendate),

AEENDTC = if_else(str_sub(AEENDTC, -5) == "-----", "", AEENDTC),

AEENDTC = if_else(str_sub(AEENDTC, -4) == "----", str_sub(AEENDTC,end=-5), AEENDTC),

AEENDTC = if_else(str_sub(AEENDTC, -2) == "--", str_sub(AEENDTC,end=-3), AEENDTC)

)

Create AE domain vars

ae2 <- ae1 %>%

cbind(

STUDYID=c("PROTOCOLID"),

DOMAIN=c("AE"),

USUBJID=str_c(c("PROTOCOLID-0"),substr(ae1SUBJECT,4,6),c("-00"),substr(ae1SUBJECT,7,9)), # str_c() is catx() in SAS

SUBJID=ae1$SUBJECT,

AESPID=str_c(c("AE_001-"),ae1$RECORDPOSITION),

AELLT=ae1$LLT_NAME,

AELLTCD=ae1$LLT_CODE,

AEDECOD=ae1$PT_NAME,

AEPTCD=ae1$PT_CODE,

AEHLT=ae1$HLT_NAME,

AEHLTCD=ae1$HLT_CODE,

AEHLGT=ae1$HGT_NAME,

AEHLGTCD=ae1$HGT_CODE,

AEBODSYS=ae1$SOC_NAME,

AEBDSYCD=ae1$SOC_CODE,

AESOC=ae1$SOC_NAME,

AESOCCD=ae1$SOC_CODE,

AESEV=ae1$AESEV_STD,

AESER=ae1$AESER_STD,

AEACN=ae1$AEACN_STD,

AEREL=ae1$AEREL_STD,

AERELNST=ae1$AEREL_WD_STD,

AEPATT=ae1$AEPATT_STD,

AEOUT=ae1$AEOUT_STD,

AESCONG=ae1$AESCONG_STD,

AESDISAB=ae1$AESDISAB_STD,

AESDTH=ae1$AESDTH_STD,

AESHOSP=ae1$AESHOSP_STD,

AESLIFE=ae1$AESLIFE_STD,

AESMIE=ae1$AESMIE_STD

)

Merge AE and SDTM.DM by USUBJID, create AESTDY/AEENDY

sdtm_dm <- select(sdtm_dm,USUBJID,RFSTDTC,RFENDTC)

ae3 <- merge (ae2, sdtm_dm, by = c("USUBJID"), all.x = T) %>%

mutate(

aestdt=as.Date(AESTDTC),

rfstdt=as.Date(RFSTDTC),

rfstdate=str_sub(RFSTDTC,1,10),

rfst_year=str_sub(RFSTDTC,1,4),

rfst_month=str_sub(RFSTDTC,6,7),

rfst_day=str_sub(RFSTDTC,9,10),

AESTDY=ifelse(!is.na(aestdt) & !is.na(rfstdt),

ifelse((aestdt>=rfstdt),aestdt-rfstdt+1,aestdt-rfstdt), ""

)

) %>%

mutate(

aeendt=as.Date(AEENDTC),

rfstdt=as.Date(RFSTDTC),

AEENDY=ifelse(!is.na(aeendt) & !is.na(rfstdt),

ifelse((aeendt>=rfstdt),aeendt-rfstdt+1,aeendt-rfstdt), ""

)

) %>%

create AEENRTPT, AEENTPT

mutate(

AEENRTPT=ifelse(AEONGO==1,"ONGOING",""),

AEENTPT=ifelse(AEONGO==1,

ifelse(is.na(rfstdt)==T,"SCREENING","END OF STUDY"),""

)

)

prepare SE dataset for creating EPOCH

sdtm_se <- select(sdtm_se,USUBJID,ETCD,SESTDTC,SEENDTC)

sest <- sdtm_se %>%

select(USUBJID,ETCD,SESTDTC) %>%

pivot_wider(names_from=ETCD, values_from=SESTDTC)

colnames(sest) <- c("USUBJID","st1","st2","st3")

seen <- sdtm_se %>%

select(USUBJID,ETCD,SEENDTC) %>%

pivot_wider(names_from=ETCD, values_from=SEENDTC)

colnames(seen) <- c("USUBJID","en1","en2","en3")

sesten <- merge (sest, seen, by = c("USUBJID"))

ae4 <- merge (ae3, sesten, by = c("USUBJID"), all.x = T)

ae5 <- ae4 %>%

mutate(EPOCH=NA) %>%

mutate(

EPOCH=ifelse((st1<=aestdt & aestdt<en1) | (aestdt<=en1 & is.na(st2)==T), "SCREENING",ifelse(st2<=aestdt & aestdt<=en2, "TREATMENT", "FOLLOW-UP"))

) %>%

#mutate(

EPOCH=ifelse(!is.na(EPOCH)==T & !is.na(stday)==T, EPOCH, ifelse())

#) %>%

arrange(STUDYID,USUBJID,AEDECOD,AESTDTC,AEENDTC,AESPID) %>%

group_by(USUBJID) %>%

mutate(AESEQ=row_number())

select target vars in AE

sdtm_ae <- select(ae5,STUDYID,DOMAIN,USUBJID,SUBJID,AESEQ,AESPID,

AETERM,AELLT,AELLTCD,AEDECOD,AEPTCD,AEHLT,AEHLTCD,AEHLGT,AEHLGTCD,AEBODSYS,AEBDSYCD,AESOC,AESOCCD,

AESEV,AESER,AEACN,AEREL,AERELNST,AEPATT,AEOUT,AESCONG,AESDISAB,AESDTH,AESHOSP,AESLIFE,AESMIE,

EPOCH,AESTDTC,AEENDTC,AESTDY,AEENDY,AEENRTPT,AEENTPT)

convert following vars to numeric per CDSIC definition

sdtm_aeAELLTCD \<- as.numeric(sdtm_aeAELLTCD)

sdtm_aeAEPTCD \<- as.numeric(sdtm_aeAEPTCD)

sdtm_aeAEHLTCD \<- as.numeric(sdtm_aeAEHLTCD)

sdtm_aeAEHLGTCD \<- as.numeric(sdtm_aeAEHLGTCD)

sdtm_aeAEBDSYCD \<- as.numeric(sdtm_aeAEBDSYCD)

sdtm_aeAESOCCD \<- as.numeric(sdtm_aeAESOCCD)

sdtm_aeAESTDY \<- as.numeric(sdtm_aeAESTDY)

sdtm_aeAEENDY \<- as.numeric(sdtm_aeAEENDY)

convert NA to null

sdtm_aeAESTDY\[is.na(sdtm_aeAESTDY)] <- ""

sdtm_aeAEENDY\[is.na(sdtm_aeAEENDY)] <- ""

add label

label(sdtm_ae) <- "Adverse Events"

label(sdtm_ae$STUDYID) <- "Study Identifier"

label(sdtm_ae$DOMAIN) <- "Domain Abbreviation"

label(sdtm_ae$USUBJID) <- "Unique Subject Identifier"

label(sdtm_ae$SUBJID) <- "Subject Identifier for the Study"

label(sdtm_ae$AESEQ) <- "Sequence Number"

label(sdtm_ae$AESPID) <- "Sponsor-Defined Identifier"

label(sdtm_ae$AETERM) <- "Reported Term for the Adverse Event"

label(sdtm_ae$AELLT) <- "Lowest Level Term"

label(sdtm_ae$AELLTCD) <- "Lowest Level Term Code"

label(sdtm_ae$AEDECOD) <- "Dictionary-Derived Term"

label(sdtm_ae$AEPTCD) <- "Preferred Term Code"

label(sdtm_ae$AEHLT) <- "High Level Term"

label(sdtm_ae$AEHLTCD) <- "High Level Term Code"

label(sdtm_ae$AEHLGT) <- "High Level Group Term"

label(sdtm_ae$AEHLGTCD) <- "High Level Group Term Code"

label(sdtm_ae$AEBODSYS) <- "Body System or Organ Class"

label(sdtm_ae$AEBDSYCD) <- "Body System or Organ Class Code"

label(sdtm_ae$AESOC) <- "Primary System Organ Class"

label(sdtm_ae$AESOCCD) <- "Primary System Organ Class Code"

label(sdtm_ae$AESEV) <- "Severity/Intensity"

label(sdtm_ae$AESER) <- "Serious Event"

label(sdtm_ae$AEACN) <- "Action Taken with Study Treatment"

label(sdtm_ae$AEREL) <- "Causality"

label(sdtm_ae$AERELNST) <- "Relationship to Non-Study Treatment"

label(sdtm_ae$AEPATT) <- "Pattern of Adverse Event"

label(sdtm_ae$AEOUT) <- "Outcome of Adverse Event"

label(sdtm_ae$AESCONG) <- "Congenital Anomaly or Birth Defect"

label(sdtm_ae$AESDISAB) <- "Persist or Signif Disability/Incapacity"

label(sdtm_ae$AESDTH) <- "Results in Death"

label(sdtm_ae$AESHOSP) <- "Requires or Prolongs Hospitalization"

label(sdtm_ae$AESLIFE) <- "Is Life Threatening"

label(sdtm_ae$AESMIE) <- "Other Medically Important Serious Event"

label(sdtm_ae$EPOCH) <- "Epoch"

label(sdtm_ae$AESTDTC) <- "Start Date/Time of Adverse Event"

label(sdtm_ae$AEENDTC) <- "End Date/Time of Adverse Event"

label(sdtm_ae$AESTDY) <- "Study Day of Start of Adverse Event"

label(sdtm_ae$AEENDY) <- "Study Day of End of Adverse Event"

label(sdtm_ae$AEENRTPT) <- "End Relative to Reference Time Point"

label(sdtm_ae$AEENTPT) <- "End Reference Time Point"

#export to xpt

write.xport(sdtm_ae, file="C://R_software/ae_R.xpt")

export to CSV

write.csv(sdtm_ae, file="C://R_software/ae.csv")

相关推荐
Omics Pro4 天前
~30,000+引用!理论2005,R包2008,多组学集成AI增强
开发语言·数据库·人工智能·算法·机器学习·自然语言处理·r语言
天桥下的卖艺者6 天前
chrals.CTI函数升级版发布,增加了CTI衍生的脂肪指数提取
数据库·r语言
小师兄吃牛肉7 天前
什么是R语言?如何快速学习R语言
开发语言·学习·r语言
喆星时瑜8 天前
Win10/11 安装 R 与 RStudio 详细图文教程
开发语言·r语言·安装
喆星时瑜8 天前
R语言基础语法入门
开发语言·r语言
喆星时瑜8 天前
R语言向量语法的详细教程
开发语言·r语言
临床数据科学和人工智能兴趣组11 天前
在R语言中, 使用 as.factor() 函数转换数值型变量
开发语言·r语言
babe小鑫20 天前
信息与计算科学专业应届生面试 怎么证明自己能解决业务问题
学习·r语言·excel
深兰科技21 天前
深兰科技受邀参与第二届中国(南宁)—东盟人工智能场景应用对接会,深化AI国际合作
人工智能·qt·r语言·scala·symfony·智能机器人·深兰科技
赵钰老师22 天前
基于ArcGIS Pro、R、INVEST等多技术融合下生态系统服务权衡与协同动态分析
python·arcgis·数据分析·r语言