_preprocessing
Preprocessing helpers for transforming and filtering AnnData objects.
The current row-operation APIs live in
_preprocessing/_adata_row_operations.py
and are re-exported from adata_science_tools._preprocessing.
Main row-operation entry points
CFG_filter_adata_by_obscompute_paired_mean_adatacompute_paired_difference_adataref_vs_target_adata
ref_vs_target_adata
ref_vs_target_adata(...) builds a new AnnData object with one observation per
matched target/reference pair. It is intended for paired Pre/Post-style
transforms such as Post - Pre, percent change, fold change, and log2 fold
change.
Full signature
The runtime Python signature keeps optional configuration in **params for
backward-compatible config-driven use:
def ref_vs_target_adata(
adata: ad.AnnData,
groupby_key: str = "Pre_or_Post_obs_col",
groupby_key_target_value: str = "Post",
groupby_key_ref_value: str = "Pre",
opperation_flavor: str | Sequence[str] = "subtraction",
obs_dfs: str = "merge",
ref_obs_suffix: str = ".src_pre",
target_obs_suffix: str = ".src_post",
keep_var_df: bool = True,
**params,
) -> ad.AnnData | tuple[ad.AnnData, pd.DataFrame]:
The full supported call surface, with **params expanded, is:
result = adtl.ref_vs_target_adata(
adata,
groupby_key="Pre_or_Post_obs_col",
groupby_key_target_value="Post",
groupby_key_ref_value="Pre",
opperation_flavor="subtraction", # or a list of operation strings
obs_dfs="merge",
ref_obs_suffix=".src_pre",
target_obs_suffix=".src_post",
keep_var_df=True,
pair_by_key="SubjectID", # required
layer=None,
layers_to_compute=None,
base_layer=None,
epsilon=1e-9,
target_min_value=None,
target_max_value=None,
ref_min_value=None,
ref_max_value=None,
bounds_fill_missing=False,
bounds_fill_missing_paired_only=False,
merge_shared_obs_cols=False,
return_df=False,
allow_unused_params=False,
logger=None,
log_level="INFO",
save_source_values_obsm=False,
target_values_obsm_key="post_values",
ref_values_obsm_key="pre_values",
)
operation_flavor is also accepted as a corrected alias for the typo-compatible
opperation_flavor.
import adata_science_tools as adtl
post_minus_pre = adtl.ref_vs_target_adata(
adata,
groupby_key="Pre_or_Post_obs_col",
groupby_key_target_value="Post",
groupby_key_ref_value="Pre",
pair_by_key="SubjectID",
)
Pairing rules
pair_by_keyis required through**params.The function selects target rows with
adata.obs[groupby_key] == groupby_key_target_value.It selects reference rows with
adata.obs[groupby_key] == groupby_key_ref_value.Pair IDs are stringified for matching and become the returned observation index.
Missing pair IDs in either selected group raise
ValueError.Duplicate pair IDs within either selected group raise
ValueError.Target-only and reference-only pair IDs are dropped, logged, and stored in
result.uns["ref_vs_target_adata"].If there are no overlapping pair IDs, the function raises
ValueError.
Operations
The default operation is subtraction, computed as target minus reference:
target - reference
Supported operation names are:
subtractionrelative_change_pctrelative_change_fcrelative_change_l2fc
opperation_flavor can also be a non-empty sequence of operation names. In
that mode, each requested operation is computed and stored as a layer in the
returned object. The returned .X uses the first requested operation for the
selected base source, so ["subtraction", "relative_change_pct"] keeps
subtraction in .X.
post_minus_pre = adtl.ref_vs_target_adata(
adata,
pair_by_key="SubjectID",
opperation_flavor=["subtraction", "relative_change_pct", "relative_change_l2fc"],
)
The corrected parameter alias operation_flavor is accepted through **params,
but the public signature keeps the existing typo-compatible
opperation_flavor.
Relative operations use epsilon from **params, defaulting to 1e-9:
relative_change_pct = ((target - reference) / (reference + epsilon)) * 100
relative_change_fc = (target + epsilon) / (reference + epsilon)
relative_change_l2fc = log2((target + epsilon) / (reference + epsilon))
Data sources
By default, the function computes from adata.X. To compute a layer, pass
layer:
post_minus_pre = adtl.ref_vs_target_adata(
adata,
pair_by_key="SubjectID",
layer="RFU",
)
To compute more than one source, pass layers_to_compute. Use None in that
list for adata.X. The returned .X is selected by base_layer, defaulting to
the first requested source.
post_minus_pre = adtl.ref_vs_target_adata(
adata,
pair_by_key="SubjectID",
layers_to_compute=[None, "RFU"],
base_layer="RFU",
)
When a layer source is requested, the computed values for that source are also
stored in result.layers[source].
When multiple operations are requested for one source, operation names become layer keys:
result.layers["subtraction"]result.layers["relative_change_pct"]
When multiple operations and multiple sources are requested together, layers are
named as source__operation. The .X source is labeled X in layer keys:
post_minus_pre = adtl.ref_vs_target_adata(
adata,
pair_by_key="SubjectID",
opperation_flavor=["subtraction", "relative_change_l2fc"],
layers_to_compute=[None, "RFU"],
base_layer="RFU",
)
This creates layers such as X__subtraction, X__relative_change_l2fc,
RFU__subtraction, and RFU__relative_change_l2fc. The returned .X is
RFU__subtraction because base_layer="RFU" and subtraction is the first
requested operation.
Bounds and LOD-style clamping
Optional bounds clamp valid paired values before the selected operation is computed:
target_min_valuetarget_max_valueref_min_valueref_max_valuebounds_fill_missingbounds_fill_missing_paired_only
For example, target_min_value=0.5 treats any selected target value below 0.5
as 0.5. Bounds are clamping controls, not filters.
By default, bounds do not impute missing values. Set
bounds_fill_missing=True to fill every missing value on each bounded side
before clipping and computation. The fill value uses side-specific precedence:
the side’s min value when present, otherwise the side’s max value.
Set bounds_fill_missing_paired_only=True to fill missing values only when the
opposite side of the same pair and variable is present. If both missing-fill
flags are True, paired-only fill behavior is used. Numeric clipping of
present values is unchanged. A missing side is filled only when that side has a
min or max bound; if no bound is provided for that side, the missing value stays
missing.
For one variable with ref_min_value=2, target_min_value=1, and
bounds_fill_missing_paired_only=True:
Raw reference |
Raw target |
Bounded reference |
Bounded target |
Reason |
|---|---|---|---|---|
|
|
|
|
Missing target is filled because reference is present. |
|
|
|
|
Both sides are missing, so neither side is filled. |
|
|
|
|
Missing reference is filled because target is present. |
|
|
|
|
Present values are still clipped to side-specific bounds. |
Returned metadata
The returned object stores:
one observation per matched pair;
ref_obs_name,target_obs_name,pair_order, source group labels, and the operation name in.obs;operation and source metadata in
.uns["ref_vs_target_adata"];multi-operation metadata such as
operation_flavors,operation_layer_keys,operation_layer_key_by_source_operation, andbase_operation_layer;dropped unmatched pair IDs in both
.uns["ref_vs_target_adata"]and flat convenience keys;copied
adata.varplus operation metadata whenkeep_var_df=True;generated operation-only
.varmetadata whenkeep_var_df=False.
With obs_dfs="merge", both source .obs tables are included with suffixes
from ref_obs_suffix and target_obs_suffix. If
merge_shared_obs_cols=True, columns whose retained pair values are identical
in both sources are collapsed to one column. obs_dfs="keep_ref" and
obs_dfs="keep_target" keep one source table plus provenance columns.
Optional source-value obsm
Set save_source_values_obsm=True to store the paired, ordered source values
used for the returned .X base source before the final operation result is
returned:
post_minus_pre = adtl.ref_vs_target_adata(
adata,
pair_by_key="SubjectID",
save_source_values_obsm=True,
target_values_obsm_key="post_values",
ref_values_obsm_key="pre_values",
)
The stored obsm values are pandas.DataFrame objects aligned to returned
observations and variables:
result.obsm["post_values"]result.obsm["pre_values"]
When bounds are requested, these source-value tables reflect the bounded values used for computation.
adtl.save_dataset(result, "path/to/result.h5ad") exports these tables by
default as .obsm.<key>.csv files, for example
result.obsm.pre_values.csv and result.obsm.post_values.csv when the
default keys are used.
Logging
logger and log_level control INFO-level argument summaries, progress
messages, and the final .X source selection. The argument summary reports
AnnData shape, matrix type, layer keys, and column names without logging
matrix values or full obs/var tables. The final .X log records the base
source, base operation, operation-layer key when applicable, shape, and dtype.
DataFrame return
Set return_df=True to return both the result object and a feature matrix
DataFrame for the base source:
result_adata, result_df = adtl.ref_vs_target_adata(
adata,
pair_by_key="SubjectID",
return_df=True,
)