【问题标题】:Python: how to get rid of nested loops?Python:如何摆脱嵌套循环?
【发布时间】:2019-01-25 15:23:05
【问题描述】:

我有 2 个 for 循环,一个接一个,我想以某种方式摆脱它们以提高代码速度。我的 pandas 数据框如下所示(标题代表不同的公司,行代表不同的用户,1 表示用户访问了该公司,否则为 0):

   100  200  300  400
0    1    1    0    1
1    1    1    1    0

我想比较我的数据集中的每一对公司,为此,我创建了一个包含公司所有 ID 的列表。代码查看列表获取第一家公司(基础),然后与其他所有公司(同行)配对,因此是第二个“for”循环。我的代码如下:

def calculate_scores():
    df_matrix = create_the_matrix(df)
    print(df_matrix)
    for base in list_of_companies:
        counter = 0
        for peer in list_of_companies:
            counter += 1
            if base == peer:
                "do nothing"
            else:
                # Calculate first the denominator since we slice the big matrix
            # In dataframes that only have accessed the base firm
            denominator_df = df_matrix.loc[(df_matrix[base] == 1)]
            denominator = denominator_df.sum(axis=1).values.tolist()
            denominator = sum(denominator) - len(denominator)

            # Calculate the numerator. This is done later because
            # We slice up more the dataframe above by
            # Filtering records which have been accessed by both the base and the peer firm
            numerator_df = denominator_df.loc[(denominator_df[base] == 1) & (denominator_df[peer] == 1)]
            numerator = len(numerator_df.index)
            annual_search_fraction = numerator/denominator
            print("Base: {} and Peer: {} ==> {}".format(base, peer, annual_search_fraction))

EDIT 1(添加代码说明):

指标如下:

1) 我试图计算的指标将告诉我与所有其他搜索相比,两家公司一起被搜索了多少次。

2) 代码首先选择所有访问过基础公司 (denominator_df = df_matrix.loc[(df_matrix[base] == 1)]) 行的用户。然后它计算分母,该分母计算基础公司和用户搜索的任何其他公司之间有多少独特组合,因为我可以计算(用户)访问的公司数量,我可以减去 1 来获得基础公司与其他公司之间的独特联系。

3) 接下来,代码过滤之前的denominator_df 以仅选择访问基地和同行公司的行。由于我需要计算访问基地和同行公司的用户数量,我使用命令:numerator = len(numerator_df.index) 来计算行数,这将给我分子。

顶部数据框的预期输出如下:

Base: 100 and Peer: 200 ==> 0.5
Base: 100 and Peer: 300 ==> 0.25
Base: 100 and Peer: 400 ==> 0.25
Base: 200 and Peer: 100 ==> 0.5
Base: 200 and Peer: 300 ==> 0.25
Base: 200 and Peer: 400 ==> 0.25
Base: 300 and Peer: 100 ==> 0.5
Base: 300 and Peer: 200 ==> 0.5
Base: 300 and Peer: 400 ==> 0.0
Base: 400 and Peer: 100 ==> 0.5
Base: 400 and Peer: 200 ==> 0.5
Base: 400 and Peer: 300 ==> 0.0

4) 健全性检查以查看代码是否给出了正确的解决方案:1 家基础公司和所有其他同行公司之间的所有指标的总和必须为 1。他们在我发布的代码中做到了

任何关于前进方向的建议或提示将不胜感激!

【问题讨论】:

  • 请解释背后的逻辑和预期输出
  • 听起来您需要使用您的DataFrame 的笛卡尔积,This post 解释了如何做到这一点。如果性能不是问题,我更喜欢分配一个虚拟键并以这种方式合并的简单语法。
  • 可能有几种方法可以删除嵌套 for 循环,但根据你想要用它做什么,我认为你不能提高它的时间复杂度。如果我理解正确,您将永远陷入 On^2 时间复杂度。我最好的建议是尽可能多地移动到 for 循环之外,这样每次通过时的操作会更少。
  • 关于移除嵌套循环的目标,了解为什么这是所需目标会很有帮助,然后可以用来设计答案。如果您的目标是性能,您可能会认为您正在“基础”上执行冗余逻辑(例如计算分母)。如果你把它移到第二个 for 循环之外,你会看到一些时间节省,因为你正在执行相同的计算 |len(list_of_companies)|次。
  • > Yatu,我已经包含了有关代码的更多详细信息> ALollz,我将研究笛卡尔积。谢谢你的建议。不幸的是,性能是这里的问题。 > Sam,我一定会尝试将更多内容移出主循环

标签: python python-3.x pandas nested-loops


【解决方案1】:

您可能正在寻找 itertools.product()。这是一个与您似乎想要做的类似的示例:

import itertools

a = [ 'one', 'two', 'three' ]

for b in itertools.product( a, a ):
    print( b )

上述代码sn-p的输出为:

('one', 'one')
('one', 'two')
('one', 'three')
('two', 'one')
('two', 'two')
('two', 'three')
('three', 'one')
('three', 'two')
('three', 'three')

或者你可以这样做:

for u,v in itertools.product( a, a ):
    print( "%s %s"%(u, v) )

那么输出是,

one one
one two
one three
two one
two two
two three
three one
three two
three three

如果你想要一个列表,你可以这样做:

alist = list( itertools.product( a, a ) ) )

print( alist )

输出是,

[('one', 'one'), ('one', 'two'), ('one', 'three'), ('two', 'one'), ('two', 'two'), ('two', 'three'), ('three', 'one'), ('three', 'two'), ('three', 'three')]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-08-04
    • 2018-11-30
    • 2014-02-12
    • 2020-02-21
    • 1970-01-01
    • 2022-12-02
    • 2011-04-06
    相关资源
    最近更新 更多