工具人时刻
Dec 9, 2019
2 minute read

昨天晚上师姐速报:「师弟,帮忙算个东西,用SAS写」。

一开始对我来说可能不是什么难事,打开文件一看,陷入了深深的沉思。 用一个截图说明:

每一行代表一个研究对象,每个研究对象有 pm1 到 pm7 这 7 天所接触的 PM2.5 浓度(实际文件里面是 365 天)。 我们需要计算连续 2 天,连续 3 天,… ,连续 7 天接触 PM2.5 浓度大于 35 的次数。 而且如果连续 3 天接触 PM2.5,那么只能算一次连续 3 天次数,而不能再重复计算连续 2 天次数。 换句话说,这里的每一个连续的天数都必须刚刚好好。

也许有些写程序的小伙伴会觉得非常好写,但对我来说,想要使用 SAS 解决这个问题,直觉上还是比较费劲的。 昨晚在参加选调生宣讲的时候,就一直在想。回宿舍立马坐下敲了出来。

首先,导入数据。将数据里每个值和 35 进行比较,先转换成由 0, 1 组成的数据集。 方便下一步的运算。

DATA example;
  infile "G:\04_code\PM2.5数据.csv" DSD MISSOVER firstobs=2;
	input id pm1-pm356;
RUN;

DATA example_35;
	set example;
	array pm {365} pm1-pm365;
	do i = 1 to 365;
		pm[i] = pm[i] > 35;
	end;
	drop i;
RUN;

接下来,就是最关键的部分。 这里是利用 SAS 在 DATA 步读取数据的同时,插入一个循环。 这个循环长度就是 365,把每个研究对象的数据由头读到尾,在读的过程中分析数值 1 的连续情况。

DATA PM_days(keep=id day2 day3 day4 day5 day6 day7);
	set example_35;
	array pm {365} pm1-pm365;
	do i = 1 to 365;
		if pm[i] eq 1 then do;
			count+1;
		end;
		if pm[i] eq 0 | i eq 365 then do;
			if count eq 2 then day2+1;
			if count eq 3 then day3+1;
			if count eq 4 then day4+1;
			if count eq 5 then day5+1;
			if count eq 6 then day6+1;
			if count eq 7 then day7+1;
			count = 0;
			
			if i eq 356 then do;
				output;
				day2 = 0;
				day3 = 0;
				day4 = 0;
				day5 = 0;
				day6 = 0;
				day7 = 0;
			end;
		end;
	end;
RUN;

PROC SQL;
	create table PM_result as
	select *
	from example, PM_days
	where example.id = PM_days.id;
quit;

PROC PRINT data=PM_result(obs=10);
RUN;

说实话,这段代码写下来还真有点 C 语言的感觉。 当然在循环中,对于 day2-day7 的操控和赋值可能还有改进的空间,即把这些变量也编入一个数组。 但既然只要求到了最多 7 天,这样其实也足够了。

最后赶在 10 点之前,交给了师姐,师姐表示很欣慰。 不过,我同样感受到了 SAS 实在是一种充满个性的数据分析语言。 打个比方,写 python 和 R 像是骑马,写 SAS 就像骑河马。 而且,此时此刻最让我难受的是,Markdown 也和河马过不去。