average_precision_score not working as expected

### Describe the bug

When compute AP with average_precision_score, I get unexpected results. The y_scores (output from the models) are very low for positive samples, so my AP should be very low. Instead I get a perfect 1.0 AP score.

### Steps/Code to Reproduce

```python
from sklearn.metrics import average_precision_score

# labels
y_true = [1, 1, 1, 1, 1]  # 5 positives
# Predicted scores
y_scores = [0.1, 0.3, 0.1, 0.2, 0.1]  # Model's confidence in predictions

# Calculate average precision
average_precision = average_precision_score(y_true, y_scores)
print("AP score:", average_precision)
```

### Expected Results

An AP score close to 0.

### Actual Results

AP score: 1.0

### Versions

```shell
System:
    python: 3.9.19 (main, May  6 2024, 19:43:03)  [GCC 11.2.0]
executable: /bin/python
   machine: Linux-5.15.0-121-generic-x86_64-with-glibc2.31

Python dependencies:
      sklearn: 1.5.2
          pip: 24.2
   setuptools: 75.1.0
        numpy: 1.26.4
        scipy: 1.13.1
       Cython: None
       pandas: 1.4.0
   matplotlib: 3.5.3
       joblib: 1.4.2
threadpoolctl: 3.5.0

Built with OpenMP: True

threadpoolctl info:
       user_api: blas
   internal_api: openblas
    num_threads: 32
         prefix: libopenblas
       filepath: user/anaconda3/envs/perception39/lib/python3.9/site-packages/numpy.libs/libopenblas64_p-r0-0cf96a72.3.23.dev.so
        version: 0.3.23.dev
threading_layer: pthreads
   architecture: SkylakeX

       user_api: blas
   internal_api: openblas
    num_threads: 32
         prefix: libopenblas
       filepath: user/anaconda3/envs/perception39/lib/python3.9/site-packages/scipy.libs/libopenblasp-r0-01191904.3.27.so
        version: 0.3.27
threading_layer: pthreads
   architecture: SkylakeX

       user_api: openmp
   internal_api: openmp
    num_threads: 32
         prefix: libgomp
       filepath: user/anaconda3/envs/perception39/lib/python3.9/site-packages/scikit_learn.libs/libgomp-a34b3233.so.1.0.0
        version: None
```

COMMENT:
TL;DR: I believe [<code>sklearn.metrics.average_precision_score</code>](https://scikit-learn.org/1.5/modules/generated/sklearn.metrics.average_precision_score.html) is working correctly.

Consider OP's siutation: a binary classification problem with almost all samples labelled positive and low `y_scores`.

As @yuanx749 pointed out in the following comment:

> Precision is the ratio TP / (TP + FP). As all the true labels are [all] positive, there will be no false positive (FP=0), and it seems  reasonable that the precision is always 1.

 the set of all $\left( \mathrm{Recall}, \mathrm{Precision} \right)$ pairs for this problem has the form $\left( \mathrm{Recall}, 1 \right)$. This means the precision-recall curve $P(\mathrm{Recall})$ is constant at 1 almost everywhere. (see the precision-recall displays provided by @glemaitre.) Hence, calculus tells us that:

$$\begin{array}{cl}
\mbox{Average Precision Score} &:=& \displaystyle\int_0^1 P(\mathrm{Recall}) \ d\left(\mathrm{Recall}\right) \\
&=& \displaystyle\int_0^1 1 \ d\left(\mathrm{Recall}\right) \\
&=& \mbox{length of the unit interval $[0,1]$} \\
&=& 1.
\end{array}$$

Thus, any valid numerical integration method should yield a result close to 1.

Regarding

> But scikit-learn assumes a recall of 1 when the threshold is 0, this is not true in object detection.

This won't affect the calculation because a single point has zero area under curve.

Therefore, I agree with @yuanx749's recommendation to consider a different evaluation metric; or to adjust how the average precision score is interpreted in the context of object detection.

Labels: Bug