我们统计师或客户在review程序员产生的报告时,会从两个方面去考虑:一个是项目的特定性,例如有效性变量的衍生方法和所使用模型参数是否正确,可以从原始数据写代码验证结果的正确性;别外就是常规处理方式,这个分不同的客户有不同的要求,尤其以日本客户要求更细致,但即便如此,还是有一些共同的点是大家约定俗成的,所以熟悉和了解一些general的TFL comments还是有必要的,这里举例说明一下。

1 改变某张table或listing的header或某一行的label,而值不变。比如一段语句中首字母大写,把start date/day变成date/day of start,连接符前后要加空格等等。大部分时候,这种更新是有依据、有意义和有必要的,它能让TFL的呈现更标准化,易于理解,更符合试验要求。

2 增加某行或某列。如果新加的变量更合适放在ADaM中生成,那可以在ADaM程序中更新代码。对于ADaM data set,因为要求ready to analysis,我认为,把在一些TFL中的programming code放到ADaM中更好些,具体原因如下:

  • 除了ADaM model要求的基础变量之外,ADaM数据集是可以按user-defined的要求,在follow ADaM general standard的前提下,运用相关的computation rules生成新的变量,并可以命名它。
  • ADaM数据集服务于TFLs,它是为更方便生成TFLs而创建的数据集。假设,现在要连接start date, start day, end date, end day, duration,形成一个变量在listing输出,但这个信息表达在另外3个listing也要求出现。编程时,我们要写一段代码:合并5个变量,生成一个新变量(由于可能要考虑到各种情况,按某种format科学合理的呈现data,代码有时挺复杂,挺长)。如果我们都在listing中执行programming过程的话,那是不是要把这段程序写3遍,如果以后算法需要更新呢?不管是同一个人只是copy自己的程序,还是其他人重新写,这都会增加production和validation的工作量。而如果我们在ADaM编程,则只用update一次,而且维护也方便。
  • 对于Listing的qc,要不要写完整的validation程序且不论。如果original和validation都在TFLs中programming的话,因为双方使用的变量属性不一样,程序中的处理也不一样,这就增加compare的时间成本。对于关键变量的qc,放在ADaM中处理,TFLs只需调用即可。
  • 服务于数据管理和统计的目的,常常需要对分类型的字符变量数值化,一些关键的分类变量常用于分层或分组,或用于模型统计,把变量值code为“1”和“2”,与code为"0"和"1",意义差别很大,在模型中的运用和解读也不一样,故统一处理较好。

所以,我觉得经过SDTM标准化数据之后,ADaM是个很重要的步骤,承上启下,科学合理运用,对整个study的高效,快速,准确的完成很重要。

3 像CM中的date信息常常不是很清楚的,在listing中如何呈现呢?首先,listing中呈现的都是"raw data"。在ADaM中,像--STDTC,--ENDTC,--DTC,这都是直接从SDTM中拿过来的,SDTM也是从raw data里拿的,那在listing应该display这样的原始变量。像“2013-06”,“2000”,这种日期时间数据不完整的就是"raw data"。而ASTDT是经过某种rule算出来的,更多的是分析所用。如果某一条CM的记录只有start date,没有end date,因而也没有duration,那要像上面要求怎么连呢?有的要求是不管后面有没有数据,也要用类似"/"来标注,以说明这个记录没有end date,比如“2013-06/”,也有表示成“2013-06”,直接把后面放空。再比如像某列要显示yes or no,如果是yes,在后面写原因。可以显示成Yes/Reasons或者No,如果结果为yes但原因缺失,仍然显示成"Yes/"。我个人prefer后者,虽然看起来好像有点多余,但信息表达很清晰。

4 有些title/footnote,analysis population需要更新。在title中,可能需要加入选定的语句来更加明确地表达出这个表的内容。footnote可能需要加入更详细一点的footnote来解释TFLs中用到的省略词语,比如severe AE中,1 = Results in Death,Baseline is defined the last non-missing record before first dose. Study day is defined as the day relative to the first dose等等。表中的标记,如[a], [b]等,要和下面的footnote一一对应。同一个footnote可能存在很多个TFLs中。比如listing中要出现的subject/age/sex/race,在footnote中就要说明这里面的值代表什么意思,比如F = Female, W = White之类。

5 针对某些table的update,改动也许比较大了。比如开始给出的shell里面,只要summarize某个SOC/PT之下的subjects and percent,而实际上一般都是SOC一个汇总,下面每一条PT也汇总,PT下面可能再有Severe Grade或者Relationship to study drug之类的分组。可以在程序里一层一层地把它都求出来,要哪些内容再输出哪些内容。因为有些draft mock-up没有finalized,后面可以需要加入更多的内容,不如开始写程序的时候,就考虑得多一点。

6 某个table后面,一般都有某个listing与之对应,以及表和图,便于cross check。比如在table中算得的年龄最小值为18,而在后面的listing中,从一条条的patient看过去,年龄都是大于20的,那这就前后矛盾了。再例如,前面的disposition表显示safety population(如果这个flag是定义为服用过试验用药的人)只有10个人,但其他表的header中的大N总和并不等于10,原因可能是这个人dose date不为空,但arm=not assigned,也就是说,这个人有用药记录,但没有给他assignment treatment。像这种comments,有的时候是data本身的issue,另外就是程序中可能出现了错误。


常见的不良事件表格编程思路


SAP gives 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 share the same meaning, if not, put comments to leader programmer or statistics or client. For instance, SAP needs a summary ae table about leading to death, but this picture is not exist in the shell, so at this time, further confirmation is necessary to the author. 

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. 
  • other contents of interest.

What you do is to select appropriate matched subjects and make small calculations.

2. Safety Analysis (Adverse Events, Laboratory Data, Vital Signs…) usually use safety population, so SAFFL=’Y‘ is required when reading data from ADaM. Most of time AETRTEM=’Y’ is also needed when AEs are "Treatment Emergent" coded using MedDRA dictionary.

zabbix项目经验怎么写 项目经验怎么写 范例_数据

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 also necessary added in ADaM to aid the certain analysis purpose.

zabbix项目经验怎么写 项目经验怎么写 范例_Events_02

4. For table counts patients with any AEs, the key wrod DISTINCT is used in PROC SQL.

5. Sometimes events is also required for summary like "n, (%)". "Events" should be concatenated into one variable to show.

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_03

Note that the naming of dataset and variable are meaningful.
6. Look at table shell like “n (%)”. Here “n” indicates category subjects number, % =100*n/population. As footnote says: n = Number of patients, Percentages is 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 the column title. Note that TFLs are analyzed by treatment group usually. Sometimes denominator number will be the value of the sub-category item total number.

7. If population is a macro variable obtained in the prior programs, please highly notice this variable is a character value. So if you use it in the 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 “Converted to…” after the codes submission. And this issue should be clear up. Notice codes like “COUNT/&TRTA” is ok because the SAS performs automatic mathematic calculation with no warning.

zabbix项目经验怎么写 项目经验怎么写 范例_数位_04

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

zabbix项目经验怎么写 项目经验怎么写 范例_数据_05

Attentions: 

1. Sort by descending frequency of the total value for SOC and preferred term within SOC. That's to say, first descending order by SOC, then descending order by PTs in each SOC.This presentation type is help to review for statistics which AEs occurrence rate is mostly happen. 

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_06

zabbix项目经验怎么写 项目经验怎么写 范例_数据_07

2. SOC analysis uses AEBODSYS and PT analysis uses variable AEDECOD. If there are missing value of them due to unmatched AE coding, then setting to “Uncoded” is necessary to avoid empty row. The statement is like this: If aebodsys=’’ then aebodsys=’Uncoded’;

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

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_08

3)  Adverse Events by System Organ Class, Preferred Term, and Maximum Severity

Shell:

zabbix项目经验怎么写 项目经验怎么写 范例_程序员项目经验怎么写_09

The core principle is the same as the following "4)" tips. Just select another variable severity instead of relationship to analysis.
Attention:
1. Generally, the maximum severity record will be used for display within SOC/PT. For example, if the same SOC/PT happens several times for one subject, first it is ‘Mild’, later it comes up with ‘Severe’, and then we flag the maximum record for the latter one. If no flag variable is available in ADaM, then we will handle this process in TFL programs.

Here assume the larger value of ASEVN stands for the higher severity. 

zabbix项目经验怎么写 项目经验怎么写 范例_程序员项目经验怎么写_10

2. Usually the integrity style of the table content is preferred to maintain, so map the missing value with "0" is necessary.

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_11

After the above procedure, the complete structure of SOC/PT/SEV will be rightly presented even though there is none subjects meet the certain severity category at one SOC/PT level.

3) Adverse Events by System Organ Class, Preferred Term, and Relationship to Study Treatment.

Shell is alike the severity table.
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 while another treatment arm B is mapped with numbers, then set ‘0’ for arm A column.
Two is that adding another group-by variable to summary the data in SQL statement.
3. As for ordering 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 the 4 relationships, and get summation of them, then use this value as sorting key.
4. Notice here the number of subjects within SOC/PT should be unique. That is to say, if a adverse event relationship with treatment is ‘Unlikely’, then it cannot be ‘Possible’ at the same time for this subject. This is the same case of severity table.

Fine, above is some common programming tips about adverse event tables derivation.  General rules here is also widely used in other similar tables as well. 

Most importantly, the programming notes are much more than this. More exciting will be shown up in the coming articles.

小数位数的精度考虑

写TFLs程序时,常常会碰到一个小数位数的显示问题,例如生成lab相关的descriptive summary表。我们要将proc means过程得到的常见统计量,如n, mean, std, median, min, max以更合适的方式呈现出来。

一般sap会规定小数点位数的保留方法。例如以raw data的最大小数位数为基准,在此基础上, mean增加一位,std增加两位,median, min, max和raw data的小数位数保持一致,如果raw data的最大小数位数超过3位的,当2位小数处理。

一般情况下的小数位数保留情况,我们可以直接使用put函数单独处理,如put(var,8.0)表示不保留小数,put(var,8.1)表示保留一位小数等。假设我们要求在TFL中的数据小数点上下对齐,右括号上下对齐,可以使用put函数将数据前面放置空格。例如,put出相应统计量及其格式为:n-6.0,mean-8.1,std-9.2,min-6.0,median-8.1,max-6.0。这样的数据前面是可能带有空格的,保证了数据呈现的整齐性。这里要注意在格式中给的总长度要足够长,才能“装下”所有的数据。如果不让数据带空格,也可以在数据中加入RTF语句,或在report语句中处理。

为了让TFLs呈现的更好看,有很多细节都是要注意的。

但像labparameter很多,不清楚哪一个parameter的test result value "AVAL"最大小数到底是多少位,那只能智能化处理了。一种方法不推荐,甚至说是不允许的,那就是先写个小程序得出每个parameter maximum decimal,然后这样写:if paramn in (1 2 3) then xxx...; else if paramn in (4 5 6) then xxx...; 如此这般,有被认为是hard-coding之嫌,而这在临床试验的编程工作中,是坚决不允许的,除非客户要求,或者更高层次的批准。再者,如果哪次update PARAMN,岂不是又要改一次程序?

所以,有种做法是根据paramater来决定它的统计值的输出格式。当然,不同的人会有不同的编程习惯。有的喜欢用宏,先做出宏变量值,在derive的时候用do循环。有的觉得可以不用,直接在data步里面就可以搞定。
这里说句题外话,并不是使用宏就一定是一个好程序,像循环,宏套宏好几层,有时候会反而会降低程序运行效率。只要用data步或sql能解决的,在没有繁琐代码的情况下,先不考虑宏。

有一个比较重要的函数PUTN,它的好处是后面的format可以用个字符型的变量表示。说一下思路。

  1. 通过lengthn, scan函数和sort过程,得到每个parameter maximum decimal。然后生成三个字符型变量dec0, dec1, dec2。例如dec1 = strip(put(8.0+0.1*(maxdec+1),best.))...;
  2. sql or merge连接。
  3. msd=putn(var_mean,dec1) || " (" || strip(putn(var_stddev,dec2)) || ")"...;

最后,各个统计量就会根据不同的lab test name来选择小数的保留位数了。
Tips:

  • 例子中最大长度是8位,对有些很大的值可能会truncated,可以用round。
  • 如果变量比较多,考虑用macro来简化每个variable derivation。
  • 模板化code,以不变应万变。
  • 还有其他对小数位数的处理方式:例如,按每一个parameter分别处理;根据值的大小范围来决定(例如,如果数据<1,保留两位小数;如果数据<10,保留一位小数,如果10-100之间,不保留小数等等)。

Many-to-Many Merge(多对多合并)

在SDTM中,EX domain是存放exposure信息,SV domain是存放subject visit信息。假设我们将EX中的dose date定位于某个visit,这里需要merge EX和SV 。现假设:EX中的数据是每条subject有多条dosing record(例如:exstdtc=2012-08-12T09-45, exstdtc=2013-9...),一条对应一次用药及其日期/时间,SV中的数据是每个subject有多个visit(例如:visitnum=1, visit=screening, svstdtc=2013-05-15, svendtc=2013-06-12; visitnum=2, visit=run-in, svstdtc=2013-06-13, svendtc=2013-6-20...)。

使用SQL连接EX和SV。

zabbix项目经验怎么写 项目经验怎么写 范例_数位_12

使用SQL得到指定不良事件内发生的所有合并用药。

zabbix项目经验怎么写 项目经验怎么写 范例_数据_13

Tips:

  • SQL能处理非常复杂的merge,甚至多对多,体现在select...on...语句使用上。
  • --STDTC, --ENDTC是标准的CDISC变量,是字符型的,如果要比较,先转化为能执行比较过程的数值型变量比较好。因为日期时间很多是不完整的,必要时查阅specification中的calculation algorithm。另外,标准字符值也是可以比较的。
  • EX中总有某个date不落入某个visit时间区间,有些computation会define visit window。例如visitnum=2这一天的前后3天都算是visitnum是等于2的。
  •  这里要找出的是哪些CM会落在对应这个人的AE中,因而on条件语句的写法异常重要,例子中配对条件有3层,要仔细推敲并验正逻辑的正确性,可以画个时间轴图来帮助理解。
  • 由于SQL overlap value,所以尽可能只选择一些要处理的变量,以防出错。
  • 在项目管理过程中,尽可能批量处理这样的编程问题。

分类数据中的缺失值和0意义很不同

如果某个分类变量的值为缺失值,这说明这个值的结果是未知的,这条观测不会纳入分析人群和分母人数的计算;而如果它的值是0,这说明结果是已知的,此时0会对应一个相应的临床意义,同时纳入分析和分母人数的计算。
比如:考察疗后病人对药物的有效反应率。1=有效,0=无效,这里的有效和无效可依据相应的医学指标来判定。很显然,如果某个病人的反应变量值为1,那说明治疗是有效的,反之,如果为0,则药物无效。但如果数据收集不完全(比如病人失访,关键指标缺失等)而导致无法判定时,那出现缺失值,而不应该是0。
从描述性统计和推断性分析角度看疾病死亡率。
1=死亡,0=没有死亡。如果有几个人的死亡情况从0变为缺失值,也就是不确定,各组(如治疗组,对照组)中变量值为1的类由于分母变小而total percenage值变大,检查相关统计量的p值,发现以前有统计学意义的变得没有统计学意义,治疗组会降低死亡率变成两组药物治疗效果没有统计上区别。
在实际的临床试验中,缺失值非常常见,有的时候是data issue。项目文件中会规定缺失值的处理方法,同时在编程的时候要注意程序的robust。

DATA Step里重新定义变量

在merge数据集的同时derive新的变量容易出现“易想不到”的问题。明明if...then...写的很明白,逻辑也很清晰,但就是得不到正确的结果,而且找不出错误在哪里,此时,分两个data步写,先完成merge,然后再执行derive,错误可能就不见了。这种方法比较保险,也通常是正确的。

这里其实隐藏了一个问题,有个PDV概念,详见SAS初级编程第六章理解DATA步的处理过程:

在data步里面通过merge, set, update语句产生一个变量的时候,sas会自动"Retain"所有衍生变量值。所以容易出现这样的情况:不仅满足条件的那条record result是赋予的新值,而且其他在同一个by group里面的也是这值,你当然不是你想要的。

我们可以用rename在一个data step里面处理这种状况。
Sample code:

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_14

TFLs编程常见处理技巧

zabbix项目经验怎么写 项目经验怎么写 范例_Events_15

各个层级术语的缩进问题。

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_16

zabbix项目经验怎么写 项目经验怎么写 范例_Events_17

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_18

我们可以尽可能使用RTF语言处理数据格式的呈现问题,因为它更智能,更强大。平常多多积累,用时就事半功倍了。

zabbix项目经验怎么写 项目经验怎么写 范例_数位_19

为了使表格更易于统计的审阅及分析的需要,常规的Rules需要去Follow,特殊目的的可以在SAP等文件中加以更详细的说明。

zabbix项目经验怎么写 项目经验怎么写 范例_程序员项目经验怎么写_20

zabbix项目经验怎么写 项目经验怎么写 范例_Events_21

zabbix项目经验怎么写 项目经验怎么写 范例_程序员项目经验怎么写_22

由于Listing的输出是对数据最基本的罗列,因而数据量会比较大。美观,简洁,合理的呈现是比较重要的。具体到每个文件的输出,需要注意的细节特别多。

zabbix项目经验怎么写 项目经验怎么写 范例_数位_23

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_24

zabbix项目经验怎么写 项目经验怎么写 范例_数据_25

如果完全strip掉统计值的所有前后空格,我们最后可以在PROC REPORT程序中去解决数据对齐问题,同时能兼顾到小数点和括号。

zabbix项目经验怎么写 项目经验怎么写 范例_Events_26

统计师在画Shell的时候,应该考虑到数据呈现的各种可能。一张表就是一份银子,这意味着要最优化TFLs的个数。出于Review的需要,可能所有相关的信息都会呈现在一个列表,这就会带来数据拥挤的问题,所以要注意更和谐和合理的表达。

zabbix项目经验怎么写 项目经验怎么写 范例_Events_27

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_28

zabbix项目经验怎么写 项目经验怎么写 范例_Events_29

可以在最终的数据集里包含格式的处理,也可以使用定义格式的宏程序。这样能保留数据的简洁完整,同时结果的输出不受影响。

zabbix项目经验怎么写 项目经验怎么写 范例_数据_30

我们可以不使用很复杂的程序来判断一个变量的存在与否。

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_31

比较推荐的代码书写建议

zabbix项目经验怎么写 项目经验怎么写 范例_数据_32

在DATA步的第一个语句,即读数据之前定义变量的属性,有这样几个优点:

  • 定义变量的长度、格式,名称等属性,对新生成的变量而言,这是必须的。如果不定义新变量的长度,可能会造成变量取值截尾。这里只是说可能,偶尔偷懒也不是完全不可以,但要保证不能出错。
  • 新生成的变量会出现在数据集的最前面,方便确认变量衍生过程是否正确。
  • 数值型变量没有定义长度的说法,我们可以统一给字符型变量赋予一个合适的长度,并全部写在一个位置,这很大程序上带来了程序编写和更新的方便。
  • 对长度大于200个字符的,我们在Log中显示Note。注意这里没有写成真正的"ERROR",这是为了防止在搜索Log错误时,把这条Note当成是Error。
  • 对字符型变量去首尾空格常常是有必要的(例如,有的原始变量在录入时包括空格),尤其是数值型变量转换成字符型变量的时候。

zabbix项目经验怎么写 项目经验怎么写 范例_程序员项目经验怎么写_33

CATX函数可以以指定的分隔符连接字符串或变量,如果所连接变量有变量为空,则不会出现多余的分隔符。

zabbix项目经验怎么写 项目经验怎么写 范例_数据_34

在SDTMIG 3.2版本,EPOCH、VISIT、TAETORD等变量对某些domain来说是Permissable,但在新一版的SDTMIG 3.3中,可能就是一个标准变量了。所以在对数据做验证的时候,要明确所使用的各种文件版本。

zabbix项目经验怎么写 项目经验怎么写 范例_程序员项目经验怎么写_35

OUTPUT语句用于输出数据。我们常规的DATA步没有使用OUTPUT语句也能产生数据集,是因为它默认输出的,如果要基于一定的条件而选择性的输出数据,那就要使用OUTPUT语句。OUTPUT语句的执行可以形象地理解为:前面的条件让合适的数据排在前面,后面的条件让合适的数据排在后面,变量值可能会一直RETAIN。在使用OUTPUT语句时要小心,各种意想不到的错误可能都会出现,还不好查出原因在哪里,为了防止可能出现的RETAIN或者其他错误,可以当变量每次重新赋值的时候,使之初始化为缺失值。

TRANWRD函数可以把字符串里的指定字符转换成另外一指定字符。

用法:TRANWRD(Source, From, To),Replace from character sting in source with to character string。语句TRANWRD(pct,’( ‘,’ (‘),把pct=“( 10.1%)”变成pct=“ (10.1%)”,也就是把括号里面的空格变到外面,这样一来数据紧密联系在一起了,还能保证括号右对齐。这里前面只有一个空格,如果是1.1%会有两个空格,那就再用一次TRANWRD函数。这样处理会使得TFL比较好看。

 对数据TRANSPOSE后形成的变量易产生缺失值,可能出现某个变量在某些行有值,有些行为空,也就是缺失的。如果需要全部替换成0或者NA,可以用ARRAY一次性处理。

如果变量较少,可以用if var=. then var=0。

但如果变量较多,用array。

array zero{*} _numeric_;
  do i=1 to dim(zero);
    if zero{i}=. then zero{i}=0;
 end;

*是种省略写法,’{}’,’()’均可,dim返回数据的维度,表示数据集中所有的数值型变量的个数。也可以把_numeric_替换成具体的变量。

对字符型变量,用法类似。

array char{*} _character_;
do i=1 to dim(char);
  if missing(char(i)) then char(i)=’0’;
end;
 OPTIONS SYMBOLGEN MPTINT MLOGIC;

SYMBOLGEN:在log中编译出宏变量的具体值,可以检查宏变量返回值的对错。

MPTINT:把编译出的宏变量值直接代入到程序中,形成一段完全的代码,用于检验整段程序运行是否正确。

MLOGIC:标识宏程序的开始和结束,可以用于区别MACRO和一般程序。

这三个选项是用于调试MACRO的,一般在项目的SETUP程序中就写进去,这样一来,可以看那些GLOBAL的MACRO在这个STUDY的适用性,另外,每个Team Member也可以测试自己的宏,省去了再加OPTIONS的麻烦。不建议在Individual程序中写类似于LIBNAME,OPTIONS之类的GLOBAL语句,全局性语言可以放在GLOBAL程序中,如SETUP.SAS。

DATA步中可用IF…THEN…来做条件判断,SQL语句中可以用CASE…WHEN。

如果是创建一个表(SQL语言中分别叫表、行、列,对应SAS中的数据集、观测、变量),结果就不会OUTPUT出来,那就不用加NOPRINT语句,CASE前面有逗号。

如果语句较复杂,WHEN后面可以根据运算结果来指定相应的值,简单的语句则直接用CASE变量。用ELSE语句来表示判断结束,而且不能是空的ELSE语句(DATA步中的SELECT WHEN可用空的OTHERWISE结尾),如果条件已经选择完毕,用ELSE+空格表示结束字符型变量的定义,ELSE +点表示结束数值型变量的定义,或者用ELSE表示最后一个条件。如果生成的是字符型变量,建义定义一个长度,以防TRUNCATED。

只要涉及到循环,最好缩进,让程序美观易读。

我们在写Specification的时候,尽量让它更美观,更直接的呈现在User那边,像下图这种,你打开Excel,拖到这个变量的时候,它占满了整个Excel Sheet,这样看起来很不方便。

zabbix项目经验怎么写 项目经验怎么写 范例_程序员项目经验怎么写_36

我们在进行各种Task时,铭记一点,你待它如初恋,它待你如真爱!

假设要以这样一种缩进方式来呈现数据。

zabbix项目经验怎么写 项目经验怎么写 范例_数据_37

有的朋友使用空格来对数据进行缩进,以及获得相应的层次感,包括各种Format,Label,Term,当然这也是可以的,在最后的表格中,把鼠标放在适当位置,指针显示有空格。其实,我们可以使用RTF语句,比如下句完成按两个字符一级的缩进。

zabbix项目经验怎么写 项目经验怎么写 范例_Events_38

zabbix项目经验怎么写 项目经验怎么写 范例_数位_39

Term太长可能会Wrap到下一行。

  • 如果使用空格,下一行的数据会直接顶格,没有体现出数据的层级。
  • 使用RTF会自动地排在相应等级,但还是个有问题,下一行的数据到底是另外一个Term,还是上一个Carry下来的呢?像这里CMCECOD就有两行了,但其实都是同一个CMDECOD,我们再加点语句以示区分:如果数据移到下一行,缩进一个空格。

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_40

如下图所示,表格更好看了。

zabbix项目经验怎么写 项目经验怎么写 范例_数据_41

在做Table的时候,有朋友喜欢以这种方式插入一个空行,以分隔不同部分的内容,针对不具有扩展性的内容是可行的。但如果某些Summary Table是by Category、by Visit、by Name,且并不清楚要空多少行时,可以在REPORT中使用COMPUTE语句,这样空行不会成为数据集的一部分,同时为OUTPUT服务。

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_42

项目管理技巧与SAS应用方法

zabbix项目经验怎么写 项目经验怎么写 范例_数位_43

zabbix项目经验怎么写 项目经验怎么写 范例_程序员项目经验怎么写_44

zabbix项目经验怎么写 项目经验怎么写 范例_程序员项目经验怎么写_45

zabbix项目经验怎么写 项目经验怎么写 范例_数位_46

zabbix项目经验怎么写 项目经验怎么写 范例_数据_47

zabbix项目经验怎么写 项目经验怎么写 范例_数据_48

zabbix项目经验怎么写 项目经验怎么写 范例_程序员项目经验怎么写_49

zabbix项目经验怎么写 项目经验怎么写 范例_数位_50

zabbix项目经验怎么写 项目经验怎么写 范例_程序员项目经验怎么写_51

zabbix项目经验怎么写 项目经验怎么写 范例_数位_52

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_53

zabbix项目经验怎么写 项目经验怎么写 范例_数位_54

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_55

zabbix项目经验怎么写 项目经验怎么写 范例_程序员项目经验怎么写_56

zabbix项目经验怎么写 项目经验怎么写 范例_Events_57

zabbix项目经验怎么写 项目经验怎么写 范例_程序员项目经验怎么写_58

zabbix项目经验怎么写 项目经验怎么写 范例_程序员项目经验怎么写_59

程序员代码处理一般考虑和建议

zabbix项目经验怎么写 项目经验怎么写 范例_数位_60

这段程序的作用是确定变量在最后数据集中的出现顺序并排序,可以用模块化程序处理。

zabbix项目经验怎么写 项目经验怎么写 范例_数位_61

给变量加label,可以用模块化程序处理。

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_62

指定变量长度,同样,我们可以用模块化程序处理。此处多说两句。

这个我在刚参加工作时,也是如此这般做法。在data definition或者叫study data specification中,一般会规定各个变量的长度,写程序时,要先length所有要生成的变量。而变量长度又不一致,要一一对照,有时还要多次重复写,显得很是麻烦。后来学乖了,将字符型变量长度都设为200,如果变量值超过200的,拆分原变量为多个,使每个变量值长度都在200以内,包含200。对数值型变量,因为SAS里默认长度就是8,所以不需要再写length语句。

注意,如果length语句放在set语句之前,那么length后面的变量会依次出现在数据集最左边,这样方便查看新生成的变量做的是不是对,而且可以重新设定一个变量的长度。

实际上,依据FDA e-submission的法规要求,我们在生成最后数据集的时候会再re-length,让每一个变量的长度刚好为里面值的最大长度,这样大大节省空间,而且,不管数据怎么更新,都不会出现cut off的情形。试想,如果我当初把某个变量的值设为了15。0,后来数据更新了,里面字符长度超过了150,这样,150个字符后面的内容就看不见了。

zabbix项目经验怎么写 项目经验怎么写 范例_Events_63

zabbix项目经验怎么写 项目经验怎么写 范例_Events_64

如果一段已经mark掉的程序确实没有用,即不能帮助理解,也不是以后备用的,而且确认以前的做法不对,那就删掉,以免混淆视听。如果是整段的,就整段删,如果是程序里面的,那就整句删。

zabbix项目经验怎么写 项目经验怎么写 范例_Events_65

对于pregnancy test,如果CRF上只记录了HCG检查结果(阴性,阳性,未查),那么对于LB domain中,unit、reference range等变量就没有实际意义了。和血液,血生化的数据放在一起的话,就要把它置空。我觉得可以不写出这些语句,因为是set在一起的,没有赋值的变量自然为空。

zabbix项目经验怎么写 项目经验怎么写 范例_数据_66

原日期时间数据形式是:EXDTC=2018-07-07,EXTAT=13:46,秒的信息没有收集。因为是数值型的,可以直接参与计算。如果只有日期值,直接用日期变量赋值,格式为e8601da.;如果有时间,全部转换成秒,格式为e8601dt.,这里分别用了两个变量保存日期时间值。

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_67

如果原变量没有时间信息,ok,没问题。但如果带有时间,因为是用e8601dt格式的输出,结果会变为2018-07-07T13:46:00,自动加上秒。而一般ISO8601的时候格式是right truncation。比如,Date and Time as Originally Recorded=December 15, 2003 13:14,如果Unknown seconds,ISO 8601 Date/Time=2003-12-15T13:14。

我们更新下程序。如果用time5.,会将08:02变为8:02,12点之前的时间前面不自动填充0。这里用tod5.格式将时间按正确的格式输出。重新生成一个变量并指定length。where语句中,一般有时间的肯定有日期。

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_68

zabbix项目经验怎么写 项目经验怎么写 范例_Events_69

这里的keep语句没有存在的必要,有点滥用了。我们用keep语句或者keep选项,drop语句或者drop选项,有个原则,那就是如果保留的变量比删掉的多,那就用drop;如果保留的变量比删掉的少,那就用keep。需要什么,就keep什么,不需要什么,就drop什么。

zabbix项目经验怎么写 项目经验怎么写 范例_程序员项目经验怎么写_70

基本可以断定,这段程序一跑,便会报error。

首先我们要明确一点:一定要根据变量的类型来执行相关语句,也就是说,只有数值型变量才能参与运算和使用数学函数,只有字符型变量才能运用字符函数,否则,一般都会报error或者warning。

这里LBORRES/LBSTRESC,给它+0,或者参与运算,可以把字符1变成数值1而不报warning,但如果它的值是字符的呢?

查看原始数据,发现这里LBSTU_RAW是字符型变量,标红处用法错误。

zabbix项目经验怎么写 项目经验怎么写 范例_程序员项目经验怎么写_71

LBORRES等于非数字的字符值很常见,这里看得到LBSTRESN是空的。所以,我们在将LBORRES转化成LBSTRESN的时候,要确保LBORRES的值全是数字才行。用下面两种compress函数是可以的,而用anyalpha可能会得不到想要的结果。它可以将值如单词字母表示的结果过虑掉,让它不参与运算或者赋值,但如果为'-',’_'等不是普通的字母数据时,同样导致input函数无法读取数据而报错。

zabbix项目经验怎么写 项目经验怎么写 范例_程序员项目经验怎么写_72

下面给出比较合理科学的处理方法。注意到这里只有非空才参与计算,not missing函数以及cmiss函数,还有缩进的应用。

zabbix项目经验怎么写 项目经验怎么写 范例_Events_73

在进行字符型变量和数值型变量的计算也好,转换也好,一定要使用INPUT函数和PUT函数,并辅之best.等各种匹配格式,strip等函数去空格,否则,很容易出错,遗漏,得不到想要的结果。a

zabbix项目经验怎么写 项目经验怎么写 范例_Events_74

这段程序里通篇没有一个quit,没有一个run,经常跑着跑着不知道程序哪里死了。虽然SAS语言中,下一个DATA步或者PROC步标志着上一个语句的结束,但是,最好在一个DATA步或者PROC步结束时,加个run或quit,强制结束。企业界里的good programming practices,SAS出品的教材,都有这样的要求。

下面是比较科学的写法。

zabbix项目经验怎么写 项目经验怎么写 范例_数位_75

zabbix项目经验怎么写 项目经验怎么写 范例_数据_76

这是打开一个ae程序之后,从第一行就开始看到的,是不是感觉有点乱乱的。

这里它有两个问题:

首先,整段程序都没有标题。

一段程序里,第一部分首先应该是header,用以描述这段程序相关信息。这样,不管以后何人何时打开这个程序,都能很快了解这个程序,比较顺利的take别人的工作。

这里我给出了一个sample header,它清晰的给出了一般header里需要包含的元素,我们只需依据程序目的,在相应地方做更新即可。比如是TFLs程序的话,可以写上标题,统计的模型,特殊的处理等。如果程序有大的、重要的更新,可以写进history。这个一般根据公司的SOP和客户的要求来follow就行,没有统一的标准,但一定得有。

zabbix项目经验怎么写 项目经验怎么写 范例_数据_77

注意到Macro Used这里只写全局性的宏,在这个程序里个人新创建的,只针对这个程序的,不列。对于global macro,其他程序能调用,其他成员也可以调用。

其次,程序里面有大量乱码。

像有些特殊符号,比如写一起的正负号,千分号,大于等于号等等,有的能正常显示,有的不能,这可能与server SAS版本和编码有关。但anyway,不管以何种方式,要尽可能地保证代码都能正常显示。

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_78

利用cport和cimport过程,可以一次性将所有sas data sets转化为xpt文件,但不推荐这种方法。

更常规一点的做法是利用xport,copy过程,将一个个数据集转换成相应的xpt。我们可以在individual程序中加入这一句,也可以考虑用宏在runall程序中批量处理。

zabbix项目经验怎么写 项目经验怎么写 范例_数据_79

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_80

在对数据集merge过程中,如果各个数据集中的某个变量类型、名称均相同,会出现长度截尾和override的情况,也就是前面变量的属性决定新变量(实际上名字一样)的属性,后面变量的值决定新变量的值。小编觉得,虽然程序不会报错,结果可能也不受影响,但这只是可能而已。

zabbix项目经验怎么写 项目经验怎么写 范例_数据_81

所以,我们可以考虑把后面的变量drop掉,或者重命名(这种用法常见)。这里因为值都是一样的,只需要取一个就可以了。

另外,我们在merge之前,给变量一个更大的长度,可以防止truncated的出现。

注意到这里merge有个常见的不报warning的warning,这是因为merge的两个data sets按by变量排序都不是唯一的,而这种问题是一定要处理的。merge语句是无法合并多对多的数据集,它只能处理一对一或者一对多的情形。

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_82

我们优化程序:

(1)变量rficdt、rfpendt只写在第一个SQL里面,因为它对每个subjid是唯一的,这里只出现一次就可以了。

(2)两个SQL中的group语句和数学函数,使得数据集sestdt、seendt按by变量排序已经是唯一的了,这里指定一个format是为了我们更好查看数据。

再次提交程序,发现log clean,结果正确。

zabbix项目经验怎么写 项目经验怎么写 范例_数位_83

另外,从程序角度,这是从SV map SE domain一种较为科学的方法,全面合理,虽然define.xml中不一定这样写。因为它不分EPOCH来讨论,也无论病人是screening failure还是early termination以及其他各种情况。

zabbix项目经验怎么写 项目经验怎么写 范例_程序员项目经验怎么写_84

箭头处的正负号,这个估计应该是从其他文件copy过来的,直接在SAS editor窗口应该也许可能大概是打不了这样的符号。这里之所以能显示,是因为使用了SAS server unicode打开方式,如果用其他方式可能就出现乱码了。这个程序使用了6个data步或者proc步,反反复复。

然而,它是可以精简的,draft阶段就用一个SQL来完成。这里用到了SQL里面的子查询,并且和另外一个条件并列。因为连接要用到USUBJID,故第一个SQL保留USUBJID,而在第二个SQL中因为不用输出STUDYID和SITEID信息,我们选择只输出SUBJID即可。

zabbix项目经验怎么写 项目经验怎么写 范例_数位_85

zabbix项目经验怎么写 项目经验怎么写 范例_程序员项目经验怎么写_86

这种宏参数叫位置参数,引用它的时候,只需直接赋值即可。

zabbix项目经验怎么写 项目经验怎么写 范例_数据_87

这里使用没有语法问题,省事,不用多写等于号。但小编觉得它可改进。

(1)各个值具体是赋值给哪个参数,这里不太明显,不好对应,宏程序如果报错,也不好调试。

(2)宏参数值出现的顺序必须和创建宏时的参数顺序一致,中间如果有遗漏,或者本身就为空值,容易出现错误。

另外,可以考虑在%mend后面再写一遍宏的名字,同时更合理科学的给参数命名,以增加程序的可读性。

这里,推荐用关键字参数,也就是说在定义和引用的时候都加上等于号。比如QNAM=AEOCUR, QLABEL=Any Adverse Events等等诸如此类的。

zabbix项目经验怎么写 项目经验怎么写 范例_数据_88

两个宏的定义不同,却使用了相同的宏名字,而且是在同一个程序当中。小编觉得,要么用两个不同的宏名字,定义两个宏,要么优化和加强一个宏,使之能包含所有情况。

zabbix项目经验怎么写 项目经验怎么写 范例_数据_89

一般而言,数据部门给了编程部门多少个原始数据集,那么我们就依据这些数据集来产生相应的ADS(SDTM或者ADaM)。这里看到有的数据后面有个_d,这实际上是对原始数据进行了更改之后的全新数据集,然后它又放在一个库里,这样容易引起混淆。

实际上,_d是把原来的数据转置了一下,只是结构变化而已,这样做是方便接下来的编程。窃以为,这种做法是可以,但要注意。

从数据库中导出多少个原始数据集,那我们后面的编程工作就是基于这些数据。比如DM给了我们56个SAS数据集,然后我们产生了37个SDTM SAS数据集,用两个库分别存放,所有的变化和处理,都放在中间过程。至于原始数据不符合某个domain标准结构,那我们在产生这个domain的SAS程序里面写。也就是说,将对原始数据加工和处理所需要的程序,放在需要使用这个数据集的单个SDTM程序中,不要分开写程序并改变原数据的结构。

zabbix项目经验怎么写 项目经验怎么写 范例_数位_90

很明显,两个if条件是互斥的,我们尽量用if...then.../else if...then代替重复的if语句。根据SAS的语法规则,两个if相当于条件判断要执行两次,而If...then.../else if...then会根据条件自上而下的进行判断。当前面条件满足的话,后面的语句不再执行,故用else会使得效率更高些。这在进行多个条件判断、数据量很大时,效果更明显。

另外,如果使用多个if语句,当后面的条件包含前面的条件时,后面的if...then语句才真正起作用,它覆盖了前面的语句。

标红框的else语句。当我们依据一系列条件生成一个新变量的时候,如果条件满足,Ok,我们依据条件赋值;如果都不满足,新变量的值很显然就是空值。所以,这个else语句可以不写。

如果语句可以写得更少,而程序完全不受影响,基本准则是代码越短越好。所以,程序要精简精简再精简。

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_91

从前面的程序可以看出,seq1和seq2的值是这样定义的。

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_92

这里先按usubjid, seq1, seq2排序,再算sequence number。

实际上,SDTMIG已经给出了我们常见的domain中的Keys,也就是一般算sequence number的方法。

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_93

首先,我们按Keys中的变量排序,然后,再按上面的方法算XXSEQ。像这里的CE domain,我们先按STUDYID, USUBJID, CETERM, CESTDTC排序,然后再算CESEQ。

这里有两点。

(1)如果客户没有指定Keys,一般就按SDTMIG中给出的排序或利用其基本思想计算SEQ。

(2)一定要使Keys中的变量是能唯一identify数据集观测的。按Keys sort数据集,需要保证no duplicate。如果有,说明现在的Keys不足以定位到每条观测,或者可能存在data issue,这需要多方确认。

像程序写到需要产生SEQ的时候,基本是最后一步,数据集中的变量都应该derive完毕了,所以在此之后不应该再有变量的derive。这里最后对CESTDTC做了个变换,它的过程是:从CESTDTC从右往左,以0-9中的任意一个为分隔符,第一个取得的值转换为空值,也就是将形如“2018-12-”变为“2018-12”,使之符合ISO8601格式。这一步其实在生成CESTDTC的时候就可以完成。

这里是从raw data EX中读取并计算首次用药信息,用于XH相关的运算。

zabbix项目经验怎么写 项目经验怎么写 范例_数位_94

这里是再次从raw data EX中读取并计算首次用药信息,用于MHCSUM相关的运算。

这里看到,首次用药信息,每人只有一条记录,而且,这个信息已经在DM中产生了,没有必要一次又一次地去重新derive,我们只需merge DM中的数据就可以了。

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_95

从CRF或者raw data上看,不管是哪个分析page,像病人随机号,中心号等都是存在于每个raw data的。像这种common variable,可以在读每个raw data的时候一并产生,用模块化程序生成。

VISIT/VISITNUM

Take SDTM programming for example, it is common that we usual meet VISITNUM/VISIT and XXTPT/XXTPTNUM mapping procedure, here XX stands for Finding Domain like PC, LB, EG and so on.

 One task is to change raw data into standard SDTM variable, such as if CPEVENT=”Treatment and Observation Period (Day1)” then VISIT=“Day 1”, or if CPEVENT=”Day 1” then VISITNUM=2001.

Another commom task is value conversion between standard SDTM variables, such as map VISITNUM by VISIT based on code lists.

Of course, it is definitely ok that if we use “if…then/else”statement to complete this task. But I think we apply user-defined format is more efficient to handle this work. And programs are more easily to maintain if plenty of codes are required to update.

 First, let's take a look at the follow code. It defines 2 formats.   

zabbix项目经验怎么写 项目经验怎么写 范例_数据_96

Then the formats are used in the PUT statement to derive VISITNUM and PCTPTNUM. The purpose of adding “+0” is to convert VISITNUM data type to numeric form. It is automatic converted in the SAS. Though it maybe does not produce “Warning“ in the log, but I propose to use PUT or INPUT function named Explicit Character-to-Numeric Conversion. 

zabbix项目经验怎么写 项目经验怎么写 范例_Events_97

Since VISITNUM and PCTPTNUM are numeric variable and the aim is to convert characters to number, then we can use the key word INVALUE in the format statement. Notice the format name did NOT contain “$” here and keep in mind the indentation is preferred. 

zabbix项目经验怎么写 项目经验怎么写 范例_数据_98

Since you are going to derive a numeric variable, we perform PUT function with its related format. Compress function is used to drop blank in the strings, transferring “DAY 1” to “DAY1”. 

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_99

Finally, the output is like the below screen spot. 

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_100

Timing variable VISIT and time point are usually the global clinical encounters and it is the same case for other analysis part if available. Therefore creating a macro to derive this general variable is strongly recommended.

CASE...WHEN in SAS SQL

data步中可用if…then…来做条件判断,SQL语句中可以用case…when

zabbix项目经验怎么写 项目经验怎么写 范例_数位_101

 

  1. 如果是创建一个表(SQL语言中分别叫表、行、列,对应SAS中的数据集、观测、变量),结果就不会output出来,那就不用加noprint语句了。
  2. case前面是有逗号的。
  3. 如果较复杂,when后面可以根据运算结果来指定相应的值,简单的直接用case变量。
  4. 用else语句来表示判断结束,而且不能是空的else语句(data步中的select when可用空的otherwise结尾),如果条件已经选择完毕,用else+空格为字符型变量,else +点为数据型变量,或者用else表示最后一个条件。如果生成的是字符型变量,建义定义一个长度,以防truncated。
  5. 只要涉及到循环,最好缩进,让程序美观易读。
  6. 这里的outobs=15是提前结束,虽然程序会报相应warning,但这是调试程序的好方法,用代表性的、较少的数据来验证程序,快速高效!

SAS SQL部分重难点 假设要编写这样一段程序:

  1. 在SV中计算每个USUBJID的访视日期;
  2. 通过某些语句(例如IF FIRST/LAST.USUBJID)得到每个USUBJID的最大日期;
  3. 通过PROC SORT对数据集排序;
  4. 最后Merge合并。

这样至少也得4个以上的data步和proc步。如果问题复杂,程序行数只会更多,而SQL却能简化程序,如下图所示。

zabbix项目经验怎么写 项目经验怎么写 范例_程序员项目经验怎么写_102

SQL可以合理地处理“多对多”,而这是Merge语句无法实现的。另外其连接数据集的功能非常强大,通过各种形式的Join语句和外加条件,有时能将数十上百行的普通SAS语句用一个SQL表示(此时的SQL语句肯定没有比一个个的data步和proc步好理解)。

SQL可以更程序表达更简洁。

zabbix项目经验怎么写 项目经验怎么写 范例_Events_103

zabbix项目经验怎么写 项目经验怎么写 范例_程序员项目经验怎么写_104

上图是PROC SQL中left join所得结果。其实PROC SQL查询优化器处理连接过程非常复杂,不像data步中的merge语句那种“往下走,匹配,再往下走”,但从概念上讲,我们可以大致了解下PROC SQL处理Joins过程的一般步骤。

zabbix项目经验怎么写 项目经验怎么写 范例_程序员项目经验怎么写_105

假设我们要生成这样一张表。

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_106

第一行“基线(筛选期)平均值”是对所有人在这个test下的基线(筛选期)BASE平均。而到了后面的访视,由于失访或未查等原因,导致在第二个阶段的人数可能会减少,此时列标题中的“基线平均值”表示这部分剩下来的人的基期值。比如说,基线期有100个人,我们对这100个人有个平均值BASE1;第二周期只剩80个人,而80个人每个人也有个基线期的BASE,我们再只对这80个人平均得到BASE2,显得BASE1和BASE2是不完全相等的,应该是一个较稳定的变化值。至于编程,最简单的方法是在两个阶段分别选择适当的条件,前者对AVAL求平均,后者对BASE求平均。另外一个思路是要找到还剩多少人在第二周期。编程中处理类似于"基于一定条件再做其他查询"的思路,我们可以试试Subset Query。语法形如:

select .. from ... where usubjid in (select distinct usubjid from ...)

GROUP BY语句最常见用于对各个分类或项目的汇总呈现。

zabbix项目经验怎么写 项目经验怎么写 范例_数据_107

但如果没有遵守运用GROUP BY语句相关语法,是得出不正确结果的。

zabbix项目经验怎么写 项目经验怎么写 范例_数据_108

下面log中的这行remerging note最好不要让它存在,因而这里“a.*”用法是不严谨的。

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_109

SQL可以进行变量和观测的修改、增加或减少,SQL视图可以大大减少数据存储空间,可以视作数据集来使用,数据索引可在一定条件下更快地完成查询过程,SQL可以在没有关键变量的情况下强行合并,等等这些情形,我们在逐渐步学习和实践工作中,发现可供讨论和研究的问题还蛮多的,文章篇幅有限,这里不再赘述。

鉴于很久之前就存在的想法,我很乐于和大家分享一些知识或者技能,如果能对大家的帮助哪怕只有一点点,我都觉得很快乐、很知足。我想起我备考研究生的那段岁月,线性代数我一直觉得比较懵懂,虽然知识不是很难,题目也能做,但感觉还是很不好,觉得学得很零散,一碰就会碎那种。直到有天我在YOUKU上看到某个辅导学校一位叫曾祥金的老师的免费考研视频资料,豁然开朗,醍醐灌顶。最后终于发现,原来线性代数的知识内容就是一条线,前后是紧密相连的,前面的小知识为后面所用,最后形成了一套很标准规范的做题方法。虽然时过境迁,物似人非,但当时那种“顿悟”的感觉还历历在目,仿佛昨日!

下面是肿瘤试验中有效性疗效终点删失规则的部分程序。

zabbix项目经验怎么写 项目经验怎么写 范例_数位_110

程序说明:

  1. 使用数据集选项,可以选择性的读入和输出。
  2. SQL程序层次结构较多,推荐使用合理的缩进和对齐,增强程序可读性。
  3. GROUP BY常用于对数据的汇总。
  4. 可以定义新变量或常数变量,并赋予属性。
  5. FROM语句可以读入一个已经存在的表,也可以是中间临时生成的In-Line View,例如“from (select * from dst where ) as a”,括号里面可以是简单的查询,也可以是表与表之间的复杂连接。
  6. 使用WHERE子句选择条件时,可以再度利用子查询。例如,这里对没有发生PD和Death的受试者按最后一次肿瘤评估日期删失。

SQL Macro部分重难点

1. SAS宏函数不同于普通的SAS函数,虽然它也承担着普通函数的功能,但实际上它是宏。另外,由于宏语言内置的复杂性,使得看似简单的转换,实际上有一套内在的语法。这里第二步的%upcase函数mask宏变量a的进一步解析,而执行普通函数功能。

zabbix项目经验怎么写 项目经验怎么写 范例_Events_111

2. 宏语言执行在SAS语言之前;对于在data里创建的宏变量而言,只有当data步彻底执行完成,才能调用相应的宏变量。

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_112

zabbix项目经验怎么写 项目经验怎么写 范例_zabbix项目经验怎么写_113

zabbix项目经验怎么写 项目经验怎么写 范例_Events_114

3. 宏变量的多重编译,使得宏变量的创建更灵活,同时可以使用循环。

zabbix项目经验怎么写 项目经验怎么写 范例_程序员项目经验怎么写_115

4. 我们在调用宏变量的时候,日志窗口经常出现未编译宏变量的Warning。这是因为每个宏变量都有自己的存放位置,只能在指定位置调用。重难点!

zabbix项目经验怎么写 项目经验怎么写 范例_数据_116

zabbix项目经验怎么写 项目经验怎么写 范例_Events_117

下图十分详细地说明了每一段宏语句的执行在SAS宏处理器所产生的影响。

zabbix项目经验怎么写 项目经验怎么写 范例_程序员项目经验怎么写_118