pydata / pydata/xarray

Appending to existing zarr store writes mostly NaN from dask arrays, but not numpy arrays

Open
#7,812 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

topic-documentation topic-zarr
Dominant language
Python
Stars
4.2k
Forks
1.4k
Avg merge
2d 15h
Merged PRs (30d)
14

Description

What is your issue?

I am using xarray to consolidate ~24 pre-existing, moderately large netCDF files into a single zarr store. Each file contains a DataArray with dimensions (channel, time), and no values are nan. Each file's timeseries picks up right where the previous one's left off, making this a perfect use case for out-of-memory file concatenation.

for i, f in enumerate(tqdm(files)):
    da = xr.open_dataarray(f) # Open the netCDF file
    da = da.chunk({'channel': da.channel.size, 'time': 'auto'}) # Chunk along the time dimension
    if i == 0:
        da.to_zarr(zarr_file, mode="w")
    else:
        da.to_zarr(zarr_file, append_dim='time')
    da.close()

This always writes the first file correctly, and every other file appends without warning or error, but when I read the resulting zarr store, ~25% of all timepoints (probably, time chunks) derived from files i > 0 are nan.

Admittedly, the above code seems dangerous, since there is no guarantee that da.chunk({'time': 'auto'}) will always return chunks of the same size, even though the files are nearly identical in size, and I don't know what the expected behavior is if the dask chunksizes don't match the chunksizes of the pre-existing zarr store. I checked the docs but didn't find the answer.

Even if the chunksizes always do match, I am not sure what will happen when appending to an existing store. If the last chunk in the store before appending is not a full chunk, will it be "filled in" when new data are appended to the store? Presumably, but this seems like it could cause problems with parallel writing, since the source chunks from a dask array almost certainly won't line up with the new chunks in the zarr store, unless you've been careful to make it so.

In any case, the following change seems to solve the issue, and the zarr store no longer contains nan.

for i, f in enumerate(tqdm(files)):
    da = xr.open_dataarray(f) # Open the netCDF file
    if i == 0:
        da = da.chunk({'channel': da.channel.size, 'time': 'auto'}) # Chunk along the time dimension
        da.to_zarr(zarr_file, mode="w")
    else:
        da.to_zarr(zarr_file, append_dim='time')
    da.close()

I didn't file this as a bug, because I was doing something that was a bad idea, but it does seem like to_zarr should have stopped me from doing it in the first place.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

No repository files or tests are named. Start by reproducing the reported loop with chunked and unchunked inputs, then trace the to_zarr append_dim path and its handling of source and existing-store chunks; done should establish whether mismatched chunks are rejected or written safely, with regression coverage or a documented limitation.

Written by the indexing model from the issue text.

Assessment

Tech stack
numpy, python
Domain
data
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.