Import range constraints vs API-driven streams for large dataset ingestion
0 reputation · 10 Jan 2021, 10:36 UTC
0 reputation · 10 Jan 2021, 10:36 UTC
When handling multi-gigabyte datasets in the Wolfram Language, memory exhaustion in the kernel is a primary constraint. Two documented strategies exist to manage this: using Import with specific range constraints to load partial data, or utilizing API-driven asynchronous streams to retrieve data incrementally.
File-based imports provide lower latency for local cached data but rely on the kernel's ability to handle the resulting object size. Conversely, API integrations reduce local memory overhead but introduce dependencies on network stability and external rate limits.
Given a requirement for high scalability with dynamic remote datasets, what are the specific memory overhead trade-offs between Import[filename, "Data", {start, end}] and an API-driven retrieval pattern? Which approach better preserves data type consistency when dealing with mixed numeric and string columns?
32275 reputation · 10 Jan 2021, 19:44 UTC
For multi-gigabyte datasets in Wolfram Language, range-constrained Import generally uses less peak kernel memory than API-driven streaming when the source file is local or on a fast network mount, because it reads only the requested row window into a single in-memory expression. API streaming (URLRead/URLExecute with chunked processing) shifts memory pressure to the HTTP layer but introduces deserialization overhead per chunk and cannot match the columnar type inference of a single Import["Data", {start, end}] call.
{start, end} works reliably for "CSV", "TSV", "Table"; for "JSON" or "XML" the entire structure is parsed first, negating the benefit.URLRead + ImportString per chunk)"ChunkSize").Range-constrained Import["Data", {start, end}] preserves per-column type consistency better. Wolfram's CSV/TSV importer scans the requested rows once, infers a single type per column (Integer, Real, String, DateObject, etc.), and coerces outliers to String. The same inference runs whether you import 10 rows or 10 million.
API streaming with per-chunk ImportString runs type inference independently on each chunk. A column that is all integers in chunk 1 but contains a single string in chunk 5 will produce {1, 2, 3} for the first chunk and {"1", "2", "3", "hello"} for the fifth — breaking downstream Dataset or numeric operations unless you manually unify types afterward.
URLDownload to a temporary file, then range-import), do so. You get consistent typing, lower peak memory, and retryable reads.URLRead[url, "Body" -> "Chunked"] with a fixed chunk size (e.g., 100k rows), parse each chunk with ImportString[#, "CSV"], and immediately Query/reduce to a compact representation (associations, packed arrays) before the next chunk arrives. Explicitly specify "Numeric" -> True or a column-type spec in ImportString to lock types across chunks."RawJSON" or "CSV" with "DataFormat" -> "String" everywhere, then post-process — this avoids silent coercion surprises in either path.What is the typical row count and column count of the window you need to process at once? If your working set fits in < 2 GB of kernel memory, range-constrained Import on a staged file is simpler and more robust. If you must process > 50 M rows incrementally without ever holding more than ~100k rows, the streaming path with explicit type specs is the only viable option.
Use comments to ask for clarification. Post a solution as an answer.
32,275 reputation · 10 Jan 2021, 19:29 UTC
When using Import[file, \"Data\", {start, end}] the kernel allocates a single expression for the slice, so peak memory ≈ (slice size) × 1.2 (Wolfram overhead). This holds only for plain‑text formats like CSV/TSV; for JSON or XML the whole file is parsed first, removing the range benefit.
API‑driven streaming with URLRead[…, \"Stream\"] keeps a small buffer (default 64 KB) plus deserialization temporaries, giving roughly constant kernel memory regardless of total size. To retain numeric types from a JSON API, wrap each chunk in ImportString[chunk, \"JSON\"] before processing; otherwise values may arrive as strings and need ToExpression.