質性編碼中的案例與案例屬性
在 Potato 中按參與者歸組訪談摘錄,為案例附上地點或年齡組等屬性,並按案例或按屬性統計程式碼。
Potato 中的案例把屬於同一個分析單元的條目歸為一組,例如一位訪談參與者,或來自同一個機構的全部文件。Potato 在伺服器啟動時根據資料中的某個欄位建立案例,在每個案例上儲存地點或年齡組等屬性,並讓管理員按案例或按案例屬性統計程式碼。QDA 模式預設開啟案例,在其他任何研究中,你也可以用一個配置塊開啟案例。案例功能隨 Potato 2.6.0 推出,與 QDA 模式屬於同一個版本。
分析單元是研究據以得出結論的物件。在訪談研究中,分析單元通常是參與者,而 Potato 編碼的單位是摘錄。每條摘錄是資料檔案中的一個條目。案例把這兩個層級連線起來。有了案例,應用在一條摘錄上的程式碼會計入說出這條摘錄的參與者,參與者的地點或年齡組也會適用於該訪談的每一條摘錄。
從 NVivo 或 ATLAS.ti 轉來的研究者熟悉這個概念,只是名稱不同。NVivo 的幫助文件寫道,案例分類“describe the type of case”(描述案例的類型),每個分類帶有你選擇記錄的屬性。幫助頁面給出的例子中包括年齡和地點。ATLAS.ti 手冊把文件組描述為“quasi dichotomous variables”(準二分變數)(例如名為 gender::female 的組)。ATLAS.ti 在程式碼-文件表(Code-Document Table)中跨文件和文件組比較程式碼。Potato 的案例最接近 NVivo 的案例。Potato 沒有分類類型,一個項目中的所有案例共用一套扁平的屬性。下文介紹的程式碼交叉表承擔程式碼-分組表的作用。交叉表的每個單元格統計的是摘錄數。
根據條目欄位檢測案例
Potato 直接從條目本身讀取案例。每個條目在某個欄位中寫明自己所屬的案例,該欄位取值相同的條目構成一個案例。下面的檔案 data/excerpts.jsonl 每行是一個 JSON 物件,檔案包含來自三場訪談的六條摘錄:
{"id": "p01-01", "text": "I stopped going because the bus only runs twice a day.", "participant_id": "P01", "site": "rural", "age_group": "18-34"}
{"id": "p01-02", "text": "The nurse remembered my name, and that mattered more than the wait.", "participant_id": "P01"}
{"id": "p02-01", "text": "My copay went up and I started skipping refills.", "participant_id": "P02", "site": "urban", "age_group": "55+"}
{"id": "p02-02", "text": "I never know which clinic is open on Saturdays.", "participant_id": "P02"}
{"id": "p03-01", "text": "The bus takes an hour each way, so I go only when it is urgent.", "participant_id": "P03", "site": "rural", "age_group": "35-54"}
{"id": "p03-02", "text": "The doctor explained everything twice without rushing me.", "participant_id": "P03"}伺服器啟動時,Potato 讀取每個條目上由 cases.key 指定的欄位,並把條目歸入同名的案例。某個名稱第一次出現時,Potato 就建立對應的案例。沒有設定 cases.key 時,Potato 依次檢查 case_id、participant_id 和 respondent_id,並使用其中第一個取值非空的欄位。Potato 對每個條目分別做這項檢查。因此,如果檔案中有些行帶 case_id、另一些行帶 participant_id,得到的案例會分別以這兩個欄位命名。設定 cases.key 可以避免這種混用。對於上面的檔案,伺服器日誌會報告 Cases auto-detect: 3 case(s), 6 instance(s) assigned。
Potato 只讀取頂層欄位,從不讀取巢狀在其他鍵下的欄位。案例欄位沒有取值的條目不屬於任何案例,一個條目最多屬於一個案例。Potato 在每次啟動時都會重新執行檢測,並且只新增或更新記錄。當你新增訪談後重啟,Potato 會把新的摘錄放進相應的案例,並保留原有的案例。Potato 從不刪除案例,你從資料中移除的參與者仍會出現在案例列表中。
Potato 把案例、案例屬性和案例成員關係儲存在 <task_dir>/project.sqlite 中。同一個項目資料庫還儲存碼本和備忘錄。案例歸在研究的 annotation_task_name 之下。如果你重新命名任務,Potato 會在下次啟動時建立一套新的案例,並把舊的那套留在檔案中。
配置
下面的配置開啟 QDA 模式,用一個跨度方案承載程式碼,按 participant_id 歸組摘錄,並把 site 和 age_group 複製到每個案例上:
annotation_task_name: Clinic Access Interviews
task_dir: .
output_annotation_dir: annotation_output/
data_files:
- data/excerpts.jsonl
item_properties:
id_key: id
text_key: text
user_config:
allow_all_users: true
qda_mode:
enabled: true
cases:
key: participant_id
attributes: [site, age_group]
annotation_schemes:
- annotation_type: span
name: codes
description: Highlight a passage and apply a code
codebook: true
labels: [transport, cost, scheduling, provider trust]QDA 模式會開啟案例,這裡的 cases 塊只指定鍵和屬性。對於不在 QDA 模式下的研究,請在該塊中加上 enabled: true。Potato 讀取的全部選項如下:
| 選項 | 預設值 | 說明 |
|---|---|---|
cases.enabled | 未設定(跟隨 qda_mode.enabled) | true 在任何研究中開啟案例。false 即使在 QDA 模式下也關閉案例。 |
cases.key | 未設定(依次檢查 case_id、participant_id 和 respondent_id) | 其取值作為案例名稱的條目欄位。 |
cases.auto_detect | true | 在伺服器啟動時根據資料建立案例。設為 false 時,Potato 不再建立新案例,此前啟動時的歸屬保持不變。 |
cases.attributes | [] | 作為案例屬性複製到每個案例上的條目欄位。 |
Potato 依照由這四個鍵組成的封閉列表檢查 cases 塊。如果配置中有拼錯的鍵,例如 atributes,python -m potato.validate_cli config.yaml 會報告該鍵並建議改為 attributes。
案例屬性
案例屬性記錄的是關於參與者而非某一條摘錄的資訊,例如研究地點或年齡組。啟動時,Potato 把 cases.attributes 中列出的每個欄位從每個條目複製到該條目所屬的案例上。因此,參與者層面的欄位只需要出現在每位參與者的一條摘錄上。在上面的資料中,site 和 age_group 只出現在每場訪談的第一條摘錄上。從電子表格匯出、每一行都帶有該欄位的資料同樣適用。
當同一案例的摘錄取值不一致時,按資料檔案順序最後讀取的摘錄決定案例的取值。值為 null 的欄位會被跳過,但空字串算作一個取值,並會替換之前的取值。請在該行中省略這個欄位,而不要寫 ""。Potato 把所有屬性值都存為文本,34 和 "34" 最終是同一個值。
雖然標註頁面不顯示案例屬性,但任何已登入的標註者都能通過 /api/cases 讀取每個案例及其屬性。作為案例屬性儲存的實驗條件,對一位本應不知曉條件的編碼者來說,只需一次請求就能看到。在這種設計中,請不要把條件放進 Potato 載入的資料檔案,而是在事後把條件合併到匯出結果中。
按案例屬性統計程式碼
對於每個程式碼和某一屬性的每個取值,程式碼交叉表統計獲得該程式碼的摘錄數。管理儀表板頁面說明了管理員金鑰的來源。管理員從 /admin/api/code_crosstab 請求這張表,並在 X-API-Key 請求頭中傳送管理員金鑰:
curl -H "X-API-Key: $KEY" "http://localhost:8000/admin/api/code_crosstab?attribute=site"兩位編碼者處理完示例資料後,響應如下:
{
"attribute_key": "site",
"codes": ["codes::cost", "codes::provider trust", "codes::transport"],
"values": ["rural", "urban"],
"cells": [
{"code": "codes::cost", "value": "urban", "count": 1},
{"code": "codes::provider trust", "value": "rural", "count": 2},
{"code": "codes::transport", "value": "rural", "count": 1}
],
"n_instances": 6,
"schema_filter": null
}Potato 按五條規則填寫單元格:
- 以摘錄為單位。 每個單元格統計的是摘錄數。兩位編碼者都對同一條摘錄應用了某個程式碼,或一位編碼者在同一條摘錄中把某個程式碼高亮了兩次時,該程式碼對這條摘錄只計一次。在示例中,兩位編碼者都在摘錄
p01-01上編碼了transport,而transport/rural單元格的值為 1。 - 回退到案例。 Potato 先從摘錄本身取屬性,摘錄沒有取值時再回退到摘錄所屬的案例。摘錄
p01-02沒有site欄位,Potato 根據案例 P01 把這條摘錄計入rural。 - 什麼算作程式碼。 程式碼的名稱形如
scheme::label。選中的標籤和高亮的跨度會被計入。滑塊的數值和自由文本不計入。 - 分母。
n_instances是在該屬性上有取值的摘錄數,無論是否已編碼。摘錄和案例都沒有取值的摘錄不進入這張表。 - 單個方案。 在請求中加上
&schema=codes後,表中只統計一個標註方案的程式碼。
要按案例統計程式碼,請把案例鍵本身作為屬性傳入(例如 attribute=participant_id)。這樣得到的表每位參與者佔一列。由於案例鍵欄位出現在案例的每一條摘錄上,按案例統計的表不需要回退。
交叉表把所有編碼者合併統計,沒有按編碼者或按案例的篩選。一次請求只涵蓋一個屬性,響應只有 JSON 格式。Potato 2.13.1 的管理儀表板沒有交叉表檢視。如需按編碼者統計,請使用下文匯出一節介紹的引文報告中的 coder 列。
通過 API 讀取案例
兩個只讀端點向任何已登入的會話返回案例,無需管理員金鑰:
| 方法 | 路徑 | 返回內容 |
|---|---|---|
| GET | /api/cases | 研究中的每個案例,按名稱排序,附帶屬性 |
| GET | /api/cases/instance/<id> | 某個條目所屬的案例及其屬性,或 {"case": null} |
對於沒有會話的請求,端點返回 401;案例關閉時返回 503。一條案例記錄包含生成的 id、取自資料的 name、attributes、created_by(檢測得到的案例為 cases-autodetect),以及 created_at 和 updated_at 中的 Unix 時間戳。
匯出案例
在 2.13.1 中,Potato 沒有任何匯出器會寫出案例。REFI-QDA .qdpx 匯出包含來源、程式碼、編碼記錄和編碼者,但不包含案例和案例屬性。在 NVivo 或 ATLAS.ti 中開啟由該檔案生成的項目時,項目中沒有案例,也沒有分類。只有當配置顯式設定 cases.enabled: true 時,Potato 才會就這一遺漏列印警告。依賴 QDA 模式預設值的研究不會收到警告。
案例資料儲存在 project.sqlite 中。用 sqlite3 命令列工具執行兩條查詢,即可把案例成員關係和案例屬性寫入 CSV:
sqlite3 -header -csv project.sqlite "SELECT d.instance_id, c.name AS case_name FROM case_documents d JOIN cases c ON c.id = d.case_id ORDER BY c.name;" > cases.csv
sqlite3 -header -csv project.sqlite "SELECT c.name AS case_name, a.key AS attribute, a.value FROM cases c JOIN case_attributes a ON a.case_id = c.id ORDER BY c.name, a.key;" > case_attributes.csv檔案 cases.csv 每條摘錄一行。cases.csv 的 instance_id 列與引文報告的 instance_id 列相對應。用下面的命令寫出引文報告:
python -m potato.export -c config.yaml --format quotation_report -o export/該命令把每個已編碼段落作為一行寫入 export/quotations.csv。各列分別是程式碼、引用的文本、字元偏移量、條目和編碼者。按 instance_id 把引文報告與 cases.csv 連線,即可為每條已編碼引文附上參與者,再按 case_name 連線一次即可加上屬性。
侷限
- 不能手動指定。 案例只能在啟動時通過檢測產生。沒有任何頁面或 API 呼叫能把條目移到另一個案例,或編輯屬性。要更改案例歸屬或屬性,請編輯資料檔案並重啟 Potato。
- 只有一層。 案例不能包含其他案例,一個條目只屬於一個案例。焦點小組的一條摘錄可以屬於該小組,也可以屬於某一位發言者,但不能同時屬於兩者。
- 程式碼附著在摘錄上。 QDA 模式無法對整個案例進行編碼。
- 按條目計算一致性。 編碼者間一致性按條目計算,不按案例分組。
上游的案例示例
上游示例從 Potato 原始碼目錄執行。示例中的六條摘錄各帶一個 participant_id 和一個 condition,配置把 condition 複製到每個案例上:
python potato/flask_server.py start examples/advanced/cases-example/config.yaml -p 8000登入,編碼幾條摘錄,然後帶上管理員金鑰請求 /admin/api/code_crosstab?attribute=condition,即可看到按條件統計的程式碼。
延伸閱讀
- QDA 模式:同時開啟案例、碼本和備忘錄的開關
- 碼本:交叉表所統計的共享程式碼集合
- 編碼訪談轉寫稿:從錄音到已編碼摘錄,每位參與者一個案例
- 管理儀表板:管理員金鑰和其他管理端點
- 匯出格式:引文報告和
.qdpx匯出
實現細節請參閱源文件。