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 내보내기 활성화
기본 출력 포맷으로 사용
output_annotation_dir: "output/"
output_annotation_format: "parquet"보조 내보내기로 사용 (JSON을 기본으로 유지)
output_annotation_dir: "output/"
output_annotation_format: "jsonl"
parquet_export:
enabled: true
output_dir: "output/parquet/"
auto_export: true # export after each annotation sessionCLI를 통한 온디맨드 실행
python -m potato.export parquet --config config.yaml --output ./parquet_output/출력 파일
Parquet 내보내기는 세 개의 파일을 생성하며, 각각 주석 데이터의 서로 다른 수준을 나타냅니다.
1. annotations.parquet
기본 출력 파일입니다. (인스턴스, 주석자, 스키마) 조합당 한 행이 생성됩니다.
| 컬럼 | 타입 | 설명 |
|---|---|---|
instance_id | string | 인스턴스 식별자 |
annotator | string | 주석자 사용자 이름 |
schema_name | string | 주석 스키마 이름 |
value | string | 주석 값 (복잡한 유형은 JSON으로 인코딩됨) |
timestamp | timestamp | 주석이 생성된 시각 |
duration_ms | int64 | 이 인스턴스에 소요된 시간 (밀리초) |
session_id | string | 주석 세션 식별자 |
단순 주석 유형(radio, likert, text)의 경우 value에 원시 값이 담깁니다. 복잡한 유형(multiselect, spans, events)의 경우 value에 JSON 문자열이 담깁니다.
2. spans.parquet
스팬 기반 주석 유형(span, span_link, event_annotation, coreference)을 위한 파일입니다. 주석이 달린 스팬당 한 행이 생성됩니다.
| 컬럼 | 타입 | 설명 |
|---|---|---|
instance_id | string | 인스턴스 식별자 |
annotator | string | 주석자 사용자 이름 |
schema_name | string | 주석 스키마 이름 |
span_id | string | 고유 스팬 식별자 |
text | string | 스팬 텍스트 내용 |
start_offset | int32 | 시작 문자 오프셋 |
end_offset | int32 | 끝 문자 오프셋 |
label | string | 스팬 레이블 |
field | string | 원본 필드 (다중 필드 스팬 주석용) |
links | string | JSON으로 인코딩된 링크 데이터 (span_link용) |
attributes | string | JSON으로 인코딩된 추가 속성 |
3. items.parquet
데이터셋의 각 인스턴스에 대한 메타데이터입니다. 인스턴스당 한 행이 생성됩니다.
| 컬럼 | 타입 | 설명 |
|---|---|---|
instance_id | string | 인스턴스 식별자 |
text | string | 주요 텍스트 내용 |
annotation_count | int32 | 받은 주석 수 |
annotators | string | 주석자 사용자 이름의 JSON 목록 |
status | string | 인스턴스 상태 (pending, in_progress, complete) |
metadata | string | JSON으로 인코딩된 인스턴스 메타데이터 |
압축 옵션
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
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
-- 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
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
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
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)증분 내보내기
장기간 진행되는 주석 프로젝트의 경우, 매번 전체 데이터셋을 다시 내보내지 않도록 증분 내보내기를 활성화하십시오.
parquet_export:
enabled: true
output_dir: "output/parquet/"
incremental: true
partition_by: date # date, annotator, or nonepartition_by: date를 사용하면 Parquet 파일이 날짜별로 분할된 디렉터리로 정리됩니다.
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
분할된 데이터셋은 모든 주요 도구에서 단일 논리 테이블로 읽을 수 있습니다.
# pandas reads partitioned directories automatically
df = pd.read_parquet("output/parquet/annotations/")
# DuckDB handles partitions natively
# SELECT * FROM 'output/parquet/annotations/**/*.parquet'구성 참조
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전체 예시
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주석 작업 후 데이터를 불러와 분석합니다.
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())더 읽어보기
- 내보내기 포맷 -- COCO, YOLO, CoNLL 및 기타 내보내기 포맷
- 원격 데이터 소스 -- 클라우드 스토리지에서 데이터 불러오기
- 관리자 대시보드 -- 내보내기 상태 모니터링
구현 세부 정보는 소스 문서를 참조하십시오.