Skill 1.1.5: Ingest from and write by using appropriate data formats (e.g., Apache Parquet, JSON, CSV, ORC) based on data access patterns
Task 1.1: Data Preparation for ML and AI – Collect and store data
The Governing Rule
Format follows the access pattern, not the system that produced the data. The producer's native format (CSV from an export, JSON from an API, binary from a device) is a source constraint, not a storage decision. Convert at the landing/curated boundary.
- Two questions decide the format: (1) Do reads touch all fields of a few records (row) or a few fields of many records (columnar)? (2) Is the write pattern append-at-a-time (row) or bulk-replace (columnar)?
- Cost is downstream of format: Athena/Redshift Spectrum bill per TB scanned. Columnar + compression + partitioning cuts scanned bytes by 10–100x versus CSV/JSON on the same data.
- Columnar files are immutable. "Appending" always means writing a new file. Mutable-row semantics require an open table format (Iceberg/Hudi/Delta), not Parquet alone.
Format Comparison Matrix
| Format | Layout | Schema | Nested | Splittable | Write Pattern | Read Pattern That Wins | ML/AI Use |
|---|---|---|---|---|---|---|---|
| CSV | Row, text | None (inferred) | No | Yes* | Append | Small interchange, manual inspection | Quick baselines, SageMaker built-in algos (File mode) |
| JSON | Row/document, text | Self-describing, loose | Yes | Yes (JSON Lines)* | Append | Semi-structured records, nested payloads | API ingestion, Ground Truth manifests, config |
| Apache Avro | Row, binary | Embedded writer schema | Yes | Yes | Append, streaming | Write-heavy landing, schema evolution | Kafka/MSK landing zone, streaming sinks |
| Apache Parquet | Columnar, binary | Embedded (footer) | Yes (Dremel) | Yes | Bulk/append-by-file | Analytic scan, projection + predicate pushdown | Default curated layer; Athena/Spark/EMR/SageMaker |
| Apache ORC | Columnar, binary | Embedded (footer) | Yes | Yes | Bulk/append-by-file | Hive/Trino analytic scan, ACID Hive tables | EMR/Hive-centric lakes |
| RecordIO-Protobuf | Row, binary | Implicit | No | Yes | Bulk | Sequential streaming consumption | SageMaker built-in algos, Pipe mode |
| TFRecord | Row, protobuf | None (external) | Yes | Limited | Append | tf.data pipelines |
TensorFlow training |
| libsvm | Row, sparse text | None | No | Yes | Bulk | Sparse high-dimensional features | SageMaker Linear Learner, FM |
| Arrow / Feather | Columnar, in-memory | Embedded | Yes | N/A | In-memory | Zero-copy IPC between processes | Not a storage format — interchange only |
* Splittability caveat: CSV/JSON lose splittability when whole-file compressed (.csv.gz). Parquet/ORC keep it regardless of codec (compression is at page/row-group level inside the file).
Row vs Columnar — The Core Trade-off
| Dimension | Row-Oriented (CSV, JSON, Avro) | Columnar (Parquet, ORC) |
|---|---|---|
| Read amplification | Reads entire record to get one field | Reads only requested columns (projection pushdown) |
| Filter efficiency | Full scan | Row-group/stripe min-max stats + bloom filters (predicate pushdown) |
| Compression | Weaker — mixed types per block | Strong — same-type values per block (dictionary, RLE, delta encoding) |
| Write cost | Cheap per-record append | Expensive per-record; designed for bulk writes |
| Single-record lookup | Fine | Poor — wrong tool (use DynamoDB/RDS) |
| Schema evolution | CSV: silent column shift. JSON: permissive | Parquet: add columns at end → old files read as NULL; renaming/reordering breaks readers |
| Human readability | Yes | No (a compliance/governance feature, not a control) |
Parquet vs ORC (common distractor): functionally equivalent for most engines. Parquet has the broader ecosystem (Spark, Athena, Presto, Impala, BigQuery, Redshift Spectrum, Arrow); ORC has denser stripe-level indexes and Hive ACID support and is preferred in Hive/Trino-centric stacks. Neither is universally "faster."
Exam trap: "Convert everything to CSV so any tool can read it." For analytic workloads this is the single most expensive anti-pattern — it forces full-column scans and eliminates pushdown.
Compression — Splittability Is the Hidden Constraint
| Codec | Splittable (Hadoop/Spark whole-file) | Ratio | CPU Cost | Notes |
|---|---|---|---|---|
| None | Yes | 1x | — | Maximum parallelism |
| Snappy | Yes (block) | Moderate | Low | Parquet default; not designed for max ratio |
| LZ4 | Yes (block) | Moderate | Low | Low-latency |
| Zstandard | Yes (seekable frames) | High | Low–Med | Modern default for Parquet/ORC |
| Gzip | No | High | Medium | Kills parallelism on .csv.gz/.json.gz |
| Bzip2 | Yes | High | Very High | Splittable but slow |
| LZO | Yes (with index) | Moderate | Low | Indexing step required |
Hard rule: A single
.csv.gzor.json.gzin S3 = one mapper / one task. All parallelism is lost. This is the classic "job slows as data grows" symptom — the fix is format/layout, not more compute.Nuance that trips candidates: Inside Parquet/ORC, compression operates on pages/stripes, so the file stays splittable no matter which codec is used. Gzip's splittability problem applies to whole-file compression only.
Write-Side Design (S3 Layout)
- File sizing: target 128 MB – 1 GB per file. Millions of tiny files cause per-object listing, request, and open overhead that dwarfs scan cost.
- Partitioning: Hive-style prefixes (
year=/month=/day=/region=) enable partition pruning. Partition on low-cardinality filter columns only. - Sorting within files: sort by the highest-selectivity filter column so row-group min/max statistics skip more data.
- Compaction: schedule periodic jobs to merge small files — Glue ETL, EMR, or an open table format's built-in compaction.
- Catalog registration: Glue Crawlers populate the Data Catalog; partition metadata must be registered (
MSCK REPAIR-equivalent or partition projection) or pruning silently degrades to a full scan. - Crawler gotchas: JSON needs a JSON classifier with a
jsonPathfor multi-document files; headerless CSV needsallowSingleColumn/custom classifier; crawlers do not resolve schema conflicts across sources — that is an engineering decision you document.
SageMaker Training Data Modes
| Mode | Mechanism | Supported Formats | Best For | Constraint |
|---|---|---|---|---|
| File | Full download from S3 to local EBS before training starts | Any | Small/medium datasets, many epochs, random access | Startup latency + EBS capacity scale with dataset size |
| Pipe | Streams directly from S3 into the container, no local copy | RecordIO-Protobuf, RecordIO-WebDataset, Augmented Manifest | Very large datasets, fast start, minimal disk | Cannot stream arbitrary CSV/Parquet |
| FastFile | POSIX-like streaming with random access, no full download | Any | Large datasets needing random access / multi-epoch | Newer default recommendation for large data |
Pipe mode is format-locked. Built-in algorithms stream RecordIO-Protobuf; raw images/audio/video require an augmented manifest. Choosing CSV as the training artifact forces File mode and forfeits Pipe-mode startup savings.
- Augmented Manifest: JSON Lines file where each line carries a
source-ref(S3 URI to the object) plus label attributes. This is also the native output format of SageMaker Ground Truth. - Format↔algorithm mapping: XGBoost/Linear Learner accept CSV, libsvm, Parquet (File mode); image/video algorithms accept RecordIO or augmented manifest (Pipe mode).
Streaming Ingestion Formats
- Kinesis Data Streams: format-agnostic byte payloads with a partition key. Ordering is guaranteed within a shard only; the consumer owns deserialization.
- Kinesis Data Firehose: can perform in-flight JSON → Parquet/ORC conversion — requires a source of JSON and a Glue Data Catalog table for the target schema. Also supports GZIP/Snappy/Zip/Hadoop-Snappy compression, buffer size/time hints, and dynamic partitioning.
- Amazon MSK / Apache Kafka: standardize on Avro + Schema Registry for schema evolution, or Protobuf for compactness. Row-oriented formats win because the write pattern is append-per-record.
- Managed Service for Apache Flink: consumes Kinesis/MSK, applies windowed transforms, and writes columnar output (Parquet) to S3 for downstream analytic/training reads.
- Backpressure diagnosis: a consumer falling progressively behind is either under-provisioned or blocked downstream. Adding consumers to a pipeline blocked on a slow write target just relocates the queue — scale the target first.
Unstructured & Multimodal Data
- Binaries stay in S3 (images, audio, video, PDFs). Metadata, labels, and embeddings go in Parquet / DynamoDB / Feature Store so they are queryable and traceable back to the source object.
- Text corpora: JSON Lines for raw ingestion; Parquet with a text column for curated storage; embeddings as Parquet float-array columns or pushed into a vector store.
- Multi-shard large-scale training: WebDataset / RecordIO-WebDataset shards for sequential streaming; TFRecord for TensorFlow-native pipelines.
- PyTorch/TensorFlow over Parquet: use Petastorm or an Arrow-backed reader rather than converting to TFRecord, avoiding a redundant copy.
Open Table Formats (When Plain Parquet Is Not Enough)
| Format | Strength | Typical Engine |
|---|---|---|
| Apache Iceberg | Hidden partitioning, snapshot isolation, time travel, row-level deletes, schema evolution | Athena, EMR, Glue, Spark |
| Apache Hudi | Upserts + incremental pulls; copy-on-write vs merge-on-read | EMR, Spark |
| Delta Lake | ACID transactions + time travel on object storage | EMR, Spark |
- Use them to solve: small-file accumulation, concurrent writers, GDPR/CCPA row-level deletes, and schema evolution — none of which plain Parquet handles.
- They do not replace the format decision — Parquet/ORC remain the underlying columnar file format.
Vector Stores & Feature Store Formats
- Vector store selection: OpenSearch Service (k-NN/HNSW, low-latency ANN), RDS PostgreSQL + pgvector (embeddings colocated with relational records — no data movement), S3 Vectors (large, cost-optimized, less frequent querying).
- Always store alongside each embedding: source document ID, chunk ID, embedding model version, and timestamp. Without the pointer you cannot cite, refresh, or re-embed.
- SageMaker Feature Store:
- Offline store → Parquet in S3 + Glue Data Catalog. Serves batch training with point-in-time-correct joins using the event time.
- Online store → low-latency single-record lookup of the latest values for inference.
- Requires a record identifier name and an event time feature name at feature group creation.
- Ingest via streaming
PutRecordor batch ingestion jobs (Spark/Glue). -
The reason Feature Store exists: training–serving skew. One feature definition serving both paths eliminates the divergence that produces models that test well and disappoint in production.
-
Feature Store ≠ vector store. Feature store answers "for this entity, what are its values?" Vector store answers "what is similar to this embedding?"
Security, Governance & Compliance
- Encryption at rest: S3 SSE-S3 / SSE-KMS / DSSE-KMS / SSE-C. KMS customer-managed keys give you key rotation, audit trails, and cross-account grants.
- Columnar-native encryption: Parquet Modular Encryption encrypts selected columns/fields with KMS-managed keys; ORC supports column-level encryption in Hive. Use when a single dataset mixes PII and non-PII columns.
- Encryption in transit: enforce TLS-only bucket policies (
aws:SecureTransport). - Network isolation: S3 gateway VPC endpoint + interface endpoints for Glue/Athena/Feature Store keep ingestion traffic off the public internet.
- Fine-grained access: Lake Formation enforces table/column/row-level and tag-based permissions across Athena, EMR, and Redshift Spectrum; the Glue Data Catalog is the enforcement point.
- PII discovery: Macie scans S3 for sensitive data — run it before choosing partition keys, since a partition like
ssn=leaks data through the path itself. - Retention & residency: S3 Lifecycle transitions to Glacier/Deep Archive; S3 Object Lock for WORM immutability; region pinning for data residency.
-
Format is not a security control. Parquet's binary encoding obscures data but does not protect it. Encryption and access policy do.
Anti-Patterns & Distractor Traps
.csv.gz/.json.gzat scale → single-task bottleneck; no split parallelism.- Pretty-printed multi-line JSON → not line-splittable; use JSON Lines (NDJSON).
- Millions of small files → per-object overhead dominates; compact to 128 MB–1 GB.
- Format inherited from the source system → the defining anti-pattern of this skill.
- Columnar for point lookups → wrong tool; OLTP key lookups belong in DynamoDB/RDS.
- Partitioning on high-cardinality keys (user_id, request_id) → partition explosion and catalog bloat.
- In-place Parquet overwrite → impossible; Parquet is immutable. Use new files + an open table format for mutation.
- Schema drift ignored → CSV silently misaligns columns; Parquet/ORC/Avro detect or evolve.
- JSON in Athena at scale → order-of-magnitude higher scan cost than Parquet/ORC.
- Embeddings without source pointers → cannot cite, refresh, or re-embed after a model upgrade.
- "ORC always beats Parquet" → false; equivalent for most engines, ecosystem differs.
- "Pipe mode works with any format" → false; RecordIO-Protobuf/WebDataset or augmented manifest only.
- "Gzip always for best compression" → false at whole-file scope; it destroys parallelism.