Skip to content

Parquet एक्सपोर्ट

Potato annotations को Apache Parquet में export करें, जो pandas, DuckDB, Spark, Polars और Hugging Face Datasets के साथ बड़े पैमाने के विश्लेषण के लिए एक columnar format है।

v2.3.0 में नया

Apache Parquet analytical workloads के लिए बना एक columnar storage format है। बड़े annotation datasets के लिए यह JSON या CSV से बेहतर compress करता है, बाकी file को छुए बिना columns का एक subset पढ़ता है, और pandas, DuckDB, PyArrow, Spark, Polars और Hugging Face Datasets में सीधे load होता है।

Parquet export के लिए PyArrow चाहिए:

bash
pip install "pyarrow>=12.0.0"

Export चलाना

Parquet, Potato के registered export formats में से एक है, इसलिए export CLI इसे सीधे लिखता है:

bash
python -m potato.export -c config.yaml -f parquet -o ./parquet_export/

Potato config के output_annotation_dir में दर्ज annotations पढ़ता है और Parquet tables को आपके बताए output directory में लिखता है।

तय अंतराल पर Export

लंबे समय तक चलने वाले study में आमतौर पर Parquet snapshot चाहिए होता है, बिना इस पर निर्भर हुए कि कोई CLI चलाना याद रखे। export_annotation_format set करने पर Potato annotation चलते समय समय-समय पर export लिखता है:

yaml
output_annotation_dir: "output/"
 
# A list is accepted, so Parquet can sit alongside another format
export_annotation_format:
  - parquet
  - jsonl
 
# Seconds between automatic exports (default: 60)
auto_export_interval: 300

Auto-exports <output_annotation_dir>/exports/<format>/ में जाते हैं, इसलिए Parquet tables output/exports/parquet/ के अंतर्गत दिखती हैं। Annotations खुद इस setting से स्वतंत्र, हमेशा <output_annotation_dir>/<user>/user_state.json के रूप में store होते हैं।

पुरानी output_annotation_format key deprecated है। Potato इसे export_annotation_format के रूप में पढ़ता है और एक warning log करता है, इसलिए इसका नाम बदल दें।

Output Files

एक export अधिकतम चार tables बनाता है, और हर table data के एक अलग स्तर को cover करती है।

annotations.parquet

हर (instance, annotator) जोड़ी के लिए एक row, जिसमें schemas columns में flatten किए जाते हैं।

ColumnTypeविवरण
instance_idstringAnnotate किए गए item का ID
user_idstringAnnotator का ID
<schema_name>बदलता हैहर annotation schema के लिए एक column

Column types annotation type के अनुसार होते हैं: radio और select चुने गए label वाली strings बनते हैं, likert, slider और number float64 बनते हैं (या named labels वाले scale के लिए string), multiselect strings की list बनता है, और text string बनता है।

जो schema एक साथ कई answers store करता है, उसे हर sub-answer के लिए एक column मिलता है, जिसका नाम <schema>.<key> होता है। handling नाम का एक multirate schema handling.Urgency और handling.Customer tone बनाता है; constant_sum और soft_label भी इसी तरह बँटते हैं। Single-select एक ही column में रहता है, क्योंकि dataframe में काम की चीज़ severity में "Serious" होना है।

हर column हर row में मौजूद होता है, और जहाँ annotator ने जवाब नहीं दिया वहाँ null भरा जाता है, इसलिए जिस schema तक केवल कुछ annotators पहुँचे, वह भी file में मौजूद रहता है।

spans.parquet

हर span annotation के लिए एक row, जो तब लिखी जाती है जब study में span schemas हों।

ColumnTypeविवरण
instance_idstringAnnotate किए गए item का ID
user_idstringAnnotator का ID
schema_namestringSpan schema का नाम
startintवह character offset जहाँ span शुरू होता है
endintवह character offset जहाँ span खत्म होता है
labelstringSpan का label
textstringSpan का text content
target_fieldstringवह display field जिसमें span बनाया गया, single-field page पर ""
titlestringLabel का display title
span_idstringStored span का id, schema_label_start_end
format_coordsstringJSON: PDF span के लिए page और box, spreadsheet span के लिए row और column
additional_partsstringJSON: discontinuous span के बाकी ranges
kb_id, kb_source, kb_labelstringवह entity जिसकी ओर linked span इशारा करता है

जब भी किसी study में एक से ज़्यादा span target हों, target_field पढ़ें। एक ही span schema page के हर span-target field में record करता है, इसलिए field एक schema के भीतर बदलता रहता है, और span_id से उसे वापस नहीं निकाला जा सकता: दो fields में mark किए गए वही शब्द ऐसी rows बनाते हैं जो बाकी हर तरह से एक जैसी होती हैं।

text को store नहीं किया जाता, बल्कि export के समय offsets से निकाला जाता है, इसलिए यह उनसे कभी असहमत नहीं हो सकता।

items.parquet

हर source item के लिए एक row, जिसमें original data होता है।

ColumnTypeविवरण
item_idstringItem का ID
<field_name>बदलता हैOriginal data के हर field के लिए एक column (nested dicts और lists JSON में serialize होती हैं)

phase_responses.parquet

तब लिखी जाती है जब study ने phase data इकट्ठा किया हो, जैसे consent form या post-study survey, और export_include_phase_data true हो। इस key का default false है, इसलिए जिस study ने consent और post-study questionnaire इकट्ठा किया, उसे तब तक कोई phase_responses.parquet नहीं मिलता जब तक आप इसे चालू न करें, और export छोड़े गए responses को एक warning में सूचीबद्ध करता है।

Export Options

Format options --option key=value से दें, हर option के लिए flag दोहराते हुए:

bash
python -m potato.export -c config.yaml -f parquet -o ./parquet_export/ \
    --option compression=zstd --option include_items=false
OptionDefaultविवरण
compressionsnappyCompression codec: snappy, gzip, zstd, lz4, या none
include_itemstrueSource data के साथ items.parquet लिखें
include_spanstrueSpan annotations होने पर spans.parquet लिखें
row_group_sizePyArrow defaultannotations.parquet के लिए row group size

ज़्यादातर projects के लिए snappy सही default है, क्योंकि यह इतनी तेज़ी से decompress करता है कि reads interactive बनी रहती हैं। जब dataset इतना बड़ा हो कि file size मायने रखे, तब zstd चुनें, क्योंकि यह लगभग उसी speed पर काफ़ी बेहतर compress करता है।

Parquet Data लोड करना

pandas

python
import pandas as pd
 
annotations = pd.read_parquet("parquet_export/annotations.parquet")
spans = pd.read_parquet("parquet_export/spans.parquet")
items = pd.read_parquet("parquet_export/items.parquet")
 
# One column per schema, so a schema is selected by name
print(annotations["sentiment"].value_counts())
 
# Compute inter-annotator agreement on the sentiment schema
from sklearn.metrics import cohen_kappa_score
pivot = annotations.pivot(index="instance_id", columns="user_id", values="sentiment")
kappa = cohen_kappa_score(pivot.iloc[:, 0], pivot.iloc[:, 1])

DuckDB

sql
-- Direct query without loading into memory
SELECT instance_id, sentiment, COUNT(*) AS annotator_count
FROM 'parquet_export/annotations.parquet'
GROUP BY instance_id, sentiment
ORDER BY annotator_count DESC;
 
-- Join annotations with the source items
SELECT a.instance_id, i.text, a.sentiment, a.user_id
FROM 'parquet_export/annotations.parquet' a
JOIN 'parquet_export/items.parquet' i
  ON a.instance_id = i.item_id;

PyArrow

python
import pyarrow.parquet as pq
 
# Read specific columns only (fast for wide tables)
table = pq.read_table(
    "parquet_export/annotations.parquet",
    columns=["instance_id", "user_id", "sentiment"]
)
 
df = table.to_pandas()
 
parquet_file = pq.ParquetFile("parquet_export/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
 
dataset = load_dataset("parquet", data_files={
    "annotations": "parquet_export/annotations.parquet",
    "spans": "parquet_export/spans.parquet",
    "items": "parquet_export/items.parquet",
})
 
print(dataset["annotations"][0])
 
dataset["annotations"].push_to_hub("my-org/my-annotations", split="train")

Potato एक huggingface exporter भी देता है जो यही splits सीधे बनाता है, जिससे Hub destination होने पर intermediate files की ज़रूरत नहीं पड़ती।

Polars

python
import polars as pl
 
annotations = pl.read_parquet("parquet_export/annotations.parquet")
 
label_counts = (
    annotations
    .group_by("sentiment")
    .agg(pl.len().alias("count"))
    .sort("count", descending=True)
)
print(label_counts)

पूर्ण उदाहरण

एक span-annotation project जो JSONL को working format के रूप में रखता है और विश्लेषण के लिए उसके साथ Parquet भी लिखता है:

yaml
annotation_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
    description: "Highlight named entities"
    labels:
      - name: PERSON
      - name: ORGANIZATION
      - name: LOCATION
 
output_annotation_dir: "output/"
 
# Both formats are written periodically to output/exports/<format>/
export_annotation_format:
  - jsonl
  - parquet
auto_export_interval: 300
 
user_config:
  allow_all_users: true

Annotation के बाद, span table ही entity offsets रखती है:

python
import pandas as pd
 
spans = pd.read_parquet("output/exports/parquet/spans.parquet")
 
# Entity type distribution
print(spans["label"].value_counts())
 
# Average span length by type
spans["length"] = spans["end"] - spans["start"]
print(spans.groupby("label")["length"].mean())

आगे पढ़ें

कार्यान्वयन विवरण के लिए, स्रोत दस्तावेज़ देखें।