[英]Python concatenate (multiple) strings to number
我有一个 python 列表['a1', 'b1', 'a2', 'b2','a3', 'b3']
。 设置m=3
并且我想使用循环获取此列表,因为这里m=3
可能是一个更大的数字,例如m=100
。
因为我们可以有
m = 3
['a' + str(i) for i in np.arange(1,m+1)]
# ['a1', 'a2', 'a3']
['b' + str(i) for i in np.arange(1,m+1)]
# ['b1', 'b2', 'b3']
然后我尝试使用['a1', 'b1', 'a2', 'b2','a3', 'b3']
[ ['a','b'] + str(i) for i in np.arange(1,m+1)]
并且有TypeError: can only concatenate list (not "str") to list
然后我尝试
[ np.array(['a','b']) + str(i) for i in np.arange(1,m+1)]
而且我仍然收到错误,因为UFuncTypeError: ufunc 'add' did not contain a loop with signature matching types (dtype('<U1'), dtype('<U1')) -> None
。
我该如何解决这个问题? 更重要的是,如何通过类似的方式获得类似['a1', 'b1', 'c1', 'a2', 'b2','c2','a3', 'b3', 'c3']
东西?
您需要对数字范围和字符串列表进行迭代
In [106]: [s+str(i) for i in range(1,4) for s in ['a','b']]
Out[106]: ['a1', 'b1', 'a2', 'b2', 'a3', 'b3']
正如@j1-lee 的答案(以及稍后在其他答案中)所指出的那样,一个简单的组合list
理解将起作用。
import string
def letter_number_loop(n, m):
letters = string.ascii_letters[:n]
numbers = range(1, m + 1)
return [f"{letter}{number}" for number in numbers for letter in letters]
类似地,可以使用itertools.product()
,正如尼克的回答所证明的那样,获得基本相同的结果:
import itertools
def letter_number_it(n, m):
letters = string.ascii_letters[:n]
numbers = range(1, m + 1)
return [
f"{letter}{number}"
for number, letter in itertools.product(numbers, letters)]
但是,可以编写 NumPy 向量化方法,利用如果dtype
是object
的事实,则操作确实遵循 Python 语义。
import numpy as np
def letter_number_np(n, m):
letters = np.array(list(string.ascii_letters[:n]), dtype=object)
numbers = np.array([f"{i}" for i in range(1, m + 1)], dtype=object)
return (letters[None, :] + numbers[:, None]).ravel().tolist()
请注意,如果消耗输出的任何东西都能够处理 NumPy 数组本身,则可以避免最终的numpy.ndarray.tolist()
,从而节省一些相对较小但绝对可观的时间。
以下确实表明这些功能是等效的:
funcs = letter_number_loop, letter_number_it, letter_number_np
n, m = 2, 3
for func in funcs:
print(f"{func.__name__!s:>32} {func(n, m)}")
letter_number_loop ['a1', 'b1', 'a2', 'b2', 'a3', 'b3']
letter_number_it ['a1', 'b1', 'a2', 'b2', 'a3', 'b3']
letter_number_np ['a1', 'b1', 'a2', 'b2', 'a3', 'b3']
对于较大的输入,这要快得多,这些基准证明了这一点:
timings = {}
k = 16
for n in (2, 20):
for k in range(1, 10):
m = 2 ** k
print(f"n = {n}, m = {m}")
timings[n, m] = []
base = funcs[0](n, m)
for func in funcs:
res = func(n, m)
is_good = base == res
timed = %timeit -r 64 -n 64 -q -o func(n, m)
timing = timed.best * 1e6
timings[n, m].append(timing if is_good else None)
print(f"{func.__name__:>24} {is_good} {timing:10.3f} µs")
要绘制:
import matplotlib.pyplot as plt
import pandas as pd
n_s = (2, 20)
fig, axs = plt.subplots(1, len(n_s), figsize=(12, 4))
for i, n in enumerate(n_s):
partial_timings = {k[1]: v for k, v in timings.items() if k[0] == n}
df = pd.DataFrame(data=partial_timings, index=[func.__name__ for func in funcs]).transpose()
df.plot(marker='o', xlabel='Input size / #', ylabel='Best timing / µs', ax=axs[i], title=f"n = {n}")
这些表明显式循环版本( letter_number_loop()
和letter_number_it()
)在某种程度上具有可比性,而 NumPy 向量化的( letter_number_np()
)对于较大的输入相对更快,最高可达约 2 倍的加速。
列表理解中可以有多个for
:
prefixes = ['a', 'b', 'c']
m = 3
output = [f"{prefix}{num}" for num in range(1, m+1) for prefix in prefixes]
print(output) # ['a1', 'b1', 'c1', 'a2', 'b2', 'c2', 'a3', 'b3', 'c3']
如果你有多个for
s,那些将被嵌套,如
for num in range(1, m+1):
for prefix in prefixes:
...
您可以使用itertools.product
获取字母和m
范围的所有组合,然后将它们连接到 f 字符串中(而不是使用join
,因为一个元素是整数,因此需要转换为字符串):
[f'{x}{y}' for x, y in itertools.product(range(1, m+1), ['a', 'b'])]
输出:
['a1', 'b1', 'a2', 'b2', 'a3', 'b3']
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.