Memory-mapped arrays for large data in NumPy - Time & Space Complexity
When working with very large data files, memory-mapped arrays let us access data without loading it all at once.
We want to understand how the time to read or write data grows as the file size increases.
Analyze the time complexity of the following code snippet.
import numpy as np
# Create or open a memory-mapped file
filename = 'large_data.dat'
mm_array = np.memmap(filename, dtype='float64', mode='r+', shape=(1000000,))
# Access a slice of the array
slice_data = mm_array[1000:2000]
# Modify a value
mm_array[500] = 42.0
# Flush changes to disk
mm_array.flush()
This code opens a large file as a memory-mapped array, reads a slice, modifies one element, and saves changes.
Look for repeated actions that take time as data size grows.
- Primary operation: Accessing or modifying elements in the memory-mapped array.
- How many times: Depends on how many elements are read or written; each element access triggers a disk read or write if not cached.
Accessing a small slice reads only that part from disk, so time grows with the slice size, not the whole file.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 elements | About 10 disk reads |
| 100 elements | About 100 disk reads |
| 1000 elements | About 1000 disk reads |
Pattern observation: Time grows roughly linearly with the number of elements accessed, not the total file size.
Time Complexity: O(k) where k is the number of elements accessed or modified.
This means the time depends on how much data you actually read or write, not the total size of the large file.
[X] Wrong: "Accessing any part of a memory-mapped array always takes time proportional to the whole file size."
[OK] Correct: Memory mapping loads only the accessed parts into memory, so time depends on the accessed slice size, not the entire file.
Understanding memory-mapped arrays shows you can handle big data efficiently by reading only what you need, a useful skill in real data science work.
What if we changed the access pattern to read the entire array sequentially? How would the time complexity change?
Practice
numpy.memmap for large data files?Solution
Step 1: Understand memory usage with large files
Loading large files fully into RAM can cause memory errors or slowdowns.Step 2: Role of
Memory-mapped arrays let you access parts of the file on disk as if they were in memory, avoiding full loading.numpy.memmapFinal Answer:
It allows working with data without loading the entire file into memory. -> Option AQuick Check:
Memory-mapping = Partial loading [OK]
- Thinking memmap compresses data
- Assuming memmap speeds up CPU/GPU
- Believing memmap auto-deletes files
data.dat with shape (100, 100) and dtype float64?Solution
Step 1: Check required parameters for memmap
Must specify filename, dtype, mode, and shape for memmap creation.Step 2: Validate each option
np.memmap('data.dat', dtype='float64', mode='r+', shape=(100, 100)) correctly uses dtype='float64', mode='r+', and shape=(100, 100). Others have wrong dtype or missing mode.Final Answer:
np.memmap('data.dat', dtype='float64', mode='r+', shape=(100, 100)) -> Option BQuick Check:
Correct dtype, mode, shape = np.memmap('data.dat', dtype='float64', mode='r+', shape=(100, 100)) [OK]
- Omitting mode parameter
- Using wrong dtype
- Swapping dtype and shape order
print(mm[0, 0])?
import numpy as np filename = 'test.dat' mm = np.memmap(filename, dtype='int32', mode='w+', shape=(2, 2)) mm[0, 0] = 10 mm.flush() mm2 = np.memmap(filename, dtype='int32', mode='r', shape=(2, 2)) print(mm2[0, 0])
Solution
Step 1: Write value and flush to disk
Value 10 is assigned to mm[0,0] and flushed, saving to file.Step 2: Read from file with new memmap
mm2 reads the same file in read mode, so mm2[0,0] is 10.Final Answer:
10 -> Option CQuick Check:
Flush saves data, read memmap shows 10 [OK]
- Forgetting to flush before reading
- Expecting zero without initialization
- Confusing write and read modes
import numpy as np filename = 'data.dat' mm = np.memmap(filename, dtype='float64', mode='r+', shape=(3, 3)) mm[0, 0] = 5.0 mm.flush()Assume
data.dat does not exist before running this code.Solution
Step 1: Understand mode 'r+'
Mode 'r+' opens file for reading and writing but requires the file to exist.Step 2: Check file existence
Since 'data.dat' does not exist, opening with 'r+' raises FileNotFoundError.Final Answer:
FileNotFoundError because mode 'r+' requires existing file. -> Option DQuick Check:
Mode 'r+' needs existing file [OK]
- Assuming 'r+' creates file
- Confusing 'r+' with 'w+'
- Ignoring file existence requirement
large_data.dat with shape (10000, 10000) and dtype float32. You want to increment all values by 1 without loading the entire file into memory. Which approach using numpy.memmap is best?Solution
Step 1: Avoid loading entire file
Loading full 10000x10000 array uses too much RAM, so avoid np.fromfile full load.Step 2: Use memmap with mode 'r+' and process in chunks
Memmap with 'r+' allows read/write access. Looping over rows increments data without full load. Flushing saves changes.Step 3: Evaluate other options
Load entire file with np.fromfile, add 1, then save back. loads full file (bad for memory). Open memmap with mode 'w+', increment only first 100 elements, flush once. only increments part of data. Use memmap with mode 'r', add 1 to the array, then flush. uses 'r' mode (read-only), so cannot modify.Final Answer:
Create a memmap with mode 'r+', loop over rows, increment each row, flush after each. -> Option AQuick Check:
Memmap + chunk update + flush = Create a memmap with mode 'r+', loop over rows, increment each row, flush after each. [OK]
- Loading entire file into memory
- Using read-only mode for writing
- Modifying only part of data
Ready to prove it?
Pass the test and this pattern moves unknown
