Numpy 将布尔数组的字符串表示转换为布尔数组

8
有没有一种原生的numpy方法可以将一个字符串表示的布尔数组转换为实际的布尔值呢?例如:
['True','False','True','False']

我可以创建一个实际的布尔数组用于掩码/索引吗?我可以使用for循环遍历并重建数组,但对于大型数组来说,这种方法速度较慢。

它是一个numpy字符串数组(如果这样的东西存在)还是一个Python字符串数组? - Eric
这是一个numpy字符串数组 - 很奇怪,我知道。 - Newmu
1
@Newmu -- 我认为解决这个问题的方法是避免在第一时间获取字符串表示的数组。你是如何得到那个数组的?也许我们应该从那里开始寻找优化的方法... - mgilson
我必须处理来自别人的代码。 - Newmu
3个回答

9

如果我理解正确,您应该能够进行布尔比较,无论 dtype 是字符串还是 object

>>> a = np.array(['True', 'False', 'True', 'False'])
>>> a
array(['True', 'False', 'True', 'False'], 
      dtype='|S5')
>>> a == "True"
array([ True, False,  True, False], dtype=bool)

或者

>>> a = np.array(['True', 'False', 'True', 'False'], dtype=object)
>>> a
array(['True', 'False', 'True', 'False'], dtype=object)
>>> a == "True"
array([ True, False,  True, False], dtype=bool)

广播的奇妙之处?而且速度很快(比其他答案快20倍)。 - Newmu
@Newmu 而且字符串内部化,如果我没记错的话(至少,a 中所有值为 'True' 的项都具有相同的 id() 值,对于所有 'False' 元素也是如此 [尽管奇怪的是,即使在将一个条目与其本身进行测试时,is 也似乎不起作用。a[0] is a[0] 返回 False,即使 id(a[0]) == id(a[0]) 返回 True ])我认为内部化是这里的相等性检查比 numpy.char.startswith() 快得多的原因,即使 numpy.char 中的函数应该在 numpy 数组上执行快速的字符串操作。 - JAB

2
我找到了一种比DSM更快的方法,受Eric启发,尽管改进在较小的值列表中效果最好;在非常大的值时,迭代本身的成本开始超过在创建numpy数组时执行真实测试的优势。使用is==进行测试(对于字符串被内部化与否的情况,因为is无法处理非内部化字符串。由于'True'可能会成为脚本中的文字,所以它应该被内部化)。测试表明,虽然我使用==的版本比使用is的版本慢,但仍比DSM的版本快得多。
测试设置:
import timeit
def timer(statement, count):
    return timeit.repeat(statement, "from random import choice;import numpy as np;x = [choice(['True', 'False']) for i in range(%i)]" % count)

>>> stateIs = "y = np.fromiter((e is 'True' for e in x), bool)"
>>> stateEq = "y = np.fromiter((e == 'True' for e in x), bool)"
>>> stateDSM = "y = np.array(x) == 'True'"

有1000个元素时,更快的语句大约只需要 DSM 语句时间的 66%:

>>> timer(stateIs, 1000)
[101.77722641656146, 100.74985342340369, 101.47228618107965]
>>> timer(stateEq, 1000)
[112.26464996250706, 112.50754567379681, 112.76057346127709]
>>> timer(stateDSM, 1000)
[155.67689949529995, 155.96820504501557, 158.32394669279802]

对于较小的字符串数组(数量在百位数而非千位数),所花费的时间少于 DSM 的一半:

>>> timer(stateIs, 100)
[11.947757485669172, 11.927990253608186, 12.057855628259858]
>>> timer(stateEq, 100)
[13.064947253943501, 13.161545451986967, 13.30599035623618]
>>> timer(stateDSM, 100)
[31.270060799078237, 30.941749748808434, 31.253922641324607]

完成50项清单时,DSM的完成率略高于25%:
>>> timer(stateIs, 50)
[6.856538342483873, 6.741083326021908, 6.708402786859551]
>>> timer(stateEq, 50)
[7.346079345032194, 7.312723444475523, 7.309259899921017]
>>> timer(stateDSM, 50)
[24.154247576229864, 24.173593700599667, 23.946403452288905]

对于5个项目,DSM的大约11%:

>>> timer(stateIs, 5)
[1.8826215278058953, 1.850232652068371, 1.8559381315990322]
>>> timer(stateEq, 5)
[1.9252821868467436, 1.894011299061276, 1.894306935199893]
>>> timer(stateDSM, 5)
[18.060974208809057, 17.916322392367874, 17.8379771602049]

1
你有注意到结果中它们都是“True”吗?:-P - mgilson
@DSM,我的新答案似乎比旧的好多了。 - JAB
@Newmu 我的两个列表即使有5K个值,仍然比DSM的稍微快一些,只是相对于较小的列表而言,改进要少得多。 - JAB
啊,好的!很难超越 DSM 的简洁性。 - Newmu

0

这个够好吗?

my_list = ['True', 'False', 'True', 'False']
np.array(x == 'True' for x in my_list)

虽然它不是本地的,但如果您已经从非本地列表开始,那么这并不重要。


1
这样写是行不通的,因为numpy与生成器表达式不兼容。 - DSM
将其作为列表推导式进行封装可以实现功能,但对于包含数千个值的数组而言,其速度比DSM的答案慢20倍。 - Newmu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接