iT邦幫忙

2026 iThome 鐵人賽

DAY 15
0
Claude AI

研究生自救指南:30 天用 Claude Code 打造我的論文工具箱系列 第 15

Day 15|匯出給 SPSS:變數標籤與值標籤的自動化,以及一個會讓平均數爆掉的數字

  • 分享至 

  • xImage
  •  

上學期我在 SPSS 的「變數檢視」裡待了一個下午。三十個變數,每一個都要設名稱、標籤、值標籤——性別 1 要標成「男」、2 要標成「女」,教育程度 1 到 4 要標成專科、學士、碩士、博士。點開值標籤視窗,輸入數字,輸入文字,按新增,三十個變數重複三十次。

做完存檔,隔天發現 Day 14 的清理規則改了一條,資料要重新匯出。變數檢視那個下午,等於白做。

今天要把這個下午變成一支腳本。跟 Day 14 一樣的邏輯:規則寫成檔案,讓工具照著做,我核對結果——只是這次的產物不是 CSV,是 SPSS 讀得懂的 .sav。

兩件事做錯了不會馬上被發現
變數檢視裡要設的東西很多,但真正危險的只有兩種。

值標籤錯了,是解讀錯誤。 性別的 1 和 2 標反了,統計數字全部是對的,只是你在論文裡寫「男性倦怠分數顯著較高」的時候,其實說反了。這種錯口試委員抓不到,因為表格看起來一切正常。

遺漏值代碼沒設,是數字本身錯了,而且錯得很安靜。 這是今天真正的重點,我用一個實際會發生的例子講:問卷裡「年資」這一題,允許受試者選「拒答」,我把拒答編碼成 999。如果我忘記在 SPSS 裡把 999 設成遺漏值,SPSS 會把它當成一個真實的年資——999 年。三個人拒答,年資的平均數不是從十二年變成十三年,是直接跳到三、四十年。

這個錯不會跳出警告。SPSS 算得很開心,數字就是不對,而且通常大到你會發現——但如果拒答的是「每月收入」,跳過去的幅度沒那麼誇張的話,錯誤可能就這樣留在描述統計表裡,一路帶進論文。

值標籤決定你怎麼解讀資料,遺漏值代碼決定資料本身對不對。 後者更危險,因為它連基本的平均數都會壞掉。

先寫字典,不要用滑鼠設定
跟 Day 11 的 APA 規則檔、Day 14 的清理規則檔同一套做法:先寫成人讀的檔案,analysis/spss-dictionary.md。

SPSS 匯出字典(03_for_spss)

輸入

data/clean/02_cleaned.csv(Day 14 輸出)

變數標籤

變數名 標籤
bo01–bo22 職業倦怠量表第 N 題
bo_missing_flag 倦怠量表缺失標記(1=遺漏超過 4 題)
hours_week 每週工時(小時)
hours_week_flag 每週工時離群標記
years 護理年資(年)
years_flag 年資離群標記
sex 性別
education 最高學歷
shift 班別

值標籤

變數名 標籤
sex 1
sex 2
sex 999 拒答
education 1 專科
education 2 學士
education 3 碩士
education 4 博士以上
shift 1 白班
shift 2 小夜班
shift 3 大夜班

遺漏值代碼

變數名 代碼 意義
sex 999 拒答,不納入該變項的描述統計

測量尺度

  • bo01–bo22:次序(ordinal)
  • hours_week、years:等距(scale)
  • sex、education、shift:名義(nominal),education 實務上可視為次序

輸出

data/clean/03_for_spss.sav
三張表對應三件事:標籤讓委員看得懂變數是什麼,值標籤讓委員看得懂數字代表什麼,遺漏值代碼讓 SPSS 不會把拒答當成真實數字去算平均。

SKILL.md 全文
存在 .claude/skills/spss-export/SKILL.md:


name: spss-export
description: 依 analysis/spss-dictionary.md 把 data/clean/02_cleaned.csv 匯出成 SPSS 的 .sav 檔,設好變數標籤、值標籤與遺漏值代碼。使用者說「匯出 SPSS」「做 sav 檔」「轉 SPSS 格式」時使用。

SPSS 匯出

輸入

data/clean/02_cleaned.csv,analysis/spss-dictionary.md

規則

  1. 只依字典檔設定,字典檔沒列出的變數維持原樣,不臆測標籤或值標籤。
  2. 遺漏值代碼必須明確宣告,不得用 NaN/空值代替。宣告了的代碼,資料裡對應的值不得是 NaN。
  3. 字典檔裡的變數若在 CSV 裡找不到,停下來回報,不得略過。
  4. 寫成 analysis/03_export.py,用 pyreadstat,不直接動 CSV,只讀不寫。
  5. 輸出後讀回來核對一次:欄數、變數標籤、值標籤是否與字典檔一致,寫成核對報告。

輸出

  • data/clean/03_for_spss.sav
  • analysis/export-check.md:核對報告
    在專案資料夾打「讀 @analysis/spss-dictionary.md,寫成 analysis/03_export.py,用 pyreadstat 匯出 SPSS 檔」。

腳本長什麼樣
import pandas as pd
import pyreadstat

df = pd.read_csv("data/clean/02_cleaned.csv")

column_labels = {
**{f"bo{i:02d}": f"職業倦怠量表第 {i} 題" for i in range(1, 23)},
"bo_missing_flag": "倦怠量表缺失標記(1=遺漏超過 4 題)",
"hours_week": "每週工時(小時)",
"hours_week_flag": "每週工時離群標記",
"years": "護理年資(年)",
"years_flag": "年資離群標記",
"sex": "性別",
"education": "最高學歷",
"shift": "班別",
}

variable_value_labels = {
"sex": {1: "男", 2: "女", 999: "拒答"},
"education": {1: "專科", 2: "學士", 3: "碩士", 4: "博士以上"},
"shift": {1: "白班", 2: "小夜班", 3: "大夜班"},
}

遺漏值:flat list,不是 {"hi":..,"lo":..} 那種寫法

missing_ranges = {"sex": [999]}

variable_measure = {
**{f"bo{i:02d}": "ordinal" for i in range(1, 23)},
"hours_week": "scale",
"years": "scale",
"sex": "nominal",
"education": "ordinal",
"shift": "nominal",
}

pyreadstat.write_sav(
df,
"data/clean/03_for_spss.sav",
column_labels=column_labels,
variable_value_labels=variable_value_labels,
missing_ranges=missing_ranges,
variable_measure=variable_measure,
)

讀回來核對

back, meta = pyreadstat.read_sav("data/clean/03_for_spss.sav")
with open("analysis/export-check.md", "w", encoding="utf-8") as f:
f.write(f"筆數:{len(back)},欄數:{len(back.columns)}\n")
f.write(f"bo01 標籤:{meta.column_names_to_labels.get('bo01')}\n")
f.write(f"sex 值標籤:{meta.variable_value_labels.get('sex')}\n")
f.write(f"sex=999 筆數(應設為遺漏):{(df['sex']==999).sum()}\n")
這支我實際跑過。279 筆、30 個變數,讀回來 bo01 的標籤是「職業倦怠量表第 1 題」,sex 的值標籤是 {1.0: '男', 2.0: '女', 999.0: '拒答'},跟字典檔一致。

哪裡會出錯
遺漏值的參數格式,第一次寫錯。 我一開始寫成 {"sex": [{"hi": 999, "lo": 999}]},以為要用範圍表示單一數值,結果 pyreadstat 直接丟型別錯誤:missing_ranges 的值必須是數字或字串。查了才知道單一遺漏值就是丟數字進列表,{"sex": [999]},範圍才用 hi/lo 那種寫法。錯誤訊息看起來很嚇人,其實是我把兩種寫法混在一起。

忘記把遺漏值代碼一起寫進規則。 第一版字典檔我只寫了值標籤,沒寫遺漏值代碼那一段。腳本照做,.sav 打開之後 sex 的 999 確實標成「拒答」了,但 SPSS 算描述統計時還是把它當一個有效值計算平均——因為標籤只是「這個數字叫什麼名字」,遺漏值代碼才是「這個數字不算數」,兩件事完全獨立,設了前者不會自動設後者。這是我在這個工具上踩到最貴的一個坑,因為它產出的表格看起來完全正常,數字卻是錯的。

布林欄位要先轉成數字。 bo_missing_flag 這種 True/False 的欄位,pyreadstat 寫不進去,要先轉型成 0 和 1,順便在字典檔的值標籤裡把 0、1 標成「否」「是」,不然打開 SPSS 只看得到 0 跟 1,看不出意思。

中文變數標籤的編碼。 寫出來的 .sav 檔用 UTF-8 存中文標籤,比較新的 SPSS(21 版以後,通用 unicode 模式)讀起來沒問題。但如果你的電腦或系所裝的是很舊的版本,或系統語言設定跟檔案編碼對不上,中文標籤打開可能是亂碼。保險的做法是匯出後先自己打開來看一眼,變數檢視裡的中文顯示正常,才算過關,不要匯出完就直接拿去分析。

什麼還是要在 SPSS 裡自己做
這支腳本做完,變數檢視那一頁該有的都有了。但 SPSS 裡有些操作我還是手動做,沒有寫進腳本:算 Cronbach's α、算加總量表分數、跑相關和迴歸。

不是做不到,是這些屬於分析,不是資料整理。今天做的三件事——變數標籤、值標籤、遺漏值代碼——都是「讓資料被正確理解」的層次,錯了會讓下游全部跟著錯。分析的每一步我要看著它跑、看著結果,這條線我還沒想清楚要不要也寫成腳本,先手動做,等熟悉了再考慮。

核對怎麼做
腳本自己輸出的 export-check.md 只驗證「腳本做了它說要做的事」,不代表資料本身沒問題。我自己另外做的核對:打開 .sav,變數檢視掃一遍三十列,資料檢視隨機挑五筆跟 02_cleaned.csv 對一次數字,再跑一次最陽春的次數分配表,確認 sex 的「拒答」在系統遺漏值那一欄,不在有效百分比裡面。

三讀的最後一讀,永遠是在目的地軟體裡親眼看一次。

我還不確定的地方
倦怠量表的測量尺度我標成「次序」,但加總之後的總分實務上大家都當「等距」處理去跑迴歸,這件事統計上有爭議,我目前照領域慣例做,但字典檔裡的標示其實不完全誠實。另外「拒答」要不要整欄排除、還是只在算到那個變項時排除,我還沒有標準答案,等做到相關分析那一步再決定。

明天
Day 16:清理好、匯出好的資料,怎麼把統計結果自動轉成論文要的 APA 格式表格——表一那種,橫線在哪裡都有規定的那種。


上一篇
Day 14|反向題、遺漏值、離群值:清理規則不是 prompt,是程式碼
下一篇
Day 16|表一被退回:不是數字錯,是格式錯,還有一個分母算錯的百分比
系列文
研究生自救指南:30 天用 Claude Code 打造我的論文工具箱21
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言