2Dアレイに於ける行ごとの要素の比較(numpy.isinな処理の2Dへの拡張)

Fast way to check if elements in sub-dimension of a numpy array is in sub-dimension of another numpy array – StackOverflow

「numpyアレイの副次元要素が別のnumpyアレイの副次元にあるかどうかを確認する高速な方法」

この手の処理は何気に多用する.だましだまし,適当にループ回したり,
それをNumbaやCythonで高速化したり,主にはPandasで処理したりしていたが,
回答のソリューションが素晴らしいと思ったのでメモっておく.

import numpy as np


def searchsorted2d(a,b):
    m,n = a.shape
    max_num = np.maximum(a.max() - a.min(), b.max() - b.min()) + 1
    r = max_num*np.arange(a.shape[0])[:,None]
    p = np.searchsorted( (a+r).ravel(), (b+r).ravel() ).reshape(m,-1)
    return p - n*(np.arange(m)[:,None])

def numpy_isin2D(A,B):
    sB = np.sort(B,axis=1)
    idx = searchsorted2d(sB,A)
    idx[idx==sB.shape[1]] = 0
    return np.take_along_axis(sB, idx, axis=1) == A
  
  
a = np.array([[5, 0, 3, 3],
       [7, 3, 5, 2],
       [4, 7, 6, 8],
       [8, 1, 6, 7],
       [7, 8, 1, 5]])
b = np.array([[8, 4, 3, 0, 3, 5],
       [0, 2, 3, 8, 1, 3],
       [3, 3, 7, 0, 1, 0],
       [4, 7, 3, 2, 7, 2],
       [0, 0, 4, 5, 5, 6]])

numpy_isin2D(a, b)
AttributeError: module 'numpy' has no attribute 'take_along_axis'

しかし,「numpy.take_along_axis」はNumpy 1.15.0からの新機能で,
Tensorflowとか別のモジュールとの兼ね合いで,中々1.15.0にもし難い.
また,非常に効率的かつ一般化されていてリーズナブルチョイスだろうと思うが,
余程慣れていないとパット見て分かるだろうか…….
 

高次元ならともかく,2次元,3次元程度であれば,
Pandasでデータを構造化して考えるのがリーズナブルかなと.

import pandas as pd


df1 = pd.DataFrame(a)
df2 = pd.DataFrame(b)
print(df1)
print(df2)
print(df1.T.isin(df2.T.to_dict('list')).T)
   0  1  2  3
0  5  0  3  3
1  7  3  5  2
2  4  7  6  8
3  8  1  6  7
4  7  8  1  5
   0  1  2  3  4  5
0  8  4  3  0  3  5
1  0  2  3  8  1  3
2  3  3  7  0  1  0
3  4  7  3  2  7  2
4  0  0  4  5  5  6
       0      1      2      3
0   True   True   True   True
1  False   True  False   True
2  False   True  False  False
3  False  False  False   True
4  False  False  False   True

Pandas.isinはnumpy.isinと同程度の時間効率で動作し極めて効率的.
(また,ブロードキャスティングで同様の処理をするより空間効率も遥かに良い)

また,良い方法とは思わないけど,2次元アレイ程度であれば,
ブロードキャストを活用すれば2Dアレイ同士の比較(numpy.isin的な処理)は可能.

import numpy as np


a = np.array([[5, 0, 3, 3],
       [7, 3, 5, 2],
       [4, 7, 6, 8],
       [8, 1, 6, 7],
       [7, 8, 1, 5]])
b = np.array([[8, 4, 3, 0, 3, 5],
       [0, 2, 3, 8, 1, 3],
       [3, 3, 7, 0, 1, 0],
       [4, 7, 3, 2, 7, 2],
       [0, 0, 4, 5, 5, 6]])

(a[:, None, :, None] == b[:, None, None]).any(-1).reshape(-1, a.shape[1])
array([[ True,  True,  True,  True],
       [False,  True, False,  True],
       [False,  True, False, False],
       [False, False, False,  True],
       [False, False, False,  True]])
カテゴリー: 未分類 パーマリンク

2Dアレイに於ける行ごとの要素の比較(numpy.isinな処理の2Dへの拡張) への2件のフィードバック

  1. ピンバック: numpy.take_along_axis | 粉末@それは風のように (日記)

  2. ピンバック: 2つの3次元アレイ(3D numpy.ndarray)に重複する2次元アレイ(2D numpy.ndarray)が幾つ含まれているかループを使わずに効率的に確認する方法 | 粉末@それは風のように (日記)

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Google フォト

Google アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください