【问题标题】:Working with list of tuples embedded in a list of dictionaries in python使用嵌入在python字典列表中的元组列表
【发布时间】:2018-10-07 17:31:09
【问题描述】:

我正在上初级编码课程,我似乎无法将我所学的基础知识转化为一个工作程序,其中包含如此复杂的列表。我应该使用哪些函数来执行此操作?

在这一点上,我们还没有讨论导入任何额外的功能(numpy 等),我知道人们经常使用 lambda(虽然我不太了解它的作用),但在本课程中还没有介绍。

#This is an example of the structure of a student dictionary
#They have an id number
#They have a first name, last name and a list of assignments
#Assignments are tuples of an assignment name and grade
#The grade is a 4 point scale from 0 to 4
'''
student_list = [{'id': 12341, 'first_name': 'Alice', 'last_name': 'Anderson',
     'assignments': [('assignment_1', 0), ('assignment_2', 2), ('assignment_3', 4)]},

 {'id': 12342, 'first_name': 'Boris', 'last_name': 'Bank',
   'assignments': [('assignment_1', 1), ('assignment_2', 3), ('assignment_3', 0)]},

 {'id': 12343, 'first_name': 'Carl', 'last_name': 'Cape',
   'assignments': [('assignment_1', 2), ('assignment_2', 4), ('assignment_3', 1)]},

 {'id': 12344, 'first_name': 'Didi', 'last_name': 'Dawson',
   'assignments': [('assignment_1', 3), ('assignment_2', 0), ('assignment_3', 2)]},

 {'id': 12345, 'first_name': 'Ed', 'last_name': 'Enders',
   'assignments': [('assignment_1', 4), ('assignment_2', 1), ('assignment_3', 3)]}]

#This function should return a list of the n student dictionaries with the
#highest grades on the assignment passed in as assignment name
#If there is a tie then it is broken by returning the student(s) with the
#lowest id number(s)
def highest_n_grades(students, assignment_name, n):

编辑

对不起,我不想得到答案。我看那看起来如何。我觉得我已经写出并删除了一百万件事,这是我的问题。我什至无法开始。

我希望找到一个正确的方向,比如哪些命令可以获取最高分等。到目前为止,我真正拥有的只是:

def highest_n_grades(student_list):
  for s in student_list:
    for assignment_name, grade in s['assignments']:
        if int(grade) >= 4:
            print(assignment_name, grade)

highest_n_grades(student_list)

但我知道这甚至不能让我真正开始。它没有三个输入,也不是在寻找最大值,而是在寻找手动输入的值 4,甚至还没有接近与学生姓名绑定或制作另一个列表。

编辑 2

还尝试了一个错误,我试图对字典而不是列表进行排序。

def highest_n_grades(student_list, assignment_name):
  for s in student_list:
    for assignment_name in s['assignments'][1]:
      s['assignments'][1] = assignment_name
      s.sort(key=assignment_name)
    print(student_list)

highest_n_grades(student_list, assignment_name='assignment_1' )

编辑 3

好的,我可能取得了一些进展?

newlist2 = sorted(newlist, key=lambda k: k['assignments'][0], reverse = True)
newlist3 = sorted(newlist, key=lambda k: k['assignments'][1], reverse = True)
newlist4 = sorted(newlist, key=lambda k: k['assignments'][2], reverse = True)

这些似乎是按任务排序的。我不明白 lambda 在做什么,但我至少可以生成一个最高等级的列表。我认为这是一个婴儿步骤。

编辑 4

这是我创建的一个函数。它似乎得到了我想要的东西,它输出了最高的 3 名学生,但它打印了 5 次?我知道这不是很灵活,但这是一个开始。

def highest_n_grades(student_list,  n):
  for s in student_list:
    newlist = sorted(student_list, key=lambda k: k['assignments'][0], reverse=True)
    print(newlist[:n])

highest_n_grades(student_list, 3)

输出:

[{'id': 12345, 'first_name': 'Ed', 'last_name': 'Enders', 'assignments': [('assignment_1', 4), ('assignment_2', 1), ('assignment_3', 3)]}, {'id': 12344, 'first_name': 'Didi', 'last_name': 'Dawson', 'assignments': [('assignment_1', 3), ('assignment_2', 0), ('assignment_3', 2)]}, {'id': 12343, 'first_name': 'Carl', 'last_name': 'Cape', 'assignments': [('assignment_1', 2), ('assignment_2', 4), ('assignment_3', 1)]}]
[{'id': 12345, 'first_name': 'Ed', 'last_name': 'Enders', 'assignments': [('assignment_1', 4), ('assignment_2', 1), ('assignment_3', 3)]}, {'id': 12344, 'first_name': 'Didi', 'last_name': 'Dawson', 'assignments': [('assignment_1', 3), ('assignment_2', 0), ('assignment_3', 2)]}, {'id': 12343, 'first_name': 'Carl', 'last_name': 'Cape', 'assignments': [('assignment_1', 2), ('assignment_2', 4), ('assignment_3', 1)]}]
[{'id': 12345, 'first_name': 'Ed', 'last_name': 'Enders', 'assignments': [('assignment_1', 4), ('assignment_2', 1), ('assignment_3', 3)]}, {'id': 12344, 'first_name': 'Didi', 'last_name': 'Dawson', 'assignments': [('assignment_1', 3), ('assignment_2', 0), ('assignment_3', 2)]}, {'id': 12343, 'first_name': 'Carl', 'last_name': 'Cape', 'assignments': [('assignment_1', 2), ('assignment_2', 4), ('assignment_3', 1)]}]
[{'id': 12345, 'first_name': 'Ed', 'last_name': 'Enders', 'assignments': [('assignment_1', 4), ('assignment_2', 1), ('assignment_3', 3)]}, {'id': 12344, 'first_name': 'Didi', 'last_name': 'Dawson', 'assignments': [('assignment_1', 3), ('assignment_2', 0), ('assignment_3', 2)]}, {'id': 12343, 'first_name': 'Carl', 'last_name': 'Cape', 'assignments': [('assignment_1', 2), ('assignment_2', 4), ('assignment_3', 1)]}]
[{'id': 12345, 'first_name': 'Ed', 'last_name': 'Enders', 'assignments': [('assignment_1', 4), ('assignment_2', 1), ('assignment_3', 3)]}, {'id': 12344, 'first_name': 'Didi', 'last_name': 'Dawson', 'assignments': [('assignment_1', 3), ('assignment_2', 0), ('assignment_3', 2)]}, {'id': 12343, 'first_name': 'Carl', 'last_name': 'Cape', 'assignments': [('assignment_1', 2), ('assignment_2', 4), ('assignment_3', 1)]}]

【问题讨论】:

  • 您好,仅仅发布您的家庭作业可能对您没有任何帮助。至少要付出一些努力,让你的程序运行起来,即使它什么也没做。然后你可以开始循环数据并输出它,只是为了开始。任何关于循环的教程都可以,例如python-course.eu/python3_for_loop.php
  • 你试过什么?你能提供一个minimal reproducible example吗?这似乎是一个家庭作业,这并不是真正的“这是我的家庭作业要求,请提供一个完整的工作示例”类型的网站。请发布您尝试过的内容,以便我们能够POINT帮助您朝着正确的方向前进。
  • 对不起。我不是想得到答案。我看那看起来如何。我觉得我已经写出并删除了一百万件事,这是我的问题。我什至在开始时都遇到了麻烦....我希望在正确的方向上找到一个点,也许哪些命令可以获取最高成绩等。到目前为止,我真正拥有的只是 defhighest_n_grades(student_list): for s in student_list: for assignment_name, grade in s['assignments']: if int(grade) >= 4: print(assignment_name,grade) highest_n_grades(student_list)
  • 好的,一个非常有用的策略是让您开始打印值。例如打印出student_list[0] 然后查看student_list[0]['id'] 的输出然后查看student_list[0]['assignments'][0] 的输出这将帮助您了解如何获取您需要的数据。而且在你的循环中,你会知道你正在处理哪些值
  • @tsb 我建议对student_list 进行排序,这是一个list 的字典。具体来说,根据传入的赋值名称按降序排序(因此我们将在排序函数中指定key 属性)。你现在能看出这种方法背后的想法了吗?

标签: python


【解决方案1】:

这可以使用lambdasorted 来完成。当使用sortedlambda 时,我们首先设置key=lambda x:。现在你可以认为x 代表一个列表索引,所以要按assignment_1 排序,我们将要转到x['assignments'] 这将带我们进行分配,然后下一步,如果我们的分配是@987654332 @ 我们知道那是assignments0 索引,所以加起来就是key=lambda x: x['assignments'][0]。现在我们也可以将sort 作为次要选项,这将是我们的决胜局,我们将使用x[id] 并将与我们的主要排序因子放在一个元组中。当然,我们应该使用reverse = True 来获得降分,但由于我们希望我们的决胜局是按升序排列的,我们可以使用-(x['id']) 抵消id 上的倒数

排序看起来像这样:

lista = sorted(students, key=lambda x: (x['assignments'][0], -(x['id'])), reverse = True)

棘手的部分是为传递的赋值选择正确的赋值索引,因为你可以使用.split('_')[1](当在'assignment_1'上使用.split('_')时,我们会生成一个新列表['assignemnt', '1'],在这种情况下我们现在可以将.split()[1] 索引(即1)作为int 并减去1 得到0,这是相应的索引,其余的都是从它们的索引中减去1索引。

def highest_n_grades(students, assignment_name, n):
    y = int(assignment_name.split('_')[1]) - 1
    lista = sorted(students, key=lambda x: (x['assignments'][y], 'id'), reverse = True)
    return lista [:n]   

print(highest_n_grades(student_list, 'assignment_1', 3))
# [{'id': 12345, 'first_name': 'Ed', 'last_name': 'Enders', 'assignments': [('assignment_1', 4), ('assignment_2', 1), ('assignment_3', 3)]}, {'id': 12344, 'first_name': 'Didi', 'last_name': 'Dawson', 'assignments': [('assignment_1', 3), ('assignment_2', 0), ('assignment_3', 2)]}, {'id': 12343, 'first_name': 'Carl', 'last_name': 'Cape', 'assignments': [('assignment_1', 2), ('assignment_2', 4), ('assignment_3', 1)]}]

使用伪分数演示决胜局:

print(highest_n_grades(student_list, 'assignment_1', 3))
# [{'id': 12344, 'first_name': 'Didi', 'last_name': 'Dawson', 'assignments': [('assignment_1', 4), ('assignment_2', 0), ('assignment_3', 2)]}, {'id': 12345, 'first_name': 'Ed', 'last_name': 'Enders', 'assignments': [('assignment_1', 4), ('assignment_2', 1), ('assignment_3', 3)]}, {'id': 12342, 'first_name': 'Boris', 'last_name': 'Bank', 'assignments': [('assignment_1', 2), ('assignment_2', 3), ('assignment_3', 0)]}]

进一步阅读

.split()

https://docs.python.org/3/library/stdtypes.html

关于使用sorted

https://docs.python.org/3/library/functions.html https://wiki.python.org/moin/HowTo/Sorting

【讨论】:

  • 我们的目标不是为 OP 完全解决它,而是引导他们找到解决方案,因为这是一个家庭作业问题。
  • @slider 是的,但我不认为 SO 是在评论框中为逐步在线指导而制作的,这里领先但未构建的 cmets 的数量变得荒谬,有什么好处一个有 30 条评论解决方案的问题
  • 是的,开枪。我很欣赏这一点,我看到它有效,但我觉得我失去了一些学习机会。 y = int(assignment_name.split('_')[1]) - 1 中的 [1] 有什么作用?是说在highest_n_grades函数中寻找第二个参数吗?
  • 也是徒劳的,你知道一个资源会更一步一步指导吗?
  • @tsb 在此过程中我尽量提供信息,我不想给你答案,但我可以解释发生了什么以及你需要学习什么的唯一方法就是一步一步来,我在关于.split()的段落中添加了一些链接
【解决方案2】:

对于初学者课程来说,这是一项艰巨的任务。困难在于 lambdas、多键排序、列表、列表切片和元组、字典,甚至是有序与无序的数据类型。我已经用 Python 编程了 10 年,但并不觉得它简单。

lambda 是一个你可以动态定义的小函数。 sorted() 将函数作为其第二个参数。它需要为每个学生调用这个函数来生成一个排序键。 sort 函数比较两个学生的排序键来决定哪个学生排在第一位。

从 lambdas 开始的一个好地方是记住:

id_key = lambda x: x[0]

相当于:

def id_key(x):
    return x[0]

还有

sorted(students, key=lambda x: x[0])

相当于:

sorted(student, key=id_key)

为了对多个值进行排序,我会查看stable sorts and their properties。稳定的排序算法非常适合对多个值进行排序。大多数 Python 排序函数都是“稳定的”。

这是使用当前结构的解决方案:

def sort_by_grade_then_id(grades):
    # sort (id, grade) tuples high grades, low ids first
    sorted_by_id = sorted(grades, key=lambda student: student[0])
    sorted_by_id_and_assignment_grade = sorted(sorted_by_id,
        key=lambda student: student[1], reverse=True)
    return sorted_by_id_and_assignment_grade


def highest_n_grades(students, assignment_name, n):
grades = []
for student in students:
    for assignment, grade in student['assignments']:
        if assignment_name == assignment:
            grades.append((student['id'], grade))
return sort_by_grade_then_id(grades)[:n]    

>>> print(highest_n_grades(student_list, 'assignment_2', 2))
[(12343, 4), (12342, 3)]

但是,如果您现在想要学生的姓名而不是他/她的身份证,则必须进行另一次连续搜索才能得到它。

作为一种不同的方法,以下将基于列表的原始学生数据库复制到基于字典的数据库中。

from copy import copy

students_dict = {student['id']: student for student in copy(student_list)}
for student in students_dict.values():
    student['assignments'] = dict(student['assignments'])

列出高分变成:

def highest_n_grades_dict(students, assignment_name, n):
    grades = [
        (id, student['assignments'][assignment_name])
        for id, student
        in students.items()
    ]
    return sort_by_grade_then_id(grades)[:n]

只有几个学生没关系,但如果你有很多学生和很多作业,这个新版本会更快。您现在也可以使用学生数据库查找资料,而不必搜索和匹配。

举个例子:

print('Highest grades dict version...')
grades = highest_n_grades_dict(students_dict, 'assignment_2', 2)
print(grades)
print("...and dict structure easily allows us to get other student details")
names_and_grades = [
    (students_dict[id]['first_name'] + ' ' + students_dict[id]['last_name'], grade)
    for id, grade
    in grades]
print(names_and_grades)
>>> python grades.py
Highest grades dict version...
[(12343, 4), (12342, 3)]
...and dict structure easily allows us to get other student details
[('Carl Cape', 4), ('Boris Bank', 3)]

旁注:如果您经常处理元组,您可能会对named tuples 感兴趣,因为它们通常使与元组相关的代码(包括 lambda 函数)更易于阅读、编写和理解。以我最近对this question 的回答为例。

【讨论】:

  • 谢谢!你的陈述确实在重申。我认为这是一个不合理的复杂数据集,因为我在这个程序的早期阶段......上周我编写了一个程序来计算数字并在它可以被 3 整除时添加嘶嘶声......这是 6 个函数之一我必须用这个数据集来写。虽然其他答案有效并且很有帮助,但我认为更像您所写的内容可能是他们想要的。希望我可以将这些知识用于我的其他功能
  • 别担心!进一步考虑这个问题,我的首选方法实际上是在进行任何处理之前将数据重写为字典。我的猜测是,这样做实际上比保持数据原样更容易、更快捷。
  • 添加了将学生数据库重写为字典的代码。
猜你喜欢
  • 1970-01-01
  • 2013-12-12
  • 2012-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-01
  • 2017-01-20
  • 1970-01-01
相关资源
最近更新 更多