在Python中使用Groupby对Access表中的列进行分组

2 投票
3 回答
1408 浏览
提问于 2025-04-17 04:10

我最近一直在玩一个叫做groupby的功能,这个功能来自一个叫itertools的模块,已经玩了好几天了。

for k, g in groupby(data, keyfunc):

不过,我真的很难理解它的语法。我尝试了很多不同的方法,但我不知道在“data”和“keyfunc”里该放什么,才能让它返回我想要的分组数据,并在打印时显示出来。

我现在做的是在一个Access表格中循环遍历每一行。

我设置了一个变量,用来进行一个排序后的游标搜索,从Access表格中提取我想要的那一列。

for row in cursor:
    print row.ROAD_TYPE

这样返回的是:

TRUNK ROAD
TRUNK ROAD
TRUNK ROAD
TRUNK ROAD
COLLECTOR HIGHWAY
COLLECTOR HIGHWAY
ACCESS ROAD
ACCESS ROAD
ACCESS ROAD
ACCESS ROAD
ACCESS ROAD
ACCESS ROAD
ACCESS ROAD
MAJOR ROAD
MAJOR HIGHWAY

我想把这些值分组在一起,并希望它能返回一个字符串值,这样打印出来的结果像这样:

TRUNK ROAD
TRUNK ROAD
TRUNK ROAD
TRUNK ROAD

COLLECTOR HIGHWAY
COLLECTOR HIGHWAY

ACCESS ROAD
ACCESS ROAD
ACCESS ROAD
ACCESS ROAD
ACCESS ROAD
ACCESS ROAD
ACCESS ROAD

MAJOR ROAD

MAJOR HIGHWAY

最后,我还想根据这些新分组再对第二列进行分组,这样我能得到类似这样的结果:

TRUNK ROAD  M1
TRUNK ROAD  M1

TRUNK ROAD  M2

TRUNK ROAD  M3


COLLECTOR HIGHWAY  M1

COLLECTOR HIGHWAY  M2


ACCESS ROAD  M1
ACCESS ROAD  M1

ACCESS ROAD  M3
ACCESS ROAD  M3

ACCESS ROAD  M7
ACCESS ROAD  M7

ACCESS ROAD  M8


MAJOR ROAD  M8


MAJOR HIGHWAY  M8

我知道这可能比我想象的简单得多,我觉得应该有个简单的答案,但我完全被难住了,而且在网上找不到一个能让我理解groupby语法的例子。请随意分享。

3 个回答

1

itertools.groupby的主要思想是解决一个基本问题:我想遍历一个序列,分析其中的每个元素,并根据分析的结果以不同的方式处理每个元素。groupby会把序列分成几个组,但会保持原序列的顺序

from itertools import groupby
i = groupby(xrange(12), lambda v: v / 3)
results = [(x[0], list(x[1])) for x in i]
print str(results)

上面的代码输出:[(0, [0, 1, 2]), (1, [3, 4, 5]), (2, [6, 7, 8]), (3, [9, 10, 11])]

你会注意到,为了得到results这个列表,我需要进行一些额外的处理。实际上,groupby给你的结果是为了迭代使用的:

i = groupby(xrange(12), lambda v: v / 3)
print str(i.next())

上面的代码输出:(0, <itertools._grouper object at 0x020BF3D0>)

所以在你的程序中,你会写key, valueiter = i.next(),然后测试key,接着把valueiter传递给正确的处理函数或方法。

那么这里的价值是什么呢?其实,你不需要在处理函数中继续测试key来判断何时停止。groupby会自动为你停止每个“子迭代器”。

groupby与SQL中的GROUP BY子句不同,它会确保原始可迭代对象中所有相同的键值一次性处理完。groupby中的键值可以重复。这是合理的,因为它的目标是保持原序列的顺序,而不是把所有东西都存储在内存中。例如:

i = groupby(xrange(6), lambda v: v % 3)  # note: modulo division
results = [(x[0], list(x[1])) for x in i]
print str(results)

上面的代码输出:[(0, [0]), (1, [1]), (2, [2]), (0, [3]), (1, [4]), (2, [5])]。这里的键值重复了,每个子迭代器只产生一个数据。这是groupby在性能方面的最坏情况,这意味着你在使用这个工具时需要遵循一定的模式。

所以像这样的代码:

i = groupby(xrange(12), lambda v: v / 3)
results = dict([(x[0], list(x[1])) for x in i])  # beware of dict() here!
print str(results)

只有在你事先知道你的键值绝对不会重复时才是正确的。

1
import itertools as it
for key, group in it.groupby(cursor, lambda row: row.ROAD_TYPE):
    for sec_col,pairs in it.groupby(group, lambda row: row.SECOND_COLUMN):
        for row in pairs:
            print('{t}  {s}'.format(t=row.ROAD_TYPE,s=row.SECOND_COLUMN))
        print
    print

这里有两个例子来帮助理解 groupby

[list(g) for k, g in it.groupby('AAAABBBCCD')]
# [['A', 'A', 'A', 'A'], ['B', 'B', 'B'], ['C', 'C'], ['D']]

上面提到的,所有相同的项目会被放在一起。

现在我们添加一个关键函数,keyfunc

keyfunc=lambda x: x//3
data=range(13)
[list(g) for k,g in it.groupby(data,keyfunc)]
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12]]
[k for k,g in it.groupby(data,keyfunc)]
# [0, 1, 2, 3, 4]

现在,我们不是根据数据中的项目来分组,而是根据每个 xdata 中的 keyfunc(x) 来分组。

1

你给出的两个 groupby 示例其实是完全一样的,唯一不同的就是你选择的 key

from itertools import groupby
from operator import attrgetter

for key, rows in groupby(cursor, key=attrgetter('ROAD_TYPE')):
    for row in rows:
        print row.ROAD_TYPE
    print

for key, rows in groupby(cursor, key=attrgetter('ROAD_TYPE', 'OTHER_COLUMN')):
    for row in rows:
        print row.ROAD_TYPE, row.OTHER_COLUMN
    print

在这两种情况下,key 的值都是通过 attrgetter() 得到的,但实际上你并不需要这个 key,因为你会遍历按这个 key 分组的行。当然,这一切都能正常工作,前提是 cursor 是按照你分组时用的同一个 key 排序的。

撰写回答