La sortie de numpy.percentile
n'est pas toujours triée
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()
[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)
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:
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)]
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)
):
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:
(lerp(a, b, t1) - lerp(a, b, t0)) * (b - a) * (t1 - t0) >= 0
)a <= lerp(a, b, t) <= b
)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.
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.
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.