有没有办法使用函数而不是嵌套循环来改善以下过程的 运行 时间?
Is there a way to use functions and not nested loops to improve the run time of the following process?
我的问题是我有一个包含评论的向量和另一个包含我试图在评论中找到的名称的向量。我的方法是使用 fuzzywuzzy 并在第一步中为每个名称分配它为相应评论获得的分数。这样我就可以在下一步中说出哪个评论中可能提到了哪些名字
例如数据可能如下所示:
# in the original data both would be DFs with more than 1 column
names = pd.DataFrame(["Anna Starkow", "Marian Mueller", "James Leo Arneau"))
# spelling mistakes and abbreviations
Comments=pd.DataFrame(["Ana Starkov was super good!", "M. mueller is in great shape", "I like Arneau and Starkow's work","Thanks Anna Starkov, credits to JL Arneau"])
# my approach:
# This is also from stack
N = len(Comments.index)
M = len(names.index)
res= pd.DataFrame([[0] * M]*N)
#My code
DF=pd.concat([Comments.reset_index(drop=True), res],axis=1)
for x in range(len(Comments.index)):
for i in range(len(names.index)):
DF[x, i+1]=fuzz.token_set_ratio(DF.Comments[x],names.names[i])
然而,它 运行 永远没有结果。
我期待这样的事情回来:
# Comments Anna Starkow Marian Mueller ....
# Ana Starkov was super good! 80 0 ....
# M. mueller is in great shape 0 70 ....
# .... .... .... ....
有没有更有效的方法来做到这一点?
我希望我的代码没有错误,因为我不得不从另一台不允许我使用 Stack 的机器上输入它。
这里的问题不是 Python 而是 DataFrame,无论您是处理 Pandas 还是 PySpark,它的工作方式都类似于 SQL 表:只要有可能,您应该向量化 DF 上的操作。这让计算机担心算法的并行化。
如果您有 pre-existing DF,则可以使用 pandas.DataFrame.apply().
有效地将函数应用于每个元素
在您的情况下,您似乎只是在寻找一种更好的初始化 DF 的方法。如果您可以将您的 DF 内容描述为字典列表(每条记录一个字典),我建议使用 pandas.DataFrame.from_records()。列表中的每个字典都会有类似 {'Comments': 'Ana Starkov was super good!', 'Anna Starkow': 80, 'Marian Mueller': 0}
的形式(from_dict()
方法在概念上相似,但输入格式略有不同。)
这将比构建/重写 DF 快得多 cell-by-cell。
您可以尝试以下方法:
i=0
l0=[]
for comment in Comments[0]:
l=[]
for name in names[0]:
l.append(fuzz.token_sort_ratio(comment,name))
l0.append(l)
DF_out=pd.concat([Comments.reset_index(drop=True), pd.DataFrame(np.matrix(l0)) ],axis=1)
其他答案集中在加快分配结果的方法上。虽然这可能对性能略有帮助,但您真正的问题是与 fuzzywuzzy 匹配的字符串真的很慢。您可以优化两个部分:
当将两个字符串传递给 fuzz.token_sort_ratio 时,它将通过将它们小写化、删除非字母数字字符和修剪空格来预处理这些字符串。由于您多次迭代名称,因此您重复了这项工作
即使使用 python-Levenshtein FuzzyWuzzy 在很多地方都没有真正优化。您应该将其替换为 RapidFuzz,它实现了具有相似接口的相同算法,但主要是用 C++ 实现的,并附带了一些额外的算法改进,使其速度更快。
from rapidfuzz import process, utils
processed_names = [utils.default_process(name) for name in names.names]
for x in range(len(Comments.index)):
for i, name in enumerate(processed_names):
DF[x, i+1]=fuzz.token_sort_ratio(
utils.default_process(DF.Comments[x]), name, processor=None)
如果您的罚款与不良匹配返回 0 分,您可以通过传递 score_cutoff
:
进一步提高性能
fuzz.token_sort_ratio(
utils.default_process(DF.Comments[x]), name,
processor=None, score_cutoff=<insert your minimum score here>)
我的问题是我有一个包含评论的向量和另一个包含我试图在评论中找到的名称的向量。我的方法是使用 fuzzywuzzy 并在第一步中为每个名称分配它为相应评论获得的分数。这样我就可以在下一步中说出哪个评论中可能提到了哪些名字
例如数据可能如下所示:
# in the original data both would be DFs with more than 1 column
names = pd.DataFrame(["Anna Starkow", "Marian Mueller", "James Leo Arneau"))
# spelling mistakes and abbreviations
Comments=pd.DataFrame(["Ana Starkov was super good!", "M. mueller is in great shape", "I like Arneau and Starkow's work","Thanks Anna Starkov, credits to JL Arneau"])
# my approach:
# This is also from stack
N = len(Comments.index)
M = len(names.index)
res= pd.DataFrame([[0] * M]*N)
#My code
DF=pd.concat([Comments.reset_index(drop=True), res],axis=1)
for x in range(len(Comments.index)):
for i in range(len(names.index)):
DF[x, i+1]=fuzz.token_set_ratio(DF.Comments[x],names.names[i])
然而,它 运行 永远没有结果。
我期待这样的事情回来:
# Comments Anna Starkow Marian Mueller ....
# Ana Starkov was super good! 80 0 ....
# M. mueller is in great shape 0 70 ....
# .... .... .... ....
有没有更有效的方法来做到这一点?
我希望我的代码没有错误,因为我不得不从另一台不允许我使用 Stack 的机器上输入它。
这里的问题不是 Python 而是 DataFrame,无论您是处理 Pandas 还是 PySpark,它的工作方式都类似于 SQL 表:只要有可能,您应该向量化 DF 上的操作。这让计算机担心算法的并行化。
如果您有 pre-existing DF,则可以使用 pandas.DataFrame.apply().
有效地将函数应用于每个元素在您的情况下,您似乎只是在寻找一种更好的初始化 DF 的方法。如果您可以将您的 DF 内容描述为字典列表(每条记录一个字典),我建议使用 pandas.DataFrame.from_records()。列表中的每个字典都会有类似 {'Comments': 'Ana Starkov was super good!', 'Anna Starkow': 80, 'Marian Mueller': 0}
的形式(from_dict()
方法在概念上相似,但输入格式略有不同。)
这将比构建/重写 DF 快得多 cell-by-cell。
您可以尝试以下方法:
i=0
l0=[]
for comment in Comments[0]:
l=[]
for name in names[0]:
l.append(fuzz.token_sort_ratio(comment,name))
l0.append(l)
DF_out=pd.concat([Comments.reset_index(drop=True), pd.DataFrame(np.matrix(l0)) ],axis=1)
其他答案集中在加快分配结果的方法上。虽然这可能对性能略有帮助,但您真正的问题是与 fuzzywuzzy 匹配的字符串真的很慢。您可以优化两个部分:
当将两个字符串传递给 fuzz.token_sort_ratio 时,它将通过将它们小写化、删除非字母数字字符和修剪空格来预处理这些字符串。由于您多次迭代名称,因此您重复了这项工作
即使使用 python-Levenshtein FuzzyWuzzy 在很多地方都没有真正优化。您应该将其替换为 RapidFuzz,它实现了具有相似接口的相同算法,但主要是用 C++ 实现的,并附带了一些额外的算法改进,使其速度更快。
from rapidfuzz import process, utils
processed_names = [utils.default_process(name) for name in names.names]
for x in range(len(Comments.index)):
for i, name in enumerate(processed_names):
DF[x, i+1]=fuzz.token_sort_ratio(
utils.default_process(DF.Comments[x]), name, processor=None)
如果您的罚款与不良匹配返回 0 分,您可以通过传递 score_cutoff
:
fuzz.token_sort_ratio(
utils.default_process(DF.Comments[x]), name,
processor=None, score_cutoff=<insert your minimum score here>)