2016-05-02 10 views
3

numpy 2次元の整数の配列。一部のエントリは0(配列1)です。最初の列がarray1の同じ非ゼロ値を持ち、もう1つの列、た​​とえばindex2が異なる数値(浮動小数点数)を持つ異なる2D配列(array2)を考えてみましょう。配列のエントリを別の配列のものに置き換える

どのように新しい配列3を作成するのですか?array1の各ゼロでないエントリを、array2の2列の対応する値で置き換えますか?あなたはどのようにそれを超清潔にしますか?

例:

>>> array1 
array([[0, 27, 43, 10], 
     [0, 80, 15, 2], 
     [0, 3, 6, 9]]) 

>>> array2 
array([[ 10., 4., 88.], 
     [ 2., 2., 95.], 
     [ 9., 2., 65.], 
     [ 43., 1., 62.], 
     [ 15., 5., 64.], 
     [ 6., 6., 67.], 
     [ 27., 5., 62.], 
     [ 80., 8., 73.], 
     [ 3., 9., 59.]]) 

>>> array3 
array([[0., 62., 62., 88.], 
     [0., 73., 64., 95.], 
     [0., 59., 67., 65.]]) 

答えて

1

array3 = array1.astype(float) # this copies the array by default. 
array3[array1 != 0] = array2[array1[array1 != 0]-1, 2] 

結果は次のとおりです:あなたは、高度なnumpyの配列インデックスと一緒にブールインデックスを使用することができます

array([[ 0, 62., 62., 88.], 
     [ 0, 73., 64., 95.], 
     [ 0, 59., 67., 65.]]) 

説明

あなたが最初の非ゼロのエントリがある場所を示すブール配列を作成:

>>> non_zero_mask = array1 != 0 
array([[False, True, True, True], 
     [False, True, True, True], 
     [False, True, True, True]], dtype=bool) 

これは、 d交換する。あなたのarray2を注文し、我々は、交換価値のための適切な行を見つけるために、1を減算する必要が値1で開始しているので

>>> non_zero_values = array1[non_zero_mask] 
array([7, 4, 1, 8, 5, 2, 9, 6, 3]) 

その後、あなたはこれらの要素の値を見つける必要があります。あなたのarray2がソートされていない場合、あなたはそれを並べ替えたり、間に別のインデックス作成を行うために必要がある場合があります。

>>> replacement_rows = array2[non_zero_values-1] 
array([[ 7., 7., 62.], 
     [ 4., 4., 62.], 
     [ 1., 1., 88.], 
     [ 8., 8., 73.], 
     [ 5., 5., 64.], 
     [ 2., 2., 95.], 
     [ 9., 9., 59.], 
     [ 6., 6., 67.], 
     [ 3., 3., 65.]]) 

>>> replacement_values = array2[non_zero_values-1, 2] # third element of that row! 
array([ 62., 62., 88., 73., 64., 95., 59., 67., 65.]) 

してからちょうど元または新しい配列にこれらの値を割り当てる:

array3[non_zero_mask] = replacement_values 

このアプローチには依存していますarray2の順番になっていますので、より複雑な条件があれば壊れます。しかし、それは値とインデックスの間の関係を見つけて、それを単純な0​​の代わりに挿入するか、または別の中間のnp.where /ブールインデックス作成の代わりに挿入する必要があります。

拡張

場合、あなたはソートarray2を持っていないと、あなたが行うことができ、それを並べ替えることはできません。

>>> array3 = array1.astype(float) 
>>> array3[array1 != 0] = array2[np.where(array2[:, 0][None, :] == array1[array1 != 0][:, None])[1], 2] 
>>> array3 
array([[ 0., 62., 62., 88.], 
     [ 0., 73., 64., 95.], 
     [ 0., 59., 67., 65.]]) 

を、これはあなたがよ互いに配列の放送で動作しますので、サイズがarray1.size * array1.sizeの配列を作成します。だから、これはあまり記憶にはならないかもしれませんが、完全にベクトル化されています。あなたはネイティブnumpyのscipyのダウンロードやバージョンが存在しないため、遅くなる事をスピードアップしたい場合は

Numba(あなたはスピードが必要な場合は)

は素晴らしいです。あなたがアナコンダやcondaをお持ちの場合はそれがすでにインストールされている、それは現実的な選択肢かもしれないので:特に大規模な配列のために

import numba as nb 
import numpy as np 

@nb.njit 
def nb_replace_values(array, old_new_array): 
    res = np.zeros(array.shape, dtype=np.float64) 

    rows = array.shape[0] 
    columns = array.shape[1] 
    rows_replace_array = old_new_array.shape[0] 

    for row in range(rows): 
     for column in range(columns): 
      val = array[row, column] 
      # only replace values that are not zero 
      if val != 0: 
       # Find the value to replace the element with 
       for ind_replace in range(rows_replace_array): 
        if old_new_array[ind_replace, 0] == val: 
         # Match found. Replace and break the innermost loop 
         res[row, column] = old_new_array[ind_replace, 2] 
         break 

    return res 

nb_replace_values(array1, array2) 
array([[ 0., 62., 62., 88.], 
     [ 0., 73., 64., 95.], 
     [ 0., 59., 67., 65.]]) 

何の一時的な配列が作成されませんので、これは明らかに最速とメモリ効率的なソリューションとなります。ファンクションがオンザフライでコンパイルする必要があるため、最初の呼び出しは非常に遅くなります。

タイミング:

%timeit nb_replace_values(array1, array2) 

100000ループ、3の最もよい:ループ当たり6.23マイクロ秒

%%timeit 
array3 = array1.astype(float) 
array3[array1 != 0] = array2[np.where(array2[:, 0][None, :] == array1[array1 != 0][:, None])[1], 2] 

10000ループ、3の最もよい:ループ

あたり74.8マイクロ秒
# Solution provided by @PDRX 
%%timeit 
array3 = array1.astype(float) 
for i in array2[:,0]: 
    i_arr1,j_arr1 = np.where(array1 == i) 
    i_arr2 = np.where(array2[:,0] == i) 
    array3[i_arr1,j_arr1] = array2[i_arr2,2] 

1000のループ、3の最高:ループあたり689マイクロ秒

+0

まあ、私は配列2が実際にソートされているが、一般的に、ランダムで、必ずしもソートされていない任意の配列を、ことになっている与えた例では、これらの数字が繰り返されない限り、一貫した順序で並べ替えることのできない数字です。その列をIDと見なします。あなたはそれに基づいてあなたの答えを改善できますか?私の例を更新します。 – Bella

+0

@Bellaそれははるかに難しく、私はそれに多くの考えを入れていない。しかし、答えの最後の部分を見てください。これは非常に効率が悪く、これらの場合には 'pandas'やカスタマイズされた' numba'関数の使用をお勧めします。 – MSeifert

+0

ああ、私は前にあなたの最終編集を見たことがなかった!私は他の答えを見たとき、すぐにそれを理解しました。それは素晴らしいものでした。あなたのものはもう少し入手が難しいですが、あなたが正しいです、私は速度とあなたのための両方のソリューションをテストしたが、より速いですが、大規模な配列のほんの一桁の速さです。今私はおそらく私はおそらく両方を保つので、受け入れる答えは未定です。 :/私はあなたが配列のサイズについて言及したものは得られませんでした - 私はarray1と同じサイズarray3と両方のソリューションgetsizeof私に同じサイズを与える見ることができますから。 – Bella

-1

私は私はあなたの要件を理解してよく分からないけどさんはlist comprehensionsで試してみましょう:

array3 = [[array2[subitem1 - 1][2] if subitem1 != 0 else 0 for subitem1 in subarray1] for subarray1 in array1] 

しかし、それは、私はそれが集計好む、読むのは難しい:

array3 = [ 
    [ 
     array2[subitem1 - 1][2] if subitem1 != 0 else 0 
     for subitem1 in subarray1 
    ] 
    for subarray1 in array1 
] 
関連する問題