在 numpy 数组中查找包含子字符串的条目?

Finding entries containing a substring in a numpy array?

我试图在一个数组中找到条目,其中包含一个带有 np.where 的子字符串和一个 in 条件:

import numpy as np
foo = "aa"
bar = np.array(["aaa", "aab", "aca"])
np.where(foo in bar)

这只是returns一个空数组。
为什么会这样?
有没有好的替代解决方案?

我们可以使用np.core.defchararray.find来查找foo字符串在bar的每个元素中的位置,如果找不到则return -1。因此,它可用于通过检查 find 的输出中的 -1 来检测每个元素中是否存在 foo。最后,我们将使用 np.flatnonzero 来获取匹配项的索引。所以,我们会有一个实现,就像这样 -

np.flatnonzero(np.core.defchararray.find(bar,foo)!=-1)

样本运行-

In [91]: bar
Out[91]: 
array(['aaa', 'aab', 'aca'], 
      dtype='|S3')

In [92]: foo
Out[92]: 'aa'

In [93]: np.flatnonzero(np.core.defchararray.find(bar,foo)!=-1)
Out[93]: array([0, 1])

In [94]: bar[2] = 'jaa'

In [95]: np.flatnonzero(np.core.defchararray.find(bar,foo)!=-1)
Out[95]: array([0, 1, 2])

您尝试使用 np.where 的方式不正确。 np.where 的第一个参数应该是一个布尔数组,你只是简单地传递一个布尔值。

foo in bar
>>> False
np.where(False)
>>> (array([], dtype=int32),)
np.where(np.array([True, True, False]))
>>> (array([0, 1], dtype=int32),)

问题是 numpy 没有将 in 运算符定义为 element-wise boolean operation

您可以通过列表理解来完成您想要的事情。

foo = 'aa'
bar = np.array(['aaa', 'aab', 'aca'])
out = [i for i, v in enumerate(bar) if foo in v]
# out = [0, 1]

bar = ['aca', 'bba', 'baa', 'aaf', 'ccc']
out = [i for i, v in enumerate(bar) if foo in v]
# out = [2, 3]

查看一些使用 in 的示例:

In [19]: bar = np.array(["aaa", "aab", "aca"])

In [20]: 'aa' in bar
Out[20]: False

In [21]: 'aaa' in bar
Out[21]: True

In [22]: 'aab' in bar
Out[22]: True

In [23]: 'aab' in list(bar) 

看起来 in 与数组一起使用时就像数组是一个列表一样。 ndarray 确实有一个 __contains__ 方法,所以 in 有效,但它可能很简单。

但无论如何,请注意 in alist 不检查子字符串。 strings __contains__ 进行子字符串测试,但我不知道任何将测试传播到组件字符串的内置 class。

Divakar 所示,有一组 numpy 函数将字符串方法应用于数组的各个元素。

In [42]: np.char.find(bar, 'aa')
Out[42]: array([ 0,  0, -1])

Docstring:
This module contains a set of functions for vectorized string operations and methods. The preferred alias for defchararray is numpy.char.

对于这样的操作,我认为 np.char 速度与以下操作大致相同:

In [49]: np.frompyfunc(lambda x: x.find('aa'), 1, 1)(bar)
Out[49]: array([0, 0, -1], dtype=object)

In [50]: np.frompyfunc(lambda x: 'aa' in x, 1, 1)(bar)
Out[50]: array([True, True, False], dtype=object)

进一步的测试表明 ndarray __contains__ 在数组的 flat 版本上运行——也就是说,形状不会影响它的行为。

您也可以这样做:

mask = [foo in x for x in bar]  
filter = bar[ np.where( mask * bar != '') ]

如果可以使用pandas,则可以使用str.contains方法。

import numpy as np
entries = np.array(["aaa", "aab", "aca"])

import pandas as pd
pd.Series(entries).str.contains('aa') # <----

结果:

0     True
1     True
2    False
dtype: bool

该方法还接受更复杂模式的正则表达式:

pd.Series(entries).str.contains(r'a.a')

结果:

0     True
1    False
2     True
dtype: bool