基于键名对元组列表求平均值:能否简化代码或单行实现?
简化元组列表按日期分组求平均值的Python代码
当然可以简化!甚至能写出单行的实现方式,下面给你几种不同的思路,从精简代码到单行写法逐步优化:
方法一:用collections.defaultdict替代setdefault,精简循环
原来的setdefault每次都要判断键是否存在,换成defaultdict(list)可以直接给键对应的列表追加值,再配合字典推导式替换第二次循环,代码清爽很多:
from collections import defaultdict modifiedL = [('10-1984', 2.8), ('10-1984', 2.8), ('10-1984', 2.85), ('10-1984', 2.82), ('10-1984', 2.78), ('10-1984', 2.75), ('09-1984', 2.82), ('09-1984', 2.9), ('09-1984', 2.9), ('09-1984', 2.94), ('09-1984', 2.99), ('09-1984', 3.02), ('09-1984', 3.05), ('09-1984', 3.04), ('09-1984', 3.11), ('09-1984', 3.22), ('09-1984', 3.13), ('09-1984', 3.09), ('09-1984', 2.94), ('09-1984', 3.02), ('09-1984', 2.97), ('09-1984', 2.98)] avg = defaultdict(list) for date, value in modifiedL: avg[date].append(value) # 用字典推导式直接计算平均值 avg = {date: sum(values)/len(values) for date, values in avg.items()}
这个写法保持了原逻辑的直观性,同时减少了冗余代码。
方法二:用itertools.groupby实现分组计算
itertools.groupby是Python标准库中专门用于分组的工具,不过要注意必须先对列表按日期排序——因为groupby只会把连续的相同键归为一组,不排序的话会漏分组:
from itertools import groupby modifiedL = [('10-1984', 2.8), ('10-1984', 2.8), ('10-1984', 2.85), ('10-1984', 2.82), ('10-1984', 2.78), ('10-1984', 2.75), ('09-1984', 2.82), ('09-1984', 2.9), ('09-1984', 2.9), ('09-1984', 2.94), ('09-1984', 2.99), ('09-1984', 3.02), ('09-1984', 3.05), ('09-1984', 3.04), ('09-1984', 3.11), ('09-1984', 3.22), ('09-1984', 3.13), ('09-1984', 3.09), ('09-1984', 2.94), ('09-1984', 3.02), ('09-1984', 2.97), ('09-1984', 2.98)] # 先按日期排序 sorted_list = sorted(modifiedL, key=lambda x: x[0]) # 分组并计算平均值 avg = {} for date, group in groupby(sorted_list, key=lambda x: x[0]): values = [val for _, val in group] avg[date] = sum(values)/len(values)
如果想更紧凑,可以用字典推导式(Python 3.8+支持海象运算符,避免重复遍历迭代器):
sorted_list = sorted(modifiedL, key=lambda x: x[0]) avg = {date: sum(val for _, val in group)/len(group) if (group := list(group)) else 0 for date, group in groupby(sorted_list, key=lambda x: x[0])}
方法三:真正的单行写法(可读性稍差,适合炫技)
如果一定要追求单行实现,可以把defaultdict和字典推导式结合起来,用匿名函数做中间处理:
from collections import defaultdict modifiedL = [('10-1984', 2.8), ('10-1984', 2.8), ('10-1984', 2.85), ('10-1984', 2.82), ('10-1984', 2.78), ('10-1984', 2.75), ('09-1984', 2.82), ('09-1984', 2.9), ('09-1984', 2.9), ('09-1984', 2.94), ('09-1984', 2.99), ('09-1984', 3.02), ('09-1984', 3.05), ('09-1984', 3.04), ('09-1984', 3.11), ('09-1984', 3.22), ('09-1984', 3.13), ('09-1984', 3.09), ('09-1984', 2.94), ('09-1984', 3.02), ('09-1984', 2.97), ('09-1984', 2.98)] avg = {k: sum(v)/len(v) for k, v in (lambda d: [(k, d[k]) for k in d])(defaultdict(list, {k: d[k].append(v) or d[k] for k, v in modifiedL}))}
不过这个写法可读性比较差,不推荐在生产代码中使用,更多是满足“单行”的需求。
方法四:用pandas一行搞定(适合数据处理场景)
如果你的项目已经在用pandas处理数据,那这个方法绝对是最简洁且可读性最高的:
import pandas as pd modifiedL = [('10-1984', 2.8), ('10-1984', 2.8), ('10-1984', 2.85), ('10-1984', 2.82), ('10-1984', 2.78), ('10-1984', 2.75), ('09-1984', 2.82), ('09-1984', 2.9), ('09-1984', 2.9), ('09-1984', 2.94), ('09-1984', 2.99), ('09-1984', 3.02), ('09-1984', 3.05), ('09-1984', 3.04), ('09-1984', 3.11), ('09-1984', 3.22), ('09-1984', 3.13), ('09-1984', 3.09), ('09-1984', 2.94), ('09-1984', 3.02), ('09-1984', 2.97), ('09-1984', 2.98)] avg = pd.DataFrame(modifiedL, columns=['date', 'value']).groupby('date')['value'].mean().to_dict()
pandas会帮你处理排序、分组、计算的所有细节,非常适合批量数据处理。
内容的提问来源于stack exchange,提问作者ehls39119
相关产品推荐
相关产品推荐

