戰(zhàn)指南:從數(shù)據(jù)清洗到報(bào)表優(yōu)化的格式映射技巧)
1. 從“數(shù)據(jù)泥潭”到“清晰報(bào)表”為什么你需要掌握 PROC FORMAT如果你用過(guò)SAS處理過(guò)真實(shí)世界的數(shù)據(jù)比如從醫(yī)院信息系統(tǒng)導(dǎo)出的患者信息或者從電商后臺(tái)拉出的銷(xiāo)售記錄你大概率遇到過(guò)這樣的場(chǎng)景數(shù)據(jù)里性別用“1”和“2”表示地區(qū)用一堆意義不明的縮寫(xiě)代碼滿(mǎn)意度調(diào)查的結(jié)果是“A”、“B”、“C”、“D”。當(dāng)你把這些數(shù)據(jù)直接扔進(jìn) PROC FREQ 或者 PROC PRINT 時(shí)生成的表格或報(bào)告對(duì)業(yè)務(wù)人員來(lái)說(shuō)簡(jiǎn)直就是天書(shū)。他們看不懂“1”代表什么更無(wú)法從“REG01”這樣的代碼里理解業(yè)務(wù)含義。于是你不得不寫(xiě)一堆 IF-THEN/ELSE 語(yǔ)句或者創(chuàng)建一個(gè)查找數(shù)據(jù)集Lookup Table在每次輸出前把代碼值“翻譯”成可讀的標(biāo)簽。這個(gè)過(guò)程繁瑣、容易出錯(cuò)而且一旦編碼規(guī)則發(fā)生變化比如新增一個(gè)地區(qū)代碼你就得在所有相關(guān)程序里手動(dòng)更新。這就是 PROC FORMAT 和 FORMAT 語(yǔ)句要解決的核心痛點(diǎn)實(shí)現(xiàn)數(shù)據(jù)值與其顯示值或存儲(chǔ)值的分離與映射。它允許你定義一套獨(dú)立的“翻譯詞典”告訴SAS“當(dāng)你在報(bào)告里看到數(shù)值1時(shí)請(qǐng)顯示為‘男’看到字符‘A’時(shí)請(qǐng)顯示為‘非常滿(mǎn)意’?!?這個(gè)“詞典”就是格式Format。而 PROC FORMAT 就是創(chuàng)建和管理這個(gè)詞典的過(guò)程。掌握它遠(yuǎn)不止是讓報(bào)表變好看。它直接關(guān)系到數(shù)據(jù)分析流程的規(guī)范性、可維護(hù)性和溝通效率。一個(gè)定義良好的格式庫(kù)可以作為團(tuán)隊(duì)甚至整個(gè)機(jī)構(gòu)的元數(shù)據(jù)標(biāo)準(zhǔn)確保不同人、不同項(xiàng)目對(duì)同一數(shù)據(jù)項(xiàng)的理解和呈現(xiàn)是一致的。當(dāng)你下次看到類(lèi)似“cannot unpack file”或“cannot detect archive format”這樣的錯(cuò)誤時(shí)你會(huì)意識(shí)到“格式”不匹配是數(shù)據(jù)處理中一個(gè)普遍且根本的問(wèn)題。在SAS里提前定義好數(shù)據(jù)的“顯示格式”就能避免后續(xù)分析中大量的“數(shù)據(jù)清洗”和“解釋成本”。2. 格式Format究竟是什么不只是“顯示面具”很多初學(xué)者會(huì)把 FORMAT 簡(jiǎn)單地理解為一個(gè)“顯示面具”認(rèn)為它只改變輸出時(shí)的樣子而不改變數(shù)據(jù)本身。這個(gè)理解不夠全面甚至在某些場(chǎng)景下會(huì)帶來(lái)誤區(qū)。更準(zhǔn)確地說(shuō)SAS中的格式是一個(gè)雙向的映射規(guī)則。2.1 格式的核心輸入格式INFORMAT與輸出格式FORMAT實(shí)際上SAS格式體系包含兩個(gè)緊密相關(guān)的概念輸出格式Format這是我們最常使用的決定一個(gè)變量值在報(bào)表、日志或輸出窗口中如何被顯示。例如將數(shù)值 0.85 顯示為 “85%” 或 “0.85”將日期值 22678 顯示為 “2022-01-01”。輸入格式Informat這決定了SAS在讀取原始數(shù)據(jù)如通過(guò) DATALINES, INFILE, 或 PROC IMPORT時(shí)如何解析和轉(zhuǎn)換輸入的字符串。例如告訴SAS如何把 “01JAN2022” 這樣的字符串轉(zhuǎn)換成SAS日期值或者如何把帶有美元符號(hào)和逗號(hào)的 “$1,234.56” 轉(zhuǎn)換成數(shù)值 1234.56。PROC FORMAT 過(guò)程步主要用來(lái)創(chuàng)建用戶(hù)自定義的輸出格式Format和輸入格式Informat。它們像是一對(duì)逆過(guò)程Informat 用于“解碼”外部數(shù)據(jù)Format 用于“編碼”輸出數(shù)據(jù)。2.2 格式如何工作一個(gè)查找與替換的過(guò)程當(dāng)你在 PROC PRINT 或數(shù)據(jù)步中為一個(gè)變量指定一個(gè)格式時(shí)例如format gender genderfmt.;SAS內(nèi)部發(fā)生了以下事情查找SAS會(huì)去內(nèi)存或磁盤(pán)上加載名為genderfmt.的格式目錄。匹配對(duì)于變量gender的每一個(gè)觀測(cè)值比如 ‘M’SAS在格式目錄中查找對(duì)應(yīng)的“鍵”Key。替換顯示找到匹配的鍵‘M’后SAS將其替換為格式定義的“標(biāo)簽”Label比如 ‘男性’并將這個(gè)標(biāo)簽發(fā)送給輸出設(shè)備如HTML、RTF、列表輸出。關(guān)鍵點(diǎn)存儲(chǔ)在數(shù)據(jù)集中的原始值‘M’ 并沒(méi)有被改變。改變的只是它的“外衣”。所有后續(xù)計(jì)算如果直接引用gender變量使用的仍然是原始值 ‘M’。這保證了數(shù)據(jù)計(jì)算的準(zhǔn)確性。2.3 格式的持久性與范圍格式定義后默認(rèn)在SAS會(huì)話(huà)期間有效。一旦退出SAS用戶(hù)自定義的格式就會(huì)丟失。為了持久化你可以使用LIBRARY選項(xiàng)將格式永久保存在某個(gè)邏輯庫(kù)中例如proc format librarywork.formats;是放在臨時(shí)庫(kù)而proc format librarymylib.formats;則可以永久保存。要使用永久庫(kù)中的格式需要在程序開(kāi)始時(shí)用OPTIONS FMTSEARCH(mylib.formats work.formats);語(yǔ)句告訴SAS去哪里搜索格式。3. 手把手創(chuàng)建你的第一個(gè)格式PROC FORMAT 語(yǔ)法詳解理論說(shuō)再多不如動(dòng)手做一遍。我們從一個(gè)最簡(jiǎn)單的例子開(kāi)始為性別變量創(chuàng)建格式。3.1 創(chuàng)建數(shù)值型格式Numeric Format假設(shè)你的數(shù)據(jù)中性別用1和2表示。我們要?jiǎng)?chuàng)建格式讓1顯示為“男”2顯示為“女”。proc format; value genderfmt 1 ‘男’ 2 ‘女’ other ‘缺失或異?!? /* 處理未在范圍內(nèi)定義的值 */ run;proc format;啟動(dòng)格式創(chuàng)建過(guò)程。value genderfmt聲明一個(gè)名為genderfmt的數(shù)值型輸出格式。名稱(chēng)長(zhǎng)度不超過(guò)32個(gè)字符不能以數(shù)字結(jié)尾避免使用SAS保留字。1 ‘男’定義映射關(guān)系。等號(hào)左邊是數(shù)據(jù)中的原始值鍵右邊是希望顯示的值標(biāo)簽。other ‘缺失或異?!?other關(guān)鍵字是一個(gè)強(qiáng)大的捕獲器用于處理所有未在之前列出的值。這是一個(gè)良好的編程習(xí)慣能讓你快速發(fā)現(xiàn)數(shù)據(jù)中的異常值。如果不指定other未定義的值將按原樣顯示。如何使用它在數(shù)據(jù)步或過(guò)程步中使用format語(yǔ)句data patient_fmt; set patient_raw; format gender genderfmt.; /* 注意格式名后面的點(diǎn)號(hào)(.)這是必須的 */ run; proc print datapatient_fmt; var patient_id gender age; run;現(xiàn)在打印出的報(bào)表中g(shù)ender列顯示的就是“男”、“女”而不是冰冷的1和2。3.2 創(chuàng)建字符型格式Character Format字符型格式用于映射字符型變量。格式名必須以$開(kāi)頭。proc format; value $regionfmt ‘BJ’ ‘北京’ ‘SH’ ‘上?!?‘GZ’ ‘廣州’ ‘SZ’ ‘深圳’ other ‘其他地區(qū)’; run;使用時(shí)同樣需要$format region $regionfmt.;3.3 創(chuàng)建數(shù)值范圍格式Range Format與多標(biāo)簽格式Multilabel Format格式可以映射一個(gè)值范圍這在處理年齡組、收入?yún)^(qū)間時(shí)非常有用。proc format; value agegroup low - 18 ‘未成年’ 18 - 35 ‘青年’ 35 - 60 ‘中年’ 60 - high ‘老年’; value income (multilabel) /* multilabel 選項(xiàng)允許一個(gè)值屬于多個(gè)區(qū)間 */ 0-5000 ‘低收入’ 5000-20000 ‘中等收入’ 10000-high ‘高收入’ . ‘缺失’; run;low和high是SAS關(guān)鍵字分別代表最小值和非缺失最大值。-表示“小于”例如18 - 35包含18但不包含35。也可以用-表示閉區(qū)間。multilabel選項(xiàng)是高級(jí)功能。例如收入15000的觀測(cè)在分類(lèi)時(shí)既可以歸入“中等收入”也可以歸入“高收入”。這在進(jìn)行復(fù)雜交叉分析時(shí)特別有用但只有部分過(guò)程步如 PROC TABULATE, PROC MEANS支持MLF選項(xiàng)來(lái)利用多標(biāo)簽格式。3.4 創(chuàng)建輸入格式Informat輸入格式用于讀取數(shù)據(jù)。假設(shè)你有一個(gè)文本文件用“Y”和“N”表示是否但你想在讀入時(shí)直接轉(zhuǎn)換為1和0。proc format; invalue $yn2num (upcase) /* upcase選項(xiàng)在匹配前將輸入轉(zhuǎn)換為大寫(xiě)更安全 */ ‘Y’ 1 ‘N’ 0 other .; /* 將其他值轉(zhuǎn)換為缺失值 */ run;在數(shù)據(jù)步中使用data read_data; infile ‘data.txt’; input response : $yn2num.; /* 使用 : 修飾符和輸入格式 */ run;4. 格式的高級(jí)應(yīng)用與實(shí)戰(zhàn)技巧掌握了基礎(chǔ)語(yǔ)法我們來(lái)看看如何在實(shí)際項(xiàng)目中更巧妙地運(yùn)用格式。4.1 用格式進(jìn)行數(shù)據(jù)分箱Binning與分組分析這是格式最強(qiáng)大的用途之一。你不需要在數(shù)據(jù)集中創(chuàng)建新的分組變量可以直接在分析過(guò)程中使用格式進(jìn)行動(dòng)態(tài)分組。proc format; value bmi_cat low - 18.5 ‘偏瘦’ 18.5 - 24 ‘正常’ 24 - 28 ‘超重’ 28 - high ‘肥胖’; run; proc freq datahealth_data; tables bmi; /* 如果不加格式這里會(huì)列出每個(gè)具體的BMI值表格很長(zhǎng) */ format bmi bmi_cat.; /* 加上格式后直接按分組輸出頻數(shù) */ run; proc means datahealth_data mean std; class bmi; /* 分類(lèi)變量 */ var blood_pressure; format bmi bmi_cat.; /* MEANS過(guò)程將按BMI分組計(jì)算血壓的均值和標(biāo)準(zhǔn)差 */ run;這樣做的好處保持原始數(shù)據(jù)的精度BMI具體值同時(shí)在需要分組統(tǒng)計(jì)時(shí)通過(guò)格式靈活定義分組標(biāo)準(zhǔn)。如果分組標(biāo)準(zhǔn)改變比如根據(jù)新的指南調(diào)整BMI切點(diǎn)你只需要修改proc format中的定義所有相關(guān)分析的結(jié)果會(huì)自動(dòng)更新無(wú)需修改數(shù)據(jù)步創(chuàng)建新變量。4.2 用格式實(shí)現(xiàn)數(shù)據(jù)驗(yàn)證與清洗格式的other選項(xiàng)和輸入格式Informat是數(shù)據(jù)質(zhì)量檢查的利器。proc format; value $valid_dept ‘SALES’, ‘MKTG’, ‘HR’, ‘IT’ ‘Valid’ /* 多個(gè)值映射到同一標(biāo)簽 */ other ‘INVALID’; run; data checked_data; set raw_data; length dept_check $10; dept_check put(dept, $valid_dept.); /* PUT函數(shù)使用格式返回值 */ if dept_check ‘INVALID’ then put ‘WARNING: 無(wú)效部門(mén)代碼在觀測(cè) ‘ _n_ ‘: ‘ dept; run;這里我們利用PUT函數(shù)和格式生成了一個(gè)新變量dept_check來(lái)標(biāo)識(shí)部門(mén)代碼是否有效并打印出警告信息。這比寫(xiě)一長(zhǎng)串if dept not in (‘SALES’, ‘MKTG’...)要清晰和易于維護(hù)得多。4.3 嵌套格式與調(diào)用現(xiàn)有格式格式可以引用其他格式實(shí)現(xiàn)復(fù)雜的映射邏輯。proc format; value agegrp low-30 ‘Young’ 31-60 ‘Middle’ 61-high ‘Senior’; value $genage (default20) /* default定義標(biāo)簽?zāi)J(rèn)長(zhǎng)度 */ ‘M’ ‘Male’ ‘F’ ‘Female’; value $detail_fmt ‘M’ ‘Male (‘ agegrp. ‘)’ /* 在字符格式中嵌套數(shù)值格式 */ ‘F’ ‘Female (‘ agegrp. ‘)’; run; /* 在數(shù)據(jù)步中組合使用 */ data test; set sashelp.class; format sex $detail_fmt. age agegrp.; run; /* 輸出中sex會(huì)顯示為 “Male (Young)” 這樣的組合形式 */這個(gè)技巧能生成信息量更豐富的標(biāo)簽但要注意保持標(biāo)簽的可讀性避免過(guò)于復(fù)雜。4.4 將格式永久化與項(xiàng)目管理對(duì)于企業(yè)級(jí)項(xiàng)目格式應(yīng)該作為核心元數(shù)據(jù)進(jìn)行管理。創(chuàng)建永久格式庫(kù)libname myfmt ‘C:\SAS\Formats’; proc format librarymyfmt.project_formats; value $status ‘A’‘Active’ ‘I’‘Inactive’; /* ... 更多格式定義 */ run;在程序開(kāi)頭搜索格式庫(kù)options fmtsearch(myfmt.project_formats work.formats);這樣在整個(gè)SAS會(huì)話(huà)中你都可以直接使用myfmt.project_formats庫(kù)中定義的格式。文檔化使用proc format的cntlout選項(xiàng)將格式定義導(dǎo)出為一個(gè)數(shù)據(jù)集方便查閱和版本控制。proc format librarymyfmt.project_formats cntloutwork.format_catalog; run; proc print datawork.format_catalog; run;5. 實(shí)戰(zhàn)中的“坑”與最佳實(shí)踐我見(jiàn)過(guò)太多人因?yàn)楦袷绞褂貌划?dāng)而調(diào)試半天。以下是一些血淚教訓(xùn)總結(jié)出的最佳實(shí)踐。5.1 必踩的坑格式名后面的點(diǎn)號(hào).這是最常見(jiàn)的錯(cuò)誤。在format語(yǔ)句或put/input函數(shù)中引用格式時(shí)格式名后面必須加點(diǎn)號(hào)如genderfmt.。這個(gè)點(diǎn)號(hào)是SAS識(shí)別“這是一個(gè)格式名”而不是變量名的關(guān)鍵。忘記點(diǎn)號(hào)SAS通常會(huì)報(bào)錯(cuò)“找不到格式”或產(chǎn)生意想不到的結(jié)果。5.2 字符與數(shù)值格式不匹配試圖將字符格式如$regionfmt.應(yīng)用到數(shù)值變量上或者反過(guò)來(lái)SAS會(huì)報(bào)錯(cuò)或直接忽略格式。務(wù)必分清你的變量類(lèi)型。一個(gè)檢查方法是在proc contents datayourdata; run;中查看變量類(lèi)型。5.3 格式標(biāo)簽長(zhǎng)度與截?cái)嗄J(rèn)情況下格式標(biāo)簽的長(zhǎng)度由第一個(gè)定義的標(biāo)簽長(zhǎng)度決定。如果你第一個(gè)標(biāo)簽是“Yes”3個(gè)字符那么后續(xù)即使定義了“Absolutely”9個(gè)字符也只會(huì)顯示前3個(gè)字符“Abs”。解決方法是在value語(yǔ)句中使用(default長(zhǎng)度)選項(xiàng)來(lái)指定默認(rèn)標(biāo)簽長(zhǎng)度例如value $resp (default12) ‘Y’‘Yes’ ‘N’‘No’;。5.4 格式的搜索順序FMTSEARCH當(dāng)你定義了多個(gè)同名的格式或者格式存放在不同的庫(kù)中時(shí)SAS按照OPTIONS FMTSEARCH指定的順序進(jìn)行搜索并使用第一個(gè)找到的。如果修改了格式但感覺(jué)沒(méi)生效檢查一下是否是舊的格式緩存在WORK庫(kù)中被優(yōu)先使用了。通常建議將項(xiàng)目永久格式庫(kù)放在work.formats前面fmtsearch(mylib.fmts work.formats)。5.5 性能考量格式目錄過(guò)大如果你定義了成千上萬(wàn)個(gè)格式特別是字符格式并且用于非常大的數(shù)據(jù)集可能會(huì)對(duì)性能產(chǎn)生輕微影響因?yàn)槊總€(gè)值都需要進(jìn)行查找。對(duì)于性能關(guān)鍵的分析可以考慮在數(shù)據(jù)預(yù)處理階段使用put函數(shù)和格式創(chuàng)建一個(gè)新的字符變量然后用這個(gè)新變量進(jìn)行分析避免在每次過(guò)程步中動(dòng)態(tài)應(yīng)用格式。5.6 缺失值的處理數(shù)值格式默認(rèn)不會(huì)自動(dòng)處理缺失值.。缺失值會(huì)原樣顯示為“.”。如果你希望為缺失值賦予一個(gè)有意義的標(biāo)簽必須在格式定義中顯式指定. ‘?dāng)?shù)據(jù)缺失’。這一點(diǎn)非常重要能讓你的報(bào)告更專(zhuān)業(yè)。6. 超越PROC FORMAT相關(guān)函數(shù)與過(guò)程步集成格式不僅用于顯示還能通過(guò)函數(shù)參與數(shù)據(jù)轉(zhuǎn)換。6.1 PUT() 函數(shù)將值根據(jù)格式轉(zhuǎn)換為字符串PUT(source, format.)函數(shù)返回一個(gè)字符值。它是數(shù)據(jù)清洗和創(chuàng)建衍生變量的神器。data derived; set raw; /* 將數(shù)值型的性別代碼轉(zhuǎn)換為中文描述字符 */ gender_char put(gender_code, genderfmt.); /* 將日期轉(zhuǎn)換為‘YYYY-MM’形式的字符用于分組 */ year_month put(date, yymmn6.); run;6.2 PUTC() 和 PUTN() 函數(shù)動(dòng)態(tài)格式這是高級(jí)技巧。當(dāng)你想根據(jù)另一個(gè)變量的值動(dòng)態(tài)決定使用哪種格式時(shí)就需要它們。PUTC(char_var, char_format)用于字符變量和字符格式。PUTN(num_var, num_format)用于數(shù)值變量和數(shù)值格式。data dynamic_fmt; set data; /* 假設(shè)有一個(gè)變量format_name存儲(chǔ)了格式名 */ length result $20; if type‘C’ then result putc(char_var, format_name); else if type‘N’ then result putn(num_var, format_name); run;6.3 PROC FORMAT 與 PROC TABULATE/PROC REPORT 的強(qiáng)強(qiáng)聯(lián)合在制作復(fù)雜報(bào)表時(shí)PROC TABULATE和PROC REPORT能充分發(fā)揮格式的威力特別是多標(biāo)簽格式Multilabel Format可以讓你從不同維度對(duì)同一變量進(jìn)行交叉統(tǒng)計(jì)生成非常靈活的分析報(bào)表。這需要結(jié)合class語(yǔ)句的mlf選項(xiàng)使用是制作商業(yè)分析報(bào)告的必備技能。最后回到那個(gè)網(wǎng)絡(luò)熱詞 “cannot detect archive format”。在數(shù)據(jù)處理中無(wú)法識(shí)別數(shù)據(jù)格式是數(shù)值還是字符日期是什么樣子是萬(wàn)惡之源。PROC FORMAT 和 SAS 的整個(gè)格式系統(tǒng)就是為你提供了一套強(qiáng)大的工具來(lái)明確定義和統(tǒng)一“數(shù)據(jù)的格式”從而讓數(shù)據(jù)從輸入、處理到輸出的全流程清晰、可控、高效?;〞r(shí)間建立一個(gè)好的格式庫(kù)在長(zhǎng)期項(xiàng)目中帶來(lái)的維護(hù)性提升和溝通成本下降絕對(duì)是值得的。