Vraag Python: functie-onderbrekingen sorteren in de aanwezigheid van nan


sorted([2, float('nan'), 1]) komt terug [2, nan, 1]

(Tenminste over Activestate Python 3.1 implementatie.)

Ik begrijp het nan is een raar object, dus ik zou niet verbaasd zijn als het op willekeurige plaatsen in het sorteerresultaat verschijnt. Maar het vervuilt ook de soort voor de niet-nan getallen in de container, wat echt onverwacht is.

Ik vroeg een gerelateerde vraag over maxen op basis daarvan begrijp ik waarom sort werkt zo. Maar zou dit als een bug moeten worden beschouwd?

Documentatie zegt alleen "Retourneer een nieuwe gesorteerde lijst [...]" zonder details te specificeren.

BEWERK: Ik ben het er nu mee eens dat dit niet in strijd is met de IEEE-standaard. Het is echter een fout vanuit elk gezichtspunt van gezond verstand, denk ik. Zelfs Microsoft, waarvan niet bekend is dat het vaak hun fouten erkent, heeft dit als een bug herkend en het in de nieuwste versie opgelost: http://connect.microsoft.com/VisualStudio/feedback/details/363379/bug-in-list-double-sort-in-list-which-contains-double-nan.

Hoe dan ook, ik eindigde met het volgen van @ khachik's antwoord:

sorted(list_, key = lambda x : float('-inf') if math.isnan(x) else x)

Ik vermoed dat het resulteert in een prestatieshit in vergelijking met de standaardtaal, maar het werkt in ieder geval (met uitzondering van de bugs die ik heb geïntroduceerd).


26
2017-11-21 20:05


oorsprong


antwoorden:


De vorige antwoorden zijn nuttig, maar misschien niet duidelijk over de oorzaak van het probleem.

In elke taal past sorteren een bepaalde volgorde toe, gedefinieerd door een vergelijkingsfunctie of op een andere manier, over het domein van de invoerwaarden. Bijvoorbeeld minder dan, a.k.a. operator <, kan overal worden gebruikt als en slechts als minder dan een geschikte volgorde definieert over de invoerwaarden.

Maar dit is met name NIET waar voor drijvende-kommawaarden en minder dan: "NaN is ongeordend: het is niet gelijk aan, groter dan of kleiner dan wat dan ook, inclusief zichzelf." (Wis proza ​​uit de GNU C handleiding, maar is van toepassing op alle moderne IEEE754 gebaseerde zwevend punt)

Dus de mogelijke oplossingen zijn:

  1. verwijder eerst de NaN's, waardoor het invoerdomein duidelijk wordt gedefinieerd via <   (of de andere sorteerfunctie die wordt gebruikt)
  2. definieer een aangepaste vergelijkingsfunctie (predikaat a.k.a.) die dat wel doet   een bestelling definiëren voor NaN, zoals minder dan een nummer of groter   dan welk nummer dan ook.

Beide benaderingen kunnen in elke taal worden gebruikt.

In de praktijk, gezien Python, zou ik er de voorkeur aan geven de NaN's te verwijderen als je het niet erg vindt om de snelste prestaties of als het verwijderen van NaNs een gewenst gedrag is in de context.

Anders zou je een geschikte predikaatfunctie via "cmp" kunnen gebruiken in oudere pythonversies, of via deze en functools.cmp_to_key(). De laatste is natuurlijk iets onhandiger dan eerst de NaN's verwijderen. En zorg moet worden voorkomen erger prestaties, bij het definiëren van deze predikaatfunctie.


12
2017-08-23 17:35



Het probleem is dat er geen juiste volgorde is als de lijst een NAN bevat, omdat een reeks a1, a2, a3, ..., a is gesorteerd als a1 <= a2 <= a3 <= ... <= an. Als een van deze waarden een NAN is, wordt de gesorteerde eigenschap verbroken, omdat voor alle a, a <= NAN en NAN <= a beide onwaar zijn.


7
2017-11-21 21:46



Ik weet het niet zeker, maar de oplossing kan de volgende zijn:

sorted(
    (2, 1, float('nan')),
    lambda x,y: x is float('nan') and -1 
                or (y is float('nan') and 1
                or cmp(x,y)))

wat resulteert in:

('nan', 1, 2)

Of verwijder nans voor het sorteren of iets anders.


5
2017-11-21 20:12



IEEE754 is de standaard die drijvende-kommabewerkingen in dit geval definieert. Deze standaard definieert de vergelijkingsbewerking van operanden, waarvan er tenminste één een NaN is, als een fout. Daarom is dit geen bug. U moet de NaN's afhandelen voordat u op uw array kunt werken.


3
2017-11-21 20:10



Ervan uitgaande dat u de NaN's wilt behouden en ze wilt rangschikken als de laagste "waarden", is hier een oplossing die werkt met zowel niet-uniek nan, unieke numpy nan, numeriek en niet-numeriek voorwerpen:

def is_nan(x):
    return (x is np.nan or x != x)

list_ = [2, float('nan'), 'z', 1, 'a', np.nan, 4, float('nan')]
sorted(list_, key = lambda x : float('-inf') if is_nan(x) else x)
# [nan, nan, nan, 1, 2, 4, 'a', 'z']

0
2018-05-24 09:25