kerchunk

repository·main·Indexed 18 days ago

https://github.com/fsspec/kerchunk

A library for creating virtual, cloud-friendly representations of large, chunked, and compressed archival datasets (such as HDF5, NetCDF, GRIB, TIFF, and FITS). It extracts metadata, including byte ranges and compression information, into separate objects to enable efficient access via fsspec-compatible backends without needing to copy or translate original files.

Tokens
32.1K
Snippets
94
Records
125
Agent score
52%

What's inside kerchunk

  1. What is Kerchunk and when to use it

    main

    Kerchunk is a library designed for cloud-friendly, efficient access to archival data stored in chunked, compressed formats (such as NetCDF, HDF5, GRIB, TIFF, and FITS).

    It works by extracting metadata—including byte ranges and compression information—and storing it in a separate object. This allows you to create virtual datasets that aggregate multiple source files into a single logical view.

    Key Use Cases:

    • Cloud-native access to legacy formats: Access data stored in object storage (S3, GCS, ABFS, etc.) without needing to copy or translate the original files.
    • Metadata consolidation: Understand complex, many-file datasets through a single metadata read.
    • Serverless data processing: Avoid the need for specific drivers (like h5py) by using Kerchunk as a gateway to in-situ data access.
    • High-performance parallel reads: Enable asynchronous, concurrent fetching of data chunks and parallel access via libraries like Zarr without locking issues.
  2. Potential use cases for non-zarr formats

    main

    While kerchunk documentation primarily focuses on zarr, the project is designed to support other use cases where accessing files in specific directory structures or reading binary pieces of large files at known offsets is required.

    Supported or planned non-zarr use cases include:

    • .tar.zstd: Enabling parallel/random access to compressed archives by indexing file offsets within the TAR format and utilizing Zstd's block-wise decompression capabilities. This allows pulling only necessary data parts without downloading and unpacking whole files.
    • .csv/.json: Enabling parallel/distributed processing of text files that contain embedded newlines (e.g., within quoted strings). By scanning the file once to identify valid record terminators, kerchunk can provide safe random access offsets.
    • parquet/orc/feather:
      • Parquet: Using the virtual file system to perform 'smart concatenation' by assigning partition values (e.g., month=may/) to files even if they weren't originally part of the same dataset. This allows for efficient data partitioning without moving or copying files.
      • Feather: Constructing logical Feather files from disparate buffer pieces and serialized metadata to be compatible with the pyarrow API.
  3. Use ReferenceFileSystem to map paths to binary data

    main

    The ReferenceFileSystem (built on fsspec) allows you to create a virtual filesystem by mapping arbitrary pathnames to specific data sources. This is the mechanism used to present fragmented binary chunks as a unified directory tree. You can map a pathname to:

    • A static binary value.
    • A whole file at a different URL.
    • A specific part (subset) of a file at a different URL.

    This allows you to assign the binary chunks of one or more HDF files to any position within a directory tree you choose.

  4. View GRIB aggregations using DataTree

    main

    Once a k_index has been created for the desired duration, you can use the DataTree model from the xarray-datatree library to view specific variables or the entire aggregation. This allows you to navigate the hierarchical structure of the aggregated GRIB data (e.g., by variable name, step type, or level).

    # Example representation of a DataTree for GEFS model aggregation
    DataTree('None', parent=None)
    ├── DataTree('prmsl')
    │   │   Dimensions:  ()
    │   │   Data variables: 
    │   │       *empty*
    │   │   Attributes:
    │   │       name:     Pressure reduced to MSL
    │   └── DataTree('instant')
    │       │   Dimensions:  ()
    │       │   Data variables: 
    │       │       *empty*
    │       │   Attributes:
    │       │       stepType:  instant
    │       └── DataTree('meanSea')
    │               Dimensions:     (latitude: 181, longitude: 360, time: 1, step: 1, model_horizons: 1, valid_times: 237)
    │               Coordinates:
    │                 * latitude    (latitude) float64 ...
    │                 * longitude   (longitude) float64 ...
    │                 * meanSea     float64 ...
    │                 * number      (time, step) int64 ...
    │                 * step        (model_horizons, valid_times) timedelta64[ns] ...
    │                 * time        (model_horizons, valid_times) datetime64[ns] ...
    │                 * valid_time  (model_horizons, valid_times) datetime64[ns] ...
    │               Dimensions without coordinates: model_horizons, valid_times
    │               Data variables:
    │                   prmsl       (model_horizons, valid_times, latitude, longitude) float64 ...
    │               Attributes:
    │                   typeOfLevel:  meanSea
    └── DataTree('ulwrf')
        │   Dimensions:  ()
        │   Data variables: 
        │       *empty*
        │   Attributes:
        │       name:     Upward long-wave radiation flux
        └── DataTree('avg')
            │   Dimensions:  ()
            │   Data variables: 
            │       *empty*
            │   Attributes:
            │       stepType:  avg
            └── DataTree('nominalTop')
                    Dimensions:     (latitude: 181, longitude: 360, time: 1, step: 1, model_horizons: 1, valid_times: 237)
                    Coordinates:
                      * latitude    (latitude) float64 ...
                      * longitude   (longitude) float64 ...
                      * nominalTop  float64 ...
                      * number      (time, step) int64 ...
                      * step        (model_horizons, valid_times) timedelta64[ns] ...
                      * time        (model_horizons, valid_times) datetime64[ns] ...
                      * valid_time  (model_horizons, valid_times) datetime64[ns] ...
                    Dimensions without coordinates: model_horizons, valid_times
                    Data variables:
                        ulwrf       (model_horizons, valid_times, latitude, longitude) float64 ...
                    Attributes:
                        typeOfLevel:  nominalTop
  5. How kerchunk enables parallel and concurrent cloud data access

    main

    kerchunk enables high-performance access to data (specifically netCDF4/HDF5) by extracting metadata in a single scan and arranging multiple chunks from multiple files into a single indexable aggregate dataset. This allows for two types of performance gains:

    1. Parallelism: Performing actions in multiple independent threads, processes, or machines. This is effective for CPU-intensive workloads and can scale speedup relative to the number of CPU cores.
    2. Concurrency: Launching many requests that wait on external systems (like cloud storage latency). By launching many requests simultaneously, you can hide the latency of getting the first byte of a read.

    When used with dask and zarr, kerchunk leverages both parallelism and concurrency simultaneously to optimize cloud storage access.

  6. Create new dimensions using coo_map in MultiZarrToZarr

    main

    If the dimension you want to concatenate along does not exist in the source datasets, or if you are combining datasets from an ensemble, use the coo_map argument to create a new dimension.

    You can provide:

    1. Literal values: A list of values to populate the new dimension.
    2. Regex functions: A compiled regex that extracts values from the input file URLs to populate the dimension.
    3. Custom functions: A function with the signature (index, fs, var, fn) -> value to generate complex types like datetime.datetime.
    # Example 1: Using literal values
    mzz = MultiZarrToZarr(
        json_list,
        remote_protocol='s3',
        remote_options={'anon':True},
        coo_map = {'new_dimension': ['a', 'b']},
        concat_dims=['new_dimension'],
        identical_dims = ['lat', 'lon']
    )
    
    # Example 2: Using a regex function
    import re
    ex = re.compile(r'.*(\d+)_air')
    mzz = MultiZarrToZarr(
        json_list,
        remote_protocol='s3',
        remote_options={'anon':True},
        coo_map = {'new_dimension': ex},
        concat_dims=['new_dimension'],
        identical_dims = ['lat', 'lon']
    )
    
    # Example 3: Using a custom function for datetime extraction
    def fn_to_time(index, fs, var, fn):
        import re
        import datetime
        subst = re.search(r"\d{12}", fn)[0]
        return datetime.datetime.strptime(subst, '%Y%m%d%H%M')
    
    mzz = MultiZarrToZarr(
        sorted(glob.iglob(r'*.json')),
        remote_protocol='file',
        coo_map={'time': fn_to_time},
        coo_dtypes={'time': np.dtype('M8[s]')},
        concat_dims=['time'],
        identical_dims=['lat', 'lon'],
    )
  7. Understand the Code of Conduct enforcement process

    main

    Once a report is submitted to community@anaconda.com, the core team follows this process:

    1. Acknowledgment: You will receive an email acknowledging receipt of your complaint.
    2. Review: The core team meets to determine the facts, whether a violation occurred, the identity of the actor, and if there is an ongoing threat to safety.
    3. Conflict Management: If a core team member is involved in the incident or has a conflict of interest, they are excluded from discussions and denied access to confidential details.
    4. Resolution: The team decides on a response, which may include:
      • No action (if no violation is found).
      • Private or public reprimand.
      • Imposed vacation.
      • Temporary or permanent ban from project spaces (e.g., GitHub repositories).
      • Request for a public or private apology.
    5. Communication: The team will respond to the reporter within one week with either a resolution or an explanation of the delay. Once a final action is determined, the reporter will be notified of the outcome.
  8. Interpreting kerchunk reference files in non-Python environments

    main

    Kerchunk reference files are stored in JSON format and are designed to be language-agnostic. Each key in the JSON contains either encoded binary data or a set of URL/offset/size values. Any language capable of accessing the specific URL type and parsing JSON can interpret these files.

    To use kerchunk references for a Zarr dataset in a non-Python environment, the implementation must:

    1. Have a Zarr implementation.
    2. Support the required binary codecs (e.g., gzip or other specific compressors).
    3. Implement a storage object that exposes the reference set to the Zarr library using the provided URL/offset/size metadata.
  9. Understand the difference between .idx and k_index

    main

    When working with GRIB aggregations, it is important to distinguish between the two types of indexing used:

    • .idx file (Index file): Contains key metadata for the GRIB messages themselves. This includes fields like index, offset, datetime, variable, and forecast time.
    • k_index (Kerchunk index): Indexes the individual variables contained within those GRIB messages.
  10. Aggregate GRIB files using .idx files

    main

    For GRIB files, you can significantly speed up reference aggregation by using accompanying .idx files. This method avoids scanning the entire archive by using the metadata contained in the index files to build the reference.

    Requirements and Restrictions:

    • GRIB files must be paired with their corresponding .idx files.
    • The .idx files must be of text type.
    • This method is specialized for time-series data where GRIB files have an identical structure.
    • Each horizon (forecast time) must be indexed separately.

    The Three-Step Workflow:

    1. Extract Metadata: Extract and persist metadata from a few arbitrary GRIB files for a specific product (e.g., HRRR, GEFS, GFS).
    2. Build Index Table: Use the metadata mapping to build an index table of every GRIB message extracted from the .idx files.
    3. Combine: Combine the index data with the metadata to build any FMRC slice (Horizon, RunTime, ValidTime, BestAvailable).
  11. Set up and build kerchunk documentation

    main

    Documentation is written in ReStructured Text (.rst) and built using sphinx. To work on the documentation:

    1. Create a dedicated docs environment:

      conda create --name kerchunk-docs python=3.8
      conda activate kerchunk-docs
      python -m pip install -r docs/requirements.txt
    2. Build the HTML documentation:

      cd docs
      make html
    conda create --name kerchunk-docs python=3.8
    conda activate kerchunk-docs
    python -m pip install -r docs/requirements.txt
    cd docs
    make html
  12. Build the documentation locally

    main

    To build the project documentation from source, follow these steps:

    1. Navigate to the documentation directory: cd docs.
    2. Create a Python environment and install the necessary dependencies using the provided requirements.txt file (e.g., pip install -r requirements.txt).
    3. Generate the HTML documentation by running make html.
    4. View the built documentation by opening build/html/index.html in your browser.
    cd docs
    pip install -r requirements.txt
    make html
    # Open build/html/index.html