Skip to content

Parquet 내보내기

Potato 주석을 Apache Parquet으로 내보냅니다. 대규모 ML 파이프라인에 최적화된 컬럼형 포맷으로 Spark, DuckDB, Pandas, HuggingFace Datasets와 통합됩니다.

v2.3.0 신규 기능

Apache Parquet은 분석 워크로드에 최적화된 컬럼형 저장 포맷입니다. 대규모 주석 데이터셋에서 JSON이나 CSV에 비해 뚜렷한 이점을 제공합니다. 파일 크기가 더 작고(일반적으로 5~10배 압축), 컬럼 일부만 조회할 때 읽기 속도가 더 빠르며, 사실상 모든 데이터 과학 도구(pandas, DuckDB, PyArrow, Spark, Polars, Hugging Face Datasets)에서 기본적으로 지원됩니다.

Potato는 주석을 Parquet 포맷으로 직접 내보낼 수 있으며, 모든 주석 유형을 포괄하는 세 개의 구조화된 파일을 생성합니다.

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 내보내기는 세 개의 파일을 생성하며, 각각 주석 데이터의 서로 다른 수준을 나타냅니다.

1. annotations.parquet

기본 출력 파일입니다. (인스턴스, 주석자, 스키마) 조합당 한 행이 생성됩니다.

컬럼타입설명
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)을 위한 파일입니다. 주석이 달린 스팬당 한 행이 생성됩니다.

컬럼타입설명
instance_idstring인스턴스 식별자
annotatorstring주석자 사용자 이름
schema_namestring주석 스키마 이름
span_idstring고유 스팬 식별자
textstring스팬 텍스트 내용
start_offsetint32시작 문자 오프셋
end_offsetint32끝 문자 오프셋
labelstring스팬 레이블
fieldstring원본 필드 (다중 필드 스팬 주석용)
linksstringJSON으로 인코딩된 링크 데이터 (span_link용)
attributesstringJSON으로 인코딩된 추가 속성

3. items.parquet

데이터셋의 각 인스턴스에 대한 메타데이터입니다. 인스턴스당 한 행이 생성됩니다.

컬럼타입설명
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

분할된 데이터셋은 모든 주요 도구에서 단일 논리 테이블로 읽을 수 있습니다.

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())

더 읽어보기

구현 세부 정보는 소스 문서를 참조하십시오.