Repository: scverse/scanpy

Issue:
score_genes throws AttributeError if len(gene_list) = 1 and raw is present
- [x] I have checked that this issue has not already been reported.
- [x] I have confirmed this bug exists on the latest version of scanpy.
- [ ] (optional) I have confirmed this bug exists on the master branch of scanpy.

---

My guess is that the issue is that `adata.raw[:, '1'].X` returns an `array` but that `adata[:, '1'].X` returns an `ArrayView`.

### Minimal code sample (that we can copy&paste without having any data)

```python
import scanpy as sc
adata = sc.datasets.blobs()
adata.raw = adata
sc.tl.score_genes(adata, ['0'])
```

```pytb
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-9-c835b3ad221d> in <module>
      2 adata = sc.datasets.blobs()
      3 adata.raw = adata
----> 4 sc.tl.score_genes(adata, ['0'])

~/miniconda3/envs/spols200618/lib/python3.7/site-packages/scanpy/tools/_score_genes.py in score_genes(adata, gene_list, ctrl_size, gene_pool, n_bins, score_name, random_state, copy, use_raw)
    174     elif len(gene_list) == 1:
    175         if _adata[:, gene_list].X.ndim == 2:
--> 176             vector = _adata[:, gene_list].X.toarray()[:, 0] # new anndata
    177         else:
    178             vector = _adata[:, gene_list].X  # old anndata

AttributeError: 'numpy.ndarray' object has no attribute 'toarray'
```

#### Versions

<details>

WARNING: If you miss a compact list, please try `print_header`!
-----
anndata     0.7.4
scanpy      1.6.0
sinfo       0.3.1
-----
PIL                 7.1.2
anndata             0.7.4
backcall            0.2.0
bottleneck          1.3.2
cairo               1.19.1
cffi                1.14.0
cloudpickle         1.3.0
colorama            0.4.3
cycler              0.10.0
cython_runtime      NA
dask                2.18.1
dateutil            2.8.1
decorator           4.4.2
get_version         2.1
google              NA
h5py                2.10.0
igraph              0.8.2
importlib_metadata  1.6.1
ipykernel           5.3.0
ipyparallel         6.3.0
ipython_genutils    0.2.0
jedi                0.17.0
joblib              0.15.1
kiwisolver          1.2.0
legacy_api_wrap     0.0.0
leidenalg           0.8.0
llvmlite            0.32.1
louvain             0.7.0
matplotlib          3.2.1
mpl_toolkits        NA
natsort             7.0.1
numba               0.49.1
numexpr             2.7.1
numpy               1.17.5
packaging           20.4
pandas              1.0.5
parso               0.7.0
pexpect             4.8.0
pickleshare         0.7.5
pkg_resources       NA
prompt_toolkit      3.0.5
psutil              5.7.0
ptyprocess          0.6.0
pygments            2.6.1
pyparsing           2.4.7
pytz                2020.1
scanpy              1.6.0
scipy               1.4.1
setuptools_scm      NA
sinfo               0.3.1
six                 1.15.0
sklearn             0.23.1
storemagic          NA
tables              3.6.1
texttable           1.6.2
tlz                 0.10.0
toolz               0.10.0
tornado             6.0.4
traitlets           4.3.3
typing_extensions   NA
wcwidth             0.2.4
yaml                5.3.1
zipp                NA
zmq                 19.0.1
-----
IPython             7.15.0
jupyter_client      6.1.3
jupyter_core        4.6.3
jupyterlab          2.1.4
notebook            6.0.3
-----
Python 3.7.6 | packaged by conda-forge | (default, Jun  1 2020, 18:57:50) [GCC 7.5.0]
Linux-4.12.14-lp151.28.44-default-x86_64-with-glibc2.10
16 logical CPU cores, x86_64
-----
Session information updated at 2020-08-28 14:42

</details>



Discussion:
I'm happy to help with a PR if you tell me how to tackle this.

Resolving patch:
diff --git a/scanpy/tools/_score_genes.py b/scanpy/tools/_score_genes.py
--- a/scanpy/tools/_score_genes.py
+++ b/scanpy/tools/_score_genes.py
@@ -110,9 +110,7 @@ def score_genes(
     gene_list = set(gene_list_in_var[:])
 
     if len(gene_list) == 0:
-        logg.warning('provided gene list has length 0, scores as 0')
-        adata.obs[score_name] = 0
-        return adata if copy else None
+        raise ValueError("No valid genes were passed for scoring.")
 
     if gene_pool is None:
         gene_pool = list(var_names)
@@ -164,21 +162,7 @@ def score_genes(
     else:
         X_control = np.nanmean(X_control, axis=1)
 
-    if len(gene_list) == 0:
-        # We shouldn't even get here, but just in case
-        logg.hint(
-            f'could not add \n'
-            f'    {score_name!r}, score of gene set (adata.obs)'
-        )
-        return adata if copy else None
-    elif len(gene_list) == 1:
-        if _adata[:, gene_list].X.ndim == 2:
-            vector = _adata[:, gene_list].X.toarray()[:, 0] # new anndata
-        else:
-            vector = _adata[:, gene_list].X  # old anndata
-        score = vector - X_control
-    else:
-        score = X_list - X_control
+    score = X_list - X_control
 
     adata.obs[score_name] = pd.Series(np.array(score).ravel(), index=adata.obs_names)
 


Test patch:
diff --git a/scanpy/tests/test_score_genes.py b/scanpy/tests/test_score_genes.py
--- a/scanpy/tests/test_score_genes.py
+++ b/scanpy/tests/test_score_genes.py
@@ -175,3 +175,18 @@ def mock_fn(x, axis):
     dense_scores = adata.obs['Test'].values
 
     np.testing.assert_allclose(sparse_scores, dense_scores)
+
+
+def test_missing_genes():
+    adata = _create_adata(100, 1000, p_zero=0, p_nan=0)
+    # These genes have a different length of name
+    non_extant_genes = _create_random_gene_names(n_genes=3, name_length=7)
+
+    with pytest.raises(ValueError):
+        sc.tl.score_genes(adata, non_extant_genes)
+
+
+def test_one_gene():
+    # https://github.com/theislab/scanpy/issues/1395
+    adata = _create_adata(100, 1000, p_zero=0, p_nan=0)
+    sc.tl.score_genes(adata, [adata.var_names[0]])