2016-12-24 9 views
2

numpy関数は、axis = parameterを使用して特定の軸を操作するオプションを提供します。私の質問はnumpy配列のサブ次元でのPython操作

  1. どのようにこの軸に沿った操作が実装されていますか?または、より直接的な質問
  2. 同様のオプションを提供する私自身の関数を効率的に書く方法はありますか?

注意numpyは、基関数入力が1次元配列の場合、答えとして機能するnumpy.apply_along_axis関数を提供します。

しかし、基底関数に多次元入力が必要な場合はどうなりますか?例えば。最初の2つの次元(5,6)に沿った形状(5,6,2,3,4)のnp行列Aの2次元移動平均Bを求める。汎用関数B = f_moving_mean(A、axis =(0,1))

私の現在の解決策は、これを達成するためにnumpy.swapaxesとnumpy.reshapeを使用することです。

import pandas as pd 
import numpy as np 
def nanmoving_mean(data,window,axis=0): 
    kw = {'center':True,'window':window,'min_periods':1} 
    if len(data.shape)==1: 
     return pd.Series(data).rolling(**kw).mean().as_matrix() 
    elif len(data.shape)>=2: 
     tmp = np.swapaxes(data,0,axis) 
     tmpshp = tmp.shape 
     tmp = np.reshape(tmp, (tmpshp[0],-1), order='C') 
     tmp = pd.DataFrame(tmp).rolling(**kw).mean().as_matrix() 
     tmp = np.reshape(tmp, tmpshp, order='C') 
     return np.swapaxes(tmp,0,axis) 
    else: 
     print('Invalid dimension!') 
     return None 

data = np.random.randint(10,size=(2,3,6)) 
print(data) 
nanmoving_mean(data,window=3,axis=2) 

これは、質問2の共通の効率的な実装方法ですか?あらゆる改善/提案/新しい方法を歓迎します。

PS。ここでパンダを使っている理由は、ローリング(...)mean()メソッドがナノデータを適切に処理できることです。

編集: 私は質問をする別の方法があると思います: '動的な'次元数のループの構文は何ですか?

答えて

1

基本的な手順は次のようになります。前処理ステップとして

  • 、我々は入力データにウィンドウ集計を行う必要があるとして0sNaNsを交換してください。
  • データ値 と、NaNsのマスクに対して、ウィンドウ付き合計をScipy's convolve2dで取得します。境界要素をゼロとして使用します。
  • ウインドウサイズからNaNsのウインドウカウントを減算して、合計の原因となる有効なエレメントの数を取得します。
  • 境界要素については、加算を説明する要素が徐々に小さくなります。

ここで、これらのintervaled-summationsは、比較的効率的なScipy's1Duniform-filterによっても得られます。他の利点は、これらの合計/平均化を行う軸を指定できることです。

Scipyの2D convolution1D uniform filterの組み合わせでは、次のようなアプローチはほとんどありません。

インポート関連scipyのダウンロード機能 -

from scipy.signal import convolve2d as conv2 
from scipy.ndimage.filters import uniform_filter1d as uniff 

アプローチ#1:

def nanmoving_mean_numpy(data, W): # data: input array, W: Window size 
    N = data.shape[-1] 
    hW = (W-1)//2 

    nan_mask = np.isnan(data) 
    data1 = np.where(nan_mask,0,data) 

    value_sums = conv2(data1.reshape(-1,N),np.ones((1,W)),'same', boundary='fill') 
    nan_sums = conv2(nan_mask.reshape(-1,N),np.ones((1,W)),'same', boundary='fill') 

    value_sums.shape = data.shape 
    nan_sums.shape = data.shape 

    b_sizes = hW+1+np.arange(hW) # Boundary sizes 
    count = np.hstack((b_sizes , W*np.ones(N-2*hW), b_sizes[::-1])) 
    return value_sums/(count - nan_sums) 

アプローチ#2:

def nanmoving_mean_numpy_v2(data, W): # data: input array, W: Window size  
    N = data.shape[-1] 
    hW = (W-1)//2 

    nan_mask = np.isnan(data) 
    data1 = np.where(nan_mask,0,data) 

    value_sums = uniff(data1,size=W, axis=-1, mode='constant')*W 
    nan_sums = conv2(nan_mask.reshape(-1,N),np.ones((1,W)),'same', boundary='fill') 
    nan_sums.shape = data.shape 

    b_sizes = hW+1+np.arange(hW) # Boundary sizes 
    count = np.hstack((b_sizes , W*np.ones(N-2*hW,dtype=int), b_sizes[::-1])) 
    out = value_sums/(count - nan_sums) 
    out = np.where(np.isclose(count, nan_sums), np.nan, out) 
    return out 

アプローチ#3:

def nanmoving_mean_numpy_v3(data, W): # data: input array, W: Window size 
    N = data.shape[-1] 
    hW = (W-1)//2 

    nan_mask = np.isnan(data) 
    data1 = np.where(nan_mask,0,data)  
    nan_avgs = uniff(nan_mask.astype(float),size=W, axis=-1, mode='constant') 

    b_sizes = hW+1+np.arange(hW) # Boundary sizes 
    count = np.hstack((b_sizes , W*np.ones(N-2*hW), b_sizes[::-1])) 
    scale = ((count/float(W)) - nan_avgs) 
    out = uniff(data1,size=W, axis=-1, mode='constant')/scale 
    out = np.where(np.isclose(scale, 0), np.nan, out) 
    return out 

ランタイムテスト

データセット#1:

In [807]: # Create random input array and insert NaNs 
    ...: data = np.random.randint(10,size=(20,30,60)).astype(float) 
    ...: 
    ...: # Add 10% NaNs across the data randomly 
    ...: idx = np.random.choice(data.size,size=int(data.size*0.1),replace=0) 
    ...: data.ravel()[idx] = np.nan 
    ...: 
    ...: W = 5 # Window size 
    ...: 

In [808]: %timeit nanmoving_mean(data,window=W,axis=2) 
    ...: %timeit nanmoving_mean_numpy(data, W) 
    ...: %timeit nanmoving_mean_numpy_v2(data, W) 
    ...: %timeit nanmoving_mean_numpy_v3(data, W) 
    ...: 
10 loops, best of 3: 22.3 ms per loop 
100 loops, best of 3: 3.31 ms per loop 
100 loops, best of 3: 2.99 ms per loop 
1000 loops, best of 3: 1.76 ms per loop 

データセット#2 [ビガーデータセット]:

In [811]: # Create random input array and insert NaNs 
...: data = np.random.randint(10,size=(120,130,160)).astype(float) 
...: 
...: # Add 10% NaNs across the data randomly 
...: idx = np.random.choice(data.size,size=int(data.size*0.1),replace=0) 
...: data.ravel()[idx] = np.nan 
...: 

In [812]: %timeit nanmoving_mean(data,window=W,axis=2) 
    ...: %timeit nanmoving_mean_numpy(data, W) 
    ...: %timeit nanmoving_mean_numpy_v2(data, W) 
    ...: %timeit nanmoving_mean_numpy_v3(data, W) 
    ...: 
1 loops, best of 3: 796 ms per loop 
1 loops, best of 3: 486 ms per loop 
1 loops, best of 3: 275 ms per loop 
10 loops, best of 3: 161 ms per loop 
1

あなたの質問にあまり出さず、ここで彼らは、2つのインデックスオブジェクト、i、多様ですindを構築

 res = func1d(arr[tuple(i.tolist())], *args, **kwargs) 
     outarr[tuple(ind)] = res 

(Ipython経由で見て)apply_along_axis機能の重要な部分です。我々はaxis=2を指定すると仮定し、その後、このコードはij、およびlのすべての可能な値について

outarr[i,j,l] = func1d(arr[i,j,:,l], ...) 

を行います。だから、かなり基本的な反復計算のためのコードがたくさんあります。

ind = [0]*(nd-1) # ind is just a nd-1 list 

i = zeros(nd, 'O')  # i is a 1d array with a `slice` object 
i[axis] = slice(None, None) 

私はパンダに慣れていませんrolling。しかし、numpyのローリング・ミディアムの質問があります。 scipy.signal.convolve2dが役に立つかもしれません。 np.lib.stride_tricks.as_stridedも使用されます。

reshapeswapaxis(またはtranspose)を使用してディメンションスペースの複雑さを減らすこともできます。

(これは解決策ではなく、むしろ他の「移動平均の質問を思い出して、心に来ていくつかのアイデアを捨てることは、より多くの開発に手遅れ。。)我々は2D convolutionを使用してのアプローチを持っている可能性が

+0

私は見ていませんapply_along_axisのコードですが、iとindをどう構築するのですか? –

+0

@ShichuZhuあなたがパフォーマンスを探しているなら、 'apply_along_axis'は助けになりません。 – Divakar

+0

@Divakar基本関数が1Dのみを扱う場合、他のすべての次元をループすることは唯一の方法です。ベクトル化された操作を含むように基本関数を修正しない限り、私は推測しますか? –

関連する問題