Python 优雅的numpy阵列移动和NaN填充?
我这里有一个具体的性能问题。我正在使用气象预报时间序列,我将其编译成一个numpy 2d数组Python 优雅的numpy阵列移动和NaN填充?,python,numpy,nan,Python,Numpy,Nan,我这里有一个具体的性能问题。我正在使用气象预报时间序列,我将其编译成一个numpy 2d数组 dim0=预测系列开始的时间 dim1=预测范围,例如0到120小时 现在,我希望dim0有每小时一次的间隔,但有些来源只会每N小时产生一次预测。例如,假设N=3,dim1中的时间步长为M=1小时。然后我得到了类似于 12:00 11.2 12.2 14.0 15.0 11.3 12.0 13:00 nan nan nan nan nan nan 14:00 na
- dim0=预测系列开始的时间
- dim1=预测范围,例如0到120小时
12:00 11.2 12.2 14.0 15.0 11.3 12.0
13:00 nan nan nan nan nan nan
14:00 nan nan nan nan nan nan
15:00 14.7 11.5 12.2 13.0 14.3 15.1
但当然,13:00和14:00也有信息,因为它可以从12:00预测运行中填写。所以我想以这样的方式结束:
12:00 11.2 12.2 14.0 15.0 11.3 12.0
13:00 12.2 14.0 15.0 11.3 12.0 nan
14:00 14.0 15.0 11.3 12.0 nan nan
15:00 14.7 11.5 12.2 13.0 14.3 15.1
假设dim0的数量级为1e4,dim1的数量级为1e2,那么到达目的地的最快方式是什么?现在我正在一行一行地做,但速度非常慢:
nRows, nCols = dat.shape
if N >= M:
assert(N % M == 0) # must have whole numbers
for i in range(1, nRows):
k = np.array(np.where(np.isnan(self.dat[i, :])))
k = k[k < nCols - N] # do not overstep
self.dat[i, k] = self.dat[i-1, k+N]
nRows,nCols=dat.shape
如果N>=M:
断言(N%M==0)#必须有整数
对于范围内的i(1,nRows):
k=np.array(np.where(np.isnan(self.dat[i,:]))
k=k[k
我相信一定有更优雅的方法可以做到这一点?任何提示都将不胜感激。使用
a=yourdata[:,1://code>对数据进行切片
def shift_time(dat):
#Find number of required iterations
check=np.where(np.isnan(dat[:,0])==False)[0]
maxiters=np.max(np.diff(check))-1
#No sense in iterations where it just updates nans
cols=dat.shape[1]
if cols<maxiters: maxiters=cols-1
for iters in range(maxiters):
#Find nans
col_loc,row_loc=np.where(np.isnan(dat[:,:-1]))
dat[(col_loc,row_loc)]=dat[(col_loc-1,row_loc+1)]
a=np.array([[11.2,12.2,14.0,15.0,11.3,12.0],
[np.nan,np.nan,np.nan,np.nan,np.nan,np.nan],
[np.nan,np.nan,np.nan,np.nan,np.nan,np.nan],
[14.7,11.5,12.2,13.0,14.3,15.]])
shift_time(a)
print a
[[ 11.2 12.2 14. 15. 11.3 12. ]
[ 12.2 14. 15. 11.3 12. nan]
[ 14. 15. 11.3 12. nan nan]
[ 14.7 11.5 12.2 13. 14.3 15. ]]
使用tiago的测试:
tmp = np.random.uniform(-10, 20, (1e4, 1e2))
nan_idx = np.random.randint(30, 1e4 - 1,1e4)
tmp[nan_idx] = np.nan
t=time.time()
shift_time(tmp,maxiter=1E5)
print time.time()-t
0.364198923111 (seconds)
如果你真的很聪明,你应该能够摆脱一个np。其中
这个pad,roll,roll组合的每次迭代基本上都能满足你的需求:
import numpy as np
from numpy import nan as nan
# Startup array
A = np.array([[11.2, 12.2, 14.0, 15.0, 11.3, 12.0],
[nan, nan, nan, nan, nan, nan],
[nan, nan, nan, nan, nan, nan],
[14.7, 11.5, 12.2, 13.0, 14.3, 15.1]])
def pad_nan(v, pad_width, iaxis, kwargs):
v[:pad_width[0]] = nan
v[-pad_width[1]:] = nan
return v
def roll_data(A):
idx = np.isnan(A)
A[idx] = np.roll(np.roll(np.pad(A,1, pad_nan),1,0), -1, 1)[1:-1,1:-1][idx]
return A
print A
print roll_data(A)
print roll_data(A)
输出结果如下:
[[ 11.2 12.2 14. 15. 11.3 12. ]
[ nan nan nan nan nan nan]
[ nan nan nan nan nan nan]
[ 14.7 11.5 12.2 13. 14.3 15.1]]
[[ 11.2 12.2 14. 15. 11.3 12. ]
[ 12.2 14. 15. 11.3 12. nan]
[ nan nan nan nan nan nan]
[ 14.7 11.5 12.2 13. 14.3 15.1]]
[[ 11.2 12.2 14. 15. 11.3 12. ]
[ 12.2 14. 15. 11.3 12. nan]
[ 14. 15. 11.3 12. nan nan]
[ 14.7 11.5 12.2 13. 14.3 15.1]]
一切都是纯numpy,所以每次迭代都应该非常快。但是,我不确定创建填充阵列和运行多次迭代的成本,如果您尝试,请告诉我结果 这似乎起到了作用:
import numpy as np
def shift_time(dat):
NX, NY = dat.shape
for i in range(NY):
x, y = np.where(np.isnan(dat))
xr = x - 1
yr = y + 1
idx = (xr >= 0) & (yr < NY)
dat[x[idx], y[idx]] = dat[xr[idx], yr[idx]]
return
并使用(1e4,1e2)阵列进行测试:
瞧,布尔索引的力量
def shift_nans(arr) :
while True:
nan_mask = np.isnan(arr)
write_mask = nan_mask[1:, :-1]
read_mask = nan_mask[:-1, 1:]
write_mask &= ~read_mask
if not np.any(write_mask):
return arr
arr[1:, :-1][write_mask] = arr[:-1, 1:][write_mask]
我认为这个名字是不言自明的。正确地进行切片是一件痛苦的事情,但它似乎起到了作用:
In [214]: shift_nans_bis(test_data)
Out[214]:
array([[ 11.2, 12.2, 14. , 15. , 11.3, 12. ],
[ 12.2, 14. , 15. , 11.3, 12. , nan],
[ 14. , 15. , 11.3, 12. , nan, nan],
[ 14.7, 11.5, 12.2, 13. , 14.3, 15.1],
[ 11.5, 12.2, 13. , 14.3, 15.1, nan],
[ 15.7, 16.5, 17.2, 18. , 14. , 12. ]])
至于时间安排:
tmp1 = np.random.uniform(-10, 20, (1e4, 1e2))
nan_idx = np.random.randint(30, 1e4 - 1,1e4)
tmp1[nan_idx] = np.nan
tmp1 = tmp.copy()
import timeit
t1 = timeit.timeit(stmt='shift_nans(tmp)',
setup='from __main__ import tmp, shift_nans',
number=1)
t2 = timeit.timeit(stmt='shift_time(tmp1)', # Ophion's code
setup='from __main__ import tmp1, shift_time',
number=1)
In [242]: t1, t2
Out[242]: (0.12696346416487359, 0.3427293070417363)
你介意换一种解释吗,我在“但是当然……”这句话里迷路了。数组中不同的源是如何表示的?dim0是指行和dim1=dimension1=columns吗?@elyase:数字正在向下和向左移动,因为,例如,如果预测是从现在起一小时后的12.2(12:00),那么在一小时后预测将是从那时起的12.2零小时(13:00)。我认为这会导致性能下降。我使用与我的答案类似的设置(运行NY迭代)对其进行了测试,在我的系统中,阵列形状(10000,100)需要33.85秒,比我的解决方案(类似于Ophion)慢20倍。您可以使用nan_mask[1:,:-1]^=write_mask
更新nan_mask,因此您只需计算np.isnan(arr)
一次。缺点是必须复制写入掩码,以便它不会更改nan_掩码中的值。根据所需的最大迭代次数,可以快得多,也可以慢得多。感谢大家提供的智能解决方案!我知道有很多方法可以做到这一点,而且我们现在似乎已经从中抽取了很好的一部分。。。我会选择这个,它会很好地解决我的问题。
def shift_nans(arr) :
while True:
nan_mask = np.isnan(arr)
write_mask = nan_mask[1:, :-1]
read_mask = nan_mask[:-1, 1:]
write_mask &= ~read_mask
if not np.any(write_mask):
return arr
arr[1:, :-1][write_mask] = arr[:-1, 1:][write_mask]
In [214]: shift_nans_bis(test_data)
Out[214]:
array([[ 11.2, 12.2, 14. , 15. , 11.3, 12. ],
[ 12.2, 14. , 15. , 11.3, 12. , nan],
[ 14. , 15. , 11.3, 12. , nan, nan],
[ 14.7, 11.5, 12.2, 13. , 14.3, 15.1],
[ 11.5, 12.2, 13. , 14.3, 15.1, nan],
[ 15.7, 16.5, 17.2, 18. , 14. , 12. ]])
tmp1 = np.random.uniform(-10, 20, (1e4, 1e2))
nan_idx = np.random.randint(30, 1e4 - 1,1e4)
tmp1[nan_idx] = np.nan
tmp1 = tmp.copy()
import timeit
t1 = timeit.timeit(stmt='shift_nans(tmp)',
setup='from __main__ import tmp, shift_nans',
number=1)
t2 = timeit.timeit(stmt='shift_time(tmp1)', # Ophion's code
setup='from __main__ import tmp1, shift_time',
number=1)
In [242]: t1, t2
Out[242]: (0.12696346416487359, 0.3427293070417363)