处理数百万条推文 - 循环非常慢。关于如何加快速度的任何想法?
Processing several million tweets - loop is very slow. Any ideas on how to speed it up?
如标题所示,我正在处理数百万条推文,其中一个数据点是两个不同列表中是否存在任何单词(每个列表包含大约 500 个单词)。可以理解,它很慢,但我会定期这样做,所以我想加快速度。有什么想法吗?
lista = ['word1', 'word2', ... 'word500']
listb = ['word1', 'word2', ..., 'word500']
def token_list_count(df):
for i, t in df.iterrows():
list_a = 0
list_b = 0
for tok in t['tokens']:
if tok in lista: list_a += 1
elif tok in listb: list_b += 1
df.loc[i, 'token_count'] = int(len(t['tokens']))
df.loc[i, 'lista_count'] = int(list_a)
df.loc[i, 'listb_count'] = int(list_b)
if i % 25000 == 0: print('25k more processed...')
return df
编辑:
输入/之前:
输出/之后:
使用下面的代码应该可以为您节省一些时间。请注意,如果您按原样使用下面的代码,您在 listb 中出现的次数是不正确的。
您不会从下面的代码中获得太多加速,因为创建集合将占用几乎所有来自更快查找的时间优势,但是如果您将 seta 和 setb 也用于另一个查找循环,您将节省时间如果进一步的代码。
也许你可以安排,直接将列表作为集合获取,从而节省创建集合的时间?
lista = ['word1a', 'word2a', 'word500a']
listb = ['word1b', 'word2a', 'word500b']
seta = set(lista)
setb = set(listb)
def token_list_count(df):
dfIterrows = df.iterrows() # TRY THIS !!!
for i, t in dfIterrows: # TRY THIS !!!
list_a = 0
list_b = 0
tTokens = t['tokens'] # TRY THIS !!!
for tok in tTokens: # TRY THIS !!!
if tok in seta: list_a += 1
elif tok in setb: list_b += 1
# why not "if tok in setb: list_b +=1" ???
df.loc[i, 'token_count'] = int(len(t['tokens']))
df.loc[i, 'lista_count'] = int(list_a)
df.loc[i, 'listb_count'] = int(list_b)
if i % 25000 == 0: print('25k more processed...')
return df
如果速度对你来说真的很重要,我建议你只使用 Python 作为一个框架, 除了多处理之外,还使用适当的可执行文件或数据库查询 进行查找用于在 seta 和 setb 中查找。
另一种选择是考虑 root
在评论中提出的建议:“”"The biggest issue here is that you're iterating over a DataFrame, which should almost always be avoided. Using a vectorized approach is what will yield the best performance improvements. "”“
为了避免迭代数据框,您可以尝试下面的函数。第一行将 tokens
列扩展为数据框,其中每个第一个标记将在第一列中,第二个标记将在第二列中,依此类推。总列数将等于 df
中最大的 token_count
。从那里可以很容易地使用 .isin
方法计算 lista
和 listb
计数,并对每一行求和。
由完整 df
制作的 tok
数据帧可能会占用大量内存,在这种情况下,可能需要将 df
分成几个块并分别处理它们。
def token_list_count(df):
tok = df['tokens'].apply(pd.Series)
df['token_count'] = tok.notnull().sum(axis=1)
df['lista_count'] = tok.isin(lista).sum(axis=1)
df['listb_count'] = tok.isin(listb).sum(axis=1)
return df
你原来的功能还有改进的余地:
- 没有必要使用
iterrows()
遍历整个 df
,因为您只需要一列即可进行计算。所以你可以使用 df.tokens.iteritems()
代替。这将使您在每次迭代时都创建一个新的 Series 对象。
- 因为您一次只设置一个新值,所以您可以使用
at
索引器而不是 loc
。请参阅 pandas 文档中的 Fast scalar value getting and setting。
我不知道这会有多大帮助,但可能会有一些帮助 =)。
def token_list_count(df):
for i, tok in df.tokens.iteritems():
list_a = sum((t in lista) for t in tok)
list_b = sum((t in listb) for t in tok)
df.at[i, 'token_count'] = int(len(tok))
df.at[i, 'lista_count'] = int(list_a)
df.at[i, 'listb_count'] = int(list_b)
if i % 25000 == 0: print('25k more processed...')
return df
你也可以不使用 for 循环但使用 apply
:
df['token_count'] = df.tokens.apply(len)
df['lista_count'] = df.tokens.apply(lambda tok: sum((t in lista) for t in tok))
df['listb_count'] = df.tokens.apply(lambda tok: sum((t in listb) for t in tok))
如标题所示,我正在处理数百万条推文,其中一个数据点是两个不同列表中是否存在任何单词(每个列表包含大约 500 个单词)。可以理解,它很慢,但我会定期这样做,所以我想加快速度。有什么想法吗?
lista = ['word1', 'word2', ... 'word500']
listb = ['word1', 'word2', ..., 'word500']
def token_list_count(df):
for i, t in df.iterrows():
list_a = 0
list_b = 0
for tok in t['tokens']:
if tok in lista: list_a += 1
elif tok in listb: list_b += 1
df.loc[i, 'token_count'] = int(len(t['tokens']))
df.loc[i, 'lista_count'] = int(list_a)
df.loc[i, 'listb_count'] = int(list_b)
if i % 25000 == 0: print('25k more processed...')
return df
编辑:
输入/之前:
输出/之后:
使用下面的代码应该可以为您节省一些时间。请注意,如果您按原样使用下面的代码,您在 listb 中出现的次数是不正确的。
您不会从下面的代码中获得太多加速,因为创建集合将占用几乎所有来自更快查找的时间优势,但是如果您将 seta 和 setb 也用于另一个查找循环,您将节省时间如果进一步的代码。
也许你可以安排,直接将列表作为集合获取,从而节省创建集合的时间?
lista = ['word1a', 'word2a', 'word500a']
listb = ['word1b', 'word2a', 'word500b']
seta = set(lista)
setb = set(listb)
def token_list_count(df):
dfIterrows = df.iterrows() # TRY THIS !!!
for i, t in dfIterrows: # TRY THIS !!!
list_a = 0
list_b = 0
tTokens = t['tokens'] # TRY THIS !!!
for tok in tTokens: # TRY THIS !!!
if tok in seta: list_a += 1
elif tok in setb: list_b += 1
# why not "if tok in setb: list_b +=1" ???
df.loc[i, 'token_count'] = int(len(t['tokens']))
df.loc[i, 'lista_count'] = int(list_a)
df.loc[i, 'listb_count'] = int(list_b)
if i % 25000 == 0: print('25k more processed...')
return df
如果速度对你来说真的很重要,我建议你只使用 Python 作为一个框架, 除了多处理之外,还使用适当的可执行文件或数据库查询 进行查找用于在 seta 和 setb 中查找。
另一种选择是考虑 root
在评论中提出的建议:“”"The biggest issue here is that you're iterating over a DataFrame, which should almost always be avoided. Using a vectorized approach is what will yield the best performance improvements. "”“
为了避免迭代数据框,您可以尝试下面的函数。第一行将 tokens
列扩展为数据框,其中每个第一个标记将在第一列中,第二个标记将在第二列中,依此类推。总列数将等于 df
中最大的 token_count
。从那里可以很容易地使用 .isin
方法计算 lista
和 listb
计数,并对每一行求和。
由完整 df
制作的 tok
数据帧可能会占用大量内存,在这种情况下,可能需要将 df
分成几个块并分别处理它们。
def token_list_count(df):
tok = df['tokens'].apply(pd.Series)
df['token_count'] = tok.notnull().sum(axis=1)
df['lista_count'] = tok.isin(lista).sum(axis=1)
df['listb_count'] = tok.isin(listb).sum(axis=1)
return df
你原来的功能还有改进的余地:
- 没有必要使用
iterrows()
遍历整个df
,因为您只需要一列即可进行计算。所以你可以使用df.tokens.iteritems()
代替。这将使您在每次迭代时都创建一个新的 Series 对象。 - 因为您一次只设置一个新值,所以您可以使用
at
索引器而不是loc
。请参阅 pandas 文档中的 Fast scalar value getting and setting。
我不知道这会有多大帮助,但可能会有一些帮助 =)。
def token_list_count(df):
for i, tok in df.tokens.iteritems():
list_a = sum((t in lista) for t in tok)
list_b = sum((t in listb) for t in tok)
df.at[i, 'token_count'] = int(len(tok))
df.at[i, 'lista_count'] = int(list_a)
df.at[i, 'listb_count'] = int(list_b)
if i % 25000 == 0: print('25k more processed...')
return df
你也可以不使用 for 循环但使用 apply
:
df['token_count'] = df.tokens.apply(len)
df['lista_count'] = df.tokens.apply(lambda tok: sum((t in lista) for t in tok))
df['listb_count'] = df.tokens.apply(lambda tok: sum((t in listb) for t in tok))