Friday 21 June 2024

Web Tutorial: Python Matplotlib Line Chart (Part 1/2)

Last month, we wrangled a bar chart in Python. This month, we will repeat the process, this time with a line chart.

Now, the considerations for a line chart are different from a bar chart. Line charts are primarily utilized to display progress over time. Therefore, the line chart will show both goals and appearances for particular players, over the course of several seasons.

There are some things we can reuse from the code, such as the dictionary from which we derive the data, and the helper function seasonName(). We will clear out the contents of the barChart() function, and rename it to lineChart(). This time, the function will not have season or stat as a parameter, because the line chart will show both goals and appearances across all seasons. We will, however, need the parameter player.

As for the rest of the code, we will discard it.
import numpy as np
import matplotlib.pyplot as plt

def lineChart(labels, vals, player):
  
def seasonName(year):
  return str(year) + "/" + str(year + 1)

data = {
  2017: {
    "Mohamed Salah": {"goals": 44, "appearances": 52},
    "Roberto Firminho": {"goals": 27, "appearances": 54},
    "Sadio Mane": {"goals": 20, "appearances": 44},
    "Alex Oxlade-Chamberlain": {"goals": 5, "appearances": 42}
  },
  2018: {
    "Mohamed Salah": {"goals": 27, "appearances": 52},
    "Roberto Firminho": {"goals": 16, "appearances": 48},
    "Sadio Mane": {"goals": 26, "appearances": 50},
    "Alex Oxlade-Chamberlain": {"goals": 0, "appearances": 2}
  },
  2019: {
    "Mohamed Salah": {"goals": 23, "appearances": 48},
    "Roberto Firminho": {"goals": 12, "appearances": 52},
    "Sadio Mane": {"goals": 22, "appearances": 47},
    "Alex Oxlade-Chamberlain": {"goals": 8, "appearances": 43}
  },
  2020: {
    "Mohamed Salah": {"goals": 31, "appearances": 51},
    "Roberto Firminho": {"goals": 9, "appearances": 48},
    "Sadio Mane": {"goals": 16, "appearances": 48},
    "Alex Oxlade-Chamberlain": {"goals": 1, "appearances": 17},
    "Diogo Jota": {"goals": 13, "appearances": 30}
  },
  2021: {
    "Mohamed Salah": {"goals": 31, "appearances": 51},
    "Roberto Firminho": {"goals": 11, "appearances": 35},
    "Sadio Mane": {"goals": 23, "appearances": 51},
    "Alex Oxlade-Chamberlain": {"goals": 3, "appearances": 29},
    "Diogo Jota": {"goals": 21, "appearances": 55},
    "Luis Diaz": {"goals": 6, "appearances": 26}
  },
  2022: {
    "Mohamed Salah": {"goals": 30, "appearances": 51},
    "Roberto Firminho": {"goals": 13, "appearances": 35},
    "Alex Oxlade-Chamberlain": {"goals": 1, "appearances": 13},
    "Diogo Jota": {"goals": 7, "appearances": 28},
    "Luis Diaz": {"goals": 5, "appearances": 21}
  }
}


We will require a variable, ans. That's the only input we will need.
  2022: {
    "Mohamed Salah": {"goals": 30, "appearances": 51},
    "Roberto Firminho": {"goals": 13, "appearances": 35},
    "Alex Oxlade-Chamberlain": {"goals": 1, "appearances": 13},
    "Diogo Jota": {"goals": 7, "appearances": 28},
    "Luis Diaz": {"goals": 5, "appearances": 21}
  }
}
  
ans = ""


Therefore, when we create the While loop, this time we only need to do it as long as ans is not 0.
  2022: {
    "Mohamed Salah": {"goals": 30, "appearances": 51},
    "Roberto Firminho": {"goals": 13, "appearances": 35},
    "Alex Oxlade-Chamberlain": {"goals": 1, "appearances": 13},
    "Diogo Jota": {"goals": 7, "appearances": 28},
    "Luis Diaz": {"goals": 5, "appearances": 21}
  }
}
  
ans = ""

while (ans != 0):


We will still need to have a menu. This time, we will have a menu of players. Create an empty list, players.
  2022: {
    "Mohamed Salah": {"goals": 30, "appearances": 51},
    "Roberto Firminho": {"goals": 13, "appearances": 35},
    "Alex Oxlade-Chamberlain": {"goals": 1, "appearances": 13},
    "Diogo Jota": {"goals": 7, "appearances": 28},
    "Luis Diaz": {"goals": 5, "appearances": 21}
  }
}

players = []
  
ans = ""

while (ans != 0):


Then iterate through the data object. For every element, grab the list of players by using the keys() method, and add it to players.
players = []
for season in data:
  players = players + list(data[season].keys())

  
ans = ""


Now, since players reappear in different seasons, we are going to have duplicates in the list players, and we'll need to remove them. If we create a dictionary from the keys of players. this automatically eliminates all duplicates (dictionaries cannot have duplicates).
players = []
for season in data:
  players = players + list(data[season].keys())
  
dict.fromkeys(players)
  
ans = ""


Then we put the result back in a list using the list() function...
players = []
for season in data:
  players = players + list(data[season].keys())
  
list(dict.fromkeys(players))
  
ans = ""


... and assign the value back to players. So now the list players has all unique values.
players = []
for season in data:
  players = players + list(data[season].keys())
  
players = list(dict.fromkeys(players))
  
ans = ""


And then use the sort() method on players.
players = []
for season in data:
  players = players + list(data[season].keys())
  
players = list(dict.fromkeys(players))
players.sort()
  
ans = ""


Now within the While loop, we iterate through the players list using a For loop. Since we want the index value, we will need to use the enumerate() function on players.
players = list(dict.fromkeys(players))
players.sort()
  
ans = ""

while (ans != 0):
  for i, p in enumerate(players):


Thrn we'll print out the options as we iterate.
while (ans != 0):
  for i, p in enumerate(players):
    print (str(i + 1) + ": " + p)


At the end of it, the option we print out is 0, to exit.
while (ans != 0):
  seasons = []
  
  for i, p in enumerate(players):
    print (str(i + 1) + ": " + p)
    
  print ("0: Exit")


And here, we ask the user to select from the list, using an input() function, and assign the value to ans.
while (ans != 0):
  seasons = []
  
  for i, p in enumerate(players):
    print (str(i + 1) + ": " + p)
    
  print ("0: Exit")

  ans = input("Select a player")


Before running the code, comment out the lineChart() function definition. We haven't written anything for it, so running the program will trigger an error.
#def lineChart(labels, vals, player):


There you go! A menu of players, nicely sorted in alphabetical order.


As before, we only want an integer value, so use the int() function here to force-convert the resultant value.
ans = int(input("Select a player"))


There's a possibility that the user will enter something non-numeric, so we encapsulate the entire thing in an infinite While loop and a Try-catch block.
while True:
  try:

    ans = int(input("Select a player"))
    break
  except:
    print("Invalid option. Please try again.")


After that, outside of the infinite While loop, we set the program to exit using the break statement if ans is 0. If the value of ans is numerical but invalid, we "restart" the While loop using the continue statement.
while True:
  try:
    ans = int(input("Select a player"))
    break
  except:
    print("Invalid option. Please try again.")  

if (ans == 0): break
if (ans > len(players) or ans < 0): continue


Let's try entering "hello". Obviously, it triggers the prompt ""Invalid option. Please try again."


Then we try a negative number. This "restarts" the While loop.


Then we try "0", and this quits the program altogether by exiting the outer While loop.


Now that we have a value, it's time to get other data. First, let's declare seasons as a list, within the first While loop.
while (ans != 0):
  seasons = []
  
  for i, p in enumerate(players):
    print (str(i + 1) + ": " + p)


Next, we define player by using the value of ans(minus 1 because counting starts from zero) as a reference to the players list. This will get you the name of the player chosen.
while (ans != 0):
  seasons = []
  
  for i, p in enumerate(players):
    print (str(i + 1) + ": " + p)
    
  print ("0: Exit")

  while True:
    try:
      ans = int(input("Select a player"))
      break
    except:
      print("Invalid option. Please try again.")  

  if (ans == 0): break
  if (ans > len(players) or ans < 0): continue
    
  player = players[ans - 1]


Then we declare stats as a dictionary object with goals and appearances as properties, both set to empty lists.
if (ans == 0): break
if (ans > len(players) or ans < 0): continue
  
player = players[ans - 1]
stats = { "goals": [], "appearances": []}


Now iterate through data using a For loop.
player = players[ans - 1]
stats = { "goals": [], "appearances": []}

for season in data:


We only want to operate on data that features the selected player. Thus we use an If block to check if player is in the list returned by running the keys() method on that current element in data.
for season in data:
  if player in data[season].keys():


If so, we append that particular season to seasons. But we'll want the proper season name instead, and that is where the seasonName() function comes in.
for season in data:
  if player in data[season].keys():
    seasons.append(seasonName(season))


We then add to stats. We want to append to the goals and appearances lists. Thus, if Luis Diaz was selected, the seasons 2021 and 2022 would be added to seasons. Then his goals and appearances for 2021 and 2022 would be appended to the goals and appearances properties of stats.
for season in data:
  if player in data[season].keys():
    seasons.append(seasonName(season))
    stats["goals"].append(data[season][player]["goals"])
    stats["appearances"].append(data[season][player]["appearances"])




Remember commenting out the call to lineChart()? Well, time to undo that action. We will next call lineChart() and use seasons, stats and player. seasons and player will be used to set labels and stats will provide values.
for season in data:
  if player in data[season].keys():
    seasons.append(seasonName(season))
    stats["goals"].append(data[season][player]["goals"])
    stats["appearances"].append(data[season][player]["appearances"])

lineChart(seasons, stats, player)


Next

Rendering the line chart with two sets of values.

No comments:

Post a Comment