请选择 进入手机版 | 继续访问电脑版
楼主: Adrian2Hope
50821 138

[原创博文] 我是一名SAS程序员   [推广有奖]

关注中,感谢楼主分享

使用道具

谢谢楼上朋友们的回复和夸奖。大家新年快乐!
其实一直都想更新的,可是天冷,人也懒。最重要的是觉得有压力了,不想轻易动手。昨晚还在想, 这毕竟不是写日记,是会有人看的,有人看就会有相应的品头论足。而我本人性格上是个完美主义者,特别是对于出自自己的东西。我记得大学有段时间学钢琴。老师跟我说,你不能啪的一下,弹出一个音就了事了。既然是你弹的,你就要对你手下的每个音“负责”,它的音准,强弱,长短等等。就像我写这个帖子一样,我既然做了,依然要对他负责。但本人能力有限,精力也有限,我只希望不要误导大家,如有不对、不妥之外,还希望大家海涵!今天是周末,还在公司,希望朋友们周末愉快!

使用道具

Adrian04Feb2017
Adverse Event TFL programming
SAP give details about AE TFLs(tables, listings, figures) display instruction. Please note that there will be inconsistent between SAP definition and shells configuration. Make sure they are the same, if not, put comments to leader programmer/statistics/client. For instance, SAP needs a summary ae table about leading to death, but this picture is not exist in shell, so at this time, confirmation is necessary. The following tips are general rules for TFL programming. It is a good habit to keep them in mind in our daily working scope. Also I will provide some reference codes to help understanding, and they are only the part of the programs. I strongly suggest you write codes by yourself once you get the idea.

1)        AE overall summary table
Attentions:
1.        The first column label usually contains follow items: any subject with at least one adverse event, serious adverse event, adverse event leading to death, adverse event related to study treatment. What you do is to select appropriate matched subjects and make small calculations.
2.        Safety Analysis (Adverse Events, Laboratory Data, Vital Signs…) use safety population, so SAFFL=’Y‘ is required when reading data from ADaM. Sometimes AETRTEM=’Y’ is also needed if All AEs is coded using MedDRA dictionary.

data adae;
  set adam.adae;
  where saffl = 'Y' and aetrtem = 'Y';
run;

3.        TFL codes will be different according to ADaM derivation. Generally, category flag variable is recommended to derive in ADaM, if not, we shall write by ourselves. Of course, another analysis indication variable like ANL01FL, ANL02FL….is required added in ADaM.

proc sql;
  create table aeall as
  select count(distinct usubjid) as count, 1 as row
  from adae;
  create table death as
  select count(distinct usubjid) as count, 2 as row
  from adae
  where aeout='FATAL'; /* dthfl='Y' */
quit;

4.        For table counts patients with any AEs, clause DISTINCT is used in PROC SQL;
5.        For events, then ignore it. Sometimes events is also required for summary and ‘n’, ‘(%)’, ‘Events’ should be concatenated into one variable to shown up.

**** Treatment-emergent Adverse Event for subjects level and events.;
proc sql;
  create table TOT as
    select count(distinct USUBJID) as CNT1, count(*) as CNT2, TRTPN
    from ADAE
    group by TRTPN;
  create table SOC as
    select count(distinct USUBJID) as CNT1, count(*) as CNT2, AEBODSYS, TRTPN
    from ADAE
        group by AEBODSYS, TRTPN;
  create table SOCPT as
    select count(distinct USUBJID) as CNT1, count(*) as CNT2, AEBODSYS, AEDECOD, TRTPN
    from ADAE
    group by AEBODSYS, AEDECOD, TRTPN;
quit;

6.        Table shell likes “n (%)”. Here “n” indicates category subjects, % =100*n/population. As Footnote says: n = Number of patients, Percentages are based on the total number of subjects in the safety population. If treatment arms are given, denominator will consider this analysis population by treatment. This value will be equal to “N=XX” in column title. Note TFLs are analyzed by treatment group usually.
7.        If population is a macro variable obtained in the prior programs, take highly notice this variable is a character value. So if you use it in next calculation, try to avoid the statements like this: IF COUNT=&TRTA THEN PCT=’100’. As the variable COUNT is numeric, there will be log issue “Convert…” after submit the codes. And this issue should be clear. Notice codes like “COUNT/&TRTA” is ok because SAS perform automatic mathematic calculation with no warning.

data final;
  set ae;
  length col1 $200;
  if count in (0 .) then col1 = '0';
  else col1 = strip(put(count,3.)) || ' (' || put(100*count/&n.,5.1) || ')';
  col1=tranwrd(col1,'100.0','100');
run;

2)        Adverse Events by System Organ Class and Preferred Term
Table shell like this:
Any Adverse Events
SOC1
PT1
PT2

SOC2
  …
Attentions:
1.        Sort by descending frequency of the total for SOC and preferred term within SOC. That is to say, first descending order by SOC, then descending order by PTs in each SOC. This display is help to review for statistics which AEs occurrence is mostly happen.

proc sql noprint;
        create table tot as
                select count(distinct usubjid) as count
                from adae;
        create table soc as
                select aebodsys, count(distinct usubjid) as count
                from adae
                group by aebodsys;
        create table pt as
                select aebodsys, aedecod, count(distinct usubjid) as count
                from adae
                group by aebodsys, aedecod;
quit;

data tab0;
        set tot soc pt;
run;

proc sort data = tab0;
        by aebodsys aedecod;
run;

data tab1;
        set tab0;
        by aebodsys aedecod;       
        length label $200. col1 $50.;
        **** Set order ****;
        retain group row;
        if first.aebodsys then group = count;
        if first.aedecod then row = count;
        if aebodsys = '' then label = 'Any subject with at least one Treatment-Emergent Adverse Event';
        else if aedecod = '' then label = aebodsys;
        else label = '^w^w' || aedecod;
run;

proc sort data = tab1 out = final;
        by descending group aebodsys descending row aedecod;
run;

2.        SOC uses AEBODSYS, PT uses AEBODSYS. If there are missing value of them due to unmatched AE coding, then setting to “Uncoded” is necessary to avoid empty row.

If aebodsys=’’ then aebodsys=’Uncoded’;

3.        Most of time there will be no records read in matched the table or listing, another explanation line is necessary to reflect this. So an empty data set with at least one row is required for report. Use &nodata in proc report procedure.

%let nodata=%str(No applicable data were reported);
proc sql noprint;
        select count(*) into:numobs from adae;
quit;

%if &numobs=0 %then %do;
proc sql;
        create table final
        (label char(200),
        col1 char(50),
        aebodsys char(200),
        aedecod char(200),
        group num,
        row num);
        insert into final
        set group=.;
quit;
%end;

%if &numobs=0 %then %do;
compute before page/style(lines)={just=c};
        line "";
        line "&nodata";
endcomp;
%end;

3)        Adverse Events by System Organ Class, Preferred Term, and Relationship to Study Treatment
Shell:
SOC/PT                         Relationship to Study Drug
Any Adverse Event Unrelated
Unlikely
Possible
Probable
SOC1
Overall
….
PT1

Attention:
1.        Note there is another column added into table, so two points need to consider: one is that if there is no subjects in certain relationship within SOC/PT, an empty line should be kept with “count=0” mapped. If there is no subjects included at treatment A and other treatment arm B is mapped with numbers, then set ‘0’ for arm A column.
2.        Two is that adding another group-by variable to summary the data in SQL statement.
3.        As for order rules, keep it the same as prior comments. It will be a little complicated for this sorting. For example, within a SOC/PT, count each subject in 4 relationship, and get summation of them, then use this value to sort order.
4.        Notice here the number of subjects within SOC/PT should be unique. That is to say, if a subject is in ‘Unlikely’, then it cannot be in ‘Prossible’.

**** Get number of subjects within SOC/PT for ordering ****;
**** XXXn is related dataset to hold subjects ****;

proc sort data=adae out=adae1 nodupkey;
        by aebodsys aedecod usubjid;
run;

proc sql;
  create table tot as
    select count(distinct usubjid) as count, areln
    from adae
    group by areln;
  create table totn as
    select count(distinct usubjid) as count
    from adae1;
  create table soc as
    select count(distinct usubjid) as count, aebodsys, areln
    from adae
        group by aebodsys, areln;
  create table socn as
    select count(distinct usubjid) as count, aebodsys
    from adae
        group by aebodsys;
  create table socpt as
    select count(distinct usubjid) as count, aebodsys, aedecod, areln
    from adae
    group by aebodsys, aedecod, areln;
  create table socptn as
    select count(distinct usubjid) as count, aebodsys, aedecod
    from adae
    group by aebodsys, aedecod;
quit;

4)        Adverse Events by System Organ Class, Preferred Term, and Maximum Severity
Shell:
SOC/PT                         Maximum Severity
Any Adverse Event
Mild
Moderate
Severe
SOC1
Overall
….
PT1


The core step is the same as 3). Just select another variable to analysis.
Attention:
1.        Generally, the maximum severity record will be used for display within SOC/PT. For example, if the same SOC/PT happen several times for one subject, first it is ‘Mild’, later it comes up with ‘Severe’, then we flag the maximum record for the latter one. If no flag variable is available in ADaM, then we will handle this in TFL programs.
2.        Sample codes:
**** Count once at the maximum severity for total.;
proc sort data = adae out = adae1 ;
  by usubjid asevn;
run;

data adae1;
  set adae1;
  by usubjid asevn;
  if last.usubjid;
run;
**** End ***;

**** Count once at the maximum severity per SOC.;
proc sort data = adae out = adae2;
  by aebodsys usubjid asevn;
run;

data adae2;
  set adae2;
  by aebodsys usubjid asevn;
  if last.usubjid;
run;
**** End ***;

**** Get dummy SOC-PT-SEV structure.;
proc sort data = tab0 out = dummy(keep = aebodsys aedecod) nodupkey;
        by aebodsys aedecod;
run;

data dummy;
        set dummy;
        do asevn = 1 to 3;
                output;
        end;
run;

data tab1;
        merge dummy tab0;
        by aebodsys aedecod asevn;
run;

Ok, that is all for common adverse event tables. And general rules here is widely used in other similar tables.
已有 1 人评分论坛币 学术水平 热心指数 信用等级 收起 理由
pobel + 5 + 5 + 5 + 5 精彩帖子

总评分: 论坛币 + 5  学术水平 + 5  热心指数 + 5  信用等级 + 5   查看全部评分

大家好,我是团长。

使用道具

xsl1136343283 发表于 2017-2-7 15:52:01 来自手机 |显示全部楼层 |坛友微信交流群
楼主新年好,本人是大四学生,现在在上海一家cro做实习生,学习做tlf,医药方面的。本人英语较差,阅读和书写还可以,口语和听力不怎么样,sas编程算是比较有灵性(常用的会,复杂的不会用但能看懂)想问问楼主,以后我们这边工资待遇开始时候怎么样,有多少?还有就是怎么才能加工资。之前有听说有高级程序员,说是三年左右,那他们做的事跟我们做的一样么(我们现在大概是只学了做tlf)?
如果一样的话,那增加工资待遇的标准是什么?年龄资历?还是工作效率?
如果不一样的话,那我们在没成高级程序员之前的三年是不是就没有增加工资待遇的机会了?
希望楼主能回的详细点,刚入社会,比较迷茫。

使用道具

myzhang1982 在职认证  发表于 2017-2-9 09:53:19 |显示全部楼层 |坛友微信交流群
xsl1136343283 发表于 2017-2-7 15:52
楼主新年好,本人是大四学生,现在在上海一家cro做实习生,学习做tlf,医药方面的。本人英语较差,阅读和书 ...
本科大概5000-6000的样子吧,加薪每年都会有的,具体数目要看个人表现还有运气(具体做的项目重不重要)

使用道具

To xls:myzhang1982说的有道理。年终(中)不都有每个人的performance考评嘛。工资涨幅一般每年有个5%吧,好的有个百分之一二十或者更多。你现在已经入行了,这很重要啊!如果你喜欢这行,那就慢慢干吧,积累技术,知识,经验和能力。你才刚毕业,却老想着怎么涨工资,要以防内心浮躁而沉不下心。祝你好运!
上次考虑很久,决定用全英文发言。我相信大家都看得懂,如果有错误或不当之外,希望朋友们指出来。

使用道具

这是我copy的某段代码。当然在实际工作中,可能会有各种各样的写法。但这里一些思想,可以看代码去体会。

Program 5.3 Creating a Typical Summary of Demographics.rar

2.79 KB

Summary of Demographics

本附件包括:

  • Program 5.3 Creating a Typical Summary of Demographics.sas

使用道具

coelicolor 发表于 2017-7-7 23:54:45 |显示全部楼层 |坛友微信交流群

大赞楼主,有时间请继续。

使用道具

PARK- 发表于 2017-7-8 10:21:37 |显示全部楼层 |坛友微信交流群
能不能借个楼请教一下SAS作业:用SAS用蒙特卡罗方法求上证50ETF期权定价编程

使用道具

Adrian2Hope 发表于 2017-7-11 14:55:12 |显示全部楼层 |坛友微信交流群
To coelicolor: 非常感谢!很久没有写了,没想到还有人看。以后还会写的。
To PARK: 这个我是医药行业的,这个我不会,我相信网上有很多资料可以查,这个论坛上肯定也有,而且这本是经济论坛。你只要买一本金融sas编程,应该就有上面的内容。我学生时代也看到过。

使用道具

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
加好友,备注cda
拉您进交流群

京ICP备16021002-2号 京B2-20170662号 京公网安备 11010802022788号 论坛法律顾问:王进律师 知识产权保护声明   免责及隐私声明

GMT+8, 2024-3-28 18:17