【问题标题】:Python for loop optimizationPython for 循环优化
【发布时间】:2020-11-07 10:16:30
【问题描述】:

给定一个如下所示的 DataFrame:

     id  days  cluster
0   aaa     0        0
1   bbb     0        0
2   ccc     0        1
3   ddd     0        1
4   eee     0        0
5   fff     0        1
6   ggg     1        0
7   hhh     1        1
8   iii     1        0
9   lll     1        1
10  mmm     1        1
11  aaa     1        3
12  bbb     1        3

我的目标是创建一个字典,其中包含 id 列的元素的键元组和作为值的 cluster 列的元素列表,如果两个 id 具有相同的 cluster 值,全部过滤通过days 列。即,如果 days 更改但 id 元素的元组具有相同的 cluster 值,我想将此值添加到我已经存在的列表中。所需的输出报告如下:

{('aaa', 'bbb'): [0, 3],('aaa', 'eee'): [0], ('bbb', 'eee'): [0], ('ccc', 'ddd'): [1], 
('ccc', 'fff'): [1], ('ddd', 'fff'): [1], ('ggg', 'iii'): [0],
 ('hhh', 'lll'): [1], ('hhh', 'mmm'): [1], ('lll', 'mmm'): [1]}

我用下面的 sn-p 代码得到了这个结果,但是有百万行它变得太慢了。如何优化代码?

y={}
for i in range(0, max(df.iloc[:,1]) + 1):
    x = df.loc[df['days'] == i]
    for j in range(0,l en(x)):
        for z in range(1, len(x)):
            if (x.iloc[z,0], x.iloc[j,0]) in y:
                pass
            else:
             if (x.iloc[j,0], x.iloc[z,0]) not in y:
                 if x.iloc[j,0] != x.iloc[z,0] and x.iloc[j,2] == x.iloc[z,2]:
                     y[(x.iloc[j,0], x.iloc[z,0])] = [x.iloc[j,2]]
             else:
                 if x.iloc[j,0] != x.iloc[z,0] and x.iloc[j,2] == x.iloc[z,2]:
                     y[(x.iloc[j,0], x.iloc[z,0])].append(x.iloc[j,2])

【问题讨论】:

  • 在您的示例中,ID 'aaa' 可能的集群值是 0 和 3(分别代表第 0 天和第 1 天)。但在您想要的输出中,ID 'aaa' 与 'ccc'、'ddd'、'fff'、'hhh'、'llll' 和 'mmm' 分组,它们的聚类值为 1 或 2。所以我不明白你的说法if the two 'id' have the same 'cluster' value
  • @mtrw 你是对的!修复它,我发布的所需输出是错误的!谢谢

标签: python loops dataframe dictionary optimization


【解决方案1】:

考虑到瓶颈是获取id的组合,为什么不把它留到最后呢?

按 id 对数据进行分组,每个 id 将显示一组找到它的“bins”(天、集群):

grouped = collections.defaultdict(set)
for index, (id_, day, cluster) in df.iterrows():
    grouped[id_].add((day, cluster))

对于找到的每个 bin 组合,列出属于每个组合的 id:

binned = collections.defaultdict(list)
for id_, bins in grouped.items():
    binned[tuple(sorted(bins))].append(id_)

如果您需要,仅通过集群进行简化:

clustered = collections.defaultdict(list)
for bins, ids in binned.items():
    clusters = set(cluster for (day, cluster) in bins)
    clustered[tuple(sorted(clusters))].extend(ids)

最后,获取每个集群 bin 的 id 组合应该不是问题:

for bins, ids in clustered.items():
    if len(ids) > 1:
        for comb_id in itertools.combinations(ids, 2):
            print(bins, comb_id) 
            # or do other stuff with it

【讨论】:

    【解决方案2】:

    您可以利用pandas.DataFrame.groupby 方法:

    result = collections.defaultdict(list)
    
    for (day, cluster), group in df.groupby(["days", "cluster"]):
        for comb in itertools.combinations(df["id"][group.index], 2):
            result[comb].append(cluster)
    

    这将为您提供所需的结果:

    defaultdict(<class 'list'>, {('aaa', 'bbb'): [0, 3], ('aaa', 'eee'): [0], ('bbb', 'eee'): [0], ('ccc', 'ddd'): [1], ('ccc', 'fff'): [1], ('ddd', 'fff'): [1], ('ggg', 'iii'): [0], ('hhh', 'lll'): [1], ('hhh', 'mmm'): [1], ('lll', 'mmm'): [1]})
    

    【讨论】:

    • 您的代码看起来更快,但不幸的是,有 200.000 行、9.000 个不同的id、2 个不同的days 和 8 个不同的cluster,我的会话在使用所有可用 RAM 后崩溃了。
    • 这并不奇怪。有了这样的数据,如果数字是随机的,您会期望在每个集群中多次看到几乎所有的 id。组合是“无限的”。您是否尝试过将集群组合作为字典键并将该集群组合中出现的 id 列表作为字典值的反向方法。获取每个集群组合的 id 组合将是可行的。我设法在大约 2 分钟内完成,使用 6GB 的 RAM 和随机样本,其中包含您提到的数字,您也可以尝试生产者-消费者的方法。
    猜你喜欢
    • 2015-04-15
    • 1970-01-01
    • 2017-02-21
    • 2017-01-17
    • 2017-05-28
    • 1970-01-01
    • 2011-08-30
    • 1970-01-01
    相关资源
    最近更新 更多