将相似的项目分组到主列表中,并基于分组的项目创建新列表。

4

我正在尝试从一个主列表中创建几个新列表,其中新列表包含来自主列表的类似项。具体来说,我有一个公交路线列表。以下是一个示例数据集:

[u'Bus04_00_00_IB_pts_Line', u'Bus04_00_00_OB_pts_Line', u'Bus15_00_00_IB_pts_Line', u'Bus15_00_00_OB_pts_Line']

大多数公交路线都有一个进站(IB)和一个出站(OB)的项目,(有些有多个IB和OB,有些只有一条路线,因为它们是环形路线)。最终,我想在地图软件中合并IB和OB路线(这已经知道如何做)...
最初,我创建文件名,以便前5个字符表示巴士路线,无论是IB还是OB。因此,我可以根据前5个字符将相似项分组。例如,当我写:
for route in routes:
    print route[0:5]

我读取到:

>>> 
Bus04
Bus04
Bus15
Bus15

如何将与Bus04和Bus04,以及Bus15和Bus15相关的文件“分组”,并将它们分别列成新列表,使得我能够获得:
[u'Bus04_00_00_IB_pts_Line',u'Bus04_00_00_OB_pts_Line'] 和 [u'Bus15_00_00_IB_pts_Line',u'Bus15_00_00_OB_pts_Line']
我的想法是循环遍历每个项目,查看每个项目的前五个字符,然后创建一个新列表,并将每个新的五个字符项目添加到该列表中,或者检查是否已经存在一个列表并将相似的项目附加到其中。
我很难用代码来描述这个过程,因此非常感谢任何帮助!
4个回答

6
我会使用 collections.defaultdict 来解决这个问题:
import collections

L = [u'Bus04_00_00_IB_pts_Line', u'Bus04_00_00_OB_pts_Line', u'Bus15_00_00_IB_pts_Line', u'Bus15_00_00_OB_pts_Line']
d = collections.defaultdict(list)
for elem in L:
    d[elem.split('_')[0]].append(elem)
print(dict(d))

这会产生:

{u'Bus04': [u'Bus04_00_00_IB_pts_Line', u'Bus04_00_00_OB_pts_Line'],
 u'Bus15': [u'Bus15_00_00_IB_pts_Line', u'Bus15_00_00_OB_pts_Line']}

与迄今为止提出的其他解决方案不同,这个方法不受输入列表中条目出现顺序的影响。

但它要求元素是可哈希的(在这种情况下它们是可哈希的),所以这没问题,但我认为这值得一提。另一个建议是,在最后不要做dict(d),而是可以这样做:d.default_factory = None,使defaultdict在几乎所有实际用途中都能像普通字典一样运行。 - mgilson
1
@mgilson: 我认为dict调用仅仅是为了在打印时更好看(即没有defaultdict(<type 'list'>, etc..)。 - DSM
@DSM -- 我也是这么想的。但当我了解到default_factory属性时,我感到非常兴奋,所以我已经记下来要从现在开始宣扬它的好处 :) - mgilson

3
您可以使用自定义键函数,例如 lambda x: x[0:5],与itertools.groupby一起使用。

以下是一个演示,它给出了一个静态列表(即不仅仅是生成器):

>>> import itertools
>>> lst = [u'Bus04_00_00_IB_pts_Line', u'Bus04_00_00_OB_pts_Line', u'Bus15_00_00_IB_pts_Line', u'Bus15_00_00_OB_pts_Line']
>>> [(key, list(val)) for key, val in itertools.groupby(lst, lambda x: x[0:5])]
Out[9]:
[(u'Bus04', [u'Bus04_00_00_IB_pts_Line', u'Bus04_00_00_OB_pts_Line']),
 (u'Bus15', [u'Bus15_00_00_IB_pts_Line', u'Bus15_00_00_OB_pts_Line'])]

1
在使用 groupby 之前不要忘记对列表进行排序。 - Facundo Casco

2
import collections

lists = collections.defaultdict(list)
for item in masterlist:
    lists[item[:5]].append(item)

1

您可以使用带有lambda键函数的groupby来实现此功能。

from itertools import groupby
results = groupby(data, key=lambda x: x[0:5])

>>> for item, values in results:
>>>     print item, list(values)
Bus04 [u'Bus04_00_00_IB_pts_Line', u'Bus04_00_00_OB_pts_Line']
Bus15 [u'Bus15_00_00_IB_pts_Line', u'Bus15_00_00_OB_pts_Line']

正如NPE在他的解决方案中提到的那样,原始列表必须是一个排序好的列表。

然而,如果你只需要一次处理一个条目,这个解决方案非常节省内存,因为生成器只产生一个值,然后等待下一个值准备好被使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接