首页 / 资讯中心 / 文章详情

小白学SAS基础教程:从数据整理到作图制表与踩坑排查

小白学SAS基础教程:从数据整理到作图制表与踩坑排查 ★ FEATURED ARTICLE
简介《小白学SAS基础教程》面向零基础的数据分析与统计初学者也适合曾被SAS繁杂命令劝退、希望系统入门的人群。全书采用对话体以“小白”的学习视角串联数据集建立、常用函数、数据清洗、绘图和制表等基础内容强调以解决实际问题为导向而非简单罗列语法命令。资源包共1个PDF文件大小约1.76MB便于下载后直接阅读与检索内容聚焦基础入门并延伸至缺失值、异常值、重复值处理及规范报表输出等实用环节。目前已有709人学习。读者可从中获得一条较清晰的学习路径理解每步操作背后的逻辑与用途掌握用SAS完成数据整理、图形呈现和报表生成的基本思路适合作为案头参考并配合练习逐步消化。1. 从打开软件到跑通第一段程序SAS 基础入门真正卡住新手的地方很多人对 SAS 基础入门的第一印象是命令太多、帮助文档太厚买本书翻两页就放下了。这份《小白学SAS基础教程》换了条路不按软件菜单逐条讲语法而是用对话把数据整理、作图、制表三件事串起来主人公小白遇到的问题基本就是新手第一天会撞上的。它适合零基础、被 SAS Help 劝退、又必须马上处理数据的人如果你已经有 SAS 底子想找统计建模的进阶内容这份资源不是给你准备的。2. 程序骨架与数据集落地data/input/cards 三件套和 Work 库的生死问题SAS 的程序结构其实比多数人想的简单。一个 data 步负责把数据弄进来一个 proc 步负责对数据做动作中间靠 run 隔开。新手最容易犯的错误不是写不出复杂逻辑而是连这个骨架都没立住就开始抄函数结果日志里红字一片。先把最小闭环跑通后面所有内容才有落点。2.1 三个窗口的分工要先弄清楚打开 SAS 9.4 中文版界面看着眼花实际只要盯住三块编辑器、日志、输出。编辑器写程序日志记录运行过程输出放结果。菜单和工具栏用得不多绝大多数数据管理和统计动作都在编辑器里敲代码完成菜单更多是保存、导入导出这类辅助操作。结果查看器会额外生成一份排版更整齐的结果代价是多花一点运行时间介意的话可以在参数选择里把「创建 HTML」勾掉。程序提交之后的第一动作永远是看日志不是看输出。日志里没有 ERROR 和 WARNING结果才有讨论价值日志里有红字输出窗口里那些数字一个都不能信。这个习惯听起来废话但我见过太多人拿着带 ERROR 的结果去做汇报。2.2 data、input、cards 的最小可用写法先用一段能跑通的程序把骨架立起来这段代码同时包含数据步和程序步/* 数据步建立数据集 */ data score; input gender $ score; cards; M 88 F 92 M 75 F 96 ; run; /* 程序步对数据做描述统计 */ proc means datascore n mean std; class gender; var score; run;逻辑说明data score;给数据集起名名字只能由字母、数字、下划线组成首字符不能是数字长度不超过 32 个字符不能带空格和中文。input gender $ score;声明变量$表示后面这个变量是字符型变量之间用空格分隔SAS 默认以空格作为字段边界。cards;告诉 SAS 下面开始是数据行数据行结束后分号必须单独占一行不能紧跟在最后一个数据后面。run;表示这一步结束可以执行了。参数说明class gender;指定分组变量SAS 会按性别各算一遍var score;指定分析变量n mean std是要输出的统计量不加的话默认输出会更多。写程序步时proc后面跟过程名过程名后面跟选项具体语句用分号断开这套格式和数据步是两套东西别混着记。字符型变量里的中文要留意编码如果数据行里出现中文而日志提示编码转换问题优先检查会话编码设置而不是怀疑语法。2.3 Work 库为什么第二天就空了建好的数据集默认落在逻辑库的 Work 目录下这是 SAS 留给用户的临时空间。Work 的特点是会话一关就自动清空所以里面的数据集叫临时数据集。SAS 自带的几个库各有用途Maps 系列放地图数据Sashelp 放帮助和示例数据Sasuser 放练习数据都不适合塞自己的东西。要留住数据就得建永久逻辑库把数据集指到一个真实存在的磁盘目录/* 建立指向本地目录的永久逻辑库 */ libname proj D:\sasproj; /* 把临时数据集转存到永久库 */ data proj.score; set work.score; run;逻辑说明libname语句把库名proj绑定到一个已经存在的文件夹之后所有proj.xxx形式的数据集都会落到这个目录里关闭软件也不会消失。set work.score;是读取临时库里的数据再写一份到永久库相当于另存为。参数说明路径必须真实存在SAS 不会帮你创建文件夹路径里尽量别用中文和空格某些版本下会有解析问题。libname是会话级的每次重新打开 SAS 都要再执行一次嫌麻烦可以写进启动配置里自动加载。库名同样遵循数据集命名规则别起个my-lib这种带横线的名字。2.4 从 Excel 导入proc import 的参数怎么设才不翻车数据量大的时候没人会在编辑器里手敲常规做法是先在其他工具里录好再导入 SAS。proc import是最省事的一条路proc import datafileD:\sasproj\sales.xlsx outwork.sales dbmsxlsx replace; getnamesyes; mixedyes; guessingrowsmax; run;逻辑说明datafile指源文件out指落地后的数据集dbms声明文件类型replace允许覆盖同名数据集避免重复运行时反复报错。getnamesyes表示把第一行当作变量名不加这行 SAS 会自己造 column1、column2 这种名字。参数说明dbms的取值跟着文件走xlsx 对应新版 Excelxls 对应老格式csv 对应文本文件。mixedyes和guessingrowsmax是重点SAS 判断列类型时只看前若干行如果前几行恰好为空或者混了非数字字符整列会被判成字符型后面的数值全部变成缺失。让它多扫描一些行、允许混合类型能挡掉大部分这类问题。代价是导入变慢数据量特别大时可以先把guessingrows设成 10000 这种具体数字而不是 max。导入完别急着分析先跑一句proc contents datawork.sales; run;看变量类型和长度类型不对就回头调参数这一步能省掉后面大量的排查时间。3. 格式、新变量与清洗informat/format 怎么分工retain 和数组各解决什么问题这一章是整份资源里信息密度最高的部分也是新手从「能建数据集」跨到「能处理数据」的分水岭。书里没按函数字典的方式罗列语法而是按「你要解决什么问题」来组织读进来的日期显示成数字、想把连续变量自动分组、想找出哪些行有缺失、想把重复记录清掉。每个需求对应一组具体写法下面按这个顺序拆。3.1 informat 和 format一个管读进来一个管显示出去这两个东西名字像作用完全相反。informat 负责解析输入告诉 SAS「这一列长这样请按这个规则读」format 负责控制显示告诉 SAS「存的是这个值但请按这个样子呈现给用户」。原始值不会因为 format 改变它只影响你眼睛看到的东西。data birth; informat d yymmdd10.; format d yymmdd10. score 8.2; input id d score; cards; 1 1990-05-12 88.5 2 1995-11-03 92.25 ; run;逻辑说明informat d yymmdd10.;让 SAS 把1990-05-12这种写法的字符串解析成日期值存进数据集时是一个天数数值format d yymmdd10.;让这个天数在输出时重新显示成年月日。两个都写才能做到「读得对、看得懂」。score 8.2表示总宽度 8 位、保留 2 位小数。参数说明日期类 informat 常见的还有mmddyy10.、date9.要和数据里的实际写法对上对不上就是缺失值而且日志不会报错静默出错最坑。数值格式里8.2的 8 是总宽度别设得比整数位还小否则显示会变成星号。自定义分组用proc format更省事把连续变量切成几档比写一长串 if-then 干净proc format; value grp low - 59 不及格 60 - 79 及格 80 - high 优秀; picture pct low-high 009.9%; run; data scored; set score; format score grp.; run;逻辑说明value grp定义了一个命名格式按区间映射成标签picture pct定义的是显示模板009.9%里 9 代表数字位、0 代表补零位用来把 0.856 显示成 085.6% 这种效果。用的时候在 data 步里写format 变量 格式名.;注意格式名后面那个点不能漏。参数说明区间写法必须覆盖完整low和high是关键字别写成-999。format 默认存在 work.formats 里会话一关就没了。要在多个项目里复用加libraryproj.formats存到永久库再用options fmtsearch(work proj);告诉 SAS 去哪找。3.2 造新变量的四条路表达式、if-then、retain、do 循环新变量无非四个来源选哪条路取决于你要不要跨行累积、要不要按条件分支。data newvar; set raw; /* 路径一表达式直接算 */ total math english; /* 路径二if-then 做分支 */ if total 180 then level A; else if total 150 then level B; else level C; /* 路径三retain 做跨行累积 */ retain cum 0; cum cum total; /* 路径四do 循环 数组 */ array s{2} math english; cnt 0; do i 1 to 2; if s{i} 90 then cnt 1; end; drop i; run;逻辑说明set raw;逐行读入每读一行后面的赋值语句执行一次。表达式那条最直白。if-then-else是有顺序的SAS 从上往下判断第一个成立就跳出所以条件要从严格往宽松写。retain cum 0;声明这个变量在迭代之间保留值不写 retain 的话每行都会重置成缺失。cum cum total;还有一种简写cum total;累加语句自带 retain 效果。参数说明数组array s{2} math english;把两个变量打包s{i}按位置取值drop i;把循环计数器从数据集里去掉不然会多出一列没用的 i。数组维度要和变量个数对上对不上直接报错。if和where的区别后面单独说这里先用 if。有个思路值得单独提round函数不只是四舍五入。把数值除以 10 再 round 再乘回去就能实现按十位自动分段比写十几个 if 分支清爽得多。这类「函数换个用法」的思路比记住函数本身有用。3.3 数组和自动变量写一个查缺失的万能程序缺失值检查是每个数据集的必经流程。手写if x . then ...遇到几十个变量就废了正确做法是用自动变量列表配合数组data miss_check; set raw; array nv _numeric_; array cv _character_; miss_n 0; miss_c 0; do i 1 to dim(nv); if missing(nv[i]) then miss_n 1; end; do i 1 to dim(cv); if missing(cv[i]) then miss_c 1; end; drop i; run;逻辑说明_numeric_和_character_是 SAS 的自动变量列表分别代表当前数据集里所有数值型和字符型变量不用一个个写名字。dim(nv)返回数组元素个数循环从头走到尾。missing()函数对数值缺失.和字符缺失空串都返回真是唯一能同时处理两种类型的判断方式。参数说明这段程序可以直接抄走改set后面的数据集名就行。miss_n和miss_c分别是数值缺失个数和字符缺失个数跑完看一眼分布就知道这份数据能不能用。drop i;别忘否则每次运行都会多一列。配套的还有first.和last.两个临时变量配合by语句用专门处理分组内的首行和末行。取每个 ID 的最新记录proc sort dataraw; by id date; run; data latest; set raw; by id date; if last.id; run;逻辑说明by id date;要求数据已按这两个变量排好序last.id在某个 id 的最后一行取值为 1其余为 0if last.id;就是只保留每组最后一行。这两个变量是临时的不会出现在结果数据集里。参数说明不排序直接用 by 会报错「BY 变量未按顺序排列」这是最高频的翻车点之一。first.id取首行last.id取末行两个都写就是每组首末各留一条。3.4 去重、找异常、填补清洗三件套的具体写法重复值最省事的处理方式是排序时直接去重proc sort dataraw outdedup nodupkey dupoutdups; by id; run;逻辑说明nodupkey按 by 变量判重保留第一条其余丢掉dupoutdups把被丢掉的记录单独存一份方便回头核对是不是误删。by 后面跟多个变量时判的是组合键不是单列。参数说明nodupkey和nodup不是一回事前者按 by 变量整体判重后者按所有变量判重。真要按全字段去重用noduprecs。删之前一定先看 dups血泪经验是有些人把「同一个人多次就诊」当成重复记录删了。异常值用 if 和 where 的差别要先搞清/* where 在读取阶段过滤效率高 */ data sub1; set raw; where score 60; run; /* if 在读取之后过滤能用新变量 */ data sub2; set raw; if score 60; run;逻辑说明where作用在读取阶段不满足条件的观测根本不会进入 PDV所以快if是读进来之后再判断如果条件里用到刚刚算出来的新变量只能用 if。反过来where里不能引用 data 步里新建的变量。参数说明where还能用在 proc 步里做子集if不行。数据量大、只是简单条件筛选时优先 where。缺失值填补要看场景别默认用均值proc stdize dataraw outfilled reponly methodmean; var score income; run;逻辑说明reponly表示只替换缺失值非缺失的原始值保持不动不加这个选项会对所有值做标准化那就不是填补了。methodmean用均值填也可以换成 median、midrange。参数说明均值填补会压缩方差做严谨分析时要考虑多重填补这里只是工程上的快速处理。分类变量的缺失通常单独设一个「未知」类比硬填更稳。3.5 函数的使用思路别只记语法记它能干什么count谁都会用但用它统计一条商品评价里出现了几次关键词就是另一回事。scan按分隔符取第 n 段配合循环可以拆开任意结构的字符串。lag取上一行值dif直接算差值做同比环比的时候比自连接快得多data trend; set sales; by month; prev lag(amount); diff dif(amount); if first.month then prev .; run;逻辑说明lag(amount)返回上一行的 amountdif(amount)等价于amount - lag(amount)。注意 lag 是在队列里排队取值如果中间有 if 跳过的行取到的可能不是你以为的上一行这是它的经典陷阱。参数说明if first.month then prev .;用来把每组的第一个观测的上一期值清掉避免跨组串数据。月份数据一定要先排序再 by。4. 作图与制表sgplot 双坐标轴、tabulate 三线表和 ODS 输出SAS 的绘图在过去是弱项从 9.3 开始补强到 9.4 已经能覆盖绝大多数常规和非常规图形。这份资源在绘图和制表上花的篇幅明显多于同类基础书原因很实际分析做完要交出去图和表是最先被看到的东西。这一章按「先出图、再出表、最后送出去」的顺序讲。4.1 sgplot 的骨架图形类型选错后面全白搭proc sgplot的基本结构是「一个 proc 语句 若干绘图语句 若干坐标轴语句」。绘图语句决定画什么坐标轴语句负责修饰。proc sgplot datasales; vbar month / responseamount statsum datalabel groupregion; xaxis label月份 fitpolicyrotate; yaxis label销售额 grid; run;逻辑说明vbar画柱状图responseamount指定柱子高度由哪个变量决定statsum表示按月汇总求和不写 stat 默认是频数。datalabel在柱顶标数值groupregion按地区分色堆叠。xaxis和yaxis是坐标轴修饰fitpolicyrotate让标签太挤时自动倾斜。参数说明常用的绘图语句还有series折线适合时间序列、scatter散点看相关性、vbox箱线看分布和异常值、band置信带。选型的判断标准很简单看趋势用 series看构成用 vbar看关系用 scatter看离群用 vbox。双坐标轴是高频需求写法上靠y2axis指定哪条线走右轴proc sgplot datats; series xmonth ysales / y2axis lineattrs(colorblue); series xmonth yrate / lineattrs(colorred) markerattrs(size4); yaxis label销售额; y2axis label增长率; run;逻辑说明y2axis只能挂在一个绘图语句上挂了两个会报错。左轴和右轴各自用yaxis/y2axis修饰标签分开写。lineattrs和markerattrs控制线型和点的大小比改模板省事。参数说明两条线的量纲差得越远双轴越容易误导读图的人用之前想清楚是不是真的需要。刻度范围可以手动设values(0 to 100 by 20)自动刻度有时候会很别扭。4.2 非常规图template、雷达图和地图各自的前提书里提到的瀑布图、气泡图、风险图、森林图、卡通图靠的都是proc template定义图形模板再用proc sgrender渲染。这条路能画出 sgplot 内置语句覆盖不到的图形代价是要写 GTL 语法proc template; define statgraph pie_demo; begingraph; layout region; piechart categorytype responseamount / datalabelattrs(size9); endlayout; endgraph; end; run; proc sgrender datasales templatepie_demo; run;逻辑说明define statgraph定义模板名begingraph到endgraph之间是图形结构layout region是布局容器piechart是具体图形元素。模板定义一次可以反复用换数据只改sgrender的 data 参数。参数说明模板名不能和已有模板重名重名会报错想覆盖就加statgraph前面的 define 时用store指定库。GTL 的报错信息不太友好写的时候一行一行加别一次写完再调。雷达图走proc gradar前提是数据已经是「分类 数值」的宽表地图走proc gmap前提是地图数据集可用SAS 自带的 maps 库在新版本里路径有变化用之前先proc contents确认一下。地图这块还有一层自带地图文件和 shape 文件是两套坐标系混用会画错位置这一点书里在对应章节有说明。4.3 tabulate 和 report三线表怎么真正落地proc tabulate是出统计表的首选维度放在 class 里指标放在 var 里表格结构用 table 语句描述proc tabulate datacls; class gender; var score; table gender性别, score得分*(n例数 mean均值 std标准差); run;逻辑说明table语句里逗号左边是行维度右边是列维度*表示嵌套后面是显示标签。n、mean、std是统计量关键字还可以用pctn算百分比、minmax算极值。参数说明三线表的样式靠 ODS 的 style 控制基本出不来精细效果常见做法是把结果输出成 RTF 后用文字处理软件微调或者用proc template定义表模板。书里给的思路是后者先定义表模板再让 tabulate 套用这样每次运行格式一致不用手工调。proc report更适合做明细报表column定列顺序define逐个声明角色proc report datasales nowd; column region month amount; define region / group 地区; define month / group 月份; define amount / analysis sum 销售额; run;逻辑说明group表示按此列分组analysis sum表示此列参与汇总计算。nowd关掉窗口模式结果直接进输出。参数说明order、across、display是另外几种常用角色用错了会报「变量角色冲突」。report 的列宽和换行靠style(column)控制细节多先用默认样式跑通再调。4.4 ODS 把结果送出去RTF/PDF/Excel 的打开和关闭结果最终要交出去ODS 负责把输出重定向到文件ods rtf filereport.rtf stylejournal; proc tabulate datacls; class gender; var score; table gender, score*(n mean std); run; ods rtf close;逻辑说明ods rtf file打开一个输出目标中间所有 proc 步的结果都写进这个文件ods rtf close;关闭。stylejournal是常用的简洁样式比默认样式更适合正式报表。换成ods pdf filereport.pdf;或ods tagsets.excelxp filereport.xml;就是另一种格式。参数说明close必须写漏了的话文件会一直被占用打开就是空白或者报损坏。中途程序报错导致 close 没执行可以在开头加一句ods _all_ close;兜底。输出到 Excel 时options(sheet_name表1)可以给每个 proc 结果指定工作表名否则全挤在一张表里。5. 踩坑排查日志红字、merge 错位、Work 清空这五类翻车怎么定位SAS 的报错信息不算友好但排查有章法。这一章把实际使用中最高频的几类问题按「现象 → 原因 → 解决」列出来都是可以直接对号入座的。看日志的顺序永远是先找第一条 ERROR往上翻一条语句八成问题就在那里WARNING 单独看它不会中断程序但会让结果悄悄变形。5.1 语法与日志类翻车现象一日志报ERROR 180-322: 语句无效或使用顺序不当位置指向一条看起来完全正确的语句。原因SAS 是按分号断句的上一条语句漏了分号解析器会把两条语句连起来读报错点就漂到了下一行。数据行后面那个分号没另起一行也会造成同样的效果。解决从报错行往上找最近的一个分号确认每条语句都以英文分号结尾cards;后面的数据结尾分号单独占一行。中文分号也是高频原因肉眼几乎看不出来把输入法切回英文再敲一遍。另外run;漏写不会立刻报错但上一步没结束下一步的行为会莫名其妙。现象二程序跑完没报错但输出里一个观测都没有。原因where条件写错或者字符变量比较时大小写、首尾空格不一致。SAS 的字符比较是区分大小写和空格的M和M 不相等。解决先用proc freq看一眼变量的实际取值分布再改条件。字符变量比较前统一strip()去空格或者用upcase()转大写。数值和字符混比较时SAS 会隐式转换并给 WARNING别忽略这条警告。5.2 数据逻辑类翻车现象三两个数据集 merge 之后行数暴涨本来一万行变成三万行。原因by 变量在其中一张表里不唯一。merge 遇到一对多时会把多的那几条都匹配上去行数就翻了。解决merge 之前对两张表分别跑一次去重检查proc sort nodupkey或者proc freq看 by 变量有没有重复值。确实是业务上的一对多就先在多的那张表里按 by 变量汇总成一条再 merge。另外 merge 前必须各自排序不排序会直接报错不会静默出错这一点反而好办。现象四字符型变量的缺失值用if x . then判断永远判不到。原因数值缺失是.字符缺失是空串两种类型的缺失表示完全不同。用数值比较去判字符变量SAS 会做隐式转换结果不可预测。解决统一用missing(x)函数它对两种类型都有效。批量检查就用第 3 章那段数组程序别一个个手写。5.3 环境与输出类翻车现象五ODS 生成的 RTF 文件打不开或者打开是空白。原因ods rtf close;没执行。程序中途报错、或者把 close 写在了 run 之前文件就一直处于打开状态内容没有落盘。解决在程序开头加ods _all_ close;清掉可能残留的输出目标结尾确认 close 在最后一步 run 之后。养成习惯只要写了ods xxx file写完立刻把对应的 close 补上再往里填内容。现象六昨天建好的数据集今天打开软件就没了。原因数据集落在 Work 库这是临时库会话结束自动清空。这不是 bug是设计如此。解决建永久逻辑库用libname指向固定目录数据集写成库名.数据集名。libname 是会话级的写进自动执行文件就不用每次手动敲。还有一个容易被忽略的proc format定义的格式也存在 work 库会话结束就丢。跨项目复用的格式要加library存到永久库再配options fmtsearch指定搜索路径否则第二天运行会报「格式未找到」而且这个错误经常被当成数据问题查半天。6. 进阶用法把清洗程序打包成宏再用一个小数据集验证它书里附录提到两件事值得单独拎出来练一是把重复使用的清洗逻辑写成宏二是在 SAS 里调用 R 补上统计图形方面的短板。宏这部分最实用第 3 章那段查缺失的程序每次改数据集名很烦包一层就一劳永逸%macro miss_report(dsn, out); data out; set dsn; array nv _numeric_; array cv _character_; miss_n 0; miss_c 0; do i 1 to dim(nv); if missing(nv[i]) then miss_n 1; end; do i 1 to dim(cv); if missing(cv[i]) then miss_c 1; end; drop i; run; %mend; %miss_report(dsnwork.raw, outwork.miss_flag);dsn和out是宏参数调用时用参数名传值宏展开后就是一段普通 data 步。参数名后面留空表示必填漏传会报错比传错好。宏定义本身不执行只登记%mend结束定义真正跑起来是最后那行调用。写完别急着上真实数据先造一个已知答案的小数据集验证三行数据、两列数值、一列字符手动数出应该有 2 个缺失跑一遍看miss_n加起来是不是 2。答案对不上说明_numeric_把不该算的 ID 列也包进去了改成显式列出变量名重新验证。这个习惯看着笨但比在几万行数据里找差异快得多。SAS 调 R 的前提是本机装了 R 且版本匹配启动选项里开启集成后用proc iml的submit / R; ... endsubmit;块把 R 代码嵌进去R 侧算完的结果可以回传给 SAS 做后续处理。不同版本开启方式有差异9.4 常见做法是options RLANG;较新版本走另一套开关跑之前先用一句最简单的 R 语句试通再往上叠逻辑。调不通的时候先怀疑环境不要怀疑代码这一步的顺序搞反会浪费一整天。从那以后我每次拿到一份新数据第一件事永远是先跑一遍缺失检查和重复检查再谈分析和作图。这两步花不了五分钟但能挡掉后面百分之八十的返工。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站