Python Numpy,其中等效

Python Numpy,其中等效,python,arrays,performance,numpy,Python,Arrays,Performance,Numpy,我正在尝试从numpy ndarray中为我的协作过滤项目选择一个子集 我的阵列具有以下形状: ratings = np.array( [ (1, 2, 3.0), (2, 2, 3.0), (4, 1, 2.0), (1, 2, 1.0), ], dtype=[ ('user_id', np.uint32), ('item_id', np.uint32), (

我正在尝试从numpy ndarray中为我的协作过滤项目选择一个子集

我的阵列具有以下形状:

ratings = np.array(
    [
        (1, 2, 3.0),
        (2, 2, 3.0),
        (4, 1, 2.0),
        (1, 2, 1.0),
    ],
    dtype=[
        ('user_id', np.uint32),
        ('item_id', np.uint32),
        ('score', np.float32)
    ]
)
现在,我想选择一个子集的评级,其中用户_id属于一个数组。类似于SQL的“WHERE IN”功能

我能够使用np.INAD实现这一点:

subset_of_users = [1, 2]
ratings[np.in1d(ratings['user_id'], subset_of_users)]
我的分析总是显示使用in1d的块是最慢的,这让我想到也许有一种更快的替代方法


非常感谢您的时间。

如果您的最大用户id不是太大,您可以使用查找表:

mask_of_users = np.zeros(shape=max(ratings['user_id'])+1, dtype=bool)
mask_of_users[subset_of_users] = True
selected_ratings = ratings[mask_of_users[ratings['user_id']]]

这似乎是一个瓶颈,它是
np.inad
,所以让我们试着加快这一部分的速度。现在,根据我到目前为止的NumPy经验,我发现了一种替代方法,可以用来替代第二个数组是排序的、唯一的数组或列表,并且要与第一个数组中的元素进行匹配的情况。下面列出的是实现-

def in1d_replacement(A,B):
    """ in1d replacement using searchsorted with optional 'left', 'right' args.
    """
    # Get left and right sorted indices for A in B
    idx_l = np.searchsorted(B,A,'left')
    idx_r = np.searchsorted(B,A,'right')

    # Look for switching indices between the left and right ones 
    # indicating the matches
    return idx_l != idx_r
运行时测试-

In [195]: # Random arrays of decent sizes
     ...: nA = 10000
     ...: nB = 1000
     ...: max_num = 100000
     ...: A = np.random.randint(0,max_num,(nA))
     ...: B = np.unique(np.random.randint(0,max_num,(nB)))
     ...: 

In [196]: np.allclose(np.in1d(A,B),in1d_replacement(A,B))
Out[196]: True

In [197]: %timeit np.in1d(A,B)
100 loops, best of 3: 2.73 ms per loop

In [198]: %timeit in1d_replacement(A,B)
100 loops, best of 3: 2.14 ms per loop
因此,即使不是很大的提升,也会有一些性能提升。

您可以尝试使用该软件包(免责声明:我是它的作者)

受您问题的启发,我对API做了一些更改,这样我们可以从重复查询中获益:

import numpy_indexed as npi
npi.contains(subset_of_users, ratings['user_id'])
应该从左到右读取;'子集包含user_id'的元素,并返回子集中存在的user_id'的索引

但是,计算中最昂贵的部分是为用户id集建立“索引”,这可以通过预计算实现:

index = npi.as_index(ratings['user_id'])
npi.contains(subset_of_users, index)
npi.contains(some_other_subset_of_users, index)
我希望在每个查询的基础上,速度会快一点

Ive还集成了一个npi.in_函数,灵感来自Divakar的答案,它允许你编写npi.in_(ratings['user_id',subset_of_users]),再次从左到右阅读;子集中存在的用户标识的元素。但我希望它的效率比使用contains要低一些。不过这都是猜测;很高兴看到一些实际数据的比较

谢谢你的回答。最大值(用户id)约为150k。使用此代码,我的函数从0.04s变为0.17s:-(