Чтение строк из текстового файла и создание словаря, где значениями являются списки кортежей

#python #python-3.x #defaultdict

#python #python-3.x #defaultdict

Вопрос:

student.txt:

 Akçam   Su Tilsim       PSYC 3.9
Aksel   Eda         POLS 2.78
Alpaydin Dilay          ECON 1.2
Atil    Turgut Uluç     IR 2.1
Deveci  Yasemin     PSYC 2.9
Erserçe Yasemin     POLS 3.0
Gülle   Halil       POLS 2.7
Gündogdu Ata Alp    ECON 4.0
Gungor  Muhammed Yasin  POLS 3.1
Hammoud  Rawan      IR 1.7
Has Atakan      POLS 1.97
Ince    Kemal Kahriman  IR 2.0
Kaptan  Deniz       IR 3.5
Kestir  Bengisu     IR 3.8
Koca    Aysu        ECON 2.5
Kolayli Sena Göksu  IR 2.8
Kumman  Gizem       PSYC 2.9
Madenoglu Zeynep    PSYC 3.1
Naghiyeva Gulustan  IR 3.8
Ok  Arda Mert   IR 3.2
Var Berna       ECON 2.9
Yeltekin Sude       PSYC 1.2
  

Здравствуйте, я хочу написать функцию, которая считывает информацию о каждом студенте в файле в словарь, где ключами являются факультеты, а значениями являются список студентов в данном отделе (список кортежей). Информация о каждом студенте хранится в кортеже
содержащий (фамилия, средний балл). У учащихся в файле может быть более одного имени, но будут сохранены только фамилия и средний балл. Функция должна вернуть словарь. (Фамилии — это первые слова в каждой строке.)

Это то, что я пробовал:

 def read_student(ifile):
    D={}
    f1=open(ifile,'r')
    for line in f1:
        tab=line.find('t')
        space=line.rfind(' ')
        rtab=line.rfind('t')
        student_surname=line[0:tab]
        gpa=line[space 1:]
        department=line[rtab 1:space]
        
        if department not in D:
            D[department]=[(student_surname,gpa)]
        else:
            D[department].append((student_surname,gpa))
    f1.close()
    return D
print(read_student('student.txt'))
  

Я думаю, что основная проблема заключается в том, что существует своего рода беспорядок, потому что иногда после слов идет табуляция, а иногда после слов пробел, поэтому я не знаю, как правильно использовать функцию поиска в этом случае.

Комментарии:

1. можете ли вы добавить ожидаемый результат

Ответ №1:

Зачем связываться с rfind и find , когда вы можете просто split ?

 def read_student(ifile):
    D = {}
    f1 = open(ifile,'r')
    for line in f1:
        cols = line.split() # Splits at one or more whitespace
        surname = cols[0].strip()
        department = cols[-2].strip() # Because you know the last-but-one is dept
        gpa = float(cols[-1].strip()) # Because you know the last one is GPA
        fname = ' '.join(cols[1:-2]).strip() 
        # cols[1:-2] gives you everything starting at col 1 up to but excluding the second-last. 
        # Then you join these with spaces.


        if department not in D:
            D[department] = [(surname, gpa)]
        else:
            D[department].append((surname, gpa))

    f1.close()
    return D
  

Если вы знаете, что ваши столбцы разделяются t всегда, вы можете сделать cols = line.split('t') вместо этого. Тогда у вас есть имя студента во втором столбце, кафедра в третьем и средний балл в четвертом.

Пара предложений:

  1. Вы можете использовать, defaultdict чтобы избежать проверки if department not in D каждый раз
  2. Вы можете использовать with для управления чтением файла, чтобы вам не приходилось беспокоиться о f1.close() . Это предпочтительный способ чтения файлов в Python.

Ответ №2:

смотрите ниже — вам нужно будет позаботиться о фамилии, но остальные детали в вопросе были обработаны

 from collections import defaultdict

data = defaultdict(list)
with open('data.txt', encoding="utf-8") as f:
    lines = [l.strip() for l in f.readlines()]
    for line in lines:
        first_space_idx = line.rfind(' ')
        sec_space_idx = line.rfind(' ', 0,first_space_idx - 1)
        grade = line[first_space_idx 1:]
        dep = line[sec_space_idx:first_space_idx]
        student = line[:sec_space_idx].strip()
        data[dep].append((student, grade))
for dep, students in data.items():
    print(f'{dep} --> {students}')
  

вывод

  PSYC --> [('Akçam   Su Tilsim', '3.9'), ('Deveci  Yasemin', '2.9'), ('Kumman  Gizem', '2.9'), ('Madenoglu Zeynep', '3.1'), ('Yeltekin Sude', '1.2')]
 POLS --> [('Aksel   Eda', '2.78'), ('Erserçe Yasemin', '3.0'), ('Gülle   Halil', '2.7'), ('Gungor  Muhammed Yasin', '3.1'), ('Has Atakan', '1.97')]
 ECON --> [('Alpaydin Dilay', '1.2'), ('Gündogdu Ata Alp', '4.0'), ('Koca    Aysu', '2.5'), ('Var Berna', '2.9')]
 IR --> [('Atil    Turgut Uluç', '2.1'), ('Hammoud  Rawan', '1.7'), ('Ince    Kemal Kahriman', '2.0'), ('Kaptan  Deniz', '3.5'), ('Kestir  Bengisu', '3.8'), ('Kolayli Sena Göksu', '2.8'), ('Naghiyeva Gulustan', '3.8'), ('Ok  Arda Mert', '3.2')]
  

Комментарии:

1. Хотя ответы только для кода могут отвечать на вопрос, вы могли бы значительно улучшить качество своего ответа, предоставив контекст для вашего кода, причину, по которой этот код работает, и некоторые ссылки на документацию для дальнейшего чтения.

Ответ №3:

Вы можете использовать split(' ', 1) для извлечения фамилии. Это дает список с двумя элементами. первое — фамилия. Затем снова разделите вторые элементы, чтобы получить using rsplit(' ', 1) . Он снова выдает список с двумя элементами, первый из которых — name и dept, а второй — gpa. Снова разделите второй элемент, чтобы получить отдел.

 def read_student(ifile):
    d = {}
    with open(ifile) as fp:
      for line in fp:
          fname, data = line.strip().split(' ', 1)
          data, gpa = data.rsplit(' ', 1)
          dept = data.split()[-1]
          d.setdefault(dept, []).append((fname, gpa))
      return d

print(read_student('student.txt'))
  

Вывод:

 {'ECON': [('Alpaydin', '1.2'),
          ('Gündogdu', '4.0'),
          ('Koca', '2.5'),
          ('Var', '2.9')],
 'IR': [('Atil', '2.1'),
        ('Hammoud', '1.7'),
        ('Ince', '2.0'),
        ('Kaptan', '3.5'),
        ('Kestir', '3.8'),
        ('Kolayli', '2.8'),
        ('Naghiyeva', '3.8'),
        ('Ok', '3.2')],
 'POLS': [('Aksel', '2.78'),
          ('Erserçe', '3.0'),
          ('Gülle', '2.7'),
          ('Gungor', '3.1'),
          ('Has', '1.97')],
 'PSYC': [('Akçam', '3.9'),
          ('Deveci', '2.9'),
          ('Kumman', '2.9'),
          ('Madenoglu', '3.1'),
          ('Yeltekin', '1.2')]}
  

Комментарии:

1. @PranavHosangadi хорошо, сэр

Ответ №4:

Это решение использует itemgetter для упрощения получения переменных: фамилия, отдел. и средний балл

 from operator import itemgetter
d = dict()

with open('f0.txt', 'r') as f:
    for line in f:
        name, dept, gpa = itemgetter(0, -2, -1)(line.split())
        d.setdefault(dept, []).append((name, gpa))
  

Комментарии:

1. Я никогда не понимал прецедент для itemgetter . Кроме краткости, есть ли какая-либо причина предпочесть a, b, c = itemgetter(0, -2, -1)(arr) вместо a, b, c = arr[0], arr[-2], arr[-1] ?

2. @Pranav Hosangadi В этом случае вы могли бы поступить так, как вы объяснили, но его также можно использовать для получения фрагмента хэшей (предоставления искомых ключей) и для получения дополнительных индексов для фрагментов списков (здесь было всего 3 индекса)..

3. Спасибо, теперь я вижу! Поскольку я большую часть времени работаю с массивами numpy, легко забыть, что списки не поддерживают прерывистые фрагменты.