geff 0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ # SCM syntax highlighting & preventing 3-way merges
2
+ pixi.lock merge=binary linguist-language=YAML linguist-generated=true
@@ -0,0 +1,26 @@
1
+ # Proposed Change
2
+ Briefly describe the contribution. If it resolves an issue or feature request, be sure to link to that issue.
3
+
4
+ # Types of Changes
5
+ What types of changes does your code introduce? Delete those that do not apply.
6
+ - Bugfix (non-breaking change which fixes an issue)
7
+ - New feature or enhancement
8
+ - Documentation update
9
+ - Tests
10
+ - Maintenance (e.g. dependencies, CI, releases, etc.)
11
+
12
+ Which topics does your change affect? Delete those that do not apply.
13
+ - Specification
14
+ - `networkx` implementation
15
+
16
+ # Checklist
17
+ Put an x in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code.
18
+
19
+ - [ ] I have read the developer/contributing docs.
20
+ - [ ] I have added tests that prove that my feature works in various situations or tests the bugfix (if appropriate).
21
+ - [ ] I have checked that I maintained or improved code coverage.
22
+ - [ ] I have written docstrings and checked that they render correctly.
23
+ - [ ] If I changed the specification, I have checked that any validation functions and tests reflect the changes.
24
+
25
+ # Further Comments
26
+ If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc...
@@ -0,0 +1,165 @@
1
+ name: Test geff
2
+
3
+ on:
4
+ pull_request:
5
+ branches:
6
+ - main
7
+ push:
8
+ branches:
9
+ - main
10
+ tags: [v*]
11
+ workflow_dispatch:
12
+
13
+ concurrency:
14
+ group: ${{ github.workflow }}-${{ github.ref }}
15
+ cancel-in-progress: true
16
+
17
+ env:
18
+ SETUPTOOLS_SCM_PRETEND_VERSION: "0.0"
19
+
20
+ jobs:
21
+ tests:
22
+ uses: ./.github/workflows/test.yaml
23
+ with:
24
+ test-target: "tests"
25
+ witty-cache: false
26
+ install-extras: "dev"
27
+
28
+ schema:
29
+ name: Check json schema
30
+ runs-on: ubuntu-latest
31
+ env:
32
+ testjson: check-json.json
33
+ steps:
34
+ - uses: actions/checkout@v4
35
+
36
+ - name: Set up Python 3.12
37
+ uses: actions/setup-python@v5
38
+ with:
39
+ python-version: "3.12"
40
+ cache-dependency-path: "pyproject.toml"
41
+ cache: "pip"
42
+
43
+ - name: Install dependencies
44
+ run: |
45
+ python -m pip install -U pip
46
+ pip install .
47
+
48
+ - name: Generate json
49
+ run: python scripts/export_json_schema.py --filename $testjson
50
+
51
+ - run: cat $testjson
52
+
53
+ - name: Compare hashes
54
+ if: ${{ hashFiles( env.testjson ) != hashFiles('geff-schema.json') }}
55
+ uses: actions/github-script@v3
56
+ with:
57
+ script: |
58
+ core.setFailed('geff-schema.json needs to be updated by locally running `pixi run update-json`. ${{ hashFiles( env.testjson ) }} vs ${{ hashFiles('geff-schema.json') }}')
59
+
60
+ benchmark:
61
+ name: Benchmark
62
+ runs-on: ubuntu-latest
63
+ if: github.event_name == 'pull_request'
64
+
65
+ steps:
66
+ - uses: actions/checkout@v4
67
+ with:
68
+ fetch-depth: 50 # this is to make sure we obtain the target base commit
69
+
70
+ - name: Set up Python
71
+ uses: actions/setup-python@v5
72
+ with:
73
+ python-version: 3.13
74
+ cache-dependency-path: "pyproject.toml"
75
+ cache: "pip"
76
+
77
+ - name: Install dependencies
78
+ run: |
79
+ python -m pip install -U pip
80
+ python -m pip install -e .[bench,dev]
81
+
82
+ - name: Retrieve cached baseline if available
83
+ uses: actions/cache/restore@v4
84
+ id: cache_baseline
85
+ with:
86
+ path: baseline.json
87
+ key: ${{ github.event.pull_request.base.sha }}
88
+
89
+ - name: Run baseline benchmark if not in cache
90
+ if: steps.cache_baseline.outputs.cache-hit != 'true'
91
+ run: |
92
+ git checkout ${{ github.event.pull_request.base.sha }}
93
+ pytest tests/bench.py -v --benchmark-json baseline.json
94
+
95
+ - name: Cache baseline results
96
+ uses: actions/cache/save@v4
97
+ if: steps.cache_baseline.outputs.cache-hit != 'true'
98
+ with:
99
+ path: baseline.json
100
+ key: ${{ github.event.pull_request.base.sha }}
101
+
102
+ - name: Run benchmark on PR head commit
103
+ run: |
104
+ git checkout ${{ github.event.pull_request.head.sha }}
105
+ pytest tests/bench.py -v --benchmark-json pr.json
106
+
107
+ - name: Generate report
108
+ run: python scripts/benchmark-pr.py baseline.json pr.json report.md
109
+
110
+ - name: Comment on commit with report for non-forks
111
+ uses: peter-evans/commit-comment@v3
112
+ if: github.event.pull_request.head.repo.fork == false
113
+ with:
114
+ body-path: report.md
115
+ token: ${{ secrets.COMMIT_MESSAGE_TOKEN }}
116
+
117
+ deploy:
118
+ name: Deploy
119
+ # TODO: Add back successful test requirement once package is more stable
120
+ # needs: test
121
+ # if: success() && startsWith(github.ref, 'refs/tags/') && github.event_name != 'schedule'
122
+ if: startsWith(github.ref, 'refs/tags/') && github.event_name != 'schedule'
123
+ runs-on: ubuntu-latest
124
+
125
+ permissions:
126
+ id-token: write
127
+ contents: write
128
+
129
+ steps:
130
+ - uses: actions/checkout@v4
131
+ with:
132
+ fetch-depth: 0
133
+
134
+ - name: 🐍 Set up Python
135
+ uses: actions/setup-python@v5
136
+ with:
137
+ python-version: "3.x"
138
+
139
+ - name: Install geff
140
+ run: pip install .
141
+
142
+ - run: echo ${{ github.ref_name }}
143
+
144
+ - name: Check supported_versions.yml
145
+ shell: python
146
+ run: |
147
+ import re; from geff.metadata_schema import SUPPORTED_VERSIONS_REGEX
148
+
149
+ if re.search(SUPPORTED_VERSIONS_REGEX, "${{ github.ref_name }}") is None:
150
+ sys.exit("`supported_versions.yml` needs to be updated to include new release ${{ github.ref_name }}")
151
+
152
+ - name: 👷 Build
153
+ run: |
154
+ python -m pip install build
155
+ python -m build
156
+
157
+ - name: 🚢 Publish to PyPI
158
+ uses: pypa/gh-action-pypi-publish@release/v1
159
+ with:
160
+ password: ${{ secrets.PYPI_API_TOKEN }}
161
+
162
+ - uses: softprops/action-gh-release@v2
163
+ with:
164
+ generate_release_notes: true
165
+ files: "./dist/*"
@@ -0,0 +1,86 @@
1
+ name: Push gh-pages updates update
2
+
3
+ # push to the gh-pages branch whenever there is a new push to main or a new tag
4
+ on:
5
+ push:
6
+ branches:
7
+ - main
8
+ tags: ["*"]
9
+ workflow_dispatch:
10
+
11
+ permissions:
12
+ contents: write
13
+ deployments: write
14
+
15
+ env:
16
+ TAG: ${{ contains(github.ref, 'tags') && 'latest' || 'dev'}}
17
+ SETUPTOOLS_SCM_PRETEND_VERSION: "0.0"
18
+
19
+ jobs:
20
+ push-docs:
21
+ runs-on: ubuntu-latest
22
+ concurrency:
23
+ group: gh-pages
24
+ steps:
25
+ - name: Checkout repository
26
+ uses: actions/checkout@v3
27
+
28
+ - name: Set up Python
29
+ uses: actions/setup-python@v3
30
+ with:
31
+ python-version: "3.12"
32
+
33
+ - name: Install dependencies
34
+ run: |
35
+ pip install -e ".[docs]"
36
+
37
+ - id: get_version
38
+ uses: battila7/get-version-action@v2
39
+
40
+ - name: configure git
41
+ run: |
42
+ git config --local user.email "github-actions[bot]@users.noreply.github.com"
43
+ git config --local user.name "github-actions[bot]"
44
+ git fetch origin gh-pages --depth=1
45
+
46
+ - name: Update schema docs
47
+ run: |
48
+ mkdir docs/schema
49
+ generate-schema-doc geff-schema.json docs/schema/schema.html
50
+
51
+ - name: Push changes to gh-pages branch
52
+ run: mike deploy ${{ steps.get_version.outputs.version }} $TAG -u -p
53
+
54
+ benchmark:
55
+ name: Report benchmarks on gh-pages
56
+ runs-on: ubuntu-latest
57
+ concurrency:
58
+ group: gh-pages
59
+ steps:
60
+ - uses: actions/checkout@v4
61
+
62
+ - name: Set up Python
63
+ uses: actions/setup-python@v5
64
+ with:
65
+ python-version: 3.12
66
+ cache-dependency-path: "pyproject.toml"
67
+ cache: "pip"
68
+
69
+ - name: Install dependencies
70
+ run: |
71
+ python -m pip install -U pip
72
+ python -m pip install -e .[dev,bench]
73
+
74
+ - name: Run benchmark
75
+ run: |
76
+ pytest tests/bench.py --benchmark-json output.json
77
+
78
+ - name: Store benchmark results
79
+ uses: benchmark-action/github-action-benchmark@v1
80
+ with:
81
+ name: Python Benchmark with pytest-benchmark
82
+ tool: 'pytest'
83
+ output-file-path: output.json
84
+ github-token: ${{ secrets.GITHUB_TOKEN }}
85
+ auto-push: true
86
+ benchmark-data-dir-path: bench/
@@ -0,0 +1,83 @@
1
+ on:
2
+ workflow_call:
3
+ inputs:
4
+ test-target:
5
+ required: true
6
+ type: string
7
+ witty-cache:
8
+ required: true
9
+ type: boolean
10
+ install-extras:
11
+ required: true
12
+ type: string
13
+
14
+ env:
15
+ SETUPTOOLS_SCM_PRETEND_VERSION: "0.0"
16
+
17
+ jobs:
18
+ test:
19
+ name: ${{ matrix.platform }} ${{ matrix.python-version }} zarr ${{ matrix.zarr-version }}
20
+ runs-on: ${{ matrix.platform }}
21
+ strategy:
22
+ fail-fast: false
23
+ matrix:
24
+ python-version: ["3.10", "3.11", "3.12"]
25
+ platform: [ubuntu-latest, macos-latest, windows-latest]
26
+ zarr-version: ["2.*", "3.*"]
27
+ exclude:
28
+ # Zarr 3 doesn't support 3.10
29
+ - zarr-version: "3.*"
30
+ python-version: "3.10"
31
+
32
+ steps:
33
+ - uses: actions/checkout@v4
34
+
35
+ - name: Set up Python ${{ matrix.python-version }}
36
+ uses: actions/setup-python@v5
37
+ with:
38
+ python-version: ${{ matrix.python-version }}
39
+ cache-dependency-path: "pyproject.toml"
40
+ cache: "pip"
41
+
42
+ - name: Install dependencies
43
+ run: |
44
+ python -m pip install -U pip
45
+ python -m pip install zarr==${{ matrix.zarr-version }}
46
+ python -m pip install -e .[${{ inputs.install-extras }}]
47
+ python -m pip list
48
+
49
+ - name: Get witty cache directory
50
+ if: ${{ inputs.witty-cache }}
51
+ id: cache-path
52
+ run: |
53
+ import Cython.Utils
54
+ import os
55
+
56
+ cache_path = os.path.join(Cython.Utils.get_cython_cache_dir(), "witty")
57
+
58
+ with open(os.environ["GITHUB_OUTPUT"], "a") as fh:
59
+ print(f"path={cache_path}", file=fh)
60
+ shell: python
61
+
62
+ - name: Check for witty cache
63
+ uses: actions/cache/restore@v4
64
+ id: witty-cache
65
+ if: ${{ inputs.witty-cache }}
66
+ with:
67
+ path: ${{ steps.cache-path.outputs.path }}
68
+ key: ${{ matrix.platform }} - ${{ matrix.python-version }}
69
+
70
+ - name: Test
71
+ run: pytest --color=yes --cov --cov-report=xml --cov-report=term-missing ${{ inputs.test-target }}
72
+
73
+ - name: Coverage
74
+ uses: codecov/codecov-action@v5
75
+ with:
76
+ token: ${{ secrets.CODECOV_TOKEN }}
77
+
78
+ - name: Save witty cache
79
+ uses: actions/cache/save@v4
80
+ if: ${{ inputs.witty-cache }}
81
+ with:
82
+ path: ${{ steps.cache-path.outputs.path }}
83
+ key: ${{ matrix.platform }} - ${{ matrix.python-version }}
geff-0.0/.gitignore ADDED
@@ -0,0 +1,27 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Temporary files
10
+ *.sw[pmno]
11
+ *.zarr
12
+
13
+ # Virtual environments
14
+ .venv
15
+
16
+ # pixi environments
17
+ .pixi
18
+ *.egg-info
19
+ pixi.lock
20
+
21
+ .DS_Store
22
+
23
+ # Autogenerated docs files
24
+ docs/schema/*
25
+
26
+ # Pytest coverage local files
27
+ .coverage*
@@ -0,0 +1,32 @@
1
+ ci:
2
+ autoupdate_schedule: monthly
3
+ autofix_commit_msg: "style(pre-commit.ci): auto fixes [...]"
4
+ autoupdate_commit_msg: "ci(pre-commit.ci): autoupdate"
5
+ autofix_prs: false
6
+
7
+ repos:
8
+ - repo: https://github.com/crate-ci/typos
9
+ rev: v1
10
+ hooks:
11
+ - id: typos
12
+
13
+ - repo: https://github.com/astral-sh/ruff-pre-commit
14
+ rev: v0.11.4
15
+ hooks:
16
+ - id: ruff
17
+ args: [--fix]
18
+ - id: ruff-format
19
+
20
+ - repo: https://github.com/abravalheri/validate-pyproject
21
+ rev: v0.24.1
22
+ hooks:
23
+ - id: validate-pyproject
24
+
25
+ - repo: https://github.com/pre-commit/mirrors-mypy
26
+ rev: v1.15.0
27
+ hooks:
28
+ - id: mypy
29
+ files: "^src/"
30
+ # # you have to add the things you want to type check against here
31
+ additional_dependencies:
32
+ - types-pyyaml
geff-0.0/CONTRIBUTING ADDED
@@ -0,0 +1,41 @@
1
+ # CONTRIBUTING
2
+
3
+ We recommend using `pixi` for environment management while developing `geff`. See the [pixi docs](https://pixi.sh/dev/) for installation instructions. The following instructions will be focused on pixi-based development, but all of the same tasks can be completed with pip and another environment manager.
4
+
5
+ For local development, clone the repo and install in editable mode.
6
+ ```
7
+ git clone https://github.com/funkelab/geff.git
8
+ pixi install
9
+ ```
10
+
11
+ ## Testing
12
+ To run tests
13
+ ```
14
+ pixi run test
15
+ ```
16
+
17
+ ## Style
18
+ We utilize `pre-commit` with ruff for linting and formatting. If you would like to run `pre-commit` locally:
19
+ ```
20
+ pixi run -e dev pre-commit install
21
+ ```
22
+ Alternatively [pre-commit.ci](https://pre-commit.ci/), will run and commit changes on any open PRs.
23
+
24
+ ## Releases
25
+ In order to deploy a new version, tag the commit with a version number and push it to github. This will trigger a github action that will build and deploy to PyPI. (see the "deploy" step in workflows/ci.yml). The version number is determined automatically based on the tag.
26
+
27
+ Prior to making a release that bumps the major or minor number, the `supported_versions.yml` needs to be updated and the json schema updated by running `pixi run update-json`.
28
+
29
+ ```
30
+ git tag -a v0.1.0 -m v0.1.0
31
+ git push --follow-tags
32
+ ```
33
+
34
+ ## Docs
35
+
36
+ Docs are written with MkDocs
37
+
38
+ Relevant commends can be run using `pixi run -e docs <mkdocs command>`
39
+ * `mkdocs serve` - Start the live-reloading docs server.
40
+ * `mkdocs build` - Build the documentation site.
41
+ * `mkdocs -h` - Print help message and exit.
geff-0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Funke lab
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
geff-0.0/PKG-INFO ADDED
@@ -0,0 +1,64 @@
1
+ Metadata-Version: 2.4
2
+ Name: geff
3
+ Version: 0.0
4
+ Summary: Reference implementation of the Graph Exchange File Format
5
+ License: MIT License
6
+ Project-URL: repository, https://github.com/live-image-tracking-tools/geff
7
+ Project-URL: homepage, https://github.com/live-image-tracking-tools/geff
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Natural Language :: English
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: zarr<4,>2
19
+ Requires-Dist: pydantic
20
+ Requires-Dist: pyyaml
21
+ Requires-Dist: numcodecs<0.16
22
+ Requires-Dist: networkx
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest>=8.3.4; extra == "dev"
25
+ Requires-Dist: pytest-cov; extra == "dev"
26
+ Requires-Dist: mypy; extra == "dev"
27
+ Requires-Dist: pre-commit; extra == "dev"
28
+ Requires-Dist: ruff; extra == "dev"
29
+ Requires-Dist: ipython; extra == "dev"
30
+ Requires-Dist: types-PyYAML; extra == "dev"
31
+ Provides-Extra: docs
32
+ Requires-Dist: mkdocs-material; extra == "docs"
33
+ Requires-Dist: mkdocstrings[python]; extra == "docs"
34
+ Requires-Dist: mkdocs-api-autonav; extra == "docs"
35
+ Requires-Dist: mkdocs-include-markdown-plugin; extra == "docs"
36
+ Requires-Dist: mike; extra == "docs"
37
+ Requires-Dist: json-schema-for-humans; extra == "docs"
38
+ Requires-Dist: typing-extensions; extra == "docs"
39
+ Provides-Extra: bench
40
+ Requires-Dist: pytest-benchmark; extra == "bench"
41
+ Requires-Dist: tabulate; extra == "bench"
42
+ Requires-Dist: pandas; extra == "bench"
43
+ Requires-Dist: typer; extra == "bench"
44
+ Dynamic: license-file
45
+
46
+ # Graph Exchange File Format (geff)
47
+
48
+ <!--intro-start-->
49
+
50
+ [![License](https://img.shields.io/pypi/l/geff.svg?color=green)](https://github.com/funkelab/geff/raw/main/LICENSE)
51
+ [![PyPI](https://img.shields.io/pypi/v/geff.svg?color=green)](https://pypi.org/project/geff)
52
+ [![Python Version](https://img.shields.io/pypi/pyversions/geff.svg?color=green)](https://python.org)
53
+ [![Test geff](https://github.com/funkelab/geff/actions/workflows/ci.yaml/badge.svg)](https://github.com/funkelab/geff/actions/workflows/ci.yaml)
54
+
55
+ geff is a specification for a file format for **exchanging** spatial graph data. It is not intended to be mutable, editable, chunked, or optimized for use in an application setting.
56
+
57
+ geff is the specification of the file format, but the library also includes implementations for writing from and reading to a networkx graph, a common Python in-memory graph data structure. The library uses semantic versioning, where changes to the specification bump the major or minor versions, and bugfixes for the example implementation bumps the patch version.
58
+
59
+ ## Installation
60
+
61
+ ```
62
+ pip install geff
63
+ ```
64
+ <!--intro-end-->
geff-0.0/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # Graph Exchange File Format (geff)
2
+
3
+ <!--intro-start-->
4
+
5
+ [![License](https://img.shields.io/pypi/l/geff.svg?color=green)](https://github.com/funkelab/geff/raw/main/LICENSE)
6
+ [![PyPI](https://img.shields.io/pypi/v/geff.svg?color=green)](https://pypi.org/project/geff)
7
+ [![Python Version](https://img.shields.io/pypi/pyversions/geff.svg?color=green)](https://python.org)
8
+ [![Test geff](https://github.com/funkelab/geff/actions/workflows/ci.yaml/badge.svg)](https://github.com/funkelab/geff/actions/workflows/ci.yaml)
9
+
10
+ geff is a specification for a file format for **exchanging** spatial graph data. It is not intended to be mutable, editable, chunked, or optimized for use in an application setting.
11
+
12
+ geff is the specification of the file format, but the library also includes implementations for writing from and reading to a networkx graph, a common Python in-memory graph data structure. The library uses semantic versioning, where changes to the specification bump the major or minor versions, and bugfixes for the example implementation bumps the patch version.
13
+
14
+ ## Installation
15
+
16
+ ```
17
+ pip install geff
18
+ ```
19
+ <!--intro-end-->
geff-0.0/docs/index.md ADDED
@@ -0,0 +1,7 @@
1
+ # Welcome to geff!
2
+
3
+ {%
4
+ include-markdown "../README.md"
5
+ start="<!--intro-start-->"
6
+ end="<!--intro-end-->"
7
+ %}
@@ -0,0 +1,74 @@
1
+ # Geff specification
2
+
3
+ The graph exchange file format is `zarr` based. A graph is stored in a zarr group, which can have any name. This allows storing multiple `geff` graphs inside the same zarr root directory. A `geff` group is identified by the presence of a `geff_version` attribute in the `.zattrs`. Other `geff` metadata is also stored in the `.zattrs` file of the `geff` group. The `geff` group must contain a `nodes` group and an `edges` group.
4
+
5
+ ## Geff metadata
6
+
7
+ {%
8
+ include "schema/schema.html"
9
+ %}
10
+
11
+ ## The `nodes` group
12
+ The nodes group will contain an `ids` array and an `attrs` group.
13
+
14
+ ### The `ids` array
15
+ The `nodes\ids` array is a 1D array of node IDs of length `N` > 0, where `N` is the number of nodes in the graph. Node ids must be unique. Node IDs can have any type supported by zarr, but we recommend integer dtypes. For large graphs, `uint64` might be necessary to provide enough range for every node to have a unique ID.
16
+
17
+ ### The `attrs` group and `node attribute` groups
18
+ The `nodes\attrs` group will contain one or more `node attribute` groups, each with a `values` array and an optional `missing` array.
19
+
20
+ - `values` arrays can be any zarr supported dtype, and can be N-dimensional. The first dimension of the `values` array must have the same length as the node `ids` array, such that each row of the attribute `values` array stores the attribute for the node at that index in the ids array.
21
+ - The `missing` array is an optional, a one dimensional boolean array to support attributes that are not present on all nodes. A 1 at an index in the `missing` array indicates that the `value` of that attribute for the node at that index is None, and the value in the `values` array at that index should be ignored. If the `missing` array is not present, that means that all nodes have values for the attribute.
22
+
23
+ !!! note
24
+ When writing a graph with missing attributes to the geff format, you must fill in a dummy value in the `values` array for the nodes that are missing the attribute, in order to keep the indices aligned with the node ids.
25
+
26
+ - The `position` group is a special node attribute group that must be present and does not allow missing attributes.
27
+ - The `seg_id` group is an optional, special node attribute group that stores the segmenatation label for each node. The `seg_id` values do not need to be unique, in case labels are repeated between time points. If the `seg_id` group is not present, it is assumed that the graph is not associated with a segmentation.
28
+ <!-- Perhaps we just let the user specify the seg id attribute in the metadata instead? Then you can point it to the node ids if you wanted to -->
29
+
30
+ ## The `edges` group
31
+ Similar to the `nodes` group, the `edges` group will contain an `ids` array and an `attrs` group. If there are no edges in the graph, the edge group is not created.
32
+
33
+ ### The `ids` array
34
+ The `edges\ids` array is a 2D array with the same dtype as the `nodes\ids` array. It has shape `(2, E)`, where `E` is the number of edges in the graph. All elements in the `edges\ids` array must also be present in the `nodes\ids` array.
35
+ Each row represents an edge between two nodes. For directed graphs, the first column is the source nodes and the second column holds the target nodes. For undirected graphs, the order is arbitrary.
36
+ Edges should be unique (no multiple edges between the same two nodes) and edges from a node to itself are not supported.
37
+
38
+ ### The `attrs` group and `edge attribute` groups
39
+ The `edges\attrs` group will contain zero or more `edge attribute` groups, each with a `values` array and an optional `missing` array.
40
+
41
+ - `values` arrays can be any zarr supported dtype, and can be N-dimensional. The first dimension of the `values` array must have the same length as the `edges\ids` array, such that each row of the attribute `values` array stores the attribute for the edge at that index in the ids array.
42
+ - The `missing` array is an optional, a one dimensional boolean array to support attributes that are not present on all edges. A 1 at an index in the `missing` array indicates that the `value` of that attribute for the edge at that index is missing, and the value in the `values` array at that index should be ignored. If the `missing` array is not present, that means that all edges have values for the attribute.
43
+
44
+ If you do not have any edge attributes, the `edges\attrs` group should still be present, but empty.
45
+
46
+ ## Example file structure and metadata
47
+
48
+ TODO: Example metadata for this file structure
49
+ ``` python
50
+ /path/to.zarr
51
+ /tracking_graph
52
+ .zattrs # graph metadata with `geff_version`
53
+ nodes/
54
+ ids # shape: (N,) dtype: uint64
55
+ attrs/
56
+ position/
57
+ values # shape: (N, 3) dtype: float16
58
+ color/
59
+ values # shape: (N, 4) dtype: float16
60
+ missing # shape: (N,) dtype: bool
61
+ edges/
62
+ ids # shape: (E, 2) dtype: uint64
63
+ attrs/
64
+ distance/
65
+ values # shape: (E,) dtype: float16
66
+ score/
67
+ values # shape: (E,) dtype: float16
68
+ missing # shape: (E,) dtype: bool
69
+ # optional:
70
+ /segmentation
71
+
72
+ # unspecified, but totally okay:
73
+ /raw
74
+ ```
@@ -0,0 +1,12 @@
1
+ # What is geff?
2
+
3
+ `geff` is a graph exchange file format that seeks to fulfill the following needs:
4
+ - Provide a storage/exchange format for graphs and optional segmentation
5
+ - Provide a common API with reference implementations for use in other projects
6
+
7
+ ## Design Decisions and Assumptions
8
+
9
+ - Raw image data is not included in the `geff` spec. However, to keep nodes linked to segmentation labels, support for specifying the seg_id of each node in a standard way, along with the path to the segmentation, are included in the `spec`.
10
+ - Since `geff` is an exchange format, we do not provide support for searching or filtering.
11
+ - We do not provide support for editing or changing the graph on the fly.
12
+ - In order to support efficient reading/writing, we assume the graph can fit into memory.
@@ -0,0 +1 @@
1
+ {"description": "Geff metadata schema to validate the attributes json file in a geff zarr", "properties": {"geff_version": {"pattern": "(0\\.0)|(0\\.1)", "title": "Geff Version", "type": "string"}, "directed": {"title": "Directed", "type": "boolean"}, "roi_min": {"items": {"type": "number"}, "title": "Roi Min", "type": "array"}, "roi_max": {"items": {"type": "number"}, "title": "Roi Max", "type": "array"}, "position_attr": {"default": "position", "title": "Position Attr", "type": "string"}, "axis_names": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "default": null, "title": "Axis Names"}, "axis_units": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "default": null, "title": "Axis Units"}}, "required": ["geff_version", "directed", "roi_min", "roi_max"], "title": "geff_metadata", "type": "object"}