Skip to content

Exportación Parquet

Exporta anotaciones al formato Apache Parquet para procesamiento eficiente de datos a gran escala.

Exportación Parquet

Nuevo en v2.3.0

Apache Parquet es un formato de almacenamiento columnar optimizado para cargas de trabajo analíticas. Ofrece ventajas significativas sobre JSON y CSV para conjuntos de datos de anotación grandes: tamaños de archivo más pequeños (típicamente 5-10x de compresión), lecturas más rápidas para consultas de subconjuntos de columnas y soporte nativo en prácticamente todas las herramientas de ciencia de datos (pandas, DuckDB, PyArrow, Spark, Polars, Hugging Face Datasets).

Potato puede exportar anotaciones directamente al formato Parquet, produciendo tres archivos estructurados que cubren todos los tipos de anotación.

Habilitar la Exportación Parquet

Como Formato de Salida Principal

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

Como Exportación Secundaria (Mantener JSON como Principal)

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

Bajo Demanda vía CLI

bash
python -m potato.export parquet --config config.yaml --output ./parquet_output/

Archivos de Salida

La exportación Parquet produce tres archivos, cada uno representando un nivel diferente de los datos de anotación.

1. annotations.parquet

El archivo de salida principal. Una fila por combinación de (instancia, anotador, esquema).

ColumnaTipoDescripción
instance_idstringIdentificador de la instancia
annotatorstringNombre de usuario del anotador
schema_namestringNombre del esquema de anotación
valuestringValor de la anotación (codificado en JSON para tipos complejos)
timestamptimestampCuándo se creó la anotación
duration_msint64Tiempo dedicado a esta instancia (milisegundos)
session_idstringIdentificador de la sesión de anotación

Para tipos de anotación simples (radio, likert, text), value contiene el valor en bruto. Para tipos complejos (multiselect, spans, events), value contiene una cadena JSON.

2. spans.parquet

Para tipos de anotación basados en spans (span, span_link, event_annotation, coreference). Una fila por span anotado.

ColumnaTipoDescripción
instance_idstringIdentificador de la instancia
annotatorstringNombre de usuario del anotador
schema_namestringNombre del esquema de anotación
span_idstringIdentificador único del span
textstringContenido textual del span
start_offsetint32Desplazamiento de carácter inicial
end_offsetint32Desplazamiento de carácter final
labelstringEtiqueta del span
fieldstringCampo fuente (para anotación de span multi-campo)
linksstringDatos de enlace codificados en JSON (para span_link)
attributesstringAtributos adicionales codificados en JSON

3. items.parquet

Metadatos sobre cada instancia en el conjunto de datos. Una fila por instancia.

ColumnaTipoDescripción
instance_idstringIdentificador de la instancia
textstringContenido textual principal
annotation_countint32Número de anotaciones recibidas
annotatorsstringLista JSON de nombres de usuario de anotadores
statusstringEstado de la instancia (pending, in_progress, complete)
metadatastringMetadatos de la instancia codificados en JSON

Opciones de Compresión

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

Comparación de Compresión

AlgoritmoRatio de CompresiónVelocidad de EscrituraVelocidad de LecturaMejor Para
snappyModeradoRápidoRápidoUso general (por defecto)
gzipAltoLentoModeradoArchivado, archivos pequeños
zstdAltoRápidoRápidoMejor equilibrio de tamaño y velocidad
lz4BajoMuy RápidoMuy RápidoCargas de trabajo críticas en velocidad
brotliMuy AltoMuy LentoModeradoCompresión máxima
noneNingunoMás RápidoMás RápidoDepuración

Para la mayoría de los proyectos de anotación, la compresión snappy por defecto es una buena opción. Para conjuntos de datos grandes donde el tamaño del archivo importa, usa zstd.

Cargar Datos 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)

Exportación Incremental

Para proyectos de anotación de larga duración, habilita la exportación incremental para evitar re-exportar todo el conjunto de datos cada vez:

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

Con partition_by: date, los archivos Parquet se organizan en directorios particionados por fecha:

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

Los conjuntos de datos particionados se pueden leer como una sola tabla lógica por todas las herramientas principales:

python
# pandas reads partitioned directories automatically
df = pd.read_parquet("output/parquet/annotations/")
 
# DuckDB handles partitions natively
# SELECT * FROM 'output/parquet/annotations/**/*.parquet'

Referencia de Configuración

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

Ejemplo 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

Después de la anotación, carga y analiza:

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

Lecturas Adicionales

Para detalles de implementación, consulta la documentación fuente.