如何根据键(key)对字典(dictionary)的值进行求和?

3

我有一个字典列表,如下所示:

data = [{'student_id': '1','mark': 7.8,'course_id': '1',},
        {'student_id': '1','mark': 34.8,'course_id': '1'},
        {'student_id': '1','mark': 12.8,'course_id': '2'},
        {'student_id': '1','mark': 39.0,'course_id': '2'},
        {'student_id': '1','mark': 70.2,'course_id': '3'},
        {'student_id': '2','mark': 7.8,'course_id': '1'},
        {'student_id': '2','mark': 34.8,'course_id': '1'}]

我正在尝试按给定课程对每个student_id的分数进行求和,例如学生1在课程1中的总分将是42.6等。理想情况下,我将创建一个新的干净列表,其中仅包含每个学生每门课程的总分。

我首先想到的方法是编写迭代来对每个学生和课程ID进行求和,如果与前一个项匹配,则将其相加:

for i in range(len(data)-1):
    if data[i]['course_id'] == data[i+1]['course_id'] and data[i]['student_id'] == data[i+1]['student_id']:
        data[i+1]['sum_mark'] = round(float(data[i]['mark'])+float(data[i+1]['mark']),3) 

我认为这不是解决问题的好方法。

7个回答

2
如果你使用一个 defaultdict ,你可以使用元组 (student_id, course_id) 作为键。之后,你可以随时添加内容。如果想获得列表,只需使用简单的列表推导式即可:
from collections import defaultdict

totals = defaultdict(float)

for d in data:
    totals[(d['student_id'], d['course_id'])] += d['mark']
    
[{'student_id':s_id, 'course_id': c_id, 'total': round(total, 3)} 
 for (s_id, c_id), total in totals.items()]

这将为您带来:

[{'student_id': '1', 'course_id': '1', 'total': 42.6},
 {'student_id': '1', 'course_id': '2', 'total': 51.8},
 {'student_id': '1', 'course_id': '3', 'total': 70.2},
 {'student_id': '2', 'course_id': '1', 'total': 42.6}]

1

与陷入低级 Python 相比,人们可以使用 pandas 数据操作库。

它支持分组操作,如求和、平均值等。

Pandas 可以接受各种输入,包括 Python 的 dict.csv 文件和其他许多格式。

data = [{'student_id': '1','mark': 7.8,'course_id': '1',},
        {'student_id': '1','mark': 34.8,'course_id': '1'},
        {'student_id': '1','mark': 12.8,'course_id': '2'},
        {'student_id': '1','mark': 39.0,'course_id': '2'},
        {'student_id': '1','mark': 70.2,'course_id': '3'},
        {'student_id': '2','mark': 7.8,'course_id': '1'},
        {'student_id': '2','mark': 34.8,'course_id': '1'}]
import pandas as pd
df = pd.DataFrame(data)
df.groupby(['student_id','course_id']).sum()  
# output in iPython or Jupyter
                      mark
student_id course_id      
1          1          42.6
           2          51.8
           3          70.2
2          1          42.6

# often teachers/students need an average, not a sum...
df.groupby(['student_id','course_id']).mean()
                      mark
student_id course_id      
1          1          21.3
           2          25.9
           3          70.2
2          1          21.3

1
我通常支持纯Python,但是你展示了Pandas的完美之处,值得赞扬。 - Matt Hall

1
如果您不介意对数据进行排序,您可以使用 itertools.groupby:
data = [
    {'student_id': '1', 'mark': 7.8, 'course_id': '1'},
    {'student_id': '1', 'mark': 34.8, 'course_id': '1'},
    {'student_id': '1', 'mark': 12.8, 'course_id': '2'},
    {'student_id': '1', 'mark': 39.0, 'course_id': '2'},
    {'student_id': '1', 'mark': 70.2, 'course_id': '3'},
    {'student_id': '2', 'mark': 7.8, 'course_id': '1'},
    {'student_id': '2', 'mark': 34.8, 'course_id': '1'}
]

def to_summed(data):
    from itertools import groupby
    from operator import itemgetter

    keys = ("student_id", "course_id")
    key = itemgetter(*keys)

    for current_key, group in groupby(sorted(data, key=key), key=key):
        sum_mark = sum(map(itemgetter("mark"), group))
        yield dict(zip(keys, current_key)) | {"sum_mark": sum_mark}

for entry in to_summed(data):
    print(entry)

输出:

{'student_id': '1', 'course_id': '1', 'sum_mark': 42.599999999999994}
{'student_id': '1', 'course_id': '2', 'sum_mark': 51.8}
{'student_id': '1', 'course_id': '3', 'sum_mark': 70.2}
{'student_id': '2', 'course_id': '1', 'sum_mark': 42.599999999999994}
>>> 

在我看来,这是个很好的回答,但是根据PEP 8建议,在脚本的开头放置所有的import - martineau

1
您可以通过在自定义字典子类上实现特殊的__missing__()方法来轻松地使用库存“低级”Python来设置和返回所需容器类型的新实例。自从Python 2.5以来,这种方法一直可用(并且已记录)。
请注意,一个可行且经常使用的替代方案是使用标准库中的更通用的{{link2:collections.defaultdict}}子类,但由于前一种方法相当简单,因此我将演示如何使用该方法完成任务:
from pprint import pprint


class CourseMarks(dict):
    def __missing__(self, course_id):
        value = self[course_id] = []
        return value


class StudentCourseMarks(dict):
    def __missing__(self, student_id):
        value = self[student_id] = CourseMarks()
        return value


data = [{'student_id': 'id 1','mark': 7.8,'course_id': 'crs 1',},
        {'student_id': 'id 1','mark': 34.8,'course_id': 'crs 1'},
        {'student_id': 'id 1','mark': 12.8,'course_id': 'crs 2'},
        {'student_id': 'id 1','mark': 39.0,'course_id': 'crs 2'},
        {'student_id': 'id 1','mark': 70.2,'course_id': 'crs 3'},
        {'student_id': 'id 2','mark': 7.8,'course_id': 'crs 1'},
        {'student_id': 'id 2','mark': 34.8,'course_id': 'crs 1'}]

scm = StudentCourseMarks()

for grade in data:
    scm[grade['student_id']][grade['course_id']].append(grade['mark'])

print('Student course marks:')
pprint(scm)

for courses in scm.values():
    for course in courses:
        courses[course] = round(sum(courses[course]), 1)

print()
print('Total marks per student per course:')
pprint(scm, compact=0)

输出:

Student course marks:
{'id 1': {'crs 1': [7.8, 34.8], 'crs 2': [12.8, 39.0], 'crs 3': [70.2]},
 'id 2': {'crs 1': [7.8, 34.8]}}

Total marks per student per course:
{'id 1': {'crs 1': 42.6, 'crs 2': 51.8, 'crs 3': 70.2}, 
 'id 2': {'crs 1': 42.6}}

0

你也可以轻松地使用pandas库来完成这个操作。

import pandas as pd
df = pd.DataFrame(data)
grouped = df.groupby(["student_id","course_id"]).sum()
new_df = grouped.reset_index()
new_df.to_dict(orient='records')

Output:

[{'course_id': '1', 'mark': 42.599999999999994, 'student_id': '1'},
 {'course_id': '2', 'mark': 51.8, 'student_id': '1'},
 {'course_id': '3', 'mark': 70.2, 'student_id': '1'},
 {'course_id': '1', 'mark': 42.599999999999994, 'student_id': '2'}]

0

你可以创建一个临时字典,在其中将分数相加,然后在之后将该字典转换为所需的格式:

tmp = {}
for d in data:
    tmp.setdefault(d["student_id"], {}).setdefault(d["course_id"], 0)
    tmp[d["student_id"]][d["course_id"]] += d["mark"]

tmp = [
    {"student_id": k, "course_id": kk, "sum_mark": vv}
    for k, v in tmp.items()
    for kk, vv in v.items()
]

print(tmp)

输出:

[
    {"student_id": "1", "course_id": "1", "sum_mark": 42.599999999999994},
    {"student_id": "1", "course_id": "2", "sum_mark": 51.8},
    {"student_id": "1", "course_id": "3", "sum_mark": 70.2},
    {"student_id": "2", "course_id": "1", "sum_mark": 42.599999999999994},
]

0
你可以使用 pandas
import pandas as pd

data = [{'student_id': '1','mark': 7.8,'course_id': '1',},
        {'student_id': '1','mark': 34.8,'course_id': '1'},
        {'student_id': '1','mark': 12.8,'course_id': '2'},
        {'student_id': '1','mark': 39.0,'course_id': '2'},
        {'student_id': '1','mark': 70.2,'course_id': '3'},
        {'student_id': '2','mark': 7.8,'course_id': '1'},
        {'student_id': '2','mark': 34.8,'course_id': '1'}]

df = pd.DataFrame(data)
result = df.groupby(by=["student_id", "course_id"], as_index=False).sum()

print(result)

输出:

  student_id course_id  mark
0          1         1  42.6
1          1         2  51.8
2          1         3  70.2
3          2         1  42.6

参见:Pandas 分组和求和


完整性附言:使用result.to_dict(orient="records")将其转换回字典。(感谢 Priya's 回答!)


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