Skip to content

set add_anndata to have an inplace argument for consistent API - #291

Open
vbrennsteiner wants to merge 2 commits into
mainfrom
add_metadata_api
Open

vbrennsteiner wants to merge 2 commits into
mainfrom
add_metadata_api

Conversation

@vbrennsteiner

Copy link
Copy Markdown
Collaborator

Relating to #270: add inplace argument to add_metadata

Also clean up logic by removing null-ops and adapting unit tests with an additional inplace decorator to check the existing cases with and without inplace modification

@vbrennsteiner
vbrennsteiner requested review from lucas-diedrich and mschwoer and removed request for lucas-diedrich September 16, 2026 15:10
@vbrennsteiner vbrennsteiner self-assigned this Sep 16, 2026

@lucas-diedrich lucas-diedrich left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tackling #270!

Should we use private methods from anndata? I assume you validated that you hit all instances of add_metadata.

metadata = metadata[~metadata.index.duplicated(keep="first")]

return add_metadata(
add_metadata(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion, in place operations in a function are a little intransparent - explicitly pass inplace=False and overwrite anndata?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point!

import numpy as np

from alphapepttools._utils import get_matrix
from alphapepttools._utils import _resolve_axis, get_matrix

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, nice!


If axis is 0, assume metadata.index <-> data.index and add metadata as '.obs' of the AnnData object.
If axis is 1, assume metadata.index <-> data.columns and add metadata as '.var' of the AnnData object.
If axis is "obs" or 0, assume metadata.index <-> data.index and add metadata as '.obs' of the AnnData object.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<-> parses weird in the documentation. Replace with corresponds to?

adata
AnnData object to add metadata to
incoming_metadata
Metadata dataframe to add. The matching entity is always the INDEX, depending on axis it is

@lucas-diedrich lucas-diedrich Sep 16, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make INDEX lower case?

If True, print additional information about the operation
inplace
If True (default), modifies adata inplace, adding metadata to the existing .obs or .var, depending on `axis`.
Note that with `keep_data_shape=False` this may also shrink `adata` in place.

@lucas-diedrich lucas-diedrich Sep 16, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Emit warning if anndata is filtered?


### Emulate join on AnnData level without copying
# 1. Reindex the AnnData object for inner join
# 1. Subset the AnnData object for inner join; _inplace_subset_* is `adata[index, :]`, but inplace

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not understand this comment when reading from top to bottom

adata = adata[shared_fields, :] if axis == 0 else adata[:, shared_fields]
if not existing_fields.equals(shared_fields):
if axis == "obs":
adata._inplace_subset_obs(shared_fields) # noqa: SLF001

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should not use a private method of an external package. What do you think?

extra_obs_df = psm_df[[sample_id_column, *extra_obs_cols]].set_index(sample_id_column, drop=True)
extra_obs_df = extra_obs_df[~extra_obs_df.index.duplicated(keep="first")]
comparison_adata = add_metadata(comparison_adata, extra_obs_df, axis=0)
add_metadata(comparison_adata, extra_obs_df, axis=0)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

set inplace=False for internal stuff?

extra_var_df = psm_df[[feature_id_column, *extra_var_cols]].set_index(feature_id_column, drop=True)
extra_var_df = extra_var_df[~extra_var_df.index.duplicated(keep="first")]
comparison_adata = add_metadata(comparison_adata, extra_var_df, axis=1)
add_metadata(comparison_adata, extra_var_df, axis=1)

@lucas-diedrich lucas-diedrich Sep 16, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Set inplace=False for internal stuff? Feels weird when a function performs an inplace operation in the pipeline

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants