Skip to content
このページはまだお使いの言語に翻訳されていません。英語版を表示しています。

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

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

Come Esportazione Secondaria (Mantieni JSON come Primario)

yaml
output_annotation_dir: "output/"
output_annotation_format: "jsonl"
 
parquet_export:
  enabled: true
  output_dir: "output/parquet/"
  auto_export: true              # esporta dopo ogni sessione di annotazione

Su Richiesta tramite CLI

bash
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).

ColonnaTipoDescrizione
instance_idstringaIdentificatore dell'istanza
annotatorstringaNome utente dell'annotatore
schema_namestringaNome dello schema di annotazione
valuestringaValore dell'annotazione (codificato JSON per i tipi complessi)
timestamptimestampQuando è stata creata l'annotazione
duration_msint64Tempo trascorso su questa istanza (millisecondi)
session_idstringaIdentificatore 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.

ColonnaTipoDescrizione
instance_idstringaIdentificatore dell'istanza
annotatorstringaNome utente dell'annotatore
schema_namestringaNome dello schema di annotazione
span_idstringaIdentificatore univoco dello span
textstringaContenuto testuale dello span
start_offsetint32Offset di carattere iniziale
end_offsetint32Offset di carattere finale
labelstringaEtichetta dello span
fieldstringaCampo sorgente (per annotazione span multi-campo)
linksstringaDati di collegamento codificati JSON (per span_link)
attributesstringaAttributi aggiuntivi codificati JSON

3. items.parquet

Metadati su ogni istanza nel dataset. Una riga per istanza.

ColonnaTipoDescrizione
instance_idstringaIdentificatore dell'istanza
textstringaContenuto testuale principale
annotation_countint32Numero di annotazioni ricevute
annotatorsstringaLista JSON dei nomi utente degli annotatori
statusstringaStato dell'istanza (pending, in_progress, complete)
metadatastringaMetadati dell'istanza codificati JSON

Opzioni di Compressione

yaml
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 query

Confronto delle Compressioni

AlgoritmoRapporto di CompressioneVelocità di ScritturaVelocità di LetturaIdeale Per
snappyModeratoVeloceVeloceUso generale (predefinito)
gzipAltoLentoModeratoArchiviazione, file piccoli
zstdAltoVeloceVeloceMiglior equilibrio dimensione/velocità
lz4BassoMolto VeloceMolto VeloceCarichi di lavoro critici per la velocità
brotliMolto AltoMolto LentoModeratoCompressione massima
noneNessunoPiù VelocePiù VeloceDebug

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

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")
 
# 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

sql
-- 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

python
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

python
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

python
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:

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

Con partition_by: date, i file Parquet vengono organizzati in directory partizionate per data:

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

I dataset partizionati possono essere letti come una singola tabella logica da tutti i principali strumenti:

python
# 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

yaml
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.parquet

Esempio Completo

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

Dopo l'annotazione, carica e analizza:

python
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

Per i dettagli di implementazione, consulta la documentazione sorgente.