定性コーディングのためのケースとケース属性
インタビューの抜粋を参加者ごとにグループ化し、サイトや年齢層などの属性を付け、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 は次の起動時に新しいケースの集合を構築し、古い集合はファイルに残します。
設定
以下の設定は、コード用に一つのスパンスキームを持つ 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エクスポートについて
実装の詳細については、ソースドキュメントを参照してください。