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",
+ " Time | \n",
+ " Operation | \n",
+ " Message | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " 2026-07-03 18:08:13 | \n",
+ " obs | \n",
+ " 'cell_type' added | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " 2026-07-03 18:08:15 | \n",
+ " subset | \n",
+ " removed 72 samples (72%), 28 samples remaining | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 2026-07-03 18:08:17 | \n",
+ " subset | \n",
+ " removed 1800 genes (90%), 200 genes remaining | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 2026-07-03 18:08:19 | \n",
+ " obsm | \n",
+ " 'X_umap' added | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " 2026-07-03 18:08:19 | \n",
+ " varm | \n",
+ " 'gene_stuff' added | \n",
+ "
\n",
+ " \n",
+ " | 5 | \n",
+ " 2026-07-03 18:08:22 | \n",
+ " layers | \n",
+ " 'log_transformed' added | \n",
+ "
\n",
+ " \n",
+ "
\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