Pergunta

sorted([2, float('nan'), 1]) retorna [2, nan, 1]

(Pelo menos na implementação do Activestate Python 3.1.)

Eu entendo nan é um objeto estranho, então eu não ficaria surpreso se aparecesse em lugares aleatórios no resultado de classificação. Mas também atrapalha o tipo para os números não-NAN no contêiner, o que é realmente inesperado.

Eu perguntei a Pergunta relacionada cerca de max, e com base nisso eu entendo porque sort Funciona assim. Mas isso deve ser considerado um bug?

A documentação apenas diz "Retorne uma nova lista classificada [...] sem especificar detalhes.

EDIT: Agora concordo que isso não viola o padrão IEEE. No entanto, é um bug de qualquer ponto de vista do senso comum, eu acho. Até a Microsoft, que não é conhecida por admitir seus erros com frequência, reconheceu este como um bug e o corrigiu na versão mais recente: http://connect.microsoft.com/visualstudio/feedback/details/363379/bug-ass-list-double-sort-list-list-which-contains-double-nan.

Enfim, acabei seguindo a resposta de @khachik:

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

Eu suspeito que isso resulta em um desempenho de desempenho em comparação com o idioma que faz isso por padrão, mas pelo menos funciona (exceto os bugs que eu introduzi).

Foi útil?

Solução

As respostas anteriores são úteis, mas talvez não sejam claras sobre a raiz do problema.

Em qualquer idioma, a classificação aplica uma determinada ordem, definida por uma função de comparação ou de alguma outra maneira, sobre o domínio dos valores de entrada. Por exemplo, menos do que, também conhecido operator <, pode ser usado por toda parte se e somente se definir uma ordem adequada sobre os valores de entrada.

Mas isso não é especificamente verdadeiro para valores de ponto flutuante e menos do que: "A NAN não é ordenada: não é igual a, maior que ou menor que qualquer coisa, inclusive a si mesma". (Prosa clara do manual GNU c, mas se aplica a todos os modernos IEEE754 Sediada ponto flutuante)

Portanto, as soluções possíveis são:

  1. Remova os Nans primeiro, tornando o domínio de entrada bem definido via <(ou a outra função de classificação sendo usada)
  2. Defina uma função de comparação personalizada (também conhecida como predicada) que define uma ordem para a NAN, como menos de qualquer número, ou maior que qualquer número.

Qualquer abordagem pode ser usada, em qualquer idioma.

Praticamente, considerando o python, eu preferiria remover os Nans se você não se importar com o desempenho mais rápido ou se a remoção de Nans é um comportamento desejado no contexto.

Caso contrário, você poderá usar uma função predicada adequada via "cmp" nas versões python mais antigas, ou por meio disso e functools.cmp_to_key(). Este último é um pouco mais estranho, naturalmente, do que remover os Nans primeiro. E cuidar será necessário para evitar pior desempenho, ao definir essa função de predicado.

Outras dicas

O problema é que não há ordem correta se a lista contiver uma nan, pois uma sequência A1, A2, A3, ..., e é classificada se A1 <= a2 <= a3 <= ... <= an. Se algum desses valores A for uma NAN, a propriedade classificada quebra, pois para todos a, a <= nan e nan <= a são falsos.

Não tenho certeza sobre o bug, mas a solução alternativa pode ser a seguinte:

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)))

o que resulta em:

('nan', 1, 2)

Ou remova nans antes de classificar ou qualquer outra coisa.

IEEE754 é o padrão que define operações de ponto flutuante nesta instância. Este padrão define a operação de comparação dos operandos, pelo menos um dos quais é uma nan, para ser um erro. Portanto, este não é um bug. Você precisa lidar com os NANs antes de operar em sua matriz.

Supondo Nan não único, Numpy Numpy exclusivo, numérico e não numérico objetos:

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']
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top