Python 中的高效列表构建
Efficient list building in Python
作为 Matlab 的长期用户,每当我在一个循环中构建一个包含多个元素的 list/array/anything 时,我习惯于得到警告,这样它每次都会改变大小,因为这会减慢速度。
当我自学 Python 2.7 时,我想知道这样的规则是否适用于字符串。我确切地知道我想要我的字符串有多长,并且我有一个我想要构建它的字符的特定列表,但除此之外我希望它是随机的。到目前为止,我最喜欢的代码是:
def BernSeq(length,freq):
"""Create a Bernoulli sequence - a random bitstring - of the given length
and with the given frequency of 1s"""
seq = '0'*length
for ii in range(length):
num = np.random.rand(1)
if num < freq:
cha = '1'
seq = seq[:ii] + cha + seq[ii+1:]
我将其称为 BernSeq(20,.25)
并得到输出 '10001000000001011101'
.
我已经尝试过 seq[ii] = '1'
,但是,用 IPython 的话来说,TypeError: 'str' object does not support item assignment
。
那么,我是不是用最 Pythonic 的方式来做这件事,还是有一些我还没有见过的花招 - 也许是一个随机字符串或列表生成器,我可以直接给它一个我希望它随机选择的字符列表,我希望每种可能性都有的概率,以及我希望这个字符串或列表有多长?
(我看过 other questions 关于随机字符串的内容,但虽然他们确实解决了如何使其长度正确的问题,但他们通常会尝试生成密码,所有 ASCII 字符的可能性均等。这不是我想要什么。)
有几种方法。
首先,您可以从一开始就创建正确的元素:
seq = "".join("1" if np.random.rand(1) < freq else "0" for _ in range(length))
但要问的第一个问题是:您想要什么作为输出?你要求它是一个字符串吗?也许您可以接受布尔值列表?
然后
seq = [np.random.rand(1) < freq for _ in range(length)]
也够了
您可以根据您的分布偏好定义一个生成随机字母的函数,然后按您喜欢的次数执行它。
import random
def my_letter():
a = random.randint(1,10)
if a > 5:
return "a"
else:
return "b"
那么对于您的长度偏好:
my_str = ""
for x in range(length):
my_str += my_letter()
python 中的字符串是不可变的
In [1]: a = '1' * 5
In [2]: a
Out[2]: '11111'
In [3]: type(a)
Out[3]: str
In [4]: a[2] = 'c'
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-4-69ed835eb212> in <module>()
----> 1 a[2] = 'c'
TypeError: 'str' object does not support item assignment
In [5]: b = [1] * 5
In [6]: b
Out[6]: [1, 1, 1, 1, 1]
调整您的代码以使用 int
列表。 (最少的修复,不修复样式或优化其他东西)
def BernSeq(length,freq):
"""Create a Bernoulli sequence - a random bitstring - of the given length
and with the given frequency of 1s"""
seq = [0] * length
for ii in range(length):
num = np.random.rand(1)
if num < freq:
cha = 1
seq.append(cha)
NumPy 中存在二项分布,np.random.binomial
。以您想要的频率从中采样,然后将字符串表示形式连接在一起,这比您自己重新发明它要快。
def bernouilli_str(N, one_freq):
return ''.join(np.random.binomial(1, one_freq, N).astype('U1'))
基准
In [112]: %timeit ''.join(np.random.binomial(1, 0.75, 10**6).astype('U1'))
637 ms ± 5.75 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [113]: %timeit "".join("1" if np.random.rand(1) < 0.75 else "0" for _ in range(10**6))
1.69 s ± 11 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
作为 Matlab 的长期用户,每当我在一个循环中构建一个包含多个元素的 list/array/anything 时,我习惯于得到警告,这样它每次都会改变大小,因为这会减慢速度。
当我自学 Python 2.7 时,我想知道这样的规则是否适用于字符串。我确切地知道我想要我的字符串有多长,并且我有一个我想要构建它的字符的特定列表,但除此之外我希望它是随机的。到目前为止,我最喜欢的代码是:
def BernSeq(length,freq):
"""Create a Bernoulli sequence - a random bitstring - of the given length
and with the given frequency of 1s"""
seq = '0'*length
for ii in range(length):
num = np.random.rand(1)
if num < freq:
cha = '1'
seq = seq[:ii] + cha + seq[ii+1:]
我将其称为 BernSeq(20,.25)
并得到输出 '10001000000001011101'
.
我已经尝试过 seq[ii] = '1'
,但是,用 IPython 的话来说,TypeError: 'str' object does not support item assignment
。
那么,我是不是用最 Pythonic 的方式来做这件事,还是有一些我还没有见过的花招 - 也许是一个随机字符串或列表生成器,我可以直接给它一个我希望它随机选择的字符列表,我希望每种可能性都有的概率,以及我希望这个字符串或列表有多长?
(我看过 other questions 关于随机字符串的内容,但虽然他们确实解决了如何使其长度正确的问题,但他们通常会尝试生成密码,所有 ASCII 字符的可能性均等。这不是我想要什么。)
有几种方法。
首先,您可以从一开始就创建正确的元素:
seq = "".join("1" if np.random.rand(1) < freq else "0" for _ in range(length))
但要问的第一个问题是:您想要什么作为输出?你要求它是一个字符串吗?也许您可以接受布尔值列表?
然后
seq = [np.random.rand(1) < freq for _ in range(length)]
也够了
您可以根据您的分布偏好定义一个生成随机字母的函数,然后按您喜欢的次数执行它。
import random
def my_letter():
a = random.randint(1,10)
if a > 5:
return "a"
else:
return "b"
那么对于您的长度偏好:
my_str = ""
for x in range(length):
my_str += my_letter()
python 中的字符串是不可变的
In [1]: a = '1' * 5
In [2]: a
Out[2]: '11111'
In [3]: type(a)
Out[3]: str
In [4]: a[2] = 'c'
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-4-69ed835eb212> in <module>()
----> 1 a[2] = 'c'
TypeError: 'str' object does not support item assignment
In [5]: b = [1] * 5
In [6]: b
Out[6]: [1, 1, 1, 1, 1]
调整您的代码以使用 int
列表。 (最少的修复,不修复样式或优化其他东西)
def BernSeq(length,freq):
"""Create a Bernoulli sequence - a random bitstring - of the given length
and with the given frequency of 1s"""
seq = [0] * length
for ii in range(length):
num = np.random.rand(1)
if num < freq:
cha = 1
seq.append(cha)
NumPy 中存在二项分布,np.random.binomial
。以您想要的频率从中采样,然后将字符串表示形式连接在一起,这比您自己重新发明它要快。
def bernouilli_str(N, one_freq):
return ''.join(np.random.binomial(1, one_freq, N).astype('U1'))
基准
In [112]: %timeit ''.join(np.random.binomial(1, 0.75, 10**6).astype('U1'))
637 ms ± 5.75 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [113]: %timeit "".join("1" if np.random.rand(1) < 0.75 else "0" for _ in range(10**6))
1.69 s ± 11 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)