做数据分析的人大概都有过这样的经历:接手一份别人留下的数据集,打开一看变量名是 v1、v2、x1、x2 这类东西,或者是一串拼音首字母缩写,光靠猜根本判断不出哪个是收入、哪个是工龄。我在最初接触 Stata 的那段时间,花在“猜变量含义”上的时间比真正做分析还多。后来才慢慢明白,变量标签、审视数据这些看起来最不起眼的基础操作,恰恰决定了后面画图、算统计特征、生成新变量时会不会反复返工。这篇记录把我自己在 Stata 里关于变量标签、审视数据、画图、统计特征、生成新变量,以及计算器功能、命令调用与终止、日志管理这几块的学习过程和踩过的坑整理出来,适合刚上手 Stata、或者用了半年但一直靠“试出来”的朋友对照着看。全程给的是能直接抄进 do 文件里跑的写法,不是那种只讲概念不落地的说明。
1. 变量标签:给数据集建一套看得懂的索引
1.1 变量名、变量标签、值标签压根不是一回事
刚学的时候我把这三个概念混成一锅粥,导致后面查资料时总是对不上号。用一句话区分:变量名是给 Stata 解析器看的,变量标签是给人眼看的,值标签是给分类取值看的。变量名有严格限制,只能用字母、数字、下划线,不能以数字开头,长度上限 32 个字符,而且大小写敏感——Income和income在 Stata 眼里是两个完全不同的变量。变量标签则是自由文本,上限 80 个字符,想写中文就写中文,它的唯一作用是让describe输出的时候人能看懂。
值标签完全是另一个维度的事。它解决的是“1 和 2 分别代表什么”的问题,比如性别变量里 1 是男性、2 是女性。值标签的创建要两步走:先用label define定义一套标签规则,再用label values把它挂到某个变量上。这一步是新手最容易漏的,我当初就干过只写了label define就以为完事,结果tabulate出来还是光秃秃的 1 和 2,盯着屏幕找了十分钟才发现少了一行。
label variable sex "受访者性别" label define sexlbl 1 "男" 2 "女" label values sex sexlbl这三行写完,tabulate sex出来的表头就是“男”“女”了。值得记住的是label define里的那套标签其实是一个独立对象,叫sexlbl,它可以被多个变量共享。比如还有一个father_sex变量,取值规则一样,直接label values father_sex sexlbl就行,不用重复定义。这个特性在做问卷数据的时候特别好用,一份问卷里十几个“是/否”题,定义一套yesno标签就能全部套上。
1.2 改标签的两个入口:modify 和 replace
已经定义好的值标签要改,得看你是改一套标签还是重定义整套。单个取值要改文字,用modify:
label define sexlbl 1 "男性" 2 "女性", modifymodify的意思是“在原有基础上打补丁”,没提到的取值保持原样。如果整套标签都错了,想推倒重来,就用replace,但这要求当前没有任何变量在用它,否则 Stata 会拒绝执行。这个限制其实是保护机制——想象一下几百个变量共用一套标签,你一个replace把它们全改了,后果不堪设想。
变量标签的修改相对简单,重新执行一次label variable就覆盖了,不需要额外的选项。我个人的习惯是在数据清洗的 do 文件开头集中写一批label variable,把原始数据里那些缩写全部翻译成人话,后面所有代码读起来就顺畅多了。这套做法在多人协作时价值翻倍,别人拿到你的 do 文件不用再问“这个lninc到底是对数收入还是低收入”。
1.3 用 labelbook 做一次标签体检
标签写多了,很容易出现“定义了却没用上”或者“两个变量不小心挂了不同的标签但内容重复”的情况。labelbook这个命令专门干这件事,它会把数据集里所有值标签列出来,标注每个标签被多少个变量使用、每个取值的频数分布。输出里如果看到某个标签的“变量数”是 0,说明是僵尸标签,可以清理掉。
配套的describe给出的是另一个视角:变量名、存储类型、变量标签、值标签名、取值范围,一行一个变量,扫一眼就能知道数据集的骨架长什么样。我现在的固定动作是拿到新数据先跑describe,看变量数量和类型分布,再跑labelbook,看标签体系是不是完整。这两步加起来不到十秒,但能省掉后面大量的猜测。
提示:Stata 14 之后默认使用 UTF-8 编码,中文标签基本不会乱码。如果你的版本比较老,或者数据来源是某些老系统导出的,中文标签可能显示成方块或者问号,这时候需要用
unicode analyze和unicode encoding系列命令转换编码,别硬扛着用乱码标签做分析。
2. 审视数据:拿到数据集的前十五分钟该干什么
2.1 describe、codebook、count 三个命令各管一段
这三个命令经常被混着用,其实分工很清楚。describe管结构:有多少个变量、多少个观测、每个变量什么类型、有没有标签。它不告诉你数据内容长什么样,只告诉你“有哪些格子”。codebook管内容:每个变量的取值分布、缺失情况、唯一值个数、最大最小值,输出很长但信息密度高。count if管精确计数,你想知道“收入大于五千的到底有多少条”,只有它能给你一个确切的数字。
我的习惯顺序是先describe摸清规模,再codebook逐变量过一遍,遇到可疑的再用count if精确核。codebook输出长这个问题有解,加变量名限定范围就行,比如codebook income age edu,只看关心的那几个。另外codebook, compact会压缩输出格式,适合变量多、只想快速扫一眼的场景。
有一类问题只有codebook能暴露出来:某个分类变量理论上有 5 个取值,实际数据里只出现了 3 个。这种情况tabulate也能看出来,但你得先知道它应该有 5 个取值。所以审视数据这件事,光看数据本身不够,还得结合你对这份数据的先验认知——问卷设计里有几道题、每个题有几个选项,这些背景信息决定了你判断“数据是否正常”的基准线。
2.2 一套可以固化成 do 文件的体检清单
用的次数多了,我把审视数据的动作整理成了一份固定清单,每次拿到新数据直接改改变量名就能跑:
| 检查项 | 命令 | 关注什么 |
|---|---|---|
| 数据规模 | describe | 观测数、变量数是否符合预期 |
| 唯一标识 | isid id或duplicates report id | 主键是否重复 |
| 分类取值 | tabulate var, missing | 有没有越界值、缺失占比 |
| 连续分布 | summarize var, detail | 极值、偏度、异常点 |
| 缺失模式 | misstable summarize | 哪些变量缺失严重 |
| 变量间逻辑 | assert系列 | 年龄不能为负、结束日期不能早于开始日期 |
isid这个命令值得单独说。它的作用是断言某个变量(或变量组合)能唯一标识每一条观测,跑通了什么都不输出,跑不通直接报错并告诉你哪几条重复。把isid id放在 do 文件靠前的位置,能挡住后面合并数据时的一大堆麻烦。我吃过一次亏,两份数据按 id 合并,结果其中一份的 id 有重复,merge出来行数凭空翻倍,当时没注意,等到做回归发现样本量不对劲,回头查了半天才定位到问题。
assert系列是另一个被低估的工具。assert age >= 0这种写法看起来多余,但它能在数据更新后第一时间告诉你“有人填了负数年龄”。把几条关键的assert写进清洗脚本,相当于给自己上了一道保险。
2.3 缺失值:Stata 里的那个点其实是个很大的正数
这一条我踩坑踩得最深,必须单独拎出来讲。Stata 里数值型缺失值显示为一个点.,但它底层存的不是 0,也不是什么特殊标记,而是一个比所有正常数值都大的数。这个设计带来的直接后果是:income > 5000这个条件,对缺失值也会返回真,因为“缺失”被当成了正无穷大。
我第一次遇到这个问题是在筛选高收入样本,筛出来的结果比预期多了几十条,查了半天发现全是 income 缺失的记录被误纳入了。解决办法是用missing()函数显式排除:
count if !missing(income) & income > 5000另一个相关的问题是tabulate默认不显示缺失值。tabulate region出来的百分比加起来可能是 97%,剩下 3% 去哪了?全在缺失里。加上, missing选项才会把缺失单独列成一行。这个默认行为很容易让人误判数据的完整程度,尤其是做描述性统计表的时候,漏掉缺失那一行,读者会以为样本是完整的。
misstable summarize是专门看缺失模式的命令,它会输出每个变量的缺失数量、缺失比例,还有不同缺失模式的组合情况。变量多的时候,它能帮你快速判断缺失是随机的还是集中在某几个变量上——如果是后者,往往意味着这几个变量来自同一份子问卷,或者采集环节有系统性问题。
3. 画图:graph 命令的骨架和几个能直接套的模板
3.1 先想清楚要表达什么,再选图形类型
画图这一步最常见的错误不是命令写错,而是图形类型选错。用散点图展示一个分类变量的分布,或者用柱状图展示时间趋势,图能画出来,但信息传达效率极低。我现在的做法是先问自己一句:这张图要回答什么问题?答案决定了图形类型。
| 想回答的问题 | 该用的图形 | 对应命令 |
|---|---|---|
| 这个连续变量长什么样 | 直方图、核密度图 | histogram、kdensity |
| 两个连续变量有关系吗 | 散点图(可叠加拟合线) | twoway scatter+lfit |
| 随时间怎么变化 | 折线图 | twoway line、tsline |
| 不同组之间差多少 | 柱状图、箱线图 | graph bar、graph box |
| 分组后的分布差异 | 分组箱线图、小提琴图 | graph box, over() |
histogram有一个经常被忽略的参数是bin(),控制分组数量。默认的分组数在样本量大时会显得过于细碎,图形看起来毛毛躁躁;样本量小的时候又会太粗,看不出分布形状。我的经验是 100 条以内的观测用 8 到 10 个 bin,1000 条左右用 20 到 30 个,上万条的话可以考虑直接上kdensity看平滑曲线,直方图反而看不清。
3.2 twoway 的“图层叠加”思路
Stata 的twoway系列是画图的主力,它的设计思路是图层叠加:括号里每一个都是一个图层,图层之间用空格隔开,后面的盖在前面的上面。这个思路一通,很多复杂的图就变得简单了。
twoway (scatter wage tenure, mcolor(%40)) /// (lfit wage tenure, lcolor(red) lwidth(medthick)), /// title("工资与工龄的关系") /// xtitle("工龄(年)") ytitle("月工资(元)") /// legend(order(1 "观测值" 2 "线性拟合")) /// scheme(s2color)这段代码里mcolor(%40)把散点设成 40% 透明度,样本量大的时候能看清点的密集程度,比实心点友好得多。legend(order(...))是给图例改名的标准写法,括号里的数字对应图层顺序。scheme()控制整体配色风格,s2color是默认的彩色方案,投期刊的话一般换成s2mono或者graphscheme里的灰度方案。
图例这个位置特别容易出问题。默认图例会显示变量名,读者看到wage、tenure这种词完全不知道什么意思。所以要么在legend里手动改名,要么在画图之前就把变量标签写好——这也是第 1 节讲标签的实用价值之一,标签写好了,图的默认输出直接就是人话。
3.3 图形导出的两个坑:replace 和分辨率
graph export是保存图形的命令,语法看起来简单,但有两个地方经常卡人。第一个是文件已存在时必须加, replace,否则直接报错,这个还好,报错信息明确。第二个是分辨率,默认导出的图片在文档里插进去经常糊得没法看,尤其是柱状图的细边框和坐标轴小字。
graph export "scatter_wage.png", replace width(2000)width()参数控制像素宽度,2000 左右在 Word 或者网页里显示已经足够清晰。如果是要印刷的图,考虑导出 PDF 或者 EPS 格式,矢量图放大不失真:
graph export "scatter_wage.pdf", replace还有一个很多人不知道的点:graph export保存的是当前内存里的那幅图,如果你画了第二幅图没保存就把它 export 出去,存下来的是第二幅。养成画完立刻graph export的习惯,或者用graph save "fig1.gph"把图形对象存下来,后面用graph combine拼图的时候再调出来。graph combine fig1.gph fig2.gph, rows(1)这个用法出双栏对比图特别顺手,比在 Word 里对齐图片省事多了。
注意:Stata 的图形窗口一旦被新的
graph命令覆盖,之前没保存的图就没了,而且没有撤销功能。批量画图的脚本里,每一幅图后面紧跟一句graph export,这是最稳妥的写法。
4. 统计特征:数字背后的信息密度
4.1 summarize 和 summarize, detail 差在哪
summarize默认输出四个数:观测数、均值、标准差、最小最大值。这四个数够用吗?大部分时候不够。均值和中位数差得远,说明分布偏斜;最小最大值之间差距异常,说明可能有极值或者录入错误。这些信息默认输出里看不到,必须加, detail。
detail输出会给出百分位数(1%、5%、10%、25%、50%、75%、90%、95%、99%)、偏度、峰度,还有最小四个值和最大四个值的具体数值。最后这一项特别有用,很多录入错误一眼就能看出来——比如年龄变量的最大值是 999,那八成是把缺失值编码成了 999 而不是留空。
summarize的结果可以通过return list取出来存成标量,这在写自动化报告的时候很有价值:
summarize wage scalar mean_wage = r(mean) scalar sd_wage = r(sd) display "均值: " %9.2f mean_wage " 标准差: " %9.2f sd_wager()里面存的是上一命令的返回值,不同命令存的返回值名字不一样,summarize存的是r(N)、r(mean)、r(sd)、r(min)、r(max)。加了detail之后还会多出r(p50)、r(skewness)这些。记不住的话直接return list看一眼就行。
4.2 tabulate 的双向表和那个隐藏的缺失值
tabulate做单变量频数表是基础用法,双向表才是它的主场:
tabulate education industry, row col chi2row和col分别输出行百分比和列百分比,chi2附带卡方检验的 p 值。做交叉分析的时候这三个选项基本是标配,光看频数很难判断两个分类变量有没有关联,百分比和检验结果才有解读价值。
前面提过缺失值的问题,这里再强调一次:双向表默认会把任一边缺失的观测整条丢掉,而且不告诉你丢了多少。如果两份变量的缺失率都不低,交叉表的样本量可能比原始数据少一大截,结论的可信度就打折扣了。稳妥的写法是加, missing,让缺失单独成行成列,自己心里有数。
4.3 分组统计:tabstat 比 tabulate 更适合连续变量
tabulate处理分类变量很顺,但要对连续变量按组分算均值标准差,它就力不从心了。tabstat是专门为这个场景设计的:
tabstat wage tenure, by(industry) /// stat(n mean sd p50 min max) /// format(%9.2f) columns(statistics)stat()里可以列任意多个统计量,format()控制小数位,columns(statistics)让输出按统计量分列而不是按变量分列,读起来更清楚。这个命令的输出直接可以复制进报告,格式比summarize加by()组合要整齐得多。
bysort是另一条路子,适合需要进一步加工的场合:
bysort industry: summarize wage bysort industry: generate mean_wage_ind = wage[1]第二种写法有坑,wage[1]取的是组内第一个观测的工资,不是均值,写错了自己不容易发现。正经做法是用egen:
bysort industry: egen mean_wage_ind = mean(wage)egen的mean()是跨观测聚合,generate是逐行计算,这个区别在下一节还会展开讲,因为它是生成新变量时最容易搞混的地方。
5. 生成新变量:generate、egen 与条件表达式的正确姿势
5.1 三个命令的边界,用一句话记住
generate、replace、egen这三个命令我用了很久才理清它们的分工。最直白的说法:generate是创建一个新变量并从零算起,replace是修改已有变量的某些取值,egen是跨观测做聚合运算。
前两个好理解,容易混的是generate和egen。区别在于运算方向:generate里写的表达式是在同一行内部做横向运算,比如generate total = math + english + chinese,每一行的总分由这一行自己的三科成绩加出来。egen则是在一列内部纵向做聚合,比如egen class_mean = mean(math), by(class),算的是整个班级的平均分,然后把这个值填到组内每一行上。一个横着算,一个竖着算,这个类比帮我省了很多次查文档的时间。
generate total = math + english + chinese egen class_mean = mean(math), by(class) egen rank_in_class = rank(math), by(class)egen支持的函数比generate多得多,rowtotal()、rowmean()、rowmiss()这几个处理问卷量表特别顺手。rowtotal()还有一个隐藏行为:默认会忽略缺失值直接加总剩余项,加, missing选项才会返回缺失。这个默认值的取舍见仁见智,做量表计分的时候忽略缺失往往是我们想要的,但做财务加总的时候忽略缺失可能是灾难性的。
5.2 条件生成:缺失值会把结果悄悄吃掉
这是我在 Stata 里踩过最隐蔽的一个坑,值得单独用一节讲。
generate high_wage = 1 if wage > 5000 replace high_wage = 0 if wage <= 5000看起来逻辑完备,大于五千标 1,小于等于五千标 0。但wage缺失的那些观测,两个条件都不满足,high_wage就留在了缺失状态。如果你后面用high_wage做分组统计,这部分样本会静默消失,而你不会收到任何提示。
正确的写法有两种。第一种是先给个默认值再改:
generate high_wage = 0 replace high_wage = 1 if !missing(wage) & wage > 5000第二种是利用 Stata 的逻辑表达式直接生成 0/1:
generate high_wage = (wage > 5000) if !missing(wage)第二种写法更简洁,但要注意括号外面的if不能省,否则缺失值会被判成 1。这个细节我当初反复验证过好几次才敢相信,因为直觉上missing > 5000应该是假,但在 Stata 的数值逻辑里,缺失被当作极大值,比较结果就是真。
顺带说一句,分类变量的条件生成用recode更清楚:
recode age (0/17 = 1) (18/35 = 2) (36/60 = 3) (61/max = 4), generate(age_group)recode的好处是会检查区间是否覆盖完整,而且加, generate()之后原变量不动,新变量单独存。recode里默认也是不含缺失值的,缺失会原样保留到新变量里,这个行为符合预期,比手写条件表达式安全。
5.3 变量类型与精度:默认的 float 可能装不下你的数
generate生成的数值变量默认是float类型,单精度浮点,有效位数大概 7 位。日常的工资、年龄、分数完全够用,但有两类数据会出问题:一是超过 7 位的大整数,二是需要精确到小数点后很多位的数。
身份证号、银行卡号、长编码这类数据,绝对值远超 float 的表达范围,存成数值型必然失真。这类数据一律存成字符串,用string类型,需要比较的时候用字符串比较或者real()转换。已经误存成数值的,用tostring转回来,但要注意如果已经失真,转换也救不回来,只能从原始数据重新导入。
大整数还有一种情况是编号,比如 12 位的订单号。这种用double能存下,但更稳妥的做法还是字符串。double的问题是精度边界比较微妙,12 位以内没问题,再长就开始丢末尾数字了。
generate double order_id_num = real(order_id_str) tostring order_id_num, format(%20.0f) generate(order_id_back)这段代码演示的是数值和字符串互转,format(%20.0f)里的.0f表示不保留小数,缺了这个格式化参数,转换出来的字符串可能带科学计数法,看着像乱码。
提示:生成新变量之后,别忘了给它加变量标签。
generate命令本身支持label variable一样的写法,用generate newvar = ..., variable这种形式是不行的,得单独再跑一句label variable newvar "标签内容"。养成“生成即标标签”的习惯,后面做表画图都能省事。
6. 计算器、命令控制与日志:天天用但很少被系统学的东西
6.1 display:Stata 自带的计算器比想象中能打
很多人不知道 Stata 可以直接当计算器用,display命令支持完整的表达式运算:
display 2^10 display sqrt(144) display exp(1) display ln(100) display %9.2f 1/3 display %9.4f 22/7^是乘方,sqrt()开方,exp()自然指数,ln()自然对数。格式化输出那两行是重点,%9.2f表示总宽度 9 位、保留 2 位小数,%9.4f是保留 4 位。不写格式化的时候,Stata 会给一个默认精度,看起来经常是 1/3 变成 .33333333 这种一长串,做报告的时候不美观。
display还能拼接字符串,做进度提示特别有用:
display "当前处理第 " `i' " 个文件,共 " `n' " 个"反引号加单引号这种写法是 Stata 的局部宏引用,i和n是在循环里定义的局部宏。这种拼接在写批量处理的脚本时几乎是必备技能。
标量也能参与运算,scalar定义的值可以直接在display里用:
scalar price = 49.9 scalar qty = 120 display "总金额: " %10.2f price * qtyscalar和局部宏的区别在于作用域,scalar是全局的,local只在当前 do 文件内有效。做小计算用scalar方便,但别定义太多,容易和自己的变量名混淆。
6.2 命令的调用、中断与容错
do文件是 Stata 执行批量命令的主要方式,do filename.do会逐行执行并把每行命令回显在结果窗口。如果你不想看回显,用run filename.do,输出安静很多,跑大循环的时候能提速,因为减少了向界面写内容的开销。
doedit打开 do 文件编辑器,doedit filename.do直接打开指定文件。这个命令本身没什么技术含量,但配合set more off使用体验会好很多。set more off的作用是关掉分页提示,否则输出长的时候会卡在--more--等你按键,批处理的时候直接卡死在那。我现在的每个 do 文件开头都固定写一句set more off,外加clear all清空内存。
中断正在跑的命令,快捷键是Ctrl+C,或者在命令窗口输入break。不过break并非在所有场景都灵,尤其是某个命令正在深度计算的时候,响应可能会有延迟。真正麻烦的是无限循环,while条件写错导致死循环,这时候只能靠Ctrl+C硬中断,运气不好还得重启 Stata。
比中断更重要的是容错。批量处理几十个文件的时候,其中一个文件读取出错,整个脚本就停了,前面跑的结果还在内存里但后面的全没了。capture命令专门解决这个:
capture noisily use "data_`i'.dta", clear if _rc { display "文件 data_`i'.dta 读取失败,错误码: " _rc continue }capture的作用是“执行后面的命令,如果出错不要停下来,把错误码存在_rc里”。noisily选项让命令正常输出,否则capture会把所有输出都吞掉,出错了也不知道错在哪。_rc为 0 表示成功,非 0 表示失败。这个模式在写循环的时候特别实用,能保证一个文件出问题不影响整批任务。
6.3 日志:把跑过的每一步都留下来
日志这件事,平时觉得多余,等到需要回溯“上次那个结果是怎么跑出来的”时候,才知道它有多重要。Stata 的日志分两种:log记录输出结果,cmdlog只记录输入的命令。
log using "analysis_20240115.log", replace text * 中间是各种分析命令 log closetext选项让日志存成纯文本,方便用任何编辑器打开;不加的话默认是 SMCL 格式,可读性差一些但保留了格式信息。replace允许覆盖同名文件,不加的话同名文件存在就报错。日志文件名的习惯是带上日期,这样同一个项目跑多次不会互相覆盖。
cmdlog的用途是另一个方向:只记命令不记输出,文件小,适合当“操作流水账”。做探索性分析的时候我经常开着cmdlog,因为探索过程中会试很多条命令,最后真正有用的可能就三五条,回头翻 cmdlog 比翻完整日志快得多。
新手最容易踩的一个坑是日志没关就再开一个,Stata 会报错说已经有日志在打开了。这时候要么先log close,要么用log close _all强制关掉所有日志。另一个坑是程序崩溃或者被强制关掉,日志文件处于“打开但没正常关闭”的状态,下次想在同一个文件上append会报错。这个状态绕不过去,只能换个文件名重新开,或者手动把遗留的文件删掉。
log close _all log using "analysis_20240115.log", replace text把这两行放在 do 文件开头,能避免绝大多数日志相关的报错。log close _all在没有任何日志打开的时候执行也不会报错,属于安全操作。
append和replace的区别值得记一下:replace是新建覆盖,append是追加到已有文件后面。一个项目分几天做,每天开一个新的日志段追加到同一个文件里,用append比较合适,所有记录按时间顺序排下来,脉络清楚。
7. 几个反复验证过的小习惯
变量命名上,我现在的原则是宁可长一点也别用拼音首字母。avg_monthly_income比pjysr好读一百倍,虽然敲起来费劲,但 Stata 有自动补全,敲前几个字母按 Tab 就出来了。变量标签则用来补全单位信息,比如label variable wage "月工资(元)",这样画图的时候坐标轴标题可以直接从标签带出来。
审视数据的动作,我把它做成了一个独立的 do 文件01_check.do,每次新数据先跑它,输出重定向到单独的日志文件,看完了再决定清洗脚本怎么写。这个习惯让我少踩了很多“数据本身就有问题但我以为是代码写错了”的坑。
画图这块,最大的体会是图是给别人看的,不是给自己看的。自己看的时候知道哪个变量是什么,别人看的时候什么都不知道。所以标题、轴标签、图例、单位,四样齐全再导出,宁可多写两行选项。
生成新变量的顺序也值得注意。我习惯先把所有原始变量标签补全,再做generate和recode,最后统一检查一遍新变量的缺失情况。顺序反过来,标签补到一半忘了,后面做表的时候就会冒出一堆newvar1、newvar2,再回头找哪行代码生成的,白白浪费时间。
日志和capture这两个东西,属于“平时嫌麻烦,出事时救命”的类型。我的做法是在正式的 do 文件里必开日志,探索性的临时命令不开,这样重要操作有记录,随手试试的东西不留垃圾文件。
最后提一个小细节:clear all和clear不是一回事。clear只清空内存里的数据,clear all会连标量、矩阵、局部宏、程序定义一起清掉。写独立脚本的时候开头用clear all比较干净,但在一个长流程的中间段用clear all会把前面定义好的东西全抹掉,这个坑我踩过一次,花了一小时才发现是清过头了。