【问题标题】:Python- group together instances of a class by attributePython-按属性将类的实例组合在一起
【发布时间】:2019-01-13 20:37:26
【问题描述】:

我想根据属性的值将类的实例组合在一起。 假设我有以下课程:

class location:

    def __init__(self,x_coord,y_coord,text):
        self.x_coord=x_coord
        self.y_coord=y_coord
        self.text=text

    def __repr___(self):
        return self.text

mylist=[location(1,0,'Date'),location(5,0,'of'),location(8,0,'Entry'), location(28,0,'Date'),location(29,0,'of'),location(30,0,'Birth') ]

如果 x_coord 属性的差异小于 10,我想对我的类列表进行分组,以便

mygroupedlist=[['Date','of','Entry'],['Date','of','Birth']]

谁能给我一个提示?

【问题讨论】:

  • 上一个项目少于 10 个,还是所有积累的项目少于 10 个? [1, 5, 8, 12, 20, 100] 将如何分组?
  • 是的,来自上一项。我们可以假设 mylist 已经按照 x 升序排序了

标签: python python-3.x class oop grouping


【解决方案1】:

这是一个使用有状态函数来记住它看到的最后一个项目的解决方案。 (不要向任何函数式程序员展示这个)。然后我们可以将该函数用作调用itertools.groupby的关键函数

def grouper(key=lambda x: x, distance=10):
    _marker = object()
    last_seen = _marker
    flag = True
    def close_enough(item):
        nonlocal last_seen, flag
        if last_seen is _marker:
            last_seen = key(item)
            return flag
        diff = abs(key(item) - last_seen)
        last_seen = key(item)
        if diff >= distance:
            flag = not flag
        return flag
    return close_enough

[[i.text for i in g] for k, g in groupby(mylist, key=grouper(lambda x: x.x_coord))]
# [['Date', 'of', 'Entry'], ['Date', 'of', 'Birth']]

【讨论】:

    【解决方案2】:

    我的尝试,使用每次大于或等于distance 的变化时增加的计数器。这样这个生成器就可以轻松地提供给groupby:

    def gen(lst, distance=10):
        counter = 0
        for cur, nxt in zip(lst[::1], lst[1::1]):
            yield counter, cur
            if abs(cur.x_coord - nxt.x_coord) >= distance:
                counter += 1
        yield counter, nxt
    
    myGroupedList = [list(i[1] for i in g) for _, g in groupby(gen(mylist), lambda v: v[0])]
    print(myGroupedList)
    

    打印:

    [[Date, of, Entry], [Date, of, Birth]]
    

    【讨论】:

    • @JuanCastaño 没问题,我刚刚清理了代码并对其进行了一些解释。
    【解决方案3】:

    如果你不介意使用外部库,使用 numpy 和 pandas 可能会获得更好的性能。

    # Create a dataframe
    df = pd.DataFrame(mylist, columns=['locations'])
    # Create columns representing the 'x' coords, and the 'text'
    df['x'] = df['locations'].apply(lambda x: x.x_coord)
    df['text'] = df['locations'].apply(lambda x: x.text)
    # Create an indicator array that tells you whether the current row is within 10 of the previous row
    closeness_indicator = np.isclose(df['x'], df['x'].shift(1), atol=10)
    # Negate that, then take the cumulative sum to get groups:
    groups = (~closeness_indicator).cumsum()
    # GRoup by that array, then create lists from the grouped text:
    df.groupby(groups)[text].apply(list)
    

    输出:

    1    [Date, of, Entry]
    2    [Date, of, Birth]
    Name: text, dtype: object
    

    【讨论】:

      【解决方案4】:

      您可以使用defaultdict 列表并迭代您的对象列表,每次差异大于或等于 10 时增加您的密钥。

      该解决方案假定您的 x_coord 属性正在增加,即按升序排序。

      from collections import defaultdict
      
      d = defaultdict(list)
      
      d[0].append(mylist[0])
      
      for item in mylist[1:]:
          last_key = len(d) - 1
          if item.x_coord - next(reversed(d[last_key])).x_coord < 10:
              d[last_key].append(item)
          else:
              d[last_key+1].append(item)
      

      测试以检查排序是否正确:

      res = [[i.x_coord for i in x] for x in d.values()]
      
      print(res)
      
      [[1, 5, 8], [28, 29, 30]]
      

      【讨论】:

        猜你喜欢
        • 2015-06-29
        • 2011-11-24
        • 1970-01-01
        • 1970-01-01
        • 2021-07-26
        • 1970-01-01
        • 2018-06-12
        • 2015-02-13
        相关资源
        最近更新 更多