Создайте список вложенных словарей из одного csv-файла на python

#python #csv #dictionary #nested-lists #dictionary-comprehension

Вопрос:

У меня есть csv-файл со следующей структурой:

 team,tournament,player
Team 1,spring tournament,Rebbecca Cardone
Team 1,spring tournament,Salina Youngblood
Team 1,spring tournament,Catarina Corbell
Team 1,summer tournament,Cara Mejias
Team 1,summer tournament,Catarina Corbell
...
Team 10, spring tournament,Jessi Ravelo
 

Я хочу создать вложенный словарь (команда, турнир) со списком словарей игроков. Желаемый результат был бы примерно таким:

 {'data': 
   {Team 1:
       {'spring tournament': 
               {'players': [
                   {name: Rebecca Cardone}, 
                   {name: Salina Youngblood},
                   {name: Catarina Corbell}]
        },
        {'summer tournament': 
               {'players': [
                   {name: Cara Mejias}, 
                   {name: Catarina Corbell}]
               }
        }

   },
    ...
  {Team 10:
       {'spring tournament': 
               {'players': [
                   {name: Jessi Ravelo}]
                }
       }
   }
}

 

Я изо всех сил пытался отформатировать его так. Мне удалось успешно вложить первый уровень (команда # —> турнир), но я не могу вложить второй уровень. В настоящее время мой код выглядит следующим образом:

 d = {}
header = True
with open("input.csv") as f:
    for line in f.readlines():
        if header:
            header = False
            continue
        team, tournament, player = line.strip().split(",")
        d_team = d.get(team,{})
        d_tournament = d_team.get(tournament, {})
        d_player = d_tournament.get('player',['name'])
        d_player.append(player)
        d_tournament['player'] = d_tournament
        d_team[tournament] = d_tournament
        d[team] = d_team
print(d)
 

Каким будет следующий шаг в исправлении моего кода, чтобы я мог создать вложенный словарь?

Ответ №1:

Некоторые проблемы с вашей реализацией:

  • Вы делаете d_player = d_tournament.get('player',['name']) . Но на самом деле вы хотите получить ключ с именем игрока s, и это должен быть список словарей. Каждый из этих словарей должен иметь форму {"name": "Player's Name"} . Итак, вы хотите l_player = d_tournament.get('players',[]) (по умолчанию пустой список), а затем делаете l_player.append({"name": player}) (я переименовал его в l_player , потому что это список, а не диктант).
  • Вы делаете d_tournament['player'] = d_tournament . Я подозреваю, что вы имели в виду d_tournament['player'] = d_player
  • Удалите пробелы с элементов в строках. Делать team, tournament, player = (word.strip() for word in line.split(","))

Ваш код отлично работает после внесения этих изменений


Я настоятельно рекомендую вам использовать csv.reader класс для чтения вашего CSV-файла вместо того, чтобы вручную разделять строку запятыми.

Кроме того, поскольку контейнеры python (списки и словари) содержат ссылки на их содержимое, вы можете просто добавить контейнер один раз , а затем изменить его с помощью mydict["key"] = value или mylist.append() , и эти изменения также будут отражены в родительских контейнерах. Из-за такого поведения вам не нужно повторно назначать эти вещи в цикле, как вы делаете с d_team[tournament] = d_tournament

 allteams = dict()
hasHeader = True
with open("input.csv") as f:
    csvreader = csv.reader(f)
    if hasHeader: next(csvreader) # Consume one line if a header exists

    # Iterate over the rows, and unpack each row into three variables
    for team_name, tournament_name, player_name in csvreader:
        # If the team hasn't been processed yet, create a new dict for it
        if team_name not in allteams:
            allteams[team_name] = dict()

        # Get the dict object that holds this team's information
        team = allteams[team_name]

        # If the tournament hasn't been processed already for this team, create a new dict for it in the team's dict
        if tournament_name not in team:
            team[tournament_name] = {"players": []}

        # Get the tournament dict object
        tournament = team[tournament_name]

        # Add this player's information to the tournament dict's "player" list
        tournament["players"].append({"name": player_name})

# Add all teams' data to the "data" key in our result dict
result = {"data": allteams}
print(result)    
 

Что дает нам то, что мы хотим (приукрашенный результат):

 {
    'data': {
        'Team 1': {
            'spring tournament': {
                'players': [
                    { 'name': 'Rebbecca Cardone' },
                    { 'name': 'Salina Youngblood' },
                    { 'name': 'Catarina Corbell' }
                ]
            },
            'summer tournament': {
                'players': [
                    { 'name': 'Cara Mejias' },
                    { 'name': 'Catarina Corbell' }
                ]
            }
        },
        'Team 10': {
            ' spring tournament': {
                'players': [
                    { 'name': 'Jessi Ravelo' }
                ]
            }
        }
    }
}
 

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

1. Спасибо! Я смог реализовать ваши изменения, и это сработало. Я собираюсь ознакомиться с csv.reader! Ваш ответ отлично объясняет, почему изменения кода были необходимы.

Ответ №2:

Пример словаря, который вы описываете, невозможен (если вам нужно несколько словарей под ключом «Команда 1», поместите их в список), но этот фрагмент:

 if __name__ == '__main__':
    your_dict = {}
    with open("yourfile.csv") as file:
        all_lines = file.readlines()

    data_lines = all_lines[1:]  #  Skipping "team,tournament,player" line

    for line in data_lines:
        line = line.strip()  # Remove n
        team, tournament_type, player_name = line.split(",")
        team_dict = your_dict.get(team, {})  # e.g. "Team 1"

        tournaments_of_team_dict = team_dict.get(tournament_type, {'players': []})  # e.g. "spring_tournament"

        tournaments_of_team_dict["players"].append({'name': player_name})

        team_dict[tournament_type] = tournaments_of_team_dict
        your_dict[team] = team_dict

    your_dict = {'data': your_dict}
 

Для этого примера yourfile.csv :

 team,tournament,player
Team 1,spring tournament,Rebbecca Cardone
Team 1,spring tournament,Salina Youngblood
Team 2,spring tournament,Catarina Corbell
Team 1,summer tournament,Cara Mejias
Team 2,summer tournament,Catarina Corbell
 

Дает следующее:

 {
  "data": {
    "Team 1": {
      "spring tournament": {
        "players": [
          {
            "name": "Rebbecca Cardone"
          },
          {
            "name": "Salina Youngblood"
          }
        ]
      },
      "summer tournament": {
        "players": [
          {
            "name": "Cara Mejias"
          }
        ]
      }
    },
    "Team 2": {
      "spring tournament": {
        "players": [
          {
            "name": "Catarina Corbell"
          }
        ]
      },
      "summer tournament": {
        "players": [
          {
            "name": "Catarina Corbell"
          }
        ]
      }
    }
  }
}

Process finished with exit code 0

 

Ответ №3:

Может быть, я что-то упускаю из виду, но не могли бы вы использовать:

 df.groupby(['team','tournament'])['player'].apply(list).reset_index().to_json(orient='records')
 

Ответ №4:

Вы могли бы подойти к этому так:

 from collections import defaultdict
import csv
from pprint import pprint

d = defaultdict(dict)

with open('f00.txt', 'r') as f:
    reader = csv.DictReader(f)
    for row in reader:
        d[ row['team'] ].setdefault(row['tournament'], []
                                    ).append(row['player'])

pprint(dict(d))
 

С принтами:

 {'Team 1': {'spring tournament': ['Rebbecca Cardone',
                                  'Salina Youngblood',
                                  'Catarina Corbell'],
            'summer tournament': ['Cara Mejias', 'Catarina Corbell']},
 'Team 10': {' spring tournament': ['Jessi Ravelo']}}