Repository: scverse/scanpy

Issue:
Bug when running PCA in chunked mode
- [x] I have checked that this issue has not already been reported.
- [ ] I have confirmed this bug exists on the latest version of scanpy.
- [x] (optional) I have confirmed this bug exists on the master branch of scanpy.

---

```python
sc.tl.pca(adata, svd_solver='arpack', use_highly_variable=True, chunked=True, chunk_size=1000)
```

```pytb
TypeError: unsupported operand type(s) for -: 'datetime.datetime' and 'int'
```

I believe the error is due overwriting of `start` variable.
 It is declared in [line 116](https://github.com/theislab/scanpy/blob/master/scanpy/preprocessing/_pca.py#L116) but is overwritten by loop variable of same name in [line 172](https://github.com/theislab/scanpy/blob/master/scanpy/preprocessing/_pca.py#L172)

#### Versions

<details>

-----
anndata     0.7.5
scanpy      1.6.0
sinfo       0.3.1
-----
PIL                 8.1.0
anndata             0.7.5
asciitree           NA
cffi                1.14.3
cloudpickle         1.6.0
constants           NA
cycler              0.10.0
cython_runtime      NA
dask                2020.12.0
dateutil            2.8.1
fasteners           NA
get_version         2.1
h5py                3.1.0
highs_wrapper       NA
igraph              0.8.3
joblib              1.0.0
kiwisolver          1.3.1
legacy_api_wrap     1.2
leidenalg           0.8.3
llvmlite            0.35.0
matplotlib          3.3.3
mpl_toolkits        NA
msgpack             1.0.2
natsort             7.1.0
numba               0.52.0
numcodecs           0.7.2
numexpr             2.7.2
numpy               1.19.4
packaging           20.8
pandas              1.2.0
pkg_resources       NA
psutil              5.8.0
pyparsing           2.4.7
pytz                2020.5
scanpy              1.6.0
scipy               1.6.0
setuptools_scm      NA
sinfo               0.3.1
six                 1.15.0
sklearn             0.24.0
sparse              0.11.2
tables              3.6.1
tblib               1.7.0
texttable           1.6.3
tlz                 0.11.1
toolz               0.11.1
typing_extensions   NA
yaml                5.3.1
zarr                2.6.1
-----
Python 3.8.5 (default, Sep  4 2020, 07:30:14) [GCC 7.3.0]
Linux-3.10.0-1062.4.1.el7.x86_64-x86_64-with-glibc2.10
2 logical CPU cores, x86_64
-----


</details>



Discussion:

Resolving patch:
diff --git a/scanpy/preprocessing/_pca.py b/scanpy/preprocessing/_pca.py
--- a/scanpy/preprocessing/_pca.py
+++ b/scanpy/preprocessing/_pca.py
@@ -113,7 +113,7 @@ def pca(
              Explained variance, equivalent to the eigenvalues of the
              covariance matrix.
     """
-    start = logg.info(f'computing PCA')
+    logg_start = logg.info(f'computing PCA')
 
     # chunked calculation is not randomized, anyways
     if svd_solver in {'auto', 'randomized'} and not chunked:
@@ -239,7 +239,7 @@ def pca(
             adata.varm['PCs'] = pca_.components_.T
         adata.uns['pca']['variance'] = pca_.explained_variance_
         adata.uns['pca']['variance_ratio'] = pca_.explained_variance_ratio_
-        logg.info('    finished', time=start)
+        logg.info('    finished', time=logg_start)
         logg.debug(
             'and added\n'
             '    \'X_pca\', the PCA coordinates (adata.obs)\n'
@@ -249,7 +249,7 @@ def pca(
         )
         return adata if copy else None
     else:
-        logg.info('    finished', time=start)
+        logg.info('    finished', time=logg_start)
         if return_info:
             return (
                 X_pca,


Test patch:
diff --git a/scanpy/tests/test_pca.py b/scanpy/tests/test_pca.py
--- a/scanpy/tests/test_pca.py
+++ b/scanpy/tests/test_pca.py
@@ -114,3 +114,27 @@ def test_pca_reproducible(pbmc3k_normalized, array_type, float_dtype):
     assert_equal(a, b)
     # Test that changing random seed changes result
     assert not np.array_equal(a.obsm["X_pca"], c.obsm["X_pca"])
+
+
+def test_pca_chunked(pbmc3k_normalized):
+    # https://github.com/theislab/scanpy/issues/1590
+    # But also a more general test
+
+    # Subsetting for speed of test
+    pbmc = pbmc3k_normalized[::6].copy()
+    pbmc.X = pbmc.X.astype(np.float64)
+    chunked = sc.pp.pca(pbmc3k_normalized, chunked=True, copy=True)
+    default = sc.pp.pca(pbmc3k_normalized, copy=True)
+
+    # Taking absolute value since sometimes dimensions are flipped
+    np.testing.assert_allclose(
+        np.abs(chunked.obsm["X_pca"]), np.abs(default.obsm["X_pca"])
+    )
+    np.testing.assert_allclose(np.abs(chunked.varm["PCs"]), np.abs(default.varm["PCs"]))
+    np.testing.assert_allclose(
+        np.abs(chunked.uns["pca"]["variance"]), np.abs(default.uns["pca"]["variance"])
+    )
+    np.testing.assert_allclose(
+        np.abs(chunked.uns["pca"]["variance_ratio"]),
+        np.abs(default.uns["pca"]["variance_ratio"]),
+    )