[ENH]: HDF5 please #147

Merged
synchon merged 122 commits from feature/hdf5-storage into main 2023-03-14 21:43:33 +00:00
synchon commented 2023-01-27 17:36:05 +00:00 (Migrated from github.com)

Are you requiring a new dataset or marker?

  • I understand this is not a marker or dataset request

Which feature do you want to include?

pretty please!

How do you imagine this integrated in junifer?

as storage

Do you have a sample code that implements this outside of junifer?

No response

Anything else to say?

No response

### Are you requiring a new dataset or marker? - [X] I understand this is not a marker or dataset request ### Which feature do you want to include? pretty please! ### How do you imagine this integrated in junifer? as storage ### Do you have a sample code that implements this outside of junifer? _No response_ ### Anything else to say? _No response_
codecov[bot] commented 2023-01-27 17:37:50 +00:00 (Migrated from github.com)

Codecov Report

Merging #147 (bdbd677) into main (7acc0cd) will increase coverage by 0.29%.
The diff coverage is 96.44%.

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #147      +/-   ##
==========================================
+ Coverage   93.62%   93.91%   +0.29%     
==========================================
  Files          79       80       +1     
  Lines        2980     3205     +225     
  Branches      538      583      +45     
==========================================
+ Hits         2790     3010     +220     
- Misses        128      129       +1     
- Partials       62       66       +4     
Flag Coverage Δ
docs 100.00% <ø> (ø)
junifer 93.91% <96.44%> (+0.29%) ⬆️
mock ?

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
docs/conf.py 100.00% <ø> (ø)
junifer/storage/base.py 81.81% <ø> (ø)
junifer/storage/hdf5.py 95.92% <95.92%> (ø)
junifer/datagrabber/aomic/id1000.py 100.00% <100.00%> (ø)
junifer/datagrabber/aomic/piop2.py 100.00% <100.00%> (ø)
junifer/storage/__init__.py 100.00% <100.00%> (ø)
junifer/storage/sqlite.py 96.36% <100.00%> (+1.82%) ⬆️
junifer/storage/utils.py 100.00% <100.00%> (ø)

... and 1 file with indirect coverage changes

## [Codecov](https://codecov.io/gh/juaml/junifer/pull/147?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=juaml) Report > Merging [#147](https://codecov.io/gh/juaml/junifer/pull/147?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=juaml) (bdbd677) into [main](https://codecov.io/gh/juaml/junifer/commit/7acc0cd188d44bce4248504ce498e6d55256f945?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=juaml) (7acc0cd) will **increase** coverage by `0.29%`. > The diff coverage is `96.44%`. [![Impacted file tree graph](https://codecov.io/gh/juaml/junifer/pull/147/graphs/tree.svg?width=650&height=150&src=pr&token=5H21JuZXMw&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=juaml)](https://codecov.io/gh/juaml/junifer/pull/147?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=juaml) ```diff @@ Coverage Diff @@ ## main #147 +/- ## ========================================== + Coverage 93.62% 93.91% +0.29% ========================================== Files 79 80 +1 Lines 2980 3205 +225 Branches 538 583 +45 ========================================== + Hits 2790 3010 +220 - Misses 128 129 +1 - Partials 62 66 +4 ``` | Flag | Coverage Δ | | |---|---|---| | docs | `100.00% <ø> (ø)` | | | junifer | `93.91% <96.44%> (+0.29%)` | :arrow_up: | | mock | `?` | | Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=juaml#carryforward-flags-in-the-pull-request-comment) to find out more. | [Impacted Files](https://codecov.io/gh/juaml/junifer/pull/147?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=juaml) | Coverage Δ | | |---|---|---| | [docs/conf.py](https://codecov.io/gh/juaml/junifer/pull/147?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=juaml#diff-ZG9jcy9jb25mLnB5) | `100.00% <ø> (ø)` | | | [junifer/storage/base.py](https://codecov.io/gh/juaml/junifer/pull/147?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=juaml#diff-anVuaWZlci9zdG9yYWdlL2Jhc2UucHk=) | `81.81% <ø> (ø)` | | | [junifer/storage/hdf5.py](https://codecov.io/gh/juaml/junifer/pull/147?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=juaml#diff-anVuaWZlci9zdG9yYWdlL2hkZjUucHk=) | `95.92% <95.92%> (ø)` | | | [junifer/datagrabber/aomic/id1000.py](https://codecov.io/gh/juaml/junifer/pull/147?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=juaml#diff-anVuaWZlci9kYXRhZ3JhYmJlci9hb21pYy9pZDEwMDAucHk=) | `100.00% <100.00%> (ø)` | | | [junifer/datagrabber/aomic/piop2.py](https://codecov.io/gh/juaml/junifer/pull/147?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=juaml#diff-anVuaWZlci9kYXRhZ3JhYmJlci9hb21pYy9waW9wMi5weQ==) | `100.00% <100.00%> (ø)` | | | [junifer/storage/\_\_init\_\_.py](https://codecov.io/gh/juaml/junifer/pull/147?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=juaml#diff-anVuaWZlci9zdG9yYWdlL19faW5pdF9fLnB5) | `100.00% <100.00%> (ø)` | | | [junifer/storage/sqlite.py](https://codecov.io/gh/juaml/junifer/pull/147?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=juaml#diff-anVuaWZlci9zdG9yYWdlL3NxbGl0ZS5weQ==) | `96.36% <100.00%> (+1.82%)` | :arrow_up: | | [junifer/storage/utils.py](https://codecov.io/gh/juaml/junifer/pull/147?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=juaml#diff-anVuaWZlci9zdG9yYWdlL3V0aWxzLnB5) | `100.00% <100.00%> (ø)` | | ... and [1 file with indirect coverage changes](https://codecov.io/gh/juaml/junifer/pull/147/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=juaml)
github-actions[bot] commented 2023-01-30 12:23:21 +00:00 (Migrated from github.com)
PR Preview Action v1.3.0
Preview removed because the pull request was closed.
2023-03-14 21:47 UTC
[PR Preview Action](https://github.com/rossjrw/pr-preview-action) v1.3.0 :---: Preview removed because the pull request was closed. 2023-03-14 21:47 UTC <!-- Sticky Pull Request Commentpr-preview -->
fraimondo (Migrated from github.com) requested changes 2023-03-03 14:21:43 +00:00
@ -0,0 +1,921 @@
"""Provide concrete implementation for feature storage via HDF5."""
fraimondo (Migrated from github.com) commented 2023-01-31 08:32:30 +00:00

If there are no kwargs, avoid having it here. I am thinking of users reading documentation.

If there are no `kwargs`, avoid having it here. I am thinking of users reading documentation.
fraimondo (Migrated from github.com) commented 2023-01-31 08:34:59 +00:00

This should not be like this. If single_output is False, then it requires an element. The _collected_ attribute should not exist.

This should not be like this. If `single_output` is `False`, then it requires an element. The `_collected_` attribute should not exist.
fraimondo (Migrated from github.com) commented 2023-01-31 08:44:47 +00:00

How is this thing collecting? How is the data in the HDF5 appended?

How is this thing collecting? How is the data in the HDF5 appended?
fraimondo (Migrated from github.com) commented 2023-03-03 14:08:00 +00:00

This still needs to be addressed.

This still needs to be addressed.
fraimondo (Migrated from github.com) commented 2023-03-03 14:09:42 +00:00

no need for else clause.

no need for `else` clause.
fraimondo (Migrated from github.com) commented 2023-03-03 14:11:38 +00:00

from the above conditions, if feature_md5 is not none, then feature_name is none. Can you simplify the if conditions here?

Indeed the only condition you need to check is to get the feature_md5 from the name.

from the above conditions, if `feature_md5` is not none, then `feature_name` is none. Can you simplify the if conditions here? Indeed the only condition you need to check is to get the feature_md5 from the name.
fraimondo (Migrated from github.com) commented 2023-03-03 14:14:09 +00:00

The matrix in the DF format should be flattened. Indeed check what we do with matrices before storing in SQLite. This is what needs to happen now, here.

The matrix in the DF format should be flattened. Indeed check what we do with matrices before storing in SQLite. This is what needs to happen now, here.
fraimondo (Migrated from github.com) commented 2023-03-03 14:19:21 +00:00

you can avoid these two if clauses with one list and then convert to tuple.

you can avoid these two if clauses with one list and then convert to tuple.
@ -0,0 +243,4 @@
msg=f"`{md5}` not found in: {uri}",
klass=IOError,
)
else:
fraimondo (Migrated from github.com) commented 2023-01-31 08:36:49 +00:00

Why an else statement? Are you expecting other exception that are not IOerror? What do we do with them?

Why an `else` statement? Are you expecting other exception that are not `IOerror`? What do we do with them?
fraimondo (Migrated from github.com) commented 2023-03-03 14:09:06 +00:00

still needs to be addressed.

still needs to be addressed.
@ -0,0 +623,4 @@
logger.info(f"Wrote HDF5 data for {meta_md5} to: {uri}")
def store_matrix(
fraimondo (Migrated from github.com) commented 2023-01-31 08:40:53 +00:00

Here's where we disagree. You are doing the same as the SQLiteFeatureStorage. This is because we can't store a 2D matrix in SQLite, so we convert to row. The benefit of HDF5 is that we can store the 2D directly.

In this function, just check for dimensions and then store the variables directly, as they are.

Here's where we disagree. You are doing the same as the `SQLiteFeatureStorage`. This is because we can't store a 2D matrix in SQLite, so we convert to row. The benefit of HDF5 is that we can store the 2D directly. In this function, just check for dimensions and then store the variables directly, as they are.
@ -0,0 +828,4 @@
# Update metadata
out_metadata.update(in_metadata)
# Save metadata
out_storage._write_processed_data(
fraimondo (Migrated from github.com) commented 2023-03-03 14:17:40 +00:00

Why writing here? For each on of the 40k files, for each feature, you are triggering a write.

This should be done on the second step of the collect.

Why writing here? For each on of the 40k files, for each feature, you are triggering a write. This should be done on the second step of the collect.
@ -0,0 +1,948 @@
"""Provide tests for HDF5 storage interface."""
fraimondo (Migrated from github.com) commented 2023-03-03 14:21:09 +00:00

See my comment on store_matrix. We should have one row per sample. In junifer terms, one row per element.

See my comment on `store_matrix`. We should have one row per sample. In junifer terms, one row per element.
synchon (Migrated from github.com) reviewed 2023-03-03 14:28:26 +00:00
@ -0,0 +1,921 @@
"""Provide concrete implementation for feature storage via HDF5."""
synchon (Migrated from github.com) commented 2023-03-03 14:28:25 +00:00

I think this comment is from an earlier review, resolving it.

I think this comment is from an earlier review, resolving it.
synchon (Migrated from github.com) reviewed 2023-03-03 14:29:45 +00:00
@ -0,0 +243,4 @@
msg=f"`{md5}` not found in: {uri}",
klass=IOError,
)
else:
synchon (Migrated from github.com) commented 2023-03-03 14:29:45 +00:00

Looks like a comment from an earlier review, resolving it.

Looks like a comment from an earlier review, resolving it.
synchon (Migrated from github.com) reviewed 2023-03-03 14:30:48 +00:00
@ -0,0 +623,4 @@
logger.info(f"Wrote HDF5 data for {meta_md5} to: {uri}")
def store_matrix(
synchon (Migrated from github.com) commented 2023-03-03 14:30:48 +00:00

From an earlier review maybe, resolving it.

From an earlier review maybe, resolving it.
synchon (Migrated from github.com) reviewed 2023-03-03 14:31:58 +00:00
@ -0,0 +1,921 @@
"""Provide concrete implementation for feature storage via HDF5."""
synchon (Migrated from github.com) commented 2023-03-03 14:31:58 +00:00

Looks like an old review, resolving it.

Looks like an old review, resolving it.
synchon (Migrated from github.com) reviewed 2023-03-03 14:35:26 +00:00
@ -0,0 +1,921 @@
"""Provide concrete implementation for feature storage via HDF5."""
synchon (Migrated from github.com) commented 2023-03-03 14:35:25 +00:00

Addressed in 1d220eff.

Addressed in `1d220eff`.
synchon (Migrated from github.com) reviewed 2023-03-03 14:37:56 +00:00
@ -0,0 +1,921 @@
"""Provide concrete implementation for feature storage via HDF5."""
synchon (Migrated from github.com) commented 2023-03-03 14:37:56 +00:00

Sorry I don't understand the reason. I am making it as explicit as possible.

Sorry I don't understand the reason. I am making it as explicit as possible.
synchon (Migrated from github.com) reviewed 2023-03-03 14:39:25 +00:00
@ -0,0 +1,921 @@
"""Provide concrete implementation for feature storage via HDF5."""
synchon (Migrated from github.com) commented 2023-03-03 14:39:24 +00:00

Maybe I'm not getting the rationale here, what do you mean?

Maybe I'm not getting the rationale here, what do you mean?
synchon (Migrated from github.com) reviewed 2023-03-03 14:40:19 +00:00
@ -0,0 +828,4 @@
# Update metadata
out_metadata.update(in_metadata)
# Save metadata
out_storage._write_processed_data(
synchon (Migrated from github.com) commented 2023-03-03 14:40:19 +00:00

Looks like an old review, resolving.

Looks like an old review, resolving.
synchon (Migrated from github.com) reviewed 2023-03-03 14:42:10 +00:00
@ -0,0 +1,921 @@
"""Provide concrete implementation for feature storage via HDF5."""
synchon (Migrated from github.com) commented 2023-03-03 14:42:10 +00:00

I know there's a bit of redundancy but kept it for explicit code. What do you mean exactly?

I know there's a bit of redundancy but kept it for explicit code. What do you mean exactly?
synchon (Migrated from github.com) reviewed 2023-03-03 14:48:03 +00:00
@ -0,0 +1,921 @@
"""Provide concrete implementation for feature storage via HDF5."""
synchon (Migrated from github.com) commented 2023-03-03 14:48:03 +00:00

We have **kwargs in the constructor signature for smooth subclassing and just makes it transparent to the reader that it has it. I don't have a preference here, but I have always found it in the wild and is natural to me at this point. If you have a strong reason, I don't mind.

We have `**kwargs` in the constructor signature for smooth subclassing and just makes it transparent to the reader that it has it. I don't have a preference here, but I have always found it in the wild and is natural to me at this point. If you have a strong reason, I don't mind.
fraimondo (Migrated from github.com) reviewed 2023-03-08 08:21:40 +00:00
@ -0,0 +1,921 @@
"""Provide concrete implementation for feature storage via HDF5."""
fraimondo (Migrated from github.com) commented 2023-03-08 08:21:39 +00:00

If new parameters arise, then we can change the classes. We are mainly focused on user-oriented documentation, so I want to avoid having things that we don't document / are difficult to follow from the end-user point of view.

If new parameters arise, then we can change the classes. We are mainly focused on user-oriented documentation, so I want to avoid having things that we don't document / are difficult to follow from the end-user point of view.
fraimondo (Migrated from github.com) reviewed 2023-03-08 08:22:38 +00:00
@ -0,0 +1,921 @@
"""Provide concrete implementation for feature storage via HDF5."""
fraimondo (Migrated from github.com) commented 2023-03-08 08:22:37 +00:00

I meant that before this point, you already checked that only one of feature_md5 or feature_name are not none. There's no need for the and not feature_name here.

I meant that before this point, you already checked that only one of `feature_md5` or `feature_name` are not none. There's no need for the `and not feature_name` here.
fraimondo (Migrated from github.com) reviewed 2023-03-08 08:23:24 +00:00
@ -0,0 +1,921 @@
"""Provide concrete implementation for feature storage via HDF5."""
fraimondo (Migrated from github.com) commented 2023-03-08 08:23:23 +00:00

When you convert a matrix to dataframe, it must generate one row per element. As a general rule, each DF must have only one row per element (except for timeseries).

When you convert a matrix to dataframe, it must generate one row per element. As a general rule, each DF must have only one row per element (except for timeseries).
synchon (Migrated from github.com) reviewed 2023-03-08 09:49:54 +00:00
@ -0,0 +1,921 @@
"""Provide concrete implementation for feature storage via HDF5."""
synchon (Migrated from github.com) commented 2023-03-08 09:49:53 +00:00

Do you mean to remove it from the signature as well as docstring or just the docstring?

Do you mean to remove it from the signature as well as docstring or just the docstring?
synchon (Migrated from github.com) reviewed 2023-03-08 09:54:16 +00:00
@ -0,0 +1,921 @@
"""Provide concrete implementation for feature storage via HDF5."""
synchon (Migrated from github.com) commented 2023-03-08 09:54:15 +00:00

Addressed in 25643d91.

Addressed in `25643d91`.
synchon (Migrated from github.com) reviewed 2023-03-08 14:10:51 +00:00
@ -0,0 +1,921 @@
"""Provide concrete implementation for feature storage via HDF5."""
synchon (Migrated from github.com) commented 2023-03-08 14:10:50 +00:00

Should be addressed in the latest commits.

Should be addressed in the latest commits.
synchon (Migrated from github.com) reviewed 2023-03-08 14:11:02 +00:00
@ -0,0 +1,948 @@
"""Provide tests for HDF5 storage interface."""
synchon (Migrated from github.com) commented 2023-03-08 14:11:02 +00:00

Addressed.

Addressed.
fraimondo (Migrated from github.com) requested changes 2023-03-13 08:23:04 +00:00
@ -0,0 +1,921 @@
"""Provide concrete implementation for feature storage via HDF5."""
fraimondo (Migrated from github.com) commented 2023-03-08 08:27:21 +00:00
array_shape = [features_data.shape[0]]
array_chunk_size = [features_data.shape[0]]
if features_data.ndim == 2:
    array_shape.append(features_data.shape[1])
    array_chunk_size.append(features_data.shape[1])
array_shape.aend(element_count)
array_chunk_size.append(chunk_size)

data_to_write["data"] = ChunkedArray(
    data=features_data,
    shape=tuple(array_shape),
    chunk_size=tuple(array_chunk_size),
    n_chunk=chunk_idx,
)
```python array_shape = [features_data.shape[0]] array_chunk_size = [features_data.shape[0]] if features_data.ndim == 2: array_shape.append(features_data.shape[1]) array_chunk_size.append(features_data.shape[1]) array_shape.aend(element_count) array_chunk_size.append(chunk_size) data_to_write["data"] = ChunkedArray( data=features_data, shape=tuple(array_shape), chunk_size=tuple(array_chunk_size), n_chunk=chunk_idx, ) ```
fraimondo (Migrated from github.com) commented 2023-03-08 10:05:02 +00:00

just the docstring will create issues with the linter no? I would go for both.

just the docstring will create issues with the linter no? I would go for both.
fraimondo (Migrated from github.com) commented 2023-03-13 08:22:51 +00:00

Check what we do with the SQLIteFeatureStorage. It's not that simple as creating the product, because we might only want to to store a triangular matrix.

Indeed, the code from here can be extracted to a function in storage.utils so an upper/lower/full matrix can be converted to row in the same way, in both places.

Check what we do with the SQLIteFeatureStorage. It's not that simple as creating the product, because we might only want to to store a triangular matrix. Indeed, the code from [here](https://github.com/juaml/junifer/blob/7acc0cd188d44bce4248504ce498e6d55256f945/junifer/storage/sqlite.py#L434) can be extracted to a function in storage.utils so an upper/lower/full matrix can be converted to row in the same way, in both places.
@ -0,0 +243,4 @@
msg=f"`{md5}` not found in: {uri}",
klass=IOError,
)
else:
fraimondo (Migrated from github.com) commented 2023-03-13 08:20:46 +00:00

nono, it is still unresolved.

Why do you have an else statement? Do you expect other kind of error?

nono, it is still unresolved. Why do you have an else statement? Do you expect other kind of error?
synchon (Migrated from github.com) reviewed 2023-03-13 09:09:45 +00:00
@ -0,0 +1,921 @@
"""Provide concrete implementation for feature storage via HDF5."""
synchon (Migrated from github.com) commented 2023-03-13 09:09:45 +00:00

Addressed in 18643864.

Addressed in `18643864`.
synchon (Migrated from github.com) reviewed 2023-03-13 09:19:48 +00:00
@ -0,0 +1,921 @@
"""Provide concrete implementation for feature storage via HDF5."""
synchon (Migrated from github.com) commented 2023-03-13 09:19:48 +00:00

The ndim would be 3 here but I get your point. Addressed in 07c769e4.

The `ndim` would be 3 here but I get your point. Addressed in `07c769e4`.
synchon (Migrated from github.com) reviewed 2023-03-13 09:21:54 +00:00
@ -0,0 +243,4 @@
msg=f"`{md5}` not found in: {uri}",
klass=IOError,
)
else:
synchon (Migrated from github.com) commented 2023-03-13 09:21:53 +00:00

else in a try...except...else is used for the condition where no exceptions are raised and the block passes.

`else` in a `try...except...else` is used for the condition where no exceptions are raised and the block passes.
synchon (Migrated from github.com) reviewed 2023-03-14 14:01:02 +00:00
@ -0,0 +1,921 @@
"""Provide concrete implementation for feature storage via HDF5."""
synchon (Migrated from github.com) commented 2023-03-14 14:01:01 +00:00

Addressed with latest commits.

Addressed with latest commits.
fraimondo (Migrated from github.com) approved these changes 2023-03-14 21:19:57 +00:00
@ -0,0 +1,921 @@
"""Provide concrete implementation for feature storage via HDF5."""
fraimondo (Migrated from github.com) commented 2023-03-14 17:29:11 +00:00

doesn't work directly sending hdf_data["element"] to the dataframe for the index?

doesn't work directly sending `hdf_data["element"]` to the dataframe for the index?
fraimondo (Migrated from github.com) commented 2023-03-14 17:33:39 +00:00

I just did this:

element_idx_dict = hdf_data["element"]

it works, no for loop.

I just did this: ```python element_idx_dict = hdf_data["element"] ``` it works, no for loop.
fraimondo (Migrated from github.com) commented 2023-03-14 17:34:08 +00:00

same here, no need for the for loop:

element_idx_dict = hdf_data["element"]
same here, no need for the for loop: ``` element_idx_dict = hdf_data["element"] ```
@ -0,0 +363,4 @@
diagonal=bool(hdf_data["diagonal"]),
)
# Convert data to proper 2D
reshaped_data = flat_data.T
fraimondo (Migrated from github.com) commented 2023-03-14 17:28:11 +00:00

What happens if we read_df of one single element?

What happens if we `read_df` of one single element?
synchon (Migrated from github.com) reviewed 2023-03-14 21:43:09 +00:00
@ -0,0 +363,4 @@
diagonal=bool(hdf_data["diagonal"]),
)
# Convert data to proper 2D
reshaped_data = flat_data.T
synchon (Migrated from github.com) commented 2023-03-14 21:43:09 +00:00

Works.

Works.
synchon (Migrated from github.com) reviewed 2023-03-14 21:43:19 +00:00
@ -0,0 +1,921 @@
"""Provide concrete implementation for feature storage via HDF5."""
synchon (Migrated from github.com) commented 2023-03-14 21:43:19 +00:00

Addressed.

Addressed.
synchon (Migrated from github.com) reviewed 2023-03-14 21:43:28 +00:00
@ -0,0 +1,921 @@
"""Provide concrete implementation for feature storage via HDF5."""
synchon (Migrated from github.com) commented 2023-03-14 21:43:27 +00:00

Addressed.

Addressed.
Sign in to join this conversation.
No reviewers
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
juaml/junifer!147
No description provided.