Install Scanpy for Single-Cell Analysis with Conda (Apple Silicon)
Install Scanpy 1.12.2 in an isolated conda environment on an Apple Silicon Mac, with native arm64 packages, Leiden clustering support, and a real smoke test.
Scanpy is the workhorse of single-cell analysis in Python. If you work with scRNA-seq data, it covers the whole pipeline: quality control, normalization, PCA, neighbor graphs, UMAP, Leiden clustering, and differential expression, all built on top of the AnnData data structure. It pulls in a big scientific stack (NumPy, SciPy, pandas, scikit-learn, numba, matplotlib), which is exactly why you want it in its own conda environment rather than smeared across your base install.
The idea behind this whole series is simple: the exact build a tool needs lives inside its environment, independent of your system Python. Break something, delete the env, recreate it in under a minute. Nothing else on your machine notices. This guide installs Scanpy that way on an Apple Silicon Mac, with native arm64 binaries.
Prerequisites
You need a working conda. If you have not set that up yet, start with our Miniconda on Apple Silicon guide and come back here. This walkthrough was run on an Apple Silicon Mac (arm64), macOS 26.5.2, with conda 25.5.1 using the fast libmamba solver.
TL;DR: copy-paste install
conda create -n bu-scanpy --override-channels -c conda-forge -c bioconda scanpy leidenalg python-igraph -y
conda activate bu-scanpy
python -c "import scanpy as sc; print(sc.__version__)"That is it. The three important choices baked into that one line: an isolated env named bu-scanpy, --override-channels so conda never touches the defaults channel (which trips a Terms-of-Service gate on conda 25.x), and leidenalg + python-igraph added explicitly so clustering works out of the box.
Step-by-step
1. Create an isolated environment
conda create -n bu-scanpy --override-channels -c conda-forge -c bioconda scanpy leidenalg python-igraph -yA few things worth understanding here:
--override-channels -c conda-forge -c biocondatells conda to use only these two channels and skipdefaults. On conda 25.x, touchingdefaultsthrows aCondaToSNonInteractiveErrorabout Terms of Service; sidestepping it entirely is cleaner than accepting the ToS.- conda-forge is listed first, on purpose. Scanpy ships as a fresh
noarchpackage (1.12.2) on conda-forge. The bioconda copy is frozen at an ancient 1.7.2, so channel order matters: conda-forge wins and you get the modern release. leidenalgandpython-igraphare separate packages. Leiden clustering is optional and is not pulled in automatically. Under conda you install these two individually (the pip-stylescanpy[leiden]extra does not apply here).
Here is the real solved plan, trimmed to the key packages:
Channels:
- conda-forge
- bioconda
Platform: osx-arm64
## Package Plan ##
environment location: /opt/homebrew/Caskroom/miniconda/base/envs/bu-scanpy
added / updated specs:
- leidenalg
- python-igraph
- scanpy
The following packages will be downloaded:
package | build
---------------------------|-----------------
anndata-0.13.2 | pyhd8ed1ab_0 142 KB conda-forge
igraph-1.0.1 | h1ee73af_0 1.5 MB conda-forge
leidenalg-0.12.0 | py314h4ed92d5_0 86 KB conda-forge
numpy-2.4.6 | py314hb79c6fa_0 6.7 MB conda-forge
python-igraph-1.0.0 | py314h93ecee7_0 633 KB conda-forge
scanpy-1.12.2 | pyhd8ed1ab_0 1.9 MB conda-forge
scipy-1.18.0 | py314h18e1515_0 13.5 MB conda-forge
------------------------------------------------------------
Total: 100.7 MB
And the tail of the run:
#
# To activate this environment, use
#
# $ conda activate bu-scanpy
#
# To deactivate an active environment, use
#
# $ conda deactivate
real 44.57
# ...
The whole solve-and-install took about 45 seconds (real 44.57 seconds) and downloaded 100.7 MB. Unpacked, the environment measures about 766 MB on disk (measured with du -sh; because conda hardlinks shared packages, the incremental cost of an extra env is usually less), most of it the compiled scientific stack (SciPy, scikit-learn, numba/llvmlite behind UMAP). That is normal for a full single-cell setup.
python-3.14.6 with numpy-2.4.6. If a downstream dependency of yours needs a specific interpreter, add python=3.12 (or your target) to the create command.2. Activate and confirm
conda activate bu-scanpyYour prompt should now show (bu-scanpy). Everything below runs inside that env.
Apple Silicon / architecture note
You do not need Rosetta for Scanpy. The scanpy package itself is pure-Python noarch, and every compiled dependency it needs has a native osx-arm64 build on conda-forge. You can see that in the install list: NumPy, SciPy, leidenalg and Python all resolved to osx-arm64, while the pure-Python pieces resolved to noarch:
leidenalg conda-forge/osx-arm64::leidenalg-0.12.0-py314h4ed92d5_0
numpy conda-forge/osx-arm64::numpy-2.4.6-py314hb79c6fa_0
python conda-forge/osx-arm64::python-3.14.6-h156bc91_100_cp314
scanpy conda-forge/noarch::scanpy-1.12.2-pyhd8ed1ab_0
scipy conda-forge/osx-arm64::scipy-1.18.0-py314h18e1515_0
So a plain conda create ... -c conda-forge -c bioconda on an M-series Mac gives you native arm64 binaries. Do not force CONDA_SUBDIR=osx-64 for a normal Scanpy setup, that would run everything under Rosetta and only slow you down. Reserve that fallback for the rare case where some unrelated package you add later has no arm64 build.
Verify the install
Scanpy is a Python-first tool, so we verify by importing it and running a tiny real analysis. Build a small AnnData matrix, normalize it, and log-transform it:
import scanpy as sc
import numpy as np
from anndata import AnnData
adata = AnnData(np.random.poisson(1.0, size=(50, 20)).astype("float32"))
sc.pp.normalize_total(adata)
sc.pp.log1p(adata)
print(f"scanpy {sc.__version__} AnnData {adata.shape} normalized+logged OK")Real output on the test machine:
scanpy 1.12.2 AnnData (50, 20) normalized+logged OK
That single line proves a lot: Scanpy 1.12.2 imported cleanly, AnnData built a 50-cell by 20-gene matrix, and the two most common preprocessing steps (normalize_total then log1p) both ran. If you get that, your environment is healthy.
To confirm the clustering libraries loaded too:
python -c "import leidenalg, igraph; print('leiden ok')"leiden ok check is a doc-grounded sanity command; run it yourself to confirm your own env, as its output was not captured in this transcript.Common errors and fixes
| Error | Fix |
|---|---|
CondaToSNonInteractiveError / "Terms of Service have not been accepted" for defaults | Do not use defaults. Create the env with --override-channels -c conda-forge -c bioconda so conda never touches it. |
PackagesNotFoundError: scanpy | Scanpy lives on conda-forge, not defaults. Add -c conda-forge with --override-channels. |
You got scanpy 1.7.2 (very old) | That is the stale bioconda build. List conda-forge first so it wins, and you get 1.12.2. |
ModuleNotFoundError when calling sc.tl.leiden | Leiden is optional. Install leidenalg and python-igraph into the same env (both are in the command above). |
"scanpy requires a different Python" when pinning python=3.10 or 3.11 | Scanpy 1.12.x needs Python 3.12+. Use python=3.12, or pin an older scanpy=1.9 for older Python. |
Import-time NumPy ABI errors after pip install scanpy | Let conda own the whole scientific stack. Do not pip-install numpy/scipy/scanpy on top of conda-provided ones. |
Managing the environment
Update everything to the latest compatible builds:
conda update -n bu-scanpy --override-channels -c conda-forge -c bioconda --allSnapshot the env so a collaborator can reproduce it exactly:
conda env export -n bu-scanpy --no-builds > environment.ymlFor a truly reproducible setup, pin the versions you care about, for example scanpy=1.12.2 anndata=0.13.* python=3.12. That stops a future install from silently pulling a newer NumPy and drifting your results.
Remove the env entirely when you are done, this is the payoff of isolation:
conda remove -n bu-scanpy --allNothing else on your system is affected, and you can rebuild in about 45 seconds.
Next steps
- New to conda channels and env layout? Read conda environments and bioconda channels.
- Need to quantify your reads before single-cell analysis? See installing Salmon.