Skip to content

Parquetエクスポート

効率的な大規模データ処理のために、アノテーションをApache Parquet形式にエクスポートします。

Parquetエクスポート

v2.3.0の新機能

Apache Parquetは分析ワークロードに最適化されたカラムナーストレージ形式です。大規模なアノテーションデータセットにおいて、JSONやCSVに比べて大きな利点を提供します:より小さなファイルサイズ(通常5〜10倍の圧縮)、カラムサブセットクエリでの高速読み取り、そして事実上すべてのデータサイエンスツール(pandas、DuckDB、PyArrow、Spark、Polars、Hugging Face Datasets)でのネイティブサポートです。

Potatoはアノテーションを直接Parquet形式にエクスポートでき、すべてのアノテーションタイプをカバーする3つの構造化ファイルを生成します。

Parquetエクスポートの有効化

プライマリ出力形式として

yaml
output_annotation_dir: "output/"
output_annotation_format: "parquet"

セカンダリエクスポートとして(JSONをプライマリに維持)

yaml
output_annotation_dir: "output/"
output_annotation_format: "jsonl"
 
parquet_export:
  enabled: true
  output_dir: "output/parquet/"
  auto_export: true              # export after each annotation session

CLIによるオンデマンド

bash
python -m potato.export parquet --config config.yaml --output ./parquet_output/

出力ファイル

Parquetエクスポートは3つのファイルを生成し、それぞれアノテーションデータの異なるレベルを表します。

1. annotations.parquet

プライマリ出力ファイル。(インスタンス、アノテーター、スキーマ)の組み合わせごとに1行。

カラム説明
instance_idstringインスタンス識別子
annotatorstringアノテーターのユーザー名
schema_namestringアノテーションスキーマ名
valuestringアノテーション値(複合型の場合はJSONエンコード)
timestamptimestampアノテーション作成日時
duration_msint64このインスタンスに費やした時間(ミリ秒)
session_idstringアノテーションセッション識別子

シンプルなアノテーションタイプ(radio、likert、text)の場合、valueには生の値が含まれます。複合型(multiselect、spans、events)の場合、valueにはJSON文字列が含まれます。

2. spans.parquet

スパンベースのアノテーションタイプ(span、span_link、event_annotation、coreference)用。アノテーション済みスパンごとに1行。

カラム説明
instance_idstringインスタンス識別子
annotatorstringアノテーターのユーザー名
schema_namestringアノテーションスキーマ名
span_idstring一意のスパン識別子
textstringスパンテキスト内容
start_offsetint32文字開始オフセット
end_offsetint32文字終了オフセット
labelstringスパンラベル
fieldstringソースフィールド(マルチフィールドスパンアノテーション用)
linksstringJSONエンコードされたリンクデータ(span_link用)
attributesstringJSONエンコードされた追加属性

3. items.parquet

データセット内の各インスタンスのメタデータ。インスタンスごとに1行。

カラム説明
instance_idstringインスタンス識別子
textstringプライマリテキスト内容
annotation_countint32受信したアノテーション数
annotatorsstringアノテーターユーザー名のJSONリスト
statusstringインスタンスステータス(pending、in_progress、complete)
metadatastringJSONエンコードされたインスタンスメタデータ

圧縮オプション

yaml
parquet_export:
  enabled: true
  output_dir: "output/parquet/"
 
  compression: snappy            # snappy (default), gzip, zstd, lz4, brotli, none
  row_group_size: 50000          # rows per row group (affects read performance)
  use_dictionary: true           # dictionary encoding for string columns
  write_statistics: true         # column statistics for query optimization

圧縮の比較

アルゴリズム圧縮率書き込み速度読み取り速度最適な用途
snappy中程度高速高速一般的な使用(デフォルト)
gzip高い低速中程度アーカイブ、小さなファイル
zstd高い高速高速サイズと速度の最適なバランス
lz4低い非常に高速非常に高速速度重視のワークロード
brotli非常に高い非常に低速中程度最大圧縮
noneなし最速最速デバッグ

ほとんどのアノテーションプロジェクトでは、デフォルトのsnappy圧縮が良い選択です。ファイルサイズが重要な大規模データセットにはzstdを使用してください。

Parquetデータの読み込み

pandas

python
import pandas as pd
 
annotations = pd.read_parquet("output/parquet/annotations.parquet")
spans = pd.read_parquet("output/parquet/spans.parquet")
items = pd.read_parquet("output/parquet/items.parquet")
 
# Filter to a specific schema
sentiment = annotations[annotations["schema_name"] == "sentiment"]
 
# Compute inter-annotator agreement
from sklearn.metrics import cohen_kappa_score
pivot = sentiment.pivot(index="instance_id", columns="annotator", values="value")
kappa = cohen_kappa_score(pivot.iloc[:, 0], pivot.iloc[:, 1])

DuckDB

sql
-- Direct query without loading into memory
SELECT instance_id, value, COUNT(*) as annotator_count
FROM 'output/parquet/annotations.parquet'
WHERE schema_name = 'sentiment'
GROUP BY instance_id, value
ORDER BY annotator_count DESC;
 
-- Join annotations with items
SELECT a.instance_id, i.text, a.value, a.annotator
FROM 'output/parquet/annotations.parquet' a
JOIN 'output/parquet/items.parquet' i
  ON a.instance_id = i.instance_id
WHERE a.schema_name = 'sentiment';

PyArrow

python
import pyarrow.parquet as pq
 
# Read specific columns only (fast for wide tables)
table = pq.read_table(
    "output/parquet/annotations.parquet",
    columns=["instance_id", "value", "annotator"]
)
 
# Convert to pandas
df = table.to_pandas()
 
# Read with row group filtering
parquet_file = pq.ParquetFile("output/parquet/annotations.parquet")
print(f"Row groups: {parquet_file.metadata.num_row_groups}")
print(f"Total rows: {parquet_file.metadata.num_rows}")

Hugging Face Datasets

python
from datasets import load_dataset
 
# Load directly from Parquet files
dataset = load_dataset("parquet", data_files={
    "annotations": "output/parquet/annotations.parquet",
    "spans": "output/parquet/spans.parquet",
    "items": "output/parquet/items.parquet",
})
 
# Access as a regular HF dataset
print(dataset["annotations"][0])
 
# Push to Hugging Face Hub
dataset["annotations"].push_to_hub("my-org/my-annotations", split="train")

Polars

python
import polars as pl
 
annotations = pl.read_parquet("output/parquet/annotations.parquet")
 
# Fast aggregation
label_counts = (
    annotations
    .filter(pl.col("schema_name") == "sentiment")
    .group_by("value")
    .agg(pl.count().alias("count"))
    .sort("count", descending=True)
)
print(label_counts)

増分エクスポート

長期間のアノテーションプロジェクトの場合、毎回データセット全体を再エクスポートすることを避けるために増分エクスポートを有効にしてください:

yaml
parquet_export:
  enabled: true
  output_dir: "output/parquet/"
  incremental: true
  partition_by: date             # date, annotator, or none

partition_by: dateを使用すると、Parquetファイルは日付パーティションされたディレクトリに整理されます:

text
output/parquet/
  annotations/
    date=2026-03-01/part-0.parquet
    date=2026-03-02/part-0.parquet
    date=2026-03-03/part-0.parquet
  spans/
    date=2026-03-01/part-0.parquet
  items/
    part-0.parquet

パーティション化されたデータセットは、すべての主要ツールで1つの論理テーブルとして読み取れます:

python
# pandas reads partitioned directories automatically
df = pd.read_parquet("output/parquet/annotations/")
 
# DuckDB handles partitions natively
# SELECT * FROM 'output/parquet/annotations/**/*.parquet'

設定リファレンス

yaml
parquet_export:
  enabled: true
  output_dir: "output/parquet/"
 
  # When to export
  auto_export: true              # export after each session (default: false)
  export_on_shutdown: true       # export when server stops (default: true)
 
  # File settings
  compression: snappy
  row_group_size: 50000
  use_dictionary: true
  write_statistics: true
 
  # Incremental settings
  incremental: false
  partition_by: none             # none, date, annotator
 
  # Schema-specific options
  flatten_complex_types: false   # flatten JSON values into columns
  include_raw_json: true         # include raw JSON alongside flattened columns
 
  # Span export
  export_spans: true             # generate spans.parquet
  export_items: true             # generate items.parquet

完全な例

yaml
task_name: "NER Annotation Project"
task_dir: "."
 
data_files:
  - "data/documents.jsonl"
 
item_properties:
  id_key: doc_id
  text_key: text
 
annotation_schemes:
  - annotation_type: span
    name: entities
    labels:
      - name: PERSON
        color: "#3b82f6"
      - name: ORGANIZATION
        color: "#22c55e"
      - name: LOCATION
        color: "#f59e0b"
 
output_annotation_dir: "output/"
output_annotation_format: "jsonl"
 
parquet_export:
  enabled: true
  output_dir: "output/parquet/"
  compression: zstd
  auto_export: true
  export_spans: true
  export_items: true

アノテーション後、読み込んで分析:

python
import pandas as pd
 
spans = pd.read_parquet("output/parquet/spans.parquet")
 
# Entity type distribution
print(spans["label"].value_counts())
 
# Average span length by type
spans["length"] = spans["end_offset"] - spans["start_offset"]
print(spans.groupby("label")["length"].mean())

参考資料

実装の詳細については、ソースドキュメントを参照してください。