Python 用排序构建映射

Python 用排序构建映射,如果不考虑Counter类,可以用更函数式的方式进行排序和分组。常用实现如下:

from typing import Dict, Any, Iterable, Tuple, List, TypeVar
Leg = Tuple[Any, Any, float]
T_ = TypeVar("T_")

def group_sort1(trip: Iterable[Leg]) -> Dict[int, int]:
    def group(
            data: Iterable[T_]
        ) -> Iterable[Tuple[T_, int]]:
        previous, count = None, 0
        for d in sorted(data):
            if d == previous:
                count += 1
            elif previous is not None: # and d != previous
                yield previous, count
                previous, count = d, 1
            elif previous is None:
                previous, count = d, 1
            else:
                raise Exception("Bad bad design problem.")
        yield previous, count
    quantized = (int(5 * (dist // 5)) for beg, end, dist in trip)
    return dict(group(quantized))

内部的group()函数遍历排序后的数据集合,如果当前值与前次循环值一致(等于previous中保存的值),给计数器加1,如果不一致,并且前次循环值不是None,说明新的键值出现了。这时首先返回前次循环值及其计数,然后创建新的键值对,键是当前值,值是计数器初始值。第三种情况只会出现一次:未设置前次循环值,这是循环的开始状态,应保存初始值。

group()函数包含两个重要的类型标示,数据源是基于某种数据类型(用T_表示)的可迭代对象,在这个例子中是整型,但适用于任何Python类型。由于返回结果中同样使用了类型变量T_,可知group()函数的返回结果中包含的数据类型与数据源的类型一致。

group_sort1()函数的最后一行基于分组后的数据创建了一个字典。与Counter创建的字典类似,主要区别在于Counter对象有most_common()方法,而普通的字典对象没有。

条件分支elif previous is None是比较笨拙的设计,去掉它难度不是太大(并且性能会略有提升)。

为了去掉该elif分支,需要调整一下group()函数的初始化方法。

def group(data: Iterable[T_]) -> Iterable[Tuple[T_, int]]:
    sorted_data = iter(sorted(data))
    previous, count = next(sorted_data), 1
    for d in sorted_data:
        if d == previous:
            count += 1
        elif previous is not None: # and d != previous
            yield previous, count
            previous, count = d, 1
        else:
            raise Exception("Bad bad design problem.")
    yield previous, count

首先从排序后的数据中取出第一个数据,作为previous的初始值,在循环中处理剩余的值。从这一设计思路中可以看到递归方法的使用:用序列的第一个值做递归的初始值,每次递归处理一个值,如果不是None则继续递归处理,否则说明数据已经处理完毕。

除此之外,用itertools.groupby()也可以达到相同的效果。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程