Esportazione Parquet
Esporta le annotazioni in formato Apache Parquet per un'elaborazione efficiente di dati su larga scala.
Esportazione Parquet
Novità della v2.3.0
Apache Parquet è un formato di archiviazione a colonne ottimizzato per i carichi di lavoro analitici. Offre vantaggi significativi rispetto a JSON e CSV per i dataset di annotazione di grandi dimensioni: dimensioni dei file più piccole (tipicamente compressione 5-10x), letture più veloci per le query su sottoinsiemi di colonne e supporto nativo in praticamente ogni strumento di data science (pandas, DuckDB, PyArrow, Spark, Polars, Hugging Face Datasets).
Potato può esportare le annotazioni direttamente in formato Parquet, producendo tre file strutturati che coprono tutti i tipi di annotazione.
Abilitazione dell'Esportazione Parquet
Come Formato di Output Primario
output_annotation_dir: "output/"
output_annotation_format: "parquet"Come Esportazione Secondaria (Mantieni JSON come Primario)
output_annotation_dir: "output/"
output_annotation_format: "jsonl"
parquet_export:
enabled: true
output_dir: "output/parquet/"
auto_export: true # esporta dopo ogni sessione di annotazioneSu Richiesta tramite CLI
python -m potato.export parquet --config config.yaml --output ./parquet_output/File di Output
L'esportazione Parquet produce tre file, ciascuno che rappresenta un diverso livello dei dati di annotazione.
1. annotations.parquet
Il file di output principale. Una riga per combinazione (istanza, annotatore, schema).
| Colonna | Tipo | Descrizione |
|---|---|---|
instance_id | stringa | Identificatore dell'istanza |
annotator | stringa | Nome utente dell'annotatore |
schema_name | stringa | Nome dello schema di annotazione |
value | stringa | Valore dell'annotazione (codificato JSON per i tipi complessi) |
timestamp | timestamp | Quando è stata creata l'annotazione |
duration_ms | int64 | Tempo trascorso su questa istanza (millisecondi) |
session_id | stringa | Identificatore della sessione di annotazione |
Per i tipi di annotazione semplici (radio, likert, text), value contiene il valore grezzo. Per i tipi complessi (multiselect, span, eventi), value contiene una stringa JSON.
2. spans.parquet
Per i tipi di annotazione basati su span (span, span_link, event_annotation, coreference). Una riga per span annotato.
| Colonna | Tipo | Descrizione |
|---|---|---|
instance_id | stringa | Identificatore dell'istanza |
annotator | stringa | Nome utente dell'annotatore |
schema_name | stringa | Nome dello schema di annotazione |
span_id | stringa | Identificatore univoco dello span |
text | stringa | Contenuto testuale dello span |
start_offset | int32 | Offset di carattere iniziale |
end_offset | int32 | Offset di carattere finale |
label | stringa | Etichetta dello span |
field | stringa | Campo sorgente (per annotazione span multi-campo) |
links | stringa | Dati di collegamento codificati JSON (per span_link) |
attributes | stringa | Attributi aggiuntivi codificati JSON |
3. items.parquet
Metadati su ogni istanza nel dataset. Una riga per istanza.
| Colonna | Tipo | Descrizione |
|---|---|---|
instance_id | stringa | Identificatore dell'istanza |
text | stringa | Contenuto testuale principale |
annotation_count | int32 | Numero di annotazioni ricevute |
annotators | stringa | Lista JSON dei nomi utente degli annotatori |
status | stringa | Stato dell'istanza (pending, in_progress, complete) |
metadata | stringa | Metadati dell'istanza codificati JSON |
Opzioni di Compressione
parquet_export:
enabled: true
output_dir: "output/parquet/"
compression: snappy # snappy (predefinito), gzip, zstd, lz4, brotli, none
row_group_size: 50000 # righe per gruppo di righe (influisce sulle prestazioni di lettura)
use_dictionary: true # codifica del dizionario per le colonne stringa
write_statistics: true # statistiche delle colonne per l'ottimizzazione delle queryConfronto delle Compressioni
| Algoritmo | Rapporto di Compressione | Velocità di Scrittura | Velocità di Lettura | Ideale Per |
|---|---|---|---|---|
snappy | Moderato | Veloce | Veloce | Uso generale (predefinito) |
gzip | Alto | Lento | Moderato | Archiviazione, file piccoli |
zstd | Alto | Veloce | Veloce | Miglior equilibrio dimensione/velocità |
lz4 | Basso | Molto Veloce | Molto Veloce | Carichi di lavoro critici per la velocità |
brotli | Molto Alto | Molto Lento | Moderato | Compressione massima |
none | Nessuno | Più Veloce | Più Veloce | Debug |
Per la maggior parte dei progetti di annotazione, la compressione predefinita snappy è una buona scelta. Per i dataset di grandi dimensioni dove le dimensioni del file contano, usa zstd.
Caricamento dei Dati 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")
# Filtra per uno schema specifico
sentiment = annotations[annotations["schema_name"] == "sentiment"]
# Calcola l'accordo inter-annotatore
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
-- Query diretta senza caricare in memoria
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;
-- Unisci annotazioni con elementi
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
# Leggi solo colonne specifiche (veloce per tabelle ampie)
table = pq.read_table(
"output/parquet/annotations.parquet",
columns=["instance_id", "value", "annotator"]
)
# Converti in pandas
df = table.to_pandas()
# Leggi con filtraggio per gruppo di righe
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
# Carica direttamente dai file Parquet
dataset = load_dataset("parquet", data_files={
"annotations": "output/parquet/annotations.parquet",
"spans": "output/parquet/spans.parquet",
"items": "output/parquet/items.parquet",
})
# Accedi come un normale dataset HF
print(dataset["annotations"][0])
# Pubblica su 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")
# Aggregazione veloce
label_counts = (
annotations
.filter(pl.col("schema_name") == "sentiment")
.group_by("value")
.agg(pl.count().alias("count"))
.sort("count", descending=True)
)
print(label_counts)Esportazione Incrementale
Per i progetti di annotazione a lungo termine, abilita l'esportazione incrementale per evitare di riesportare l'intero dataset ogni volta:
parquet_export:
enabled: true
output_dir: "output/parquet/"
incremental: true
partition_by: date # date, annotator o noneCon partition_by: date, i file Parquet vengono organizzati in directory partizionate per data:
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
I dataset partizionati possono essere letti come una singola tabella logica da tutti i principali strumenti:
# pandas legge automaticamente le directory partizionate
df = pd.read_parquet("output/parquet/annotations/")
# DuckDB gestisce nativamente le partizioni
# SELECT * FROM 'output/parquet/annotations/**/*.parquet'Riferimento alla Configurazione
parquet_export:
enabled: true
output_dir: "output/parquet/"
# Quando esportare
auto_export: true # esporta dopo ogni sessione (predefinito: false)
export_on_shutdown: true # esporta quando il server si ferma (predefinito: true)
# Impostazioni del file
compression: snappy
row_group_size: 50000
use_dictionary: true
write_statistics: true
# Impostazioni incrementali
incremental: false
partition_by: none # none, date, annotator
# Opzioni specifiche dello schema
flatten_complex_types: false # appiattisci i valori JSON in colonne
include_raw_json: true # includi JSON grezzo insieme alle colonne appiattite
# Esportazione span
export_spans: true # genera spans.parquet
export_items: true # genera items.parquetEsempio Completo
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: trueDopo l'annotazione, carica e analizza:
import pandas as pd
spans = pd.read_parquet("output/parquet/spans.parquet")
# Distribuzione dei tipi di entità
print(spans["label"].value_counts())
# Lunghezza media degli span per tipo
spans["length"] = spans["end_offset"] - spans["start_offset"]
print(spans.groupby("label")["length"].mean())Ulteriori Letture
- Formati di Esportazione -- COCO, YOLO, CoNLL e altri formati di esportazione
- Fonti di Dati Remote -- caricamento di dati da archivi cloud
- Dashboard di Amministrazione -- monitoraggio dello stato di esportazione
Per i dettagli di implementazione, consulta la documentazione sorgente.