Numpy: BOGUE: la sortie numpy.percentile n'est pas triée

Créé le 12 oct. 2019  ·  16Commentaires  ·  Source: numpy/numpy

La sortie de numpy.percentile n'est pas toujours triée

Exemple de code de reproduction:

import numpy as np
q = np.arange(0, 1, 0.01) * 100
percentile = np.percentile(np.array([0, 1, 1, 2, 2, 3, 3 , 4, 5, 5, 1, 1, 9, 9 ,9, 8, 8, 7]) * 0.1, q)
equals_sorted = np.sort(percentile) == percentile
print(equals_sorted)
assert equals_sorted.all()

Message d'erreur:

[Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai
Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai
Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai
Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai
Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai
Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai
Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai Vrai
Vrai Vrai Vrai Vrai Vrai Faux Faux Vrai Vrai Vrai Vrai Faux
Vrai Vrai Vrai Faux]
AssertionError Traceback (dernier appel le plus récent)
dans
1 q = np.percentile (np.array ([0, 1, 1, 2, 2, 3, 3, 4, 5, 5, 1, 1, 9, 9, 9, 8, 8, 7]) * 0,1, np. Plage (0, 1, 0,01) * 100)
2 equals_sorted = np.sort (q) == q
----> 3 assert equals_sorted.all ()

AssertionError:

Informations sur la version Numpy / Python:

1.17.2 3.6.8 (v3.6.8: 3c6b436a57, 24 décembre 2018, 02:04:31)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)]

00 - Bug numpy.lib good first issue

Commentaire le plus utile

Hé, il semble y avoir eu une mise à jour de l'une des réponses de stackexchange fournies par @ eric-wieser avec une bonne interpolation alternative.
Le fil de discussion comprend une preuve de monotonie et le correctif proposé semble résoudre tous les problèmes mentionnés.
Si cela avait du sens pour le problème, je serais prêt à l'implémenter en tant que premier commit, ou quelqu'un d'autre pourrait l'essayer.
20191209_020250

Tous les 16 commentaires

Pourquoi vous attendez-vous à ce qu'il soit trié? Le centile est par élément - les sorties sont dans l'ordre des entrées.

Salut !
En effet, le centile est elmenet - quand on considère q , ce qui dans notre cas est
np.arange(0, 1, 0.01) * 100 .
Je m'attends à ce que la sortie soit triée car q est triée.

Il existe des erreurs numériques dans une seule ULP, qui diffèrent pour différentes entrées avec la même valeur de sortie. Je doute qu'il y ait quelque chose à faire à ce sujet.

Un cas d'échec légèrement réduit:

In [40]: np.percentile(np.array([0, 1, 1, 2, 2, 3, 3 , 4, 5, 5, 1, 1, 9, 9 ,9, 8, 8, 7]) * 0.1, [89, 90, 95, 96, 98, 99])
Out[40]: array([0.9, 0.9, 0.9, 0.9, 0.9, 0.9])

In [41]: np.diff(_)
Out[41]:
array([-1.11022302e-16,  2.22044605e-16, -1.11022302e-16,  1.11022302e-16,
       -1.11022302e-16])

ici montrant la non-tri-ness via le diff.

Je pense que nous pouvons probablement faire quelque chose à ce sujet. Je pense que cela revient à la stabilité de ces lignes, qui effectuent une opération lerp (essentiellement add(v_below*weights_below, v_above*weights_above) ):

https://github.com/numpy/numpy/blob/b9fa88eec62e34e906689408096beb2450830d9a/numpy/lib/function_base.py#L3907 -L3908

https://github.com/numpy/numpy/blob/b9fa88eec62e34e906689408096beb2450830d9a/numpy/lib/function_base.py#L3928 -L3929

https://github.com/numpy/numpy/blob/b9fa88eec62e34e906689408096beb2450830d9a/numpy/lib/function_base.py#L3939 -L3942

Il y a un tas de compromis à faire lors de l'interpolation linéaire des valeurs à virgule flottante, mais je soupçonne qu'il y a un choix «correct» ici, et nous ne l'avons tout simplement pas fait.

Quelques informations supplémentaires ici: https://math.stackexchange.com/questions/907327/accurate-floating-point-linear-interpolation

Ouais, je suis d'accord, +1 sur la réorganisation des opérations pour qu'elle soit strictement monotone (numériquement). Ce serait bien si ce n'est pas pire non plus, ou du moins presque identique en termes de précision. Je suis sûr que nous n'avons vraiment pas à nous soucier de quelques opérations / vitesse supplémentaires ici.

EDIT: Marqué comme bon premier numéro. Mais après cela, il s'agit probablement d'une réorganisation assez simple dans le code python.

Je serais intéressé à aborder cette question. Je regardais certains des cas défaillants et j'ai remarqué qu'ils impliquaient tous une interpolation linéaire entre le même nombre. c'est-à-dire que dans l'exemple d'Eric, tous les centiles qu'il a énumérés sont situés entre deux 9. Par conséquent, je pense que l'interpolation linéaire entre eux doit être exactement correcte? la résolution du problème de l'interpolation linéaire entre deux nombres identiques semble que cela résoudrait les problèmes présentés dans ce bogue et ne causerait pas de perte de performances notable. Si toutefois nous voulons nous assurer que l'interpolation linéaire sera toujours monotone, nous pouvons le faire, mais cela nécessitera une fonction par morceaux qui, je pense, diminuerait les performances.

@ ngonzo95 il devrait y avoir un moyen d'épeler l'arithmétique de l'interpolation différemment pour y parvenir, c'est-à-dire changer / réorganiser la formule utilisée pour le calcul (de sorte qu'elle soit mathématiquement identique, mais garantit numériquement la monotonie). Aucun calcul par morceaux ne devrait être nécessaire.

Aucun calcul par morceaux ne devrait être nécessaire.

Cela dépend de vos besoins sur lerp . Certains dont nous pouvons ou non nous soucier:

  • monotone ( (lerp(a, b, t1) - lerp(a, b, t0)) * (b - a) * (t1 - t0) >= 0 )
  • borné ( a <= lerp(a, b, t) <= b )
  • symétrique ( lerp(a, b, t) == lerp(b, a, 1-t) )

( 0 <= t <= 1 )

Oh OK, je ne m'attendais pas à ce que ce soit nécessaire par morceaux, mais je ne connais pas assez bien les intrinsèques de cela, je suppose.

en l'examinant davantage, j'ai découvert que la fonction a + (ba) * t a la propriété d'être à la fois monotone (définition mentionnée ci-dessus) et cohérente (lerp (a, a, t) = a). Je pense que cela devrait être suffisant pour les exigences des fonctions. Il semble que l'un des principaux inconvénients de cette fonction soit que lerp (a, b, 1)! = B. Cependant, je pense que la façon dont nous calculons les poids garantit que 0 <= t <1.

Il semble que l'un des principaux inconvénients de cette fonction soit que lerp (a, b, 1)! = B. Cependant, je pense que la façon dont nous calculons les poids garantit que 0 <= t <1.

Notez que malheureusement lerp(a, b. 1-eps) > b) est possible avec cette formulation.

Nouveau sur l'open source.
Je voulais résoudre cela comme mon bon premier problème. Comment puis-je contribuer? Y a-t-il des prérequis?

Je regardais certains des cas défaillants et j'ai remarqué qu'ils impliquaient tous une interpolation linéaire entre le même nombre

Dans scikit-learn, nous sommes récemment tombés sur ce numéro: https://github.com/scikit-learn/scikit-learn/issues/15733

Puisque nous nous attendons à ce que q soit strictement croissant, nous pouvons appliquer np.maximum.accumulate réorganiser le tableau. Cependant, si nous pouvions résoudre le problème directement dans NumPy, ce serait formidable. Y a-t-il un endroit où nous pouvons creuser pour avoir une bonne solution?

@glemaitre : Toutes les lignes pertinentes de numpy sont liées dans mon commentaire ci-dessus, https://github.com/numpy/numpy/issues/14685#issuecomment -541467915

Hé, il semble y avoir eu une mise à jour de l'une des réponses de stackexchange fournies par @ eric-wieser avec une bonne interpolation alternative.
Le fil de discussion comprend une preuve de monotonie et le correctif proposé semble résoudre tous les problèmes mentionnés.
Si cela avait du sens pour le problème, je serais prêt à l'implémenter en tant que premier commit, ou quelqu'un d'autre pourrait l'essayer.
20191209_020250

Notez qu'il y a un autre problème avec lerp dans quantile() : les valeurs inf ne sont pas gérées correctement, voir # 12282.

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

dcsaba89 picture dcsaba89  ·  3Commentaires

astrofrog picture astrofrog  ·  4Commentaires

ghost picture ghost  ·  4Commentaires

kevinzhai80 picture kevinzhai80  ·  4Commentaires

keithbriggs picture keithbriggs  ·  3Commentaires