查找包含子字符串的NumPy数组中的所有位置(效率最高?)

时间:2017-02-09 22:41:29

标签: python numpy indexing substring where

我想在数组中找到包含子字符串的所有索引,在这种情况下它是substring = "contig_"。我开始逐行迭代,然后迭代遍历数组中的每个元素,但这是最蛮力的。

numpy或scipy中是否有任何函数可以比蛮力方法更快?

A = np.array([['K00180:55:H3NHMBBXX:7:1101:30340:1068', '83',
        'contig_1758_2278_4341_-', '1487', '60', '140M', '=', '1334',
        '293', "=",
        '*', 'RG:Z:RG_0', 'MD:Z:23A30A85', 'NM:i:2\n'],
       ['K00180:55:H3NHMBBXX:7:1101:30340:1068', '163',
        'contig_1758_2278_4341_-', '1334', '60', '87M1I3M1D17M', '=',
        '1487', '293', "contig_1297_3232_198298_+",
        '*', 'RG:Z:RG_0', 'MD:Z:31G3G2G6T6C6A9C4T15^G17', 'NM:i:10\n'],
       ['K00180:55:H3NHMBBXX:7:1101:28026:1103', '83',
        'contig_1281_415_1704_-', '514', '60', '142M', '=', '396', '260', "=",
        '*', 'RG:Z:RG_0', 'MD:Z:11C130', 'NM:i:1\n']], 
      dtype='<U149')

for row in A:
    print(np.where(["contig_" in x for x in row])[0])

# [2]
# [2 9]
# [2]

2 个答案:

答案 0 :(得分:5)

np.char是一组函数,它们将字符串方法应用于像您这样的数组元素。所以使用find函数:

In [311]: np.char.find(A, 'contig')
Out[311]: 
array([[-1, -1,  0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
       [-1, -1,  0, -1, -1, -1, -1, -1, -1,  0, -1, -1, -1, -1],
       [-1, -1,  0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]])

-1表示未找到它的元素,0表示找不到。

In [312]: np.where(np.char.find(A, 'contig')>=0)
Out[312]: (array([0, 1, 1, 2], dtype=int32), array([2, 2, 9, 2], dtype=int32))

In [313]: A[_]
Out[313]: 
array(['contig_1758_2278_4341_-', 'contig_1758_2278_4341_-',
       'contig_1297_3232_198298_+', 'contig_1281_415_1704_-'], 
      dtype='<U149')

这样的函数必须迭代元素,并应用相应的字符串方法,因此它们不像通常的numpy数字代码那么快,但它们比自己的迭代要容易得多。

np.vectorizenp.frompyfunc也可用于将函数应用于数组的每个元素。它们也是迭代的,因此对你自己的迭代没有显着的加速。我仍然发现frompyfunc经常提供30%的加速。

In [331]: f=np.frompyfunc(lambda x: x.find('contig'), 1,1)  # like char.find

In [332]: f=np.frompyfunc(lambda x: 'contig' in x, 1,1)  # your 'in'

In [333]: f(A)
Out[333]: 
array([[False, False, True, False, False, False, False, False, False,
        False, False, False, False, False],
       [False, False, True, False, False, False, False, False, False, True,
        False, False, False, False],
       [False, False, True, False, False, False, False, False, False,
        False, False, False, False, False]], dtype=object)

In [334]: np.where(f(A))
Out[334]: (array([0, 1, 1, 2], dtype=int32), array([2, 2, 9, 2], dtype=int32))

答案 1 :(得分:0)

扩展hpaulj的答案,我使用了一些代码对Pandas中数据集的所有列进行了一次热编码。这是因为pd.dummies()不允许您对数据进行编码,如下例所示。

Pandas中的一个常见问题是在列B中搜索一些值A的索引。但是,当列B是字符串数组(例如B = ["Drama", "Comedy", "Thriller"]。我们要匹配A在B列数组中的所有行。

通常,如果我们不将数组作为特征处理,则可以改用df.loc[df[col] == 'Drama', col]检查字符串等于行和列的索引。

注意:count_unique是功能词典,但是df.columns也一样容易。

for key in count_unique.keys():
    values = np.zeros(df.shape[0], dtype=bool)

    # get indices where current key in column
    f = np.frompyfunc(lambda x: key in x, 1, 1)
    true_indices = np.where(f(df[col]))[0]
    
    # set the indices where the key does exist and create column
    values[true_indices] = True
    df[f"{prefix}_{key}"] = values

请注意,此代码也在快速发展。我们可以轻松地使用df.iterrows()遍历所有行,但是我们选择了有效的路由。

另一个糟糕的解决方案(适用于loc):

我们在这里有一个需要的id列,我们还假设已经创建了空列,例如col_Dramacol_Comedycol_Thriller列。我们仍然有B列,其中的值A之前已引用。

def distribute_suffixes(x):
    for suffix in x[col]:
        df.loc[df['id'] == x.id, f"{prefix}_{suffix}"] = True

_ = df.apply(distribute_suffixes, axis=1)

对于每个后缀,搜索这样的数据帧根本就花了很长时间。