Python: сортировка массива с помощью NaNs

Примечание: я использую массивы Python и numpy.

у меня есть много массивов, все из которых имеют два столбца и много строк. Во втором столбце есть некоторые значения NaN; в первом столбце есть только числа.

Я хотел бы отсортировать каждый массив в порядке возрастания в соответствии со вторым столбцом, оставив значения NaN. Это большой набор данных, поэтому я бы предпочел не преобразовывать значения NaN в нули или что-то еще.

Я хотел бы, чтобы он сортировал, как Итак:

105.  4.
22.   10.
104.  26.
...
...
...
53.   520.
745.  902.
184.  nan
19.   nan

сначала я попытался с помощью fix_invalid который преобразует NaNs в 1x10^20:

#data.txt has one of the arrays with 2 columns and a bunch of rows.
Data_0_30 = array(genfromtxt(fname='data.txt'))

g = open("iblah.txt", "a") #saves to file

def Sorted_i_M_W(mass):
    masked = ma.fix_invalid(mass)
    print  >> g, array(sorted(masked, key=itemgetter(1)))

Sorted_i_M_W(Data_0_30)

g.close()

или Я заменил функцию что-то вроде этого:

def Sorted_i_M_W(mass):
    sortedmass = sorted( mass, key=itemgetter(1))
    print  >> g, array(sortedmass)

для каждой попытки я получил что-то вроде:

...
[  4.46800000e+03   1.61472200e+11]
[  3.72700000e+03   1.74166300e+11]
[  4.91800000e+03   1.75502300e+11]
[  6.43500000e+03              nan]
[  3.95520000e+04   8.38907500e+09]
[  3.63750000e+04   1.27625700e+10]
[  2.08810000e+04   1.28578500e+10]
...

где в месте расположения значения NaN сортировка возобновляется снова.

(для fix_invalid NaN в приведенном выше отрывке показывает 1.00000000e+20 значение). Но я бы хотел, чтобы сортировка игнорировала значение NaN полностью.

какой самый простой способ, чтобы отсортировать этот массив так, как я хочу?

5 ответов


не уверен, что это можно сделать с numpy.sort, но вы можете использовать numpy.argsort обязательно:

>>> arr
array([[ 105.,    4.],
       [  53.,  520.],
       [ 745.,  902.],
       [  19.,   nan],
       [ 184.,   nan],
       [  22.,   10.],
       [ 104.,   26.]])
>>> arr[np.argsort(arr[:,1])]
array([[ 105.,    4.],
       [  22.,   10.],
       [ 104.,   26.],
       [  53.,  520.],
       [ 745.,  902.],
       [  19.,   nan],
       [ 184.,   nan]])

вы можете создать массив в маске:

a = np.loadtxt('test.txt')

mask = np.isnan(a)
ma = np.ma.masked_array(a, mask=mask)

и потом вроде a использование маскированного массива:

a[np.argsort(ma[:, 1])]

вы можете использовать функцию сравнения

def cmpnan(x, y):
    if isnan(x[1]):
        return 1 # x is "larger"
    elif isnan(y[1]):
        return -1 # x is "smaller"
    else:
        cmp(x[1], y[1]) # compare numbers

sorted(data, cmp=cmpnan)

см.http://docs.python.org/2.7/library/functions.html#sorted


Если вы используете более старую версию numpy и не хотите обновлять (или если вы хотите код, поддерживающий более старые версии numpy), вы можете сделать:

import numpy as np

def nan_argsort(a):
    temp = a.copy()
    temp[np.isnan(a)] = np.inf
    return temp.argsort()

sorted = a[nan_argsort(a[:, 1])]

в более новых версиях numpy, по крайней мере 1.6 я думаю, сортировка/argsort numpy уже имеет это поведение. Если вам нужно использовать сортировку в Python, вы можете сделать свою собственную функцию сравнения, как описано в других ответах.


Если вы действительно не хотите использовать массив numpy, вы можете отсортировать второй столбец, а затем получить индекс для вызова массива.

Это можно сделать только в одной строке:

yourarray[sorted(range(len(yourarray[:,1])), key=lambda k: yourarray[:,1][k])]