ehrapy 0.16.0 adds ep.ml to fit, evaluate, calibrate and explain prediction models on static and longitudinal EHR data. It also adds ep.tl.comorbidity_index for the Charlson and Elixhauser indices, competing risks in ep.tl.kaplan_meier, cohort definition by inclusion criteria with ep.pp.filter_observations, longitudinal quality control, PCA and FAMD, ep.pl.trajectories, and ep.pp.gradient_boosting_impute. Preprocessing supports numpy, scipy sparse and dask arrays for static 2D and longitudinal 3D data.
Warning
This release has breaking changes: the API conventions are unified (copy replaces inplace, keyword-only arguments), ep.pp.mice_forest_impute and several deprecated functions are removed, and survival, normalization and plotting defaults have changed. See 💥 Breaking changes below.
ehrapy now requires ehrdata>=0.5.0.
🚀 Features
-
ep.tl.comorbidity_indexscores every patient with the Charlson or Elixhauser comorbidity index from ICD-9-CM and ICD-10 codes, and from SNOMED codes through the ICD codes mapped to them, using the coding algorithms of Quan et al. 2005 with the Charlson, Quan 2011 or van Walraven weights, and stores the score and every comorbidity inobs(#1161) @Zethson -
ep.get.obs_dfand the functions that read variables through it, the survival and regression models, the causal estimators,ep.pl.scatter,ep.pl.catplotandep.pl.ols, take variables of longitudinal 3D data at their first non-missing value, the baseline, or at another statistic over time passed toobs_dfasstatistic(#1164) @ZethsonThe survival models derive the duration and event from an
event_colthat is a longitudinal variable, so thatep.tl.kaplan_meier(edata, event_col="SepsisLabel")estimates the time to sepsis on PhysioNet 2019.
The event is whether the variable is ever 1, at the time of its first 1 inedata.tem["time_value"], and observations without one are censored at their last non-missing value.
The fitted models hold the derived durations and events asdurationsandevent_observed. -
ep.pp.summarize_measurementssummarizes the timepoints selected bytem_names, or several named windows such as the first 6 and the last 24 hours in columns{var}_{stat}_{window}, andep.mlcomputes the features of its observation windows with it (#1159) @Zethson -
ep.tl.kaplan_meierestimates the cumulative incidence of anevent_of_interestwith the Aalen-Johansen estimator when the event column holds competing events,ep.tl.nelson_aalenits cause-specific cumulative hazard, andep.pl.kaplan_meierplots the cumulative incidence (#1155) @ZethsonWithout
event_of_interest, the univariate survival models raise an error for event columns with more than one event type instead of silently treating every event type as the same event. -
ep.pp.filter_observationsdefines a cohort by inclusion criteria inqueryonobscolumns and variables, which for longitudinal data take the value at the timepoints selected bytem_namesreduced byagg, and records every step in aep.tl.CohortTrackerpassed astrackerfor CONSORT-style flowcharts (#1157) @Zethson -
ep.pp.pcaandep.tl.famdrun on longitudinal data by unfolding every variable and timepoint into a feature, store loadings per variable and timepoint invarm, andep.pp.neighborsuses the resultingX_pcafor longitudinal.X(#1158) @Zethson -
ep.pp.qc_metricsadds, for longitudinal data, the share of observations that measure every variable and the median time between its consecutive values tovar, and the number of measured timepoints with the first and last measured time toobs, in the times ofedata.tem[time_key](#1156) @Zethson -
ep.pp.qc_lab_measurementsflags implausible jumps between consecutive values of longitudinal variables inobs, beyond an absolute or relativemax_changeper time for all or single variables, or else beyond the range of normal changes estimated withmethod(#1156) @Zethson -
Models of time series in
ep.mlmeasure the time since the last observation with the times of the timepoints inedata.tem[time_key], a new argument ofep.ml.fit, instead of counting timepoints, so that irregular timepoints are spaced correctly (#1151) @Zethson -
ep.ml.Taskwithrolling=Truepredicts a longitudinal variable, such as the hourly sepsis label of PhysioNet 2019, at every timepoint from the timepoints before it, and every function ofep.mlfits, stores, evaluates, calibrates and explains these predictions per timepoint while keeping patients together (#1152) @ZethsonOn the held-out patients of PhysioNet 2019, a GRU predicts the hourly sepsis label from the hours before with an AUROC of 0.75 and AUPRC of 0.05, and gradient boosting on summaries of those hours with 0.67 and 0.02, for 1.2% positive hours.
-
ep.pp.summarize_measurementscomputes the number of values, their standard deviation and, for longitudinal data, their slope over time (#1150) @Zethson -
ep.pl.trajectoriesplots the mean of longitudinal variables over time with a confidence band for every group of observations (#1146) @Zethson -
ep.pp.locf_imputetakes alimiton how many timepoints an observed value is carried forward, and its fallback only fills timepoints before a patient's first observation (#1147) @Zethson -
ep.mlfits, applies and evaluates models that predict patient outcomes from static and longitudinal data (#1144) @Zethsonep.ml.splitassigns patients totrain,tuningandheld_outsets, at random or by time, andep.ml.fitfits imputation, scaling and the model ontrainonly.
Aep.ml.Tasksets binary, multiclass, multilabel, regression or survival targets and the observation window that longitudinal variables are summarized over.
ep.ml.fittrains linear, gradient boosting, random forest and Cox models, multilayer perceptrons, and GRU, LSTM, GRU-D, TCN, transformer and RETAIN models of time series, whose dependencies the newmlextra installs.
ep.ml.evaluatereports the metrics of every kind of task with confidence intervals from resampled patients, overall, per subgroup and as differences between subgroups such as demographic parity and equalized odds.
ep.ml.calibratecalibrates predicted probabilities with Platt scaling, isotonic regression or temperature scaling, andep.ml.conformalizeadds conformal prediction sets and intervals, both on the tuning set.
ep.ml.permutation_importancestores how much every variable matters to a model invarandvarm.
ep.pl.prediction_performanceplots the ROC, precision-recall and calibration curves, per class or label for multiclass and multilabel tasks, andep.pl.subgroup_performancethe metrics of every subgroup.
On PhysioNet 2012, a GRU on the first 48 hours predicts in-hospital mortality with a held-out AUROC of 0.87 and AUPRC of 0.57, and gradient boosting on their summaries with 0.86 and 0.51. -
ep.pp.gradient_boosting_imputeimputes every variable with a gradient boosting model, which for longitudinal data also uses the closest observed values before and after each timepoint (#1135) @ZethsonWith every variable held out at 10% of the observed hours of all 11,988 PhysioNet 2012 patients, its RMSE on the standardized held-out values is 0.62 in 36 seconds, against 1.09 in 232 seconds for
ep.pp.miss_forest_imputeand 0.82 forep.pp.locf_impute. -
Preprocessing functions support numpy, scipy sparse and dask arrays, including dask arrays with sparse chunks, for static 2D and longitudinal 3D data (#1132) @Zethson
Sparse arrays stay sparse and dask arrays stay lazy, and functions that store summaries compute once.
The few combinations that cannot work this way raise aNotImplementedErrorthat says why, such as centering or ComBat on sparse data, the faiss backend ofep.pp.knn_imputeon sparse or dask arrays, andep.pp.miss_forest_imputeorep.pp.detect_biason dask arrays. -
ep.pp.summarize_measurementsaggregates longitudinal data over time into a 2D object with one column per variable and statistic (min,max,mean,median,first,last), which makes every 2D-only function usable on longitudinal data (#1132) @Zethson -
ep.pp.winsorize,ep.pp.clip_quantileandep.pp.qc_lab_measurementssupport longitudinal data (#1132) @Zethson -
Tools, plots and
ep.getfunctions support numpy, scipy sparse and dask arrays, and densify and compute only the variables they use (#1134) @ZethsonThe causal estimators,
ep.tl.famdandep.tl.rank_features_supervisedaccept sparse and dask arrays instead of rejecting or fully densifying them.
Embedding plots such asep.pl.umapcolor longitudinal data byobscolumns, andep.pl.timeseriesplots 3D.Xwithlayer=None.
💥 Breaking changes
-
ep.tl.weibull_afttakesevent_colas keyword argument and defaults tomodel_ancillary=Falselikeep.tl.log_logistic_aft, so that it no longer models the shape parameter with all covariates unless asked to, which changes the fitted models of calls that relied on the default (#1193) @Zethson -
Remove the single-cell leftovers
feature_symbols,use_rawand the unused fields ofep.settings(#1154, #1162) @ZethsonThe
feature_symbolsargument of theep.plandep.getfunctions and theuse_rawargument of theep.plfunctions are gone, and plots always read.Xorlayer, never.raw.
ep.settingskeepsverbosityandn_jobs, the only fields ehrapy reads, and dropsplot_suffix,file_format_data,file_format_figs,autosave,autoshow,writedir,cachedir,datasetdir,figdir,cache_compression,max_memory,categories_to_ignoreandn_pcs. -
The
ep.pl.missing_values_*plots are interactive HoloViews plots that accept longitudinal data, and themissingnodependency is dropped (#1149) @Zethsonep.pl.missing_values_matrixshows the percentage of observed values per observation, or per timepoint for longitudinal data, and the bar plot, heatmap and dendrogram count every timepoint of every observation as one row.
They takevar_names,width,heightandtitleinstead of themissingnoarguments. -
ep.pl.timeseriesplots the 3D.Xby default instead of.layers["tem_data"], like every other function (#1121) @sueoglu -
Survival analysis and regression models take their columns from
edata.obsor variables and only drop observations missing a column the model uses (#1133) @Zethsonep.tl.kaplan_meier,ep.tl.cox_phand the other survival fitters,ep.tl.olsandep.tl.glmaccept obs columns wherever they accept variables, and work on longitudinal data when every column they use lives inobs.
Previously, every observation with a missing value in any variable was silently dropped, even in variables the model did not use.
Regression fitters gainedcovariates, the univariate fitters takeentry_colandweights_colinstead of theentryandweightsarrays, and numeric columns are passed to the models as numbers, so a binomialep.tl.glmon a 0/1 outcome models the probability of 1. -
Normalization functions take explicit parameters instead of forwarding
**kwargsto scikit-learn or dask-ml (#1132) @Zethsonep.pp.scale_normtakeswith_meanandwith_std,ep.pp.minmax_normfeature_range,ep.pp.robust_scale_normwith_centering,with_scaling,quantile_rangeandunit_variance,ep.pp.quantile_normn_quantiles,output_distribution,subsampleandrandom_state, andep.pp.power_normmethodandstandardize.
Agroupbycolumn with missing values now raises instead of leaving those observations unnormalized, and thedaskextra no longer installs dask-ml. -
ep.pp.winsorizetakesinclusiveinstead of**kwargs, ignores missing values when ranking, and cuts 1% from each side by default (#1132) @ZethsonThe previous default
limits=(0.01, 0.99)cut 99% of the largest values, replacing almost every value of a variable with the same number. -
ep.pp.summarize_measurementsno longer acceptsstatistics=None, which always raised (#1132) @Zethson -
Remove
ep.pp.mice_forest_imputeand drop themiceforestdependency (#1097) @Zethsonmiceforestis effectively unmaintained (last commit 2025-10-27) and broken againstlightgbm>=4.7.0, which it calls through a private, name-mangled internal (miceforest#104).
Useep.pp.gradient_boosting_imputeorep.pp.miss_forest_imputeinstead.
For a LightGBM backend, passIterativeImputer(estimator=LGBMRegressor(...))directly. -
Unify the API conventions across
ep.pp,ep.tl,ep.plandep.get(#1126) @ZethsonRequired arguments are positional and every argument with a default is keyword-only.
Grouping keys are calledgroupby(wasgroup_key,cluster_key,balanced_key), feature subsetsvar_names(wasvars,input_features,feature_names), feature-name columnsfeature_symbols(wasgene_symbols,features), and result keyskey_addedwhen written andkeywhen read (wasuns_key).
ep.pp.combattakesbatch_key(waskey), andep.pp.pcaandep.pp.sampletakeedata(wasdata). -
Replace
inplacewithcopy(#1126) @Zethsonep.pp.combat,ep.pp.highly_variable_features,ep.tl.dendrogramandep.tl.ingesttakecopyinstead ofinplace.
ep.pp.qc_metrics,ep.pp.detect_bias,ep.tl.embedding_density,ep.tl.filter_rank_features_groups,ep.tl.rank_features_supervisedandep.tl.cox_ph_adjusted_curvesgainedcopy.
qc_metricsanddetect_biasstore their results inedatainstead of returning them (detect_biasunderuns["bias"]), andrank_features_supervisedstores the model's test score inuns[key_added]instead of returning it. -
Remove the deprecated
ep.tl.kmf,ep.pp.subsampleand then_neighboursalias ofep.pp.knn_impute(#1126) @Zethson
🐛 Bug Fixes
-
ep.tl.aipwcross-fits its propensity and outcome models overn_foldsfolds assigned withrandom_state, which now defaults to 0, so that its standard error holds for flexible models such as gradient boosting, which on 21 baseline covariates of MIMIC-II gave an in-sample standard error of 0.010 against 0.048 when cross-fitted (#1200) @Zethson -
ep.pl.subgroup_performanceputs the difference between subgroups on a second line of every title, so that titles of metrics such ascalibration_interceptare no longer cut off at the default width (#1197) @Zethson -
ep.pp.detect_biasandep.tl.rank_features_supervisedno longer fail for variables whose name is also a column ofobsor that have missing values, and leave out the observations without a value of the predicted variable or the sensitive feature (#1196) @Zethson -
ep.tl.cox_ph_adjusted_curvesraises an error whenstratais neither a covariate nor one of thestrataofcph, which gave every group the same curve, and its documentation no longer asks to leavestrataout of the model (#1192) @Zethson -
ep.pp.qc_metricsandep.pp.qc_lab_measurementsmeasure the longitudinal metrics and changes per time of 3D data in the unit ofedata.tem["time_value"], such as days oned.dt.mimic_iv_meds, instead of in seconds ofedata.tem["interval_start_offset"], which they keep using whentemhas notime_value(#1202) @Zethson -
The confidence intervals of the subgroup
"difference"ofep.ml.evaluateresample the difference between the subgroups with the highest and the lowest value instead of the largest difference of every bootstrap sample, which was never negative, so that they include 0 when the subgroups do not differ (#1198) @Zethson -
ep.pl.cox_ph_forestplotdraws the confidence intervals as horizontal error bars, puts the reference line at a coefficient of 0, which is a hazard ratio of 1, instead of 1, and gives the header its own row instead of overlapping the top coefficient (#1191) @Zethson -
The survival models that derive durations from a longitudinal
event_colmeasure them in the unit ofedata.tem["time_value"], such as hours on PhysioNet 2012 and 2019, instead of in seconds ofedata.tem["interval_start_offset"], which they keep using whentemhas notime_value(#1190) @Zethson -
The examples of
ep.tl.kaplan_meier,ep.tl.nelson_aalen,ep.tl.weibull,ep.tl.weibull_aft,ep.tl.log_logistic_aft,ep.tl.cox_phandep.pl.kaplan_meierno longer flipcensor_flgof MIMIC-II, whiched.dt.mimic_2already codes as 1 for death, so that they model the time to death instead of the time to censoring (#1189) @Zethson -
The calibration curve of
ep.pl.prediction_performanceends its axes at the largest predicted probability or observed frequency instead of at 1, so that rare outcomes such as the hourly sepsis label of PhysioNet 2019 no longer squeeze it into a corner (#1201) @Zethson -
The logistic propensity model of the causal estimators and diagnostics standardizes the covariates before fitting, so that estimates no longer depend on the units of the covariates and the fit converges on unscaled data such as the 21 baseline covariates of MIMIC-II (#1186) @Zethson
-
ep.pp.neighborswithmetric="dtw","soft_dtw"or"gak"stores the name of the metric inuns["neighbors"]["params"]instead of the internal distance function together with the whole time series array, so thated.io.write_h5edwrites the result (#1188) @Zethson -
ep.pl.violinno longer draws a legend that repeats the x axis whengroupbyhas integer categories such as a 0/1 treatment flag (#1182) @Zethson -
ep.ml.fittakes atrainerthat sets how deep learning models are trained, which torch modules passed asmodelcould not be given before (#1203) @Zethson -
The
summary()of aep.tl.CausalEstimatelabels the effect with its newestimandattribute, so thatep.tl.propensity_score_matchingwithtarget="att"reports an ATT instead of an ATE (#1183) @Zethson -
ep.tl.s_learner,ep.tl.t_learnerandep.tl.x_learnertake arandom_statethat seeds their built-in gradient boosting and random forest models, and the effect estimators pass theirrandom_stateto these models too, so that their estimates are reproducible (#1184) @Zethson -
The causal estimators and diagnostics such as
ep.tl.covariate_balancetreat numeric variables of an objectX, such as that ofed.dt.mimic_2(), as numeric instead of one-hot encoding each of their values (#1185) @Zethson -
ep.pp.explicit_imputeno longer warns for every variable missing from areplacementmapping, because leaving variables out of it is the intended way to impute only some of them (#1178) @Zethson -
ep.tl.rank_features_groupsreports the log2 ratio of the fractions of observations with value 1 as log fold change of binary categorical features and NaN for categorical features with more levels, instead of 1 regardless of direction, and orders features with equal adjusted p-values by their p-values (#1180) @Zethson -
ep.tl.rank_features_groupstests numeric variables with missing values on their observed values instead of giving them a score of 0 and a p-value of 1 with t-tests, or ranking missing values as the largest with the Wilcoxon test, and computes their log fold changes from the observed values instead of returning NaN (#1179) @Zethson -
ep.ml.calibratedefaults tomethod="platt"instead of"isotonic", because isotonic regression overfits tuning sets of typical size, such as on PhysioNet 2012 with a calibration slope of 0.54 against 0.95 with Platt scaling (#1169) @Zethson -
ep.ml.evaluateandep.pl.subgroup_performancekeep the subgroups ofgroupbyin the order of its categories, or of their appearance, instead of sorting them (#1169) @Zethson -
ep.pp.summarize_measurementsreturns NaN without RuntimeWarnings for variables without values in a window of longitudinal data (#1169) @Zethson -
ep.pp.gradient_boosting_imputeleaves out predictors observed too rarely to split on instead of crashing when their few values all fall into the validation split of a model trained on more than 10,000 rows, and warns for variables that stay missing because they have no observed values in the training observations (#1174) @Zethson -
ep.pp.winsorizeandep.pp.clip_quantilewithoutvar_namesandobs_colsapply to all numeric variables instead of silently changing nothing (#1173) @Zethson -
ep.pp.mcar_testtests longitudinal data at the timepoint selected bytem_namesor reduced byaggover several timepoints, and raises an error naming the variables that are not observed together in at least two observations instead of returning aNaNp-value for Little's test (#1176) @Zethson -
ep.tl.CohortTracker.plot_cohort_barplotwithshow=Truelays out its rows next to the legend instead of squeezing them together by the height of the legend (#1177) @Zethson -
Matplotlib figures in notebooks display inline after a holoviews-based ehrapy plot that runs before the first matplotlib figure, without
%matplotlib inline(#1168) @Zethson -
ep.tl.olsandep.tl.glmfind theobscolumns and variables quoted asQ('...')in a formula, so that names with spaces work again, by requiring formulaic 1.2.2 (#1165) @Zethson -
ep.pp.pcauses scanpy's dask-nativecovariance_eighsolver for dask arrays by default instead of requiring dask-ml (#1163) @Zethson -
The
rapids12andrapids13extras install the CUDA wheels of rapids-singlecell, whose 0.18 release only builds from source under its old name and extras (#1153) @Zethson -
In
ep.tl.rank_features_groups, thelogfoldchangesof numeric features are the log2 ratio of the group mean to the reference mean instead of scanpy's fold change for log1p-transformed data, and categorical features withreference="rest"and agroupssubset are compared to all other observations like numeric features (#1148) @ZethsonThe previous fold changes were meaningless on EHR data and
NaNwith aRuntimeWarningon standardized data, and observations outsidegroupswere counted with the tested group. -
ep.tl.rank_features_groupsstores results only for the compared groups like scanpy, instead of also for thereferenceand for groups outsidegroups, which drew empty panels inep.pl.rank_features_groups, and no longer crashes on categorical features with agroupssubset (#1145) @Zethson -
ep.pl.violin,ep.pl.scatter,ep.pl.olsand the causal estimators and diagnostics such asep.tl.iptwwork on 3D data when they only useobscolumns, andep.pl.dendrogramwhen the dendrogram is precomputed (#1143) @Zethson -
ep.pp.neighborsuses the 3D.Xby default with the time series metrics and rejects it with a clear error for other metrics instead of failing inside scikit-learn, andep.tl.rank_features_groupsrejects 3D data with the same error as other 2D-only functions (#1143) @Zethson -
ep.pp.simple_impute,ep.pp.miss_forest_imputeandep.pp.locf_imputeraise aKeyErrorfor unknownvar_namesinstead of imputing the last variable, andep.pp.explicit_imputeraises one for unknownreplacementkeys instead of ignoring them (#1140) @Zethsonep.pp.knn_imputeand the normalization functions raise aKeyErrorfor unknownvar_namesinstead of reporting them as non-numeric. -
Matplotlib figures in notebooks no longer disappear after the first holoviews-based ehrapy plot (#1136) @Zethson
-
ep.pp.knn_imputeimputes all numeric variables by default instead of raising on data with encoded categorical variables (#1139) @Zethson -
ep.pp.miss_forest_imputekeeps only one forest in memory instead of every forest it fitted, cutting peak memory about sixfold, and is reproducible for a fixedrandom_state(#1135) @Zethson -
On dask arrays with missing values,
ep.pp.minmax_normandep.pp.robust_scale_normreturned all-NaN variables andep.pp.quantile_normreturned wrong values (#1132) @Zethson -
ep.pp.filter_featuresandep.pp.filter_observationscrashed for every non-numpy array,ep.pp.encodecrashed on sparse arrays, and the imputers crashed on variables without any observed value (#1132) @Zethson -
ep.pp.qc_metricsno longer computes dask arrays once per variable or reports all-NaN statistics when any variable holds strings (#1132) @Zethson -
ep.tl.rank_features_groupsno longer computes dask arrays once per group statistic or crashes withpts=Trueon sparse arrays, on 3D data with a single timepoint, or withnum_cols_method="logreg", and itsptsonly hold the compared groups (#1134) @Zethson -
ep.tl.filter_rank_features_groupsno longer crashes on sparse arrays andep.tl.dendrogramno longer crashes on scipy sparse matrices (#1134) @Zethson -
ep.tl.tsne,ep.tl.dendrogram,ep.tl.ingest, theep.pl.rank_features_groups_*plots,ep.pl.dpt_timeseriesand embedding plots colored by variables reject 3D data with a clear error instead of failing inside scikit-learn or scanpy (#1134) @Zethson -
The
ep.pl.missing_values_*plots read only the missing value mask of the plotted variables instead of densifying or computing the whole data matrix (#1134) @Zethson -
ep.pl.timeseries,ep.pl.sankey_diagram_timeandep.pl.ncp_cluster_trajectoriescompute only the plotted values,sankey_diagram_timeno longer crashes on dask arrays or on a single timepoint, andep.tl.ncpreports 2D sparse input as not 3D (#1134) @Zethson -
ep.pp.explicit_impute()now accepts falsy mapping replacement values such as0,0.0, and empty strings (#1087) @driavysinus -
ep.pp.knn_impute()now raises a clearNotImplementedErrorfor unsupported array types (dask and sparse arrays) instead of failing silently (#1109) @sueoglu -
_little_mcar_testnow computes its global covariance matrix with true pairwise deletion instead of centering on the global mean, fixing incorrect p-values under moderate-to-high missingness (#1110) @sueoglu -
ep.pp.encodeandep.pp.clip_quantile(copy=True)no longer modify their input (#1126) @Zethson -
ep.tl.filter_rank_features_groupsno longer raisesKeyError: 'use_raw'(#1126) @Zethson -
ep.tl.rank_features_supervisedreports R² instead of accuracy for numeric targets (#1126) @Zethson -
ep.tlno longer leaks implementation details such asnpandsc, and its__all__now listsleiden,dendrogram,dpt,pagaandingest;ep.plgained an__all__(#1126) @Zethson -
ep.tl.famdworks on 2D.Xand layers with numeric or mixed variables, rejects 3D data, and stores per-variable loadings in.varmand all category loadings in.uns(#1129) @Zethson -
ep.tl.ncpraises aValueErrorfor missing or negative values instead of returning NaN or negative factors (#1129) @Zethson -
ep.pl.olsgainedlayer, supports sparse arrays and rejects 3D data instead of flattening the time axis into extra points (#1129) @Zethson -
ep.get.obs_dfraises a clear error when reading variables from 3D data andep.get.var_dffor any 3D data, instead of failing inside pandas (#1129) @Zethson -
ep.tl.stratified_table_onestores its table withvariableandlevelcolumns so that results can be written to h5ad (#1129) @Zethson -
ep.tl.rank_features_groupssupports categorical features in sparse arrays (#1129) @Zethson -
ep.pp.neighborswith a time series metric no longer makes patients without comparable measurements everyone's nearest neighbours but leaves them unconnected (#1129) @Zethson -
Align all scanpy wrappers with scanpy 1.12: the
ep.pl.rank_features_groups_*plots read theep.tl.rank_features_groupsresults by default, which now honoursn_featuresand storesptsper feature,ep.pp.pcausesvar["highly_variable"]by default as documented, every plot passesfeature_symbolson to scanpy, and the deprecatedsave,ep.tl.umap(method=...)and the no-opn_binsofep.pp.highly_variable_featuresare removed (#1130) @Zethson
📖 Documentation
-
Add general introductions to the API sections and redirect removed tutorials to the tutorial gallery (#1170) @Zethson
-
Refresh the README and installation guide (optional extras, install from GitHub) and drop dead Sphinx extensions (#1128) @Zethson
-
Add imputation methods tutorial notebook, benchmarking six imputation strategies on the PhysioNet2012 dataset (#1101) @sueoglu
-
Document the
ep.getmodule,ep.tl.famdandep.tl.anova_glm(#1126) @Zethson -
Describe
ep.tl.ncpin plain words and drop the doubled period in the docs footer (#1131) @Zethson -
Docstring examples keep time series in the 3D
.Xinstead of.layers["tem_data"]and show the outputs the examples actually produce (#1098) @sueoglu -
Describe ehrapy's data sources and analyses in the README, docs landing page and package metadata, and add a longitudinal quickstart, a sitemap, an
llms.txt, stable canonical URLs and aCITATION.cff(#1141) @Zethson -
Fix the docstring examples that no longer ran and add examples to
ep.pp.pca,ep.pp.neighbors,ep.tl.umap,ep.tl.tsne,ep.tl.leidenandep.tl.paga(#1142) @ZethsonThe tests now run every docstring example with xdoctest, so examples can no longer break silently.
🧰 Maintenance
- Require
ehrdata>=0.5.0(#1212) @eroell - Require
pynndescent<0.7for now, as pynndescent 0.7 breaksep.pp.neighborsandep.tl.ingestthrough scanpy (scverse/scanpy#4444) (#1210) @eroell ep.pp.qc_metricswithqc_varssums sparse and dask arrays blockwise withfast_array_utilsinstead of rechunking dask arrays with sparse chunks (#1137) @Zethson- Run the imputation, causal inference and effect estimation tutorials in the notebook CI (#1120) @sueoglu
- Update to cookiecutter-scverse v0.8.0, derive the version from git tags via hatch-vcs, and move the
devextra to adevdependency group (#1125) @Zethson import ehrapyno longer loads the holoviews extensions, which now load on the first holoviews-backed plot, and no longer installs a globalSyntaxWarningfilter (#1129) @Zethson- Drop the unused
thefuzz,fhiryandfilelockdependencies and moverequeststo thetestextra (#1129) @Zethson - Remove dead code and let codecov compare project coverage against the base commit (#1129) @Zethson
Full Changelog: v0.15.0...v0.16.0