【问题标题】:how to make this function more (time) efficient?如何使这个功能更(时间)高效?
【发布时间】:2017-08-27 17:20:39
【问题描述】:

我有一个包含句子的数据框系列。 (有些有点长)

我还有 2 个字典,其中包含单词作为键和整数作为计数。

并非所有字符串中的单词都出现在两个字典中。有些只在一个中,有些不在。

数据帧长 124011 个单位。函数让我每串大约 0.4 个。这太长了。

W 只是字典的参考值(weights = {}, weights[W] = {})

函数如下:

def match_share(string, W, weights, rel_weight):

    words = string.split()

    words_counts = Counter(words)

    ratios = []

    for word in words:

        if ((word in weights[W].keys())&(word in rel_weight[W].keys())):

            if (weights[W][word]!=0):

                ratios.append(words_counts[word]*rel_weight[W][word]/weights[W][word])

        else:

            ratios.append(0)

    if len(words)>0:

        ratios = np.divide(ratios, float(len(words)))

    ratio = np.sum(ratios)

    return ratio

谢谢

【问题讨论】:

  • 我建议删除双倍间距和重复代码。然后添加一个或多个示例函数调用,包括一些带有计时的极端情况。你能代替np的使用来使代码独立吗?
  • 您能否发布您的建议的示例/答案?

标签: python performance pandas dictionary coding-efficiency


【解决方案1】:

让我们清理一下:

def match_share(string, W, weights, rel_weight):

    words = string.split()

    words_counts = Counter(words)

    words = string.split()

    words_counts = Counter(words)

那是多余的!将 4 个语句替换为 2 个:

def match_share(string, W, weights, rel_weight):

    words = string.split()    
    words_counts = Counter(words)

下一步:

    ratios = []

    for word in words:    

        if ((word in weights[W].keys())&(word in rel_weight[W].keys())):

            if (weights[W][word]!=0):

                ratios.append(words_counts[word]*rel_weight[W][word]/weights[W][word])

        else:

            ratios.append(0)

我不知道您认为该代码的作用。我希望你没有狡猾。但是.keys 返回一个可迭代对象,并且X in <iterable>X in <dict> 慢得多。另外,注意:如果最里面的 (weights[W][word] != 0) 条件失败,您不会附加任何内容。这可能是一个错误,因为您尝试在另一个 else 条件中附加 0。 (我不知道你在做什么,所以我只是指出来。)这是 Python,而不是 Perl、C 或 Java。所以if <test>:周围不需要括号

让我们开始吧:

    ratios = []

    for word in words:
        if word in weights[W] and word in rel_weight[W]:
            if weights[W][word] != 0:    
                ratios.append(words_counts[word] * rel_weight[W][word] / weights[W][word])

        else:
            ratios.append(0)

下一步:

    if len(words)>0:

        ratios = np.divide(ratios, float(len(words)))

您试图防止除以零。但是您可以使用列表的truthiness 来检查这一点,并避免比较:

    if words:
        ratios = np.divide(ratios, float(len(words)))

其余的都很好,但你不需要变量。

    ratio = np.sum(ratios)

    return ratio

应用这些模块后,您的函数如下所示:

def match_share(string, W, weights, rel_weight):

    words = string.split()    
    words_counts = Counter(words)
    ratios = []

    for word in words:
        if word in weights[W] and word in rel_weight[W]:
            if weights[W][word] != 0:    
                ratios.append(words_counts[word] * rel_weight[W][word] / weights[W][word])

        else:
            ratios.append(0)

    if words:
        ratios = np.divide(ratios, float(len(words)))

    ratio = np.sum(ratios)
    return ratio

仔细观察一下,我知道你正在这样做:

word_counts = Counter(words)

for word in words:
    append(   word_counts[word] * ...)

据我说,这意味着如果“apple”出现 6 次,您将在列表中附加 6*...,每个单词一次。因此,您的列表中将出现 6 次不同的 6*...。你确定那是你想要的吗?还是应该是 for word in word_counts 来遍历不同的单词?

另一个优化是从循环内部删除查找。即使W 的值永远不会改变,您仍会继续查找weights[W]rel_weight[W]。让我们在循环之外缓存这些值。另外,让我们缓存一个指向ratios.append 方法的指针。

def match_share(string, W, weights, rel_weight):

    words = string.split()    
    words_counts = Counter(words)
    ratios = []

    # Cache these values for speed in loop
    ratios_append = ratios.append
    weights_W = weights[W]
    rel_W = rel_weight[W]

    for word in words:
        if word in weights_W and word in rel_W:
            if weights_W[word] != 0:    
                ratios_append(words_counts[word] * rel_W[word] / weights_W[word])

        else:
            ratios_append(0)

    if words:
        ratios = np.divide(ratios, float(len(words)))

    ratio = np.sum(ratios)
    return ratio

试试看,看看效果如何。请查看上面的粗体 注释 和问题。可能有错误,可能有更多方法可以加快速度。

【讨论】:

  • 你先生,是一种祝福。很多的解决方案和很好的解释。 ps:“for word in word_counts”很好理解
  • 成功了吗?您是否回答了问题(错误、追加、计数)?
  • 是的。效果很好。从 10 小时功能变为 10 秒。 ratio.append 指针对我来说是一个新指针,不错的技巧。
  • 看一下,大概可以分分吧。只需计算 np.sum 然后返回 ratio/len(words)。
  • 或者,如果npnumpy 的缩写,您可以只返回numpy.mean of ratios。
【解决方案2】:

我认为您的时间效率低下可能是因为您使用的是 Counter 而不是 dict。一些discussion here 建议dict 类的部分是用纯c 编写的,而counter 是用python 编写的。

我建议将您的代码更改为使用 dict 并测试以查看是否可以提供更快的时间

还有为什么这段代码是重复的?:

words = string.split()

words_counts = Counter(words)

words = string.split()

words_counts = Counter(words)

ratios = []

【讨论】:

  • 泰。糟糕的复制和粘贴。有点困。但它根本不影响函数处理时间
【解决方案3】:

如果您有该函数执行的概要文件会很好,但这里有一些通用的想法:

  1. 您在每次迭代中都会不必要地获取一些元素。您可以在循环之前提取这些

例如

weights_W = weights[W]
rel_weights_W = rel_weights[W]
  1. 您无需在 dicts 上调用 .keys()

这些是等价的:

word in weights_W.keys()
word in weights_W
  1. 尝试获取值而不先查找它们。这将为您节省一次查找。

例如代替:

if ((word in weights[W].keys())&(word in rel_weight[W].keys())):
        if (weights[W][word]!=0):

你可以这样做:

word_weight = weights_W.get(word)
if word_weight is not None:
    word_rel_weight = rel_weights_W.get(word)
    if word_rel_weight is not None:
        if word_weight != 0:  # lookup saved here

【讨论】:

    猜你喜欢
    • 2022-01-24
    • 1970-01-01
    • 2015-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多