Export Parquet
Exportez les annotations Potato vers Apache Parquet — un format en colonnes pensé pour les chaînes d'apprentissage automatique à grande échelle, avec intégration Spark, DuckDB, Pandas et HuggingFace Datasets.
Nouveau dans la v2.3.0
Apache Parquet est un format de stockage en colonnes conçu pour les charges de travail analytiques. Sur de gros jeux de données d'annotation, il apporte plusieurs avantages par rapport à JSON et CSV : des fichiers plus petits (compression de l'ordre de 5 à 10x), des lectures plus rapides quand la requête ne porte que sur quelques colonnes, et une prise en charge native dans à peu près tous les outils de science des données (pandas, DuckDB, PyArrow, Spark, Polars, Hugging Face Datasets).
Potato exporte les annotations directement au format Parquet, sous la forme de trois fichiers structurés qui couvrent tous les types d'annotation.
Activer l'export Parquet
Comme format de sortie principal
output_annotation_dir: "output/"
output_annotation_format: "parquet"Comme export secondaire (en gardant JSON comme format principal)
output_annotation_dir: "output/"
output_annotation_format: "jsonl"
parquet_export:
enabled: true
output_dir: "output/parquet/"
auto_export: true # export after each annotation sessionÀ la demande, via la CLI
python -m potato.export parquet --config config.yaml --output ./parquet_output/Fichiers produits
L'export Parquet produit trois fichiers, chacun correspondant à un niveau différent des données d'annotation.
1. annotations.parquet
Le fichier de sortie principal. Une ligne par combinaison (instance, annotateur, schéma).
| Colonne | Type | Description |
|---|---|---|
instance_id | string | Identifiant de l'instance |
annotator | string | Nom d'utilisateur de l'annotateur |
schema_name | string | Nom du schéma d'annotation |
value | string | Valeur de l'annotation (encodée en JSON pour les types complexes) |
timestamp | timestamp | Date de création de l'annotation |
duration_ms | int64 | Temps passé sur cette instance (en millisecondes) |
session_id | string | Identifiant de la session d'annotation |
Pour les types d'annotation simples (radio, likert, text), value contient la valeur brute. Pour les types complexes (multiselect, empans, événements), value contient une chaîne JSON.
2. spans.parquet
Pour les types d'annotation par empans (span, span_link, event_annotation, coreference). Une ligne par empan annoté.
| Colonne | Type | Description |
|---|---|---|
instance_id | string | Identifiant de l'instance |
annotator | string | Nom d'utilisateur de l'annotateur |
schema_name | string | Nom du schéma d'annotation |
span_id | string | Identifiant unique de l'empan |
text | string | Contenu textuel de l'empan |
start_offset | int32 | Décalage du caractère de début |
end_offset | int32 | Décalage du caractère de fin |
label | string | Étiquette de l'empan |
field | string | Champ source (pour l'annotation d'empans sur plusieurs champs) |
links | string | Données de lien encodées en JSON (pour span_link) |
attributes | string | Attributs supplémentaires encodés en JSON |
3. items.parquet
Les métadonnées de chaque instance du jeu de données. Une ligne par instance.
| Colonne | Type | Description |
|---|---|---|
instance_id | string | Identifiant de l'instance |
text | string | Contenu textuel principal |
annotation_count | int32 | Nombre d'annotations reçues |
annotators | string | Liste JSON des noms d'utilisateur des annotateurs |
status | string | État de l'instance (pending, in_progress, complete) |
metadata | string | Métadonnées de l'instance encodées en JSON |
Options de compression
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 optimizationComparaison des algorithmes de compression
| Algorithme | Taux de compression | Vitesse d'écriture | Vitesse de lecture | Convient à |
|---|---|---|---|---|
snappy | Moyen | Rapide | Rapide | Usage courant (par défaut) |
gzip | Élevé | Lent | Moyen | Archivage, petits fichiers |
zstd | Élevé | Rapide | Rapide | Meilleur compromis taille/vitesse |
lz4 | Faible | Très rapide | Très rapide | Charges où la vitesse prime |
brotli | Très élevé | Très lent | Moyen | Compression maximale |
none | Aucune | La plus rapide | La plus rapide | Débogage |
Pour la plupart des projets d'annotation, la compression snappy par défaut convient. Sur de gros jeux de données où la taille des fichiers compte, préférez zstd.
Charger des données 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)Export incrémental
Sur les projets d'annotation au long cours, activez l'export incrémental pour ne pas réexporter tout le jeu de données à chaque fois :
parquet_export:
enabled: true
output_dir: "output/parquet/"
incremental: true
partition_by: date # date, annotator, or noneAvec partition_by: date, les fichiers Parquet sont rangés dans des répertoires partitionnés par date :
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
Tous les grands outils savent lire un jeu de données partitionné comme une seule table logique :
# pandas reads partitioned directories automatically
df = pd.read_parquet("output/parquet/annotations/")
# DuckDB handles partitions natively
# SELECT * FROM 'output/parquet/annotations/**/*.parquet'Référence de configuration
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.parquetExemple complet
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: trueUne fois l'annotation terminée, chargez les données et analysez-les :
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())Pour aller plus loin
- Formats d'export -- COCO, YOLO, CoNLL et les autres formats d'export
- Sources de données distantes -- charger les données depuis un stockage en ligne
- Tableau de bord d'administration -- suivre l'état des exports
Pour les détails d'implémentation, consultez la documentation source.