查找 NumPy 数组中包含子字符串的所有位置(最有效?)
Find all positions in a NumPy array that contain a substring (most efficient?)
我想在包含子字符串的数组中查找所有索引,在本例中为 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]
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.vectorize
或 np.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))
扩展 hpaulj 的答案,我使用了一些代码 one-hot 对 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_Drama
、col_Comedy
和 col_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)
像这样为每个后缀搜索数据帧所花费的时间太长了。
我想在包含子字符串的数组中查找所有索引,在本例中为 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]
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.vectorize
或 np.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))
扩展 hpaulj 的答案,我使用了一些代码 one-hot 对 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_Drama
、col_Comedy
和 col_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)
像这样为每个后缀搜索数据帧所花费的时间太长了。