跳轉到內容

質性編碼的共享碼本

設定誰可以新增、重新命名、合併或停用程式碼,一次修改如何影響已編碼的摘錄,以及 Potato 在哪裡記錄程式碼定義和修改。

Potato 的碼本是一個項目的編碼者所使用的程式碼列表。Potato 把碼本儲存在項目資料庫中,而不是 YAML 配置裡。方案通過 codebook: true 啟用碼本,此後該方案的選項來自碼本。編碼者可以在一次會話進行到一半時新增程式碼,無需任何人編輯配置或重啟伺服器。程式碼可以掛在一個父程式碼之下,每個程式碼的條目都有類型化欄位,其中包括定義、納入與排除規則以及示例。Potato 會給每條儲存的標註打上儲存時生效的碼本修訂號。碼本隨 QDA 模式在 Potato 2.6.0 中加入,可用於標準標註、單人模式和 QDA 模式。

在質性研究中,碼本列出一個團隊使用的程式碼,並說明每個程式碼在什麼情況下適用。MacQueen 等人(1998)認為,每個程式碼都採用同一固定結構的碼本能給編碼團隊提供穩定的框架,並提高編碼者間一致性。Potato 為每個程式碼提供同一套類型化欄位,從簡短定義到反例都有。碼本開發指南介紹團隊如何撰寫和修訂這些條目。

碼本的儲存與初始化

  • 儲存。 程式碼儲存在 <task_dir>/project.sqlite 中。備忘錄和案例也使用同一個項目資料庫。YAML 只儲存初始標籤。
  • 初始化。 伺服器首次啟動時,Potato 會把每個標記為 codebook: true 的方案的 labels 複製到空碼本中。從那以後,資料庫就是唯一的準據,伺服器會忽略之後對 YAML 標籤的修改。
  • 每個項目一個碼本。 每個標記為 codebook: true 的方案都顯示同一組程式碼。一個同時有 themes 方案和 quotes 方案的項目,兩個方案都從同一個列表取程式碼。
  • 方案類型。 radio、multiselect、select 和 span 支援 codebook: true。
  • 修訂號。 新增、重新命名、改色、移動、刪除、合併或拆分一個程式碼,都會讓項目的碼本修訂號加一。Potato 會在之後儲存的每條標註上記錄該修訂號。
  • 編碼者在哪裡檢視碼本。 標註頁面右邊緣的 Codebook 開關會開啟一個側邊欄,列出每個程式碼及其簡短定義。/codebook 頁面顯示完整碼本,包括每個程式碼的所有欄位。

配置

下面的配置以 extensible 模式為一個團隊設定訪談編碼。編碼者可以在閱讀過程中新增程式碼,只有裁定者可以重新命名或刪除程式碼。兩個初始標籤採用物件形式,物件形式的標籤會為程式碼設定顏色和簡短定義。

yaml
annotation_task_name: Clinic Access Interviews
task_dir: .
output_annotation_dir: annotation_output/
data_files:
- data/interviews.json
item_properties:
  id_key: id
  text_key: text
user_config:
  allow_all_users: true
 
codebook_mode: extensible
 
annotation_schemes:
- annotation_type: span
  name: codes
  description: Highlight a passage and apply a code
  codebook: true
  labels:
  - name: access barriers
    color: "#4682b4"
    description: Anything the speaker names as standing between them and care they wanted.
  - name: cost concerns
    color: "#b44682"
    description: Money, insurance, or the fear of a bill.
選項預設值說明
annotation_schemes[].codebookfalse讓方案的選項來自項目碼本。
annotation_schemes[].labels(無)在首次啟動時為空碼本提供初始程式碼。標籤可以是字串,也可以是帶有 name、color 和 description(或 tooltip)的物件。description 會成為該程式碼的簡短定義。
codebook_modeqda_mode.enabled 或 solo_mode.enabled 為 true 時為 open,否則為 fixed誰可以修改程式碼:fixed、extensible 或 open。兩者都設定時優先於 codebook.mode。其他任何值都無法通過驗證。
codebook.mode與 codebook_mode 相同codebook_mode 的巢狀寫法。
codebook.enabled未設定開啟或關閉側邊欄、/codebook 頁面和碼本 API。未設定時,只要有方案設定了 codebook: true,或處於 QDA 模式或單人模式下,碼本就會開啟。設定了 codebook_mode 的配置無論 codebook.enabled 怎麼寫,碼本都是開啟的。
codebook.distiller.include(或 include_types)short_def、definition、use_when、avoid_when、example、counter_example每個程式碼的哪些欄位進入 Potato 新增到 ICL 標註提示詞中的文本。
codebook.distiller.include_doc_sectionspreamble、general_instructions哪些碼本級章節進入同一段提示詞文本。
codebook.distiller.scopeper_codeper_code 新增每個程式碼的欄位。whole_doc 只新增碼本級章節。
codebook.distiller.procedureconcat提示詞文本的組裝方式。llm_summarize 和 select_icl_examples 可以通過驗證,但在 2.13.1 中產生的輸出與 concat 相同。
codebook.distiller.max_chars無上限把提示詞文本截斷到給定的字元數。
codebook_invivo_keyi在以碼本為支撐的 span 方案中選中文本時,開啟原位編碼輸入框的按鍵。只有第一個字元有效。

qda_mode.codebook.mode 能通過驗證。Potato 2.13.1 在決定誰可以編輯時不會讀取這個鍵。在 QDA 模式下,要改變預設的 open,請設定 codebook_mode。

誰可以修改程式碼

已登入的編碼者能做什麼取決於模式。在 extensible 和 open 模式下,裁定者可以做任何結構性修改。在 fixed 模式下,編碼者和裁定者都不能新增、重新命名、改色、移動或刪除程式碼。管理員和裁定者仍然可以在側邊欄的 Curate 區域合併和拆分程式碼。

模式編碼者可以典型用途
fixed只能應用程式碼信度檢驗輪次、眾包工作、編碼前已商定的碼本
extensible新增程式碼有商定核心程式碼、又留有新增空間的團隊編碼
open新增、重新命名、改色、移動和刪除程式碼,以及編輯定義一位分析者,或共同開發碼本的小團隊

對於任何眾包配置,Potato 都會把模式鎖定為 fixed,並在配置要求更寬許可權時記錄一條警告。Potato 施加這一鎖定,是為了防止招募來的標註者改動共享碼本。鎖定適用於 crowdsourcing.provider 取 expert 以外的任何值、頂層的 prolific: 或 mturk: 程式碼塊,以及 login.type: prolific 或 mturk。無法識別的 provider 名稱和空的 provider 值同樣會鎖定模式。

crowdsourcing.provider: expert 會保留所要求的模式,因為專家是按名字邀請的。只設置 login.type: url_direct 也會保留所要求的模式,並記錄一條警告。任何拿到這類研究連結的人都可以註冊,然後編輯開放的碼本。

在 2.13.1 中,每種修改都有各自的途徑:

修改在瀏覽器中通過 API誰可以
新增程式碼側邊欄的 "Add a code" 輸入框,或原位編碼鍵POST /api/codebookextensible 或 open
重新命名、改色或移到某個父程式碼下2.13.1 中沒有控制元件PATCH /api/codebook/<id>open,或 extensible 下的裁定者
刪除程式碼2.13.1 中沒有控制元件DELETE /api/codebook/<id>open,或 extensible 下的裁定者
把一個程式碼合併到另一個程式碼側邊欄的 Curate 區域POST /api/codebook/admin/merge管理員或裁定者,任何模式
按編碼者拆分程式碼側邊欄的 Curate 區域POST /api/codebook/admin/split管理員或裁定者,任何模式
編輯定義或新增示例側邊欄中程式碼旁的鉛筆圖示,或 /codebookPUT /api/codebook/blocksopen,或 extensible 下的裁定者。其他人的編輯會進入佇列,等待管理員或裁定者確認。

側邊欄的新增輸入框建立的是頂層程式碼。要把程式碼放到某個父程式碼下,需通過 POST 或 PATCH 的 parent_id 欄位。

從選中文本進行原位編碼

在標記為 codebook: true 的 span 方案中,編碼者選中一段文字並按下原位編碼鍵。一個小輸入框隨即開啟,名稱取自所選文字(最多 60 個字元),點選 "Create & code" 會一步完成新增程式碼並用新程式碼高亮該段文字。編碼者輸入名稱時,名稱相近的已有程式碼會以標籤形式出現。編碼者選擇某個標籤,就會應用這個已有程式碼,而不會建立近似重複的程式碼,例如在 cost concerns 旁邊再建一個 cost。

模型提議的修改

模型從不直接修改共享碼本。智慧體或單人模式中的模型通過 POST /api/codebook/proposals 並帶上 actor_kind: "model" 來暫存一項修改。這項修改以一句話的形式在 Curate 區域等待處理,例如 "Merge «cost» into «cost concerns»"。管理員或裁定者確認或拒絕該提議。確認後的提議與人工編輯走同一條路徑,並在修改日誌中標記為來自模型。

程式碼的巢狀

帶有 parent_id 的程式碼掛在該父程式碼之下,父程式碼也可以掛在另一個父程式碼之下。如果一次移動會把程式碼放到它自己的某個後代之下,這次移動會報錯失敗。在同一父程式碼下新增第二個同名程式碼也會以同樣的方式失敗。刪除一個父程式碼會刪除其下的所有程式碼。

側邊欄和 /codebook 以樹形顯示程式碼。標註表單則按樹的順序把程式碼顯示為一個扁平列表,父程式碼在子程式碼之前。表單選項和已儲存的標註都只儲存程式碼的名稱。因此,兩個父程式碼下的同名程式碼會在表單上出現兩次,之後也無法區分這兩個程式碼。請讓程式碼名稱在整個碼本中保持唯一。

在 2.13.1 中,codebook CSV 匯出和 .qdpx 匯出都會把以碼本為支撐的方案的程式碼寫成扁平列表。對於這些程式碼,CSV 的 parent 和 color 列為空。如果其他程式需要程式碼層級,就得在那個程式裡重建,或把層級記錄在程式碼名稱中。

程式碼修改對已儲存編碼結果的影響

Potato 把每條編碼結果以程式碼名稱作為標籤,儲存在編碼者的 user_state.json 中。合併或拆分從不刪除資料庫中的行。Potato 對碼本的每次修改只作用於碼本本身,從不改寫已儲存的標註:

  • 新增程式碼不會改變已儲存的編碼結果。
  • 重新命名程式碼會從此改變表單。以舊名稱儲存的編碼結果保留舊名稱,匯出和一致性統計也按舊名稱計算這些編碼結果。請在一輪編碼開始前重新命名,或重新編碼受影響的摘錄。
  • 刪除程式碼會從表單中移除該程式碼及其下的所有程式碼。已儲存的編碼結果保留該名稱,修改日誌也保留被刪除程式碼的名稱。
  • 合併會歸檔原始碼。被歸檔的程式碼從表單中消失,但該程式碼的行和歷史仍留在資料庫中。用原始碼儲存的編碼結果保留原始碼的名稱。
  • 按編碼者拆分指定一名編碼者和一個新的或已有的目的碼。拆分用於兩名編碼者用同一個程式碼名稱表示不同含義的情況,設計上只移動被指定編碼者的使用記錄。在 2.13.1 中,標註表單不會記錄拆分所移動的按編碼者連結。因此,只要原始碼沒有子程式碼,拆分就會建立目的碼,並對所有編碼者歸檔原始碼。已儲存的編碼結果保留每位編碼者當時選擇的名稱。

Potato 通過兩種方式告知編碼者碼本的變化。第一,在新程式碼加入之前儲存的摘錄上會出現一條橫幅,列出新加入的程式碼。第二,側邊欄的 Review 列表收錄該編碼者在最近一次任意類型修改之前儲存的每條摘錄。列表中的每條摘錄都有一個 Go 按鈕。這兩種提示都可以關閉,Potato 從不強制重新編碼。管理員或裁定者可以在 GET /api/codebook/admin/stale 檢視整個項目的同一列表。

定義的編輯有單獨的計數器。對程式碼的簡短定義、定義、"Use when" 或 "Avoid when" 欄位的編輯會提升語義修訂號,Potato 會在之後儲存的每條標註上記錄該語義修訂號。分析者據此可以區分定義修改前後做出的編碼結果。在 /codebook 編輯器中勾選 Minor edit (no re-review),可以儲存措辭而不提升語義修訂號。在 2.13.1 中,Review 列表只跟蹤結構性修改。

在信度檢驗輪次中請設定 codebook_mode: fixed,因為 Potato 基於已儲存的程式碼名稱計算一致性,輪次中途重新命名的程式碼會變成兩個程式碼。標註者間一致性介紹各種係數。/admin/iaa 上的一致性時間線會把每次碼本修訂標註在一致性隨時間的變化上。一致性漂移說明如何解讀這條時間線。

每個程式碼條目中的類型化欄位

每個程式碼的條目都是一組類型化欄位。/codebook 是整頁編輯器,為每個程式碼提供版本歷史、版本間差異對比,以及把舊版本另存為新版本的恢復功能。"Paste markdown" 按鈕會按標題把貼上進來的碼本文字轉換為欄位,對解析器無法識別的章節,會請使用者選擇類型。兩個人同時儲存同一個程式碼時不會互相覆蓋。第二次儲存會失敗,並返回當前文本供對照協調。

欄位編輯器中的標題語義欄位
short_defShort definition是
definitionDefinition是
use_whenUse when是
avoid_whenAvoid when是
exampleExamples否
counter_exampleCounter-examples否
rationaleRationale否
backgroundBackground否
downstream_usageDownstream usage否
keywordsKeywords否
notesNotes否
custom使用者自定義的標題否

碼本整體在程式碼之外還有四個章節,標題分別是 Preamble、General instructions、Background 和 How codes are used downstream。側邊欄顯示每個程式碼的簡短定義,最多顯示兩行。如果某個程式碼沒有簡短定義,側邊欄會改為顯示完整定義。

提煉器(distiller)把所選欄位轉換為 Potato 新增到 ICL 標註提示詞中的文本,並在每次碼本修改後重新生成該文本。GET /api/codebook/distilled 返回模型當前收到的文本。在上面的配置中新增一個 codebook.distiller 程式碼塊來選擇欄位:

yaml
codebook:
  distiller:
    include: [definition, use_when, avoid_when, example]
    procedure: concat

用 CLI 初始化和遷移

potato codebook 會為每個 codebook: true 方案中碼本尚未包含的每個標籤新增一個程式碼,已有程式碼保持不變:

bash
potato codebook config.yaml

該命令會列印新建了多少個程式碼、多少個已經存在。由 CLI 建立的程式碼在每臺機器上都得到相同的 ID,因為 CLI 根據項目名稱、父程式碼和程式碼名稱計算每個 ID。如果需要在項目的多個副本之間保持 ID 一致,請在伺服器首次啟動之前執行 CLI,因為伺服器初始化的程式碼使用隨機 ID。CLI 只複製名稱。物件形式標籤中的顏色和定義只能通過伺服器首次啟動時的初始化進入碼本。

由於伺服器只為空碼本做初始化,要把新的 YAML 標籤加入已有程式碼的碼本,就得使用 CLI。若只想預覽而不寫入,請直接執行該模組。在 2.13.1 中,potato codebook --dry-run 會丟掉這個參數並直接寫入程式碼。

bash
python -m potato.codebook_cli config.yaml --dry-run

API

所有路由都位於 /api/codebook 之下,並需要已登入的會話。管理路由接受管理員或裁定者。

方法路徑用途
GET/api/codebook樹、扁平標籤列表、修訂號、使用碼本的方案,以及呼叫者的 can_add 和 can_edit
GET/api/codebook/version結構修訂號和內容修訂號,用於低開銷輪詢
GET/api/codebook/similar?name=名稱與 name 相近的已有程式碼
GET/api/codebook/provenance?instance_id=呼叫者對某個條目的編碼是否早於當前修訂號,以及此後新增的程式碼
GET/api/codebook/stale呼叫者的 Review 列表
POST/api/codebook新增程式碼(name,可選 color 和 parent_id)
PATCH/api/codebook/<id>重新命名(name)、改色(color)或移動(parent_id)
DELETE/api/codebook/<id>刪除程式碼及其下的所有程式碼
POST/api/codebook/proposals把修改加入佇列(op、payload、actor_kind)
GET/api/codebook/admin/stale所有編碼者的 Review 列表(管理)
POST/api/codebook/admin/merge把 src_id 合併到 dst_id(管理)
POST/api/codebook/admin/split把 annotator 的連結從 src_id 移到 target_id 或名為 new_name 的新程式碼(管理)
GET/api/codebook/admin/changes修改日誌(管理)
GET/api/codebook/admin/proposals待處理的提議(管理)
POST/api/codebook/admin/proposals/<id>/confirm執行一項提議(管理)
POST/api/codebook/admin/proposals/<id>/reject丟棄一項提議(管理)

定義編輯器使用 GET /api/codebook/document、GET 和 PUT /api/codebook/blocks、POST /api/codebook/parse、GET /api/codebook/history、GET /api/codebook/history/<id>、POST /api/codebook/restore 以及 GET /api/codebook/distilled。在 QDA 模式下,/qda/codebook 返回碼本的只讀檢視。

碼本匯出與修改歷史

codebook 匯出會寫出 codebook.csv,每個碼本方案中的每個程式碼佔一行,列為 schema、annotation_type、code、parent、description、color 和 n_uses。該匯出讀取碼本資料庫。編碼過程中新增的程式碼會被包含,被合併的程式碼則被排除。description 儲存程式碼的簡短定義,如果程式碼沒有簡短定義,則儲存完整定義。n_uses 按名稱統計已儲存的編碼結果。以碼本中已不存在的名稱儲存的編碼結果不計入任何一行。

bash
python -m potato.export -c config.yaml --format codebook -o ./export/

quotation_report 匯出列出每段已編碼的文字及其程式碼。qdpx 匯出把程式碼和編碼結果交給另一個 QDA 程式。QDA 模式介紹這兩種匯出,匯出格式列出各列。

歷史記錄儲存在 project.sqlite 中。codebook_change 表記錄每一次重新命名、改色、移動、刪除、合併、拆分,以及被確認或拒絕的提議,包括舊值和新值、操作者、操作者是人還是模型,以及修訂號。在 2.13.1 中,通過 PATCH 或 DELETE 進行的重新命名、改色、移動或刪除,記錄的操作者是 system;新增程式碼不出現在日誌中,而是帶著建立者的使用者名稱出現在 codes 表中。GET /api/codebook/admin/changes 以 JSON 形式返回修改日誌。

codebook_snapshot 儲存每個定義的每一個已儲存版本,codebook_revision_history 記錄每次修訂發生的時間。項目快照和 potato deploy pull 生成的歸檔都包含 project.sqlite。這兩種打包檔案都可以作為碼本的審計軌跡,寫進方法部分。

在 Potato 程式碼倉庫中執行自帶的示例:

bash
python potato/flask_server.py start examples/advanced/codebook-example/config.yaml -p 8000

該示例用一個包含四個主題的開放式多選碼本,對五條關於診所就醫的患者評論進行編碼。examples/advanced/codebook-document-example/ 增加了一個提煉器程式碼塊。在該示例中開啟 /codebook 即可看到定義編輯器。

延伸閱讀

參考文獻

MacQueen, K. M., McLellan, E., Kay, K., & Milstein, B. (1998). Codebook Development for Team-Based Qualitative Analysis. CAM Journal, 10(2), 31-36. https://doi.org/10.1177/1525822X980100020301

實現細節請參閱源文件。