정성 코딩을 위한 케이스와 케이스 속성
인터뷰 발췌문을 참가자별로 묶고, 장소나 연령대 같은 속성을 붙이며, Potato에서 코드를 케이스별 또는 속성별로 집계합니다.
Potato의 케이스는 인터뷰 참가자나 한 조직의 문서처럼 하나의 분석 단위에 속하는 항목을 묶습니다. Potato는 서버를 시작할 때 데이터의 필드로 케이스를 만들고, 장소나 연령대 같은 속성을 각 케이스에 저장하며, 관리자가 코드를 케이스별 또는 케이스 속성별로 집계할 수 있게 합니다. QDA 모드는 케이스를 기본으로 켜며, 다른 연구에서는 구성 블록 하나로 케이스를 켤 수 있습니다. 케이스는 QDA 모드와 같은 릴리스인 Potato 2.6.0에 도입되었습니다.
분석 단위는 연구가 결론을 도출하는 대상 개체입니다. 인터뷰 연구에서 분석 단위는 보통 참가자이며, Potato가 코딩하는 단위는 발췌문입니다. 각 발췌문은 데이터 파일의 항목 하나입니다. 케이스는 두 수준을 연결합니다. 케이스가 있으면 한 발췌문에 적용된 코드는 그 발췌문을 말한 참가자에게 집계되고, 참가자의 장소나 연령대는 그 인터뷰의 모든 발췌문에 적용됩니다.
NVivo나 ATLAS.ti를 쓰던 연구자는 이 개념을 다른 이름으로 알고 있습니다. NVivo 도움말은 케이스 분류가 "케이스의 유형을 설명"하며 각 분류에는 사용자가 기록하기로 선택한 속성이 있다고 말합니다. 연령과 위치는 도움말 페이지의 예시에 들어 있습니다. ATLAS.ti 매뉴얼은 문서 그룹을 "준이분 변수"(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는 다음 시작 때 새로운 케이스 집합을 만들고 이전 집합은 파일에 남겨 둡니다.
구성
아래 구성은 코드를 위한 span 스킴 하나와 함께 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로 연 프로젝트에는 케이스나 분류가 없습니다. Potato는 구성이 cases.enabled: true를 명시적으로 설정한 경우에만 그 누락에 대한 경고를 출력합니다. 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.csvcases.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내보내기
구현 세부 사항은 소스 문서를 참조하세요.