五一五天眨眼就过去了,还是感叹一句时间过得真快。现在不管是工作日还是放假,每天睁开眼,打开电脑,工作,偷懒,一天就这样过去了。感觉时间不够用,但是又好像在浪费时间。
今天讲一个轻松的话题,就是SAS中的那些左连接,右连接,乱七八糟的连接,对于初学者来说,不太好理解,我希望初学者看完我今天这篇文章后能有一个更清晰的了解。
对于工作中的需求,SAS一般能提供好几种解决方式,比如上面讲的连接方式,就可以通过data步中的merge或者是proc sql解决。
需要注意的是:
外连接有3种:左连接(left join)、右连接(right join)、全连接(full join)
与外连接相反的是内连接(inner join)
用到的数据集:
test1: 6条观测
![](https://img.haomeiwen.com/i1998906/4d825246c4257710.png)
![](https://img.haomeiwen.com/i1998906/6b17199a134cc065.png)
test2:5条观测
![](https://img.haomeiwen.com/i1998906/d97d193102a871d4.png)
![](https://img.haomeiwen.com/i1998906/7edd441a2db54059.png)
注意test1多了subjid="005"和subjid="006",test2多了subjid="008"
当他们通过左连接合并的时候
![](https://img.haomeiwen.com/i1998906/033ca4ff291af755.png)
这里我们以test1作为“主数据集”,test2作为“副数据集”,将“副数据集”拼接到
“主数据集”上,我们通过 if a来实现。
注意这里的 in=a 或者是 in = b相当于分别在两个数据集里面创建了临时变量a和b,它们的值好像都是为1。注意,in=a/b你随便取什么名字都可以,你可以 in=main/ss/aaa 任何你喜欢的名字,然后下面对应的ifmain/ss/aaa,只要不要跟数据集里面的变量重名就可以了,要不然会会报错,像这样
如果你的数据集里面a变量是字符型,会报这个错
![](https://img.haomeiwen.com/i1998906/f87bb68454a2b42a.png)
如果你的数据集里面a变量是数值型,会报这个warning,
![](https://img.haomeiwen.com/i1998906/edab4eca6fa4143e.png)
所以大家写程序的时候要注意一下,一般越简单越好。
看下输出:
看下输出:发现test1里面的观测都保留下来了(绿色),test2里面有相同subjid的观测记录也保留了下来(红色),因为subjid="005"和subjid="006"不在数据集test2里面,所以可以看到var2和age2都是空的。
![](https://img.haomeiwen.com/i1998906/8d2eb3b5b6d3743a.png)
不知道是不是还有点抽象,对于初学者可能不太好理解。我们假设有两个俄罗斯方块,a和b,if a就是说a是老大,要向他靠齐(就是说现在有一个人把方块b往左边推,要和a合并,同样颜色的可以合并在一起;不一样的颜色,a的都保留下来,b的就被删除了,所以我们看到test2数据集里面的subjid="008"没出现)
![](https://img.haomeiwen.com/i1998906/b3248fe6b1d0dceb.png)
大家可以在脑海里想象一下这个过程,我希望我讲明白了。
那么通过proc sql怎么实现这个过程呢?可以通过left join:
![](https://img.haomeiwen.com/i1998906/fd6d640c026c4714.png)
这算是最简单的proc sql写法,但是大家要先熟悉这种写法,注意as不能漏掉,哪里该加逗号,哪里该加分号都要注意,我记得我刚学习proc sql的时候,总是报错,就是犯了上面的错。
那么右连接呢?其实把if a 换成if b 就可以了,相当于这时候把右边的当成老大了,也就是test2数据集里面的观测都需要保留下来,test1数据集里面的观测只保留有共同subjid的。
![](https://img.haomeiwen.com/i1998906/774385c65218f49d.png)
![](https://img.haomeiwen.com/i1998906/0c759b2bd05b1764.png)
看到subjid="005"和subjid="006"都被删除了。同理,我们在proc sql里面把left join换成right join就可以了,但是同时还要注意,如果我还是如图这样写,会发现subjid="008"怎么不见了,但是这条观测对应的其他变量还是存在的(19,ww)。
![](https://img.haomeiwen.com/i1998906/ec6c079eaf807a9f.png)
![](https://img.haomeiwen.com/i1998906/306a0f98e17bf701.png)
这是因为我们只写成 b.age2,b.var2,然后加上on a.subjid=b.subjid,导致只显示subjid都一样的subjid,其他的subjid则为空,这时候我们可以把a.*换成b.*,同时对a也进行筛选变量,不写成a.*;
![](https://img.haomeiwen.com/i1998906/84706698b626aa55.png)
![](https://img.haomeiwen.com/i1998906/f07ad9be69e539eb.png)
换句话说,只要是你去拼接那个数据集,那个目标数据集就写成xxx.*,相当于保留它的所有记录,同时要对另一个数据集进行筛选,像图片那样a.age1 ,a.var1,如果你写成a.*,b.*,会出现这样的warning:
![](https://img.haomeiwen.com/i1998906/7244d2b2906afb8b.png)
![](https://img.haomeiwen.com/i1998906/28158d226bd68334.png)
或者大家粗暴点,不熟悉右连接,那就都用左连接,然后把test2数据集放到left join前面就可以了
![](https://img.haomeiwen.com/i1998906/2e9f232934d6893d.png)
其实左右连接就像一面镜子,左连接就像另一种形式的右连接,只是把数据集位置换了而已。
我发现写这些东西就快花了一个多小时,真是醉了,太浪费时间了。
全连接简单讲讲,全连接就相当于输出两个数据集所有的记录,不管是相同的观测还是不同的观测。
data 步这样写
![](https://img.haomeiwen.com/i1998906/cf8cbbbaa3d2c533.png)
![](https://img.haomeiwen.com/i1998906/914b16426c63c331.png)
proc sql就有点麻烦了,需要用到coalesce函数,这个函数就是返回参数中第一个不为空的值。
![](https://img.haomeiwen.com/i1998906/95aef9a6011c170f.png)
![](https://img.haomeiwen.com/i1998906/725a20c7c3e4bd8e.png)
注意这时候不能简单地写a.*,b.*,要不然又会报subjid已经存在的warning,所以还是得手动筛选一下量(b.age2,b.var2,a.age1,a.var1),同时这个coalesce(a.subjid,b.subjid) as subjid,可以换成你自己取的变量名(比如你想取sub_subjid,coalesce(a.subjid,b.subjid) as sub_subjid,但是你下面的on 还是要保留成subjid)
如果你只想输出两个数据集共同的部分,不管是只在test1中有记录的观测(subjid="005"和subjid="006")或者只在b中有记录的观测(subjid="008")都被删除.
data 步这样写:
![](https://img.haomeiwen.com/i1998906/bbbe8c94aaeb8e05.png)
![](https://img.haomeiwen.com/i1998906/4810fbc57325e788.png)
如果要用proc sql实现这种效果,不用写任何join方式(本质上它属于内连接),但是注意这时候使用where筛选,不是用on了,也就是说外连接的3种方式要用on筛选,但是内连接要用where,要不然会报错,这是需要注意的另一个地方。
![](https://img.haomeiwen.com/i1998906/e9a02050bff8752d.png)
左连接(left join)、右连接(right join)、全连接(full join),内连接(inner join)好像只是proc sql的概念,用在data步中好像不合适,同时我感觉data步更好理解,但是存在以下弊端:
1:用data步merge需要先排序
2:如果排序变量长度不一致(可以新建排序变量使长度一致),还会报warning,但是proc sql就不会。
网友评论