diff --git a/demo.ipynb b/demo.ipynb index 9cf0481..1516484 100644 --- a/demo.ipynb +++ b/demo.ipynb @@ -12,7 +12,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 1, "id": "14411cbf", "metadata": {}, "outputs": [], @@ -37,7 +37,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 2, "id": "c285a9b4", "metadata": {}, "outputs": [ @@ -52,7 +52,7 @@ " uns: '_omicslog'\n", "\n", "Operation log:\n", - "[2026-05-20 13:49:41] obs: 'cell_type' added\n" + "[2026-07-03 18:08:13] obs: 'cell_type' added\n" ] }, { @@ -84,7 +84,7 @@ " \n", " \n", " 0\n", - " 2026-05-20 13:49:41\n", + " 2026-07-03 18:08:13\n", " obs\n", " 'cell_type' added\n", " \n", @@ -94,10 +94,10 @@ ], "text/plain": [ " Time Operation Message\n", - "0 2026-05-20 13:49:41 obs 'cell_type' added" + "0 2026-07-03 18:08:13 obs 'cell_type' added" ] }, - "execution_count": 14, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } @@ -128,7 +128,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 3, "id": "b88b1e3a", "metadata": {}, "outputs": [ @@ -136,13 +136,13 @@ "name": "stdout", "output_type": "stream", "text": [ - "AnnData object with n_obs × n_vars = 27 × 2000\n", + "AnnData object with n_obs × n_vars = 28 × 2000\n", " obs: 'cell_type'\n", " uns: '_omicslog'\n", "\n", "Operation log:\n", - "[2026-05-20 13:49:41] obs: 'cell_type' added\n", - "[2026-05-20 13:49:43] subset: removed 73 samples (73%), 27 samples remaining\n" + "[2026-07-03 18:08:13] obs: 'cell_type' added\n", + "[2026-07-03 18:08:15] subset: removed 72 samples (72%), 28 samples remaining\n" ] }, { @@ -174,15 +174,15 @@ " \n", " \n", " 0\n", - " 2026-05-20 13:49:41\n", + " 2026-07-03 18:08:13\n", " obs\n", " 'cell_type' added\n", " \n", " \n", " 1\n", - " 2026-05-20 13:49:43\n", + " 2026-07-03 18:08:15\n", " subset\n", - " removed 73 samples (73%), 27 samples remaining\n", + " removed 72 samples (72%), 28 samples remaining\n", " \n", " \n", "\n", @@ -190,15 +190,15 @@ ], "text/plain": [ " Time Operation \\\n", - "0 2026-05-20 13:49:41 obs \n", - "1 2026-05-20 13:49:43 subset \n", + "0 2026-07-03 18:08:13 obs \n", + "1 2026-07-03 18:08:15 subset \n", "\n", " Message \n", "0 'cell_type' added \n", - "1 removed 73 samples (73%), 27 samples remaining " + "1 removed 72 samples (72%), 28 samples remaining " ] }, - "execution_count": 15, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -219,7 +219,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 4, "id": "8f7ed85f", "metadata": {}, "outputs": [ @@ -227,14 +227,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "AnnData object with n_obs × n_vars = 27 × 200\n", + "AnnData object with n_obs × n_vars = 28 × 200\n", " obs: 'cell_type'\n", " uns: '_omicslog'\n", "\n", "Operation log:\n", - "[2026-05-20 13:49:41] obs: 'cell_type' added\n", - "[2026-05-20 13:49:43] subset: removed 73 samples (73%), 27 samples remaining\n", - "[2026-05-20 13:49:46] subset: removed 1800 genes (90%), 200 genes remaining\n" + "[2026-07-03 18:08:13] obs: 'cell_type' added\n", + "[2026-07-03 18:08:15] subset: removed 72 samples (72%), 28 samples remaining\n", + "[2026-07-03 18:08:17] subset: removed 1800 genes (90%), 200 genes remaining\n" ] }, { @@ -266,19 +266,19 @@ " \n", " \n", " 0\n", - " 2026-05-20 13:49:41\n", + " 2026-07-03 18:08:13\n", " obs\n", " 'cell_type' added\n", " \n", " \n", " 1\n", - " 2026-05-20 13:49:43\n", + " 2026-07-03 18:08:15\n", " subset\n", - " removed 73 samples (73%), 27 samples remaining\n", + " removed 72 samples (72%), 28 samples remaining\n", " \n", " \n", " 2\n", - " 2026-05-20 13:49:46\n", + " 2026-07-03 18:08:17\n", " subset\n", " removed 1800 genes (90%), 200 genes remaining\n", " \n", @@ -288,17 +288,17 @@ ], "text/plain": [ " Time Operation \\\n", - "0 2026-05-20 13:49:41 obs \n", - "1 2026-05-20 13:49:43 subset \n", - "2 2026-05-20 13:49:46 subset \n", + "0 2026-07-03 18:08:13 obs \n", + "1 2026-07-03 18:08:15 subset \n", + "2 2026-07-03 18:08:17 subset \n", "\n", " Message \n", "0 'cell_type' added \n", - "1 removed 73 samples (73%), 27 samples remaining \n", + "1 removed 72 samples (72%), 28 samples remaining \n", "2 removed 1800 genes (90%), 200 genes remaining " ] }, - "execution_count": 16, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" } @@ -319,7 +319,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 5, "id": "0fb2c8b9", "metadata": {}, "outputs": [ @@ -327,18 +327,18 @@ "name": "stdout", "output_type": "stream", "text": [ - "AnnData object with n_obs × n_vars = 27 × 200\n", + "AnnData object with n_obs × n_vars = 28 × 200\n", " obs: 'cell_type'\n", " uns: '_omicslog'\n", " obsm: 'X_umap'\n", " varm: 'gene_stuff'\n", "\n", "Operation log:\n", - "[2026-05-20 13:49:41] obs: 'cell_type' added\n", - "[2026-05-20 13:49:43] subset: removed 73 samples (73%), 27 samples remaining\n", - "[2026-05-20 13:49:46] subset: removed 1800 genes (90%), 200 genes remaining\n", - "[2026-05-20 13:49:48] obsm: 'X_umap' added\n", - "[2026-05-20 13:49:48] varm: 'gene_stuff' added\n" + "[2026-07-03 18:08:13] obs: 'cell_type' added\n", + "[2026-07-03 18:08:15] subset: removed 72 samples (72%), 28 samples remaining\n", + "[2026-07-03 18:08:17] subset: removed 1800 genes (90%), 200 genes remaining\n", + "[2026-07-03 18:08:19] obsm: 'X_umap' added\n", + "[2026-07-03 18:08:19] varm: 'gene_stuff' added\n" ] }, { @@ -370,31 +370,31 @@ " \n", " \n", " 0\n", - " 2026-05-20 13:49:41\n", + " 2026-07-03 18:08:13\n", " obs\n", " 'cell_type' added\n", " \n", " \n", " 1\n", - " 2026-05-20 13:49:43\n", + " 2026-07-03 18:08:15\n", " subset\n", - " removed 73 samples (73%), 27 samples remaining\n", + " removed 72 samples (72%), 28 samples remaining\n", " \n", " \n", " 2\n", - " 2026-05-20 13:49:46\n", + " 2026-07-03 18:08:17\n", " subset\n", " removed 1800 genes (90%), 200 genes remaining\n", " \n", " \n", " 3\n", - " 2026-05-20 13:49:48\n", + " 2026-07-03 18:08:19\n", " obsm\n", " 'X_umap' added\n", " \n", " \n", " 4\n", - " 2026-05-20 13:49:48\n", + " 2026-07-03 18:08:19\n", " varm\n", " 'gene_stuff' added\n", " \n", @@ -404,21 +404,21 @@ ], "text/plain": [ " Time Operation \\\n", - "0 2026-05-20 13:49:41 obs \n", - "1 2026-05-20 13:49:43 subset \n", - "2 2026-05-20 13:49:46 subset \n", - "3 2026-05-20 13:49:48 obsm \n", - "4 2026-05-20 13:49:48 varm \n", + "0 2026-07-03 18:08:13 obs \n", + "1 2026-07-03 18:08:15 subset \n", + "2 2026-07-03 18:08:17 subset \n", + "3 2026-07-03 18:08:19 obsm \n", + "4 2026-07-03 18:08:19 varm \n", "\n", " Message \n", "0 'cell_type' added \n", - "1 removed 73 samples (73%), 27 samples remaining \n", + "1 removed 72 samples (72%), 28 samples remaining \n", "2 removed 1800 genes (90%), 200 genes remaining \n", "3 'X_umap' added \n", "4 'gene_stuff' added " ] }, - "execution_count": 17, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } @@ -440,7 +440,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 6, "id": "a8c41222", "metadata": {}, "outputs": [ @@ -448,7 +448,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "AnnData object with n_obs × n_vars = 27 × 200\n", + "AnnData object with n_obs × n_vars = 28 × 200\n", " obs: 'cell_type'\n", " uns: '_omicslog'\n", " obsm: 'X_umap'\n", @@ -456,12 +456,12 @@ " layers: 'log_transformed'\n", "\n", "Operation log:\n", - "[2026-05-20 13:49:41] obs: 'cell_type' added\n", - "[2026-05-20 13:49:43] subset: removed 73 samples (73%), 27 samples remaining\n", - "[2026-05-20 13:49:46] subset: removed 1800 genes (90%), 200 genes remaining\n", - "[2026-05-20 13:49:48] obsm: 'X_umap' added\n", - "[2026-05-20 13:49:48] varm: 'gene_stuff' added\n", - "[2026-05-20 13:49:50] layers: 'log_transformed' added\n" + "[2026-07-03 18:08:13] obs: 'cell_type' added\n", + "[2026-07-03 18:08:15] subset: removed 72 samples (72%), 28 samples remaining\n", + "[2026-07-03 18:08:17] subset: removed 1800 genes (90%), 200 genes remaining\n", + "[2026-07-03 18:08:19] obsm: 'X_umap' added\n", + "[2026-07-03 18:08:19] varm: 'gene_stuff' added\n", + "[2026-07-03 18:08:22] layers: 'log_transformed' added\n" ] }, { @@ -493,37 +493,37 @@ " \n", " \n", " 0\n", - " 2026-05-20 13:49:41\n", + " 2026-07-03 18:08:13\n", " obs\n", " 'cell_type' added\n", " \n", " \n", " 1\n", - " 2026-05-20 13:49:43\n", + " 2026-07-03 18:08:15\n", " subset\n", - " removed 73 samples (73%), 27 samples remaining\n", + " removed 72 samples (72%), 28 samples remaining\n", " \n", " \n", " 2\n", - " 2026-05-20 13:49:46\n", + " 2026-07-03 18:08:17\n", " subset\n", " removed 1800 genes (90%), 200 genes remaining\n", " \n", " \n", " 3\n", - " 2026-05-20 13:49:48\n", + " 2026-07-03 18:08:19\n", " obsm\n", " 'X_umap' added\n", " \n", " \n", " 4\n", - " 2026-05-20 13:49:48\n", + " 2026-07-03 18:08:19\n", " varm\n", " 'gene_stuff' added\n", " \n", " \n", " 5\n", - " 2026-05-20 13:49:50\n", + " 2026-07-03 18:08:22\n", " layers\n", " 'log_transformed' added\n", " \n", @@ -533,23 +533,23 @@ ], "text/plain": [ " Time Operation \\\n", - "0 2026-05-20 13:49:41 obs \n", - "1 2026-05-20 13:49:43 subset \n", - "2 2026-05-20 13:49:46 subset \n", - "3 2026-05-20 13:49:48 obsm \n", - "4 2026-05-20 13:49:48 varm \n", - "5 2026-05-20 13:49:50 layers \n", + "0 2026-07-03 18:08:13 obs \n", + "1 2026-07-03 18:08:15 subset \n", + "2 2026-07-03 18:08:17 subset \n", + "3 2026-07-03 18:08:19 obsm \n", + "4 2026-07-03 18:08:19 varm \n", + "5 2026-07-03 18:08:22 layers \n", "\n", " Message \n", "0 'cell_type' added \n", - "1 removed 73 samples (73%), 27 samples remaining \n", + "1 removed 72 samples (72%), 28 samples remaining \n", "2 removed 1800 genes (90%), 200 genes remaining \n", "3 'X_umap' added \n", "4 'gene_stuff' added \n", "5 'log_transformed' added " ] }, - "execution_count": 18, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -559,11 +559,122 @@ "print(logdata)\n", "logdata.uns[\"_omicslog\"]" ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "11c1abb4", + "metadata": {}, + "outputs": [], + "source": [ + "logdata.write(\"test_omicslog.h5ad\")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "06e22f91", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
TimeOperationMessage
02026-07-03 18:08:13obs'cell_type' added
12026-07-03 18:08:15subsetremoved 72 samples (72%), 28 samples remaining
22026-07-03 18:08:17subsetremoved 1800 genes (90%), 200 genes remaining
32026-07-03 18:08:19obsm'X_umap' added
42026-07-03 18:08:19varm'gene_stuff' added
52026-07-03 18:08:22layers'log_transformed' added
\n", + "
" + ], + "text/plain": [ + " Time Operation \\\n", + "0 2026-07-03 18:08:13 obs \n", + "1 2026-07-03 18:08:15 subset \n", + "2 2026-07-03 18:08:17 subset \n", + "3 2026-07-03 18:08:19 obsm \n", + "4 2026-07-03 18:08:19 varm \n", + "5 2026-07-03 18:08:22 layers \n", + "\n", + " Message \n", + "0 'cell_type' added \n", + "1 removed 72 samples (72%), 28 samples remaining \n", + "2 removed 1800 genes (90%), 200 genes remaining \n", + "3 'X_umap' added \n", + "4 'gene_stuff' added \n", + "5 'log_transformed' added " + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "new_adata = ad.read_h5ad(\"test_omicslog.h5ad\")\n", + "new_adata.uns[\"_omicslog\"]" + ] } ], "metadata": { "kernelspec": { - "display_name": ".venv", + "display_name": "omicslog", "language": "python", "name": "python3" }, @@ -577,7 +688,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.14" + "version": "3.14.6" } }, "nbformat": 4, diff --git a/src/omicslog/__init__.py b/src/omicslog/__init__.py index 71c626b..88c3a48 100644 --- a/src/omicslog/__init__.py +++ b/src/omicslog/__init__.py @@ -1,7 +1,7 @@ from omicslog.core import ( LoggedAnnDataStandalone, AnnDataSnapshot, - log_start, + log_start ) __all__ = ["LoggedAnnDataStandalone", "AnnDataSnapshot", "log_start"] diff --git a/src/omicslog/core.py b/src/omicslog/core.py index 40db784..0ad120d 100644 --- a/src/omicslog/core.py +++ b/src/omicslog/core.py @@ -284,6 +284,39 @@ def var(self): def var(self, value): ad.AnnData.var.fset(self, value) + def _unwrap(self) -> ad.AnnData: + """Plain AnnData view of the real underlying data, without the logging proxies. + + anndata's writers use `type(elem)` to look up a serializer, and don't + recognize `_LoggingProxy`, so the proxies must be unwrapped before writing. + """ + return ad.AnnData( + X=super().X, + obs=super().obs, + var=super().var, + uns=self.uns, + obsm=dict(super().obsm), + varm=dict(super().varm), + layers=dict(super().layers), + obsp=dict(super().obsp), + varp=dict(super().varp), + raw=self.raw, + ) + + def write_h5ad(self, filename=None, **kwargs): + return self._unwrap().write_h5ad(filename, **kwargs) + + write = write_h5ad + + def write_loom(self, filename, **kwargs): + return self._unwrap().write_loom(filename, **kwargs) + + def write_zarr(self, store, **kwargs): + return self._unwrap().write_zarr(store, **kwargs) + + def write_csvs(self, dirname, **kwargs): + return self._unwrap().write_csvs(dirname, **kwargs) + # --- snapshot & subsetting --- def _snapshot(self) -> AnnDataSnapshot: @@ -324,4 +357,5 @@ def operation_log(self) -> list[str]: def log_start(adata: ad.AnnData) -> LoggedAnnDataStandalone: - return LoggedAnnDataStandalone.from_anndata(adata) \ No newline at end of file + return LoggedAnnDataStandalone.from_anndata(adata) + diff --git a/test_omicslog.h5ad b/test_omicslog.h5ad new file mode 100644 index 0000000..65cfad6 Binary files /dev/null and b/test_omicslog.h5ad differ