Allow NearestNeighbor radius_neighbors to accept an array for the `radius` keyword argument

### Describe the workflow you want to enable

In KDTree and BallTree, there is a way to pass in a radius array that queries nearest-neighbors for each sample point for different radii.

- BallTree: https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.BallTree.html#sklearn.neighbors.BallTree.query_radius
- KDTree: https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KDTree.html#sklearn.neighbors.KDTree.query_radius

However, in `NearestNeighbors` class, the https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.NearestNeighbors.html#sklearn.neighbors.NearestNeighbors.radius_neighbors function only allows a single radius. 

In my current application, I am first computing the distance to the say Kth nearest-neighbor, `R` for each sample point in some space, and then for each sample point, computing the number of neighbors within that radius in some other spaces:

```
neigh = NearestNeighbors(
        n_neighbors=k,
        algorithm=algorithm,
        metric=metric,
        n_jobs=n_jobs
    ).fit(dists)

dists, _ = neigh.kneighbors()
radius_per_sample = dists[:, -1]

# compute on the subspace of XZ
 xz_data = np.hstack((X, Z))
 neigh = compute_nn(
        xz_data, algorithm=algorithm, metric='forest', k=knn_here, n_jobs=n_jobs)

# here, though I have to loop over each sample
for idx in range(n_samples):
        num_nn = neigh.radius_neighbors(radius=radius_per_sample[idx])

# If I repeat this multiple times across different subspaces, it is a bit cumbersome
```

### Describe your proposed solution

Allow `radius` parameter in `NearestNeighbors.radius_neighbors` to be an array of shape `(n_samples,)`, which would have a similar functionality as the KDTree and BallTree.

Presumably, this would be possible for BruteForce as well, although maybe computationally more expensive.

### Describe alternatives you've considered, if relevant

Alternatively, I showed how users would loop through samples just to pass in a different radius. Since the API of KDTree and BallTree inherently have this ability, it seems natural to allow `radius` in `NearestNeighbors.radius_neighbors` to be an array.

### Additional context

I am happy to submit a PR if this is deemed relevant.

COMMENT:
So the issue with @TomDLT's solution is that this is super slow if we want a separate radius per sample. For example, I only want to query the radius_neighbors of the first sample with radius_0, then the second sample with radius_1. The current API doesn't allow this to work. For example:

```
neigh_est_ = NearestNeighbors(
            n_neighbors=n_neighbors,
            algorithm=self.algorithm,
            metric="precomputed",
            n_jobs=self.n_jobs,
        )

# compute the distance matrix (n_samples, n_samples)
dists = _compute_distance_matrix(X)

neigh_est_.fit(dists)

num_nn_data = np.zeros((n_samples,))
for idx in range(n_samples):
        nn = neigh_est_.radius_neighbors( radius=radius_per_sample[idx], return_distance=False)
        num_nn_data[idx] = len(nn)
```


produces the errors:

```
~/miniforge3/envs/sktree/lib/python3.9/site-packages/sklearn/neighbors/_base.py in radius_neighbors(self, X, radius, return_distance, sort_results)
   1215                 results = neigh_dist, neigh_ind
   1216             else:
-> 1217                 neigh_ind_list = sum(chunked_results, [])
   1218                 results = _to_object_array(neigh_ind_list)
   1219 

~/miniforge3/envs/sktree/lib/python3.9/site-packages/sklearn/metrics/pairwise.py in pairwise_distances_chunked(X, Y, reduce_func, metric, n_jobs, working_memory, **kwds)
   1997         else:
   1998             X_chunk = X[sl]
-> 1999         D_chunk = pairwise_distances(X_chunk, Y, metric=metric, n_jobs=n_jobs, **kwds)
   2000         if (X is Y or Y is None) and PAIRWISE_DISTANCE_FUNCTIONS.get(
   2001             metric, None

~/miniforge3/envs/sktree/lib/python3.9/site-packages/sklearn/metrics/pairwise.py in pairwise_distances(X, Y, metric, n_jobs, force_all_finite, **kwds)
   2137 
   2138     if metric == "precomputed":
-> 2139         X, _ = check_pairwise_arrays(
   2140             X, Y, precomputed=True, force_all_finite=force_all_finite
   2141         )

~/miniforge3/envs/sktree/lib/python3.9/site-packages/sklearn/metrics/pairwise.py in check_pairwise_arrays(X, Y, precomputed, dtype, accept_sparse, force_all_finite, copy)
    182     if precomputed:
    183         if X.shape[1] != Y.shape[0]:
--> 184             raise ValueError(
    185                 "Precomputed metric requires shape "
    186                 "(n_queries, n_indexed). Got (%d, %d) "

ValueError: Precomputed metric requires shape (n_queries, n_indexed). Got (1, 1) for 1000 indexed.
```

However, if using BallTree/KDTree, the query allows each sample to be passed to `radius_neighbors` separately such as `X[idx, :]`.

Labels: New Feature