NumPy: вычислить средние значения с удаленными NaN

Как я могу вычислить средние значения матрицы по матрице, но удалить nan значений из расчета? (Для людей R подумайте na.rm = TRUE).

Вот мой [не]рабочий пример:

import numpy as np
dat = np.array([[1, 2, 3],
                [4, 5, np.nan],
                [np.nan, 6, np.nan],
                [np.nan, np.nan, np.nan]])
print(dat)
print(dat.mean(1))  # [  2.  nan  nan  nan]

После удаления NaN мой ожидаемый результат будет таким:

array([ 2.,  4.5,  6.,  nan])

person Mike T    schedule 30.03.2011    source источник
comment
Начиная с numpy 1.8, доступны nanmean и nanstd.   -  person Roman Shapovalov    schedule 02.10.2014


Ответы (10)


Я думаю, что вы хотите, это замаскированный массив:

dat = np.array([[1,2,3], [4,5,nan], [nan,6,nan], [nan,nan,nan]])
mdat = np.ma.masked_array(dat,np.isnan(dat))
mm = np.mean(mdat,axis=1)
print mm.filled(np.nan) # the desired answer

Редактировать. Объединение всех данных о времени.

   from timeit import Timer

    setupstr="""
import numpy as np
from scipy.stats.stats import nanmean    
dat = np.random.normal(size=(1000,1000))
ii = np.ix_(np.random.randint(0,99,size=50),np.random.randint(0,99,size=50))
dat[ii] = np.nan
"""  

    method1="""
mdat = np.ma.masked_array(dat,np.isnan(dat))
mm = np.mean(mdat,axis=1)
mm.filled(np.nan)    
"""

    N = 2
    t1 = Timer(method1, setupstr).timeit(N)
    t2 = Timer("[np.mean([l for l in d if not np.isnan(l)]) for d in dat]", setupstr).timeit(N)
    t3 = Timer("np.array([r[np.isfinite(r)].mean() for r in dat])", setupstr).timeit(N)
    t4 = Timer("np.ma.masked_invalid(dat).mean(axis=1)", setupstr).timeit(N)
    t5 = Timer("nanmean(dat,axis=1)", setupstr).timeit(N)

    print 'Time: %f\tRatio: %f' % (t1,t1/t1 )
    print 'Time: %f\tRatio: %f' % (t2,t2/t1 )
    print 'Time: %f\tRatio: %f' % (t3,t3/t1 )
    print 'Time: %f\tRatio: %f' % (t4,t4/t1 )
    print 'Time: %f\tRatio: %f' % (t5,t5/t1 )

Возвращает:

Time: 0.045454  Ratio: 1.000000
Time: 8.179479  Ratio: 179.950595
Time: 0.060988  Ratio: 1.341755
Time: 0.070955  Ratio: 1.561029
Time: 0.065152  Ratio: 1.433364
person JoshAdel    schedule 30.03.2011
comment
Я думаю, что scipy.nanmean должен быть первым, что вы попробуете. Интересно, это все еще медленно? - person mathtick; 13.11.2012
comment
@mathtick Существует множество способов выполнить то, о чем просил ОП. Я предложил один такой метод, который немного более подробный, но быстрее, чем все другие предложенные, которые были протестированы выше, по крайней мере, на моей машине (это все еще верно и сейчас с обновленными версиями scipy и numpy). - person JoshAdel; 14.11.2012
comment
@mathtick Кроме того, насколько я могу судить, в scipy 0.10 или 0.11 нет метода scipy.nanmean. Есть scipy.stats.stats.nanmean и scipy.stats.nanmean, которые эквивалентны, и я проверял выше. - person JoshAdel; 14.11.2012
comment
Извините, это должно быть scipy.stats.nanmean ... и я запускаю cipy.__version__ '0.10.1'. - person mathtick; 14.11.2012
comment
scipy.stats.nanmean и .nanstd тоже делают ось = (по умолчанию ось = 0, а не None) - person denis; 17.11.2012
comment
Я проверил это в одном измерении, и np.nansum(dat) / np.sum(~np.isnan(dat)) немного быстрее, чем np.mean(np.ma.masked_array(dat, np.isnan(dat))). Однако, как указывалось ранее, узкое место работает в 10 раз быстрее. - person Dr. Jan-Philip Gehrcke; 01.03.2013
comment
Кажется, np.nansum(dat) лучший. Python 2.7.11 |Anaconda 2.4.1 (64-bit) IPython 4.0.1 In[190]: %timeit method1() 100 loops, best of 3: 7.09 ms per loop In[191]: %timeit [np.mean([l for l in d if not np.isnan(l)]) for d in dat] 1 loops, best of 3: 1.04 s per loop In[192]: %timeit np.array([r[np.isfinite(r)].mean() for r in dat]) 10 loops, best of 3: 19.6 ms per loop In[193]: %timeit np.ma.masked_invalid(dat).mean(axis=1) 100 loops, best of 3: 11.8 ms per loop In[194]: %timeit nanmean(dat,axis=1) 100 loops, best of 3: 6.36 ms per loop - person Sklavit; 11.02.2016

Если производительность имеет значение, вместо этого следует использовать bottleneck.nanmean():

http://pypi.python.org/pypi/Bottleneck

person deprecated    schedule 30.03.2011

Предполагая, что у вас также установлен SciPy:

http://www.scipy.org/doc/api_docs/SciPy.stats.stats.html#nanmean

person Shaun Dubuque    schedule 30.03.2011
comment
Просто для полноты, так как я рассчитал время всего остального кода - stats.stats.nanmean примерно в 1,5 раза медленнее, чем решение np.ma. - person JoshAdel; 30.03.2011


Маскированный массив с отфильтрованными нанами также можно создать «на лету»:

print np.ma.masked_invalid(dat).mean(1)
person Sven Marnach    schedule 30.03.2011
comment
Я не думал использовать это. Это хороший однострочник, но он все еще в 1,5-2 раза медленнее, чем мое решение в моих тестах. Тем не менее +1 за то, что вы показали мне метод np.ma, на который я раньше не смотрел. - person JoshAdel; 30.03.2011

Вы всегда можете найти обходной путь в чем-то вроде:

numpy.nansum(dat, axis=1) / numpy.sum(numpy.isfinite(dat), axis=1)

numpy.mean Numpy 2.0 имеет опцию skipna, которая должна позаботиться об этом.

person Benjamin    schedule 08.11.2011

Это основано на решении, предложенном ДжошАделем.

Определите следующую функцию:

def nanmean(data, **args):
    return numpy.ma.filled(numpy.ma.masked_array(data,numpy.isnan(data)).mean(**args), fill_value=numpy.nan)

Пример использования:

data = [[0, 1, numpy.nan], [8, 5, 1]]
data = numpy.array(data)
print data
print nanmean(data)
print nanmean(data, axis=0)
print nanmean(data, axis=1)

Распечатает:

[[  0.   1.  nan]
 [  8.   5.   1.]]

3.0

[ 4.  3.  1.]

[ 0.5         4.66666667]
person Eugene Yurtsev    schedule 12.01.2012

Как насчет использования Pandas для этого:

import numpy as np
import pandas as pd
dat = np.array([[1, 2, 3], [4, 5, np.nan], [np.nan, 6, np.nan], [np.nan, np.nan, np.nan]])
print dat
print dat.mean(1)

df = pd.DataFrame(dat)
print df.mean(axis=1)

Дает:

0    2.0
1    4.5
2    6.0
3    NaN
person zbinsd    schedule 29.01.2014

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

import laxarray as la
la.array(dat).mean(axis=1)

следуя протоколу ДжошАделя, я получаю:

Time: 0.048791  Ratio: 1.000000   
Time: 0.062242  Ratio: 1.275689   # laxarray's one-liner

Таким образом, laxarray немного медленнее (нужно проверить, почему, возможно, это можно исправить), но гораздо проще в использовании и позволяет маркировать размеры строками.

проверить: https://github.com/perrette/laxarray

РЕДАКТИРОВАТЬ: я проверил с другим модулем "la", larry, который проходит все тесты:

import la
la.larry(dat).mean(axis=1)

By hand, Time: 0.049013 Ratio: 1.000000
Larry,   Time: 0.005467 Ratio: 0.111540
laxarray Time: 0.061751 Ratio: 1.259889

Впечатляющий !

person Mahé    schedule 04.12.2013

Еще одна проверка скорости для всех предложенных подходов:

Python 2.7.11 |Anaconda 2.4.1 (64-bit)| (default, Jan 19 2016, 12:08:31) [MSC v.1500 64 bit (AMD64)]
IPython 4.0.1 -- An enhanced Interactive Python.

import numpy as np
from scipy.stats.stats import nanmean    
dat = np.random.normal(size=(1000,1000))
ii = np.ix_(np.random.randint(0,99,size=50),np.random.randint(0,99,size=50))
dat[ii] = np.nan
In[185]: def method1():
    mdat = np.ma.masked_array(dat,np.isnan(dat))
    mm = np.mean(mdat,axis=1)
    mm.filled(np.nan) 

In[190]: %timeit method1()
100 loops, best of 3: 7.09 ms per loop
In[191]: %timeit [np.mean([l for l in d if not np.isnan(l)]) for d in dat]
1 loops, best of 3: 1.04 s per loop
In[192]: %timeit np.array([r[np.isfinite(r)].mean() for r in dat])
10 loops, best of 3: 19.6 ms per loop
In[193]: %timeit np.ma.masked_invalid(dat).mean(axis=1)
100 loops, best of 3: 11.8 ms per loop
In[194]: %timeit nanmean(dat,axis=1)
100 loops, best of 3: 6.36 ms per loop
In[195]: import bottleneck as bn
In[196]: %timeit bn.nanmean(dat,axis=1)
1000 loops, best of 3: 1.05 ms per loop
In[197]: from scipy import stats
In[198]: %timeit stats.nanmean(dat)
100 loops, best of 3: 6.19 ms per loop

Таким образом, лучше всего использовать 'bottleneck.nanmean(dat, axis=1)', 'scipy.stats.nanmean(dat)' не быстрее, чем numpy.nanmean(dat, axis=1).

person Sklavit    schedule 11.02.2016