【问题标题】:Most efficient way to merge lists of objects based on max value of object's property基于对象属性的最大值合并对象列表的最有效方法
【发布时间】:2020-11-14 03:23:07
【问题描述】:

我想做与this post 完全相同的事情,但使用二维对象数组而不是二维数字数组。

class ExchangeRate:
    rate = None
    name = None
    otherUsefulProperty = None

    def __init__(self, _rate, _name, _otherUsefulProperty):
        self.rate = _rate
        self.name = _name
        self.otherUsefulProperty = _otherUsefulProperty

这是我对象的类。 rate 属性将是在将图合并在一起时使用的属性。

下面的代码非常适用于二维数字数组,但我无法弄清楚如何有效地使用二维对象数组来做同样的事情。请注意,性能对我正在做的事情至关重要。这是假设二维对象数组确实是高性能的。如果不是并且有更高效的方法,请告诉我。

import numpy as np

graph = np.ndarray(shape=(4, 3, 3), dtype=float, order='F')
graph[0] = [[0, 0, 1], [1, 0, 1], [2, 0, 0]]
graph[1] = [[0, 0, 1], [1, 0, 1], [2, 0, 0]]
graph[2] = [[5, 0, 0], [1, 0, 1], [2, 0, 0]]
graph[3] = [[2, 1, 0], [9, 0, 1], [0, 0, 0]]

PrintAndLog("graph of type " + str(type(graph)) + " = \n" + str(graph))
PrintAndLog("\n\n")
resultGraph = graph.max(axis=0)
PrintAndLog("resultGraph of type " + str(type(resultGraph)) + " = \n" + str(resultGraph))

输出:

graph of type <class 'numpy.ndarray'> = 
[[[ 0.  0.  1.]
  [ 1.  0.  1.]
  [ 2.  0.  0.]]

 [[ 0.  0.  1.]
  [ 1.  0.  1.]
  [ 2.  0.  0.]]

 [[ 5.  0.  0.]
  [ 1.  0.  1.]
  [ 2.  0.  0.]]

 [[ 2.  1.  0.]
  [ 9.  0.  1.]
  [ 0.  0.  0.]]]


resultGraph of type <class 'numpy.ndarray'> = 
[[ 5.  1.  1.]
 [ 9.  0.  1.]
 [ 2.  0.  0.]]

可能的最终解决方案:

希望其他人觉得这很有用。我刚刚进行了大量的性能测试,并且有一个明显的赢家。

TLDR: 获胜者 = np.array + ExchangeRate object + graph.max(axis=0)。这是迄今为止我尝试过的最快的方法。再一次,目标是合并许多费率图,其中每个费率还具有与之关联的元数据,需要与之合并

这是我缩小范围的最终方法的测试结果。每个测试仅基于合并进行计时(我根据速率将多个图表合并为一个)。我记录了以下数据点:200 次运行的平均持续时间,以及第一次运行的持续时间。第一次运行很重要,因为它有时似乎比平均时间长。它可能与缓存有关。

  • test_graph_4 200 次运行平均 = 0.0003026 秒(第一次运行,~相同)

  • test_graph_3 200 次运行平均 = 0.0003836 秒(第一次运行,~相同)

  • test_graph_2b 200 次运行平均值 = 0.000018 秒(第一次运行 0.000092 秒

  • test_graph_2a 200 次运行平均 = 0.000066 秒(第一次运行 0.000143 秒)

【问题讨论】:

  • object dtype 数组不是“高性能”。
  • 与您的班级一起构建一个工作示例,并展示您已成功完成的工作,无论是否有效。如果我必须构建对象列表等来说明任何想法,那就需要更多的工作。然而,过去的答案表明,对象数组的性能类似于对象列表。编译后的代码都不会“进入”你的对象。
  • 我当前的实现既不使用对象也不使用numpy。我目前只是使用 python 列表制作一个 2dlist 然后手动合并它们。我也没有使用对象,我使用单独的二维列表来表示速率、名称和 otherUsefulProperty。因此,当我合并时,我只需要暴力迭代所有这些并找到最大速率值,然后更新最终合并的 2d 列表。它的计算成本相当高。
  • 我希望我可以使用 numpy 函数 graph.max(axis=0) 并让它以某种方式进入我的对象。如果我不能这样做,那么也许我的蛮力合并方法是最优化的方法?为了确认,您是说您已经看到 python 列表显示出与 numpy 数组类似的性能?我还想知道我是否要保留我的蛮力合并方法,只是从 python 列表切换到 numpy 数组,如果这会给我带来任何性能提升。
  • 我为此测试了各种不同的解决方案,对它们进行计时,并在上面发布了代码+结果。

标签: python pandas algorithm performance numpy


【解决方案1】:

相信你只需要实现__lt__方法:

class ExchangeRate:
    rate = None
    name = None
    otherUsefulProperty = None

    def __init__(self, _rate, _name, _otherUsefulProperty):
        self.rate = _rate
        self.name = _name
        self.otherUsefulProperty = _otherUsefulProperty
    
    # this method allows sorting, comparison, etc.
    def __lt__(self, other):
        return self.rate < other.rate

a = np.array([ExchangeRate(3,2,1)])

b = np.array([ExchangeRate(1,2,3)])

a>b

输出:

True

【讨论】:

  • 这行得通。我必须使用__ge__ 而不是__lt__ 才能使用graph.max(axis=0),但它完全有效。现在我要做一些测试,看看它是否真的比我目前的蛮力方法更有效。
  • 我用当前确实有效的解决方案更新了 OP,但我不确定如何使其性能最高。我要尝试一些事情。我读到这似乎与性能方面的主题有关:stackoverflow.com/a/11233356/1558119
  • 我进行了大量测试并更新了 OP。找到了我能想到的最佳性能方式,它确实包括 np.array + __ge__
猜你喜欢
  • 1970-01-01
  • 2015-08-11
  • 2017-06-29
  • 1970-01-01
  • 2020-03-24
  • 1970-01-01
  • 2023-04-05
  • 1970-01-01
  • 2019-09-14
相关资源
最近更新 更多