[英]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.