Eigenranking

edited March 2020 in Assignments

(15 pts)

Use an eigenvalue analysis to rank your favorite team’s conference in your favorite season. The conference should have at least 4 teams but, as you’ll be entering a matrix by hand, I recommend that it not be too large. Be sure to include the source of your data and all the code you use to produce your answer. You might double check to make sure that your code runs when copied from our forum into a Jupyter notebook.

No favorite team?!!? Well, one good source is Pro Football Reference. For example, the Cleveland Brown's best season in my lifetime was 1986. I can examine that year on Pro Football Reference's summary of 1986. I'm not a particular big NFL fan (because my team is no good) but it's nice because it's broken into such small divisions, all of which are fair game. Looking at the AFC Central that year, I see that it was composed of the following teams:

  • Cleveland Browns
  • Cincinnati Bengals
  • Pittsburgh Steelers
  • Houston Oilers

Following the links for each team, I can find the total number of points they scored against each other to get the following matrix:

$$\left(\begin{matrix}
0 & 47 & 64 & 23 \\
33 & 0 & 33 & 59 \\
55 & 52 & 0 & 43 \\
30 & 60 & 26 & 0
\end{matrix}\right).$$

Now, I can proceed with my analysis.

Comments

  • edited March 2020

    My favorite team is the Carolina Panthers, so I decided to look at their 2015 Season (15-1 record). Their division (NFC South) consists of the following teams:

    • Carolina Panthers
    • Atlanta Falcons
    • New Orleans Saints
    • Tampa Bay Buccaneers

    Looking at each time those teams played each other and recording the sum of the scores, we find the following matrix:

    $$\begin{pmatrix}
    0 & 51 & 68 & 75 \\
    20 & 0 & 38 & 39 \\
    60 & 51 & 0 & 43 \\
    33 & 46 & 43 & 0
    \end{pmatrix}$$

    To start our analysis, we first need to import the necessary libraries:

    import numpy as np
    from scipy.linalg import eig
    

    We can then enter the above matrix into Python, and call it M. We will also create a list of each team in the NFC South, ordered by the rank of their overall record on the season

    M = np.matrix([
        [0, 51, 68, 75],
        [20, 0, 38, 39],
        [60, 51, 0, 43],
        [33, 46, 43, 0]
    ])
    
    NFCSouth = [
        'Carolina', 'Atlanta', 'New Orleans', 'Tampa Bay'
    ]
    

    Then, from using the code found in EigenRankings, we input the following code to solve for our eigenvalues:

    vals, vecs = eig(M)
    vals
    
    Out:  array([139.03766418+0.j, -63.67242693+0.j, -31.11030941+0.j,
           -44.25492784+0.j])
    

    We then find the absolute value of each eigen value, and use the argsort command to order them properly:

    vec = abs(vecs[:,0])
    ranking = np.argsort(vec).tolist()
    ranking.reverse()
    ranking
    
    Out: [0, 2, 3, 1]
    

    This tells us that, within the division, the NFC South would ranked in the following manner:

    • Carolina
    • New Orleans
    • Tampa Bay
    • Atlanta

    This can also be expressed by using the code

    [NFCSouth[i] for i in ranking]
    

    We can then use [vec[i] for i in ranking] to find the strength of each team within the division

    Out:
    [0.6282590242040226,
     0.5369215681005994,
     0.4338819178062993,
     0.3588207207162519]
    

    I think it's interesting to see that while Atlanta had the second best record overall, they statistically were the worst team in the division. However, we we juts take a look at their divisional records, we find that they would be ordered

    • Carolina (5-1)
    • New Orleans/Tampa Bay (both 3-3, with a tie at cumulative scores as well)
    • Atlanta (1-5)

    Which follows closer to what we found in the above analysis. This would then get into the strengths of each team's overall schedule which would involve a 32x32 matrix, and would be a little more hairy.

    mark
  • edited April 2020

    Not having a favorite team, I simply chose the the 2019 NFL AFC North season; the teams are:

    1) Baltimore Ravens
    2) Pittsburgh Steelers
    3) Cleveland Browns
    4) Cincinnati Bengals

    Creating a matrix corresponding to the sum of the points earned against one another gives us,

    $$
    \begin{pmatrix}
    0 & 54 & 56 & 72 \\
    33 & 0 & 27 & 43 \\
    55 & 34 & 0 & 50 \\
    30 & 13 & 52 & 0
    \end{pmatrix}
    $$

    Import libraries,

    import numpy as np
    from scipy.linalg import eig
    

    Create matrix and list of teams,

    A = np.matrix([
        [0, 54, 56, 72],
        [33, 0, 27, 43],
        [55, 34, 0, 50],
        [30, 13, 52, 0]
    ])
    
    AFC_North = [
        'Baltimore', 'Pittsburgh', 'Cleveland', 'Cincinnati'
    ]
    

    Determine eigenvalues/vectors and print eigenvalues,

    Eval, Evec = eig(A)
    Eval
    

    $$
    [ 128.68530542+0.j , -48.45547934+5.80447665j , -48.45547934-5.80447665j , -31.77434673+0.j ]
    $$

    Determine order of the absolute values for each eigenvalue,

    vec = abs(Evec[:,0])
    ranking = np.argsort(vec).tolist()
    ranking.reverse()
    ranking
    

    $$[0, 2, 1, 3]$$

    List teams in the same order,

    [AFC_North[i] for i in ranking]
    

    $$[ Baltimore, Cleveland, Pittsburgh, Cincinnati ]$$

    Determine relative strengths according to the eigenvector,

    [vec[i] for i in ranking]
    

    $$
    [0.6260993289458243,
    0.5309009843399298,
    0.4061133811064022,
    0.4015167453628903]
    $$

    mark
  • edited March 2020

    I chose to do an eigneranking for the 2007 NFL AFC East. The teams in the AFC East are

    • New England Patriots
    • Buffalo Bills
    • New York Jets
    • Miami Dolphins

    In 2007, New England went undefeated at 16-0, and the other teams were significantly less fortunate with records of 7-9, 4-12, and 1-15, in the same order listed above. Using the scores of these teams I created the matrix
    $$
    M=
    \begin{bmatrix}
    0 & 94 & 58 & 77 \\
    17 & 0 & 30 & 51 \\
    24 & 17 & 0 & 71 \\
    35 & 27 & 41 & 0
    \end{bmatrix}
    $$
    This matrix keeps the same ordering where each row represents the team that scored the points and each column are the points that were scored against the other team. I put this in python.

    import numpy as np
    from scipy.linalg import eig
    AFC_East = ['Patriots', 'Bills', 'Jets', 'Dolphins']
    M = [
        [0,94,58,77],
        [17,0,30,51],
        [24,17,0,71],
        [35,27,41,0]
    ]
    

    Then I used eig to calculate the eigenvalues and eigenvectors.

    eigenValues, eigenVectors = eig(M)
    eigenValues
    output:
    array([125.97665073+0.j        , -42.0047295 +9.85282474j,
           -42.0047295 -9.85282474j, -41.96719174+0.j        ])
    

    The first eigenvalues will be used to rank the teams since its positive and has no imaginary component.
    I created a new vector to hold the first eigenvector and used argsort

    rankVector = abs(eigenVectors[:,0])
    ranking = np.argsort(rankVector).tolist()
    ranking.reverse()
    ranking
    output:
    [0, 2, 3, 1]
    

    to rank the teams' strength.

    [AFC_East[i] for i in ranking]
    output:
    ['New England', 'New York', 'Miami', 'Buffalo']
    

    Finally the number representing each team's strength is given by

    [rankVector[i] for i in ranking]
    output:
    [0.7196085363125204,
     0.42024788618313785,
     0.41496836969159956,
     0.3651801210091427]
    
    mark
  • @PrinceHumperdinck That's great! I did make one change by adding a link that points directly at the season in question. Thus, to get:

    I entered

    the [2007 NFL AFC East](https://www.pro-football-reference.com/years/2007/)
    
  • @Opie and @joshua Your responses look really good - I'm so close to pushing that button! It'd be nice to have a reference to the data that you used. My response to PrinceHumberdinck shows how to do that.

  • edited March 2020

    For no reason in particular, I chose to study the 2012 AFC South.
    The teams in this division are the Houston Texans, the Indianapolis Colts, the Tennessee Titans, and the Jacksonville Jaguars. Why a team from Indianapolis is in the South division is beyond me. I'm not in charge.

    I imported the necessary libraries, set up a list with the team names and matrix with values for how many points each team scored against the others. Each row contains the points scored by each team. Each column contains points conceded.

    import numpy as np
    from scipy.linalg import eig
    AFC_South = ['Texans', 'Colts', 'Titans', 'Jaguars']
    M = [
        [0,45,62,70],
        [45,0,46,44],
        [24,36,0,57],
        [44,32,44,0]
    ]
    

    Next, I used the eig() function to determine the eigenvalues for this matrix. Recall that each eigenvalue will be associated with the given team. We will also just examine the absolute value of each one.

    eVals,eVecs = eig(M)
    eVals
    #Output:array([134.79019756+0.j        , -49.2470303 +1.78141834j,
    #     -49.2470303 -1.78141834j, -36.29613695+0.j        ]) 
    

    Next I used argsort() to sort the entries, and then reverse to switch the order, so the best performing team is listed in the 0th position.

    ranks = np.argsort(abs(eVecs[:,0])).tolist()
    ranks.reverse()
    [AFC_South[i] for i in ranks]
    #Output: ['Texans', 'Colts', 'Jaguars', 'Titans']
    

    Finally, to examine each team's rank:

    [abs(eVecs[:,0])[i] for i in ranks]
    # Output: [0.6004355492984136,
    # 0.49624916950250064,
    # 0.4547539615126675,
    # 0.4317554254382059]
    
    mark
  • dandan
    edited April 2020

    I'm from the Illinois. So I did my thing about the 1969 49ers: 1969 NFL Costal

    I set up my matrix as a # of points by these teams (left to right and up to down):
    LA Rams, Baltimore Colts, ATL Falcons, San Fran:
    (Side note: Didn't know Bal had the Colts at one point)

    $$\begin{pmatrix}
    0 & 34 & 55 & 68 \\
    33 & 0 & 34 & 38 \\
    13 & 20 & 0 & 45 \\
    51 & 44 & 19 & 0
    \end{pmatrix}$$

    What this looked liked in Java:

    NFL_Costal = [
    'LA Rams', 'Baltimore Colts', 'ATL Falcons', 'San Fran 49ers']
    
    M = np.matrix([
    [0, 34, 55, 68],
    [33, 0, 34, 38],
    [13, 20, 0, 45],
    [51, 44, 19, 0]
    ])
    

    Using Java to solve for the eigenvalues:

    import numpy as np
    from scipy.linalg import eig
    eVals,eVecs = eig(M)
    eVals
    
    Out: array([113.52438543 +0.j        , -41.90998217+11.38050813j,
       -41.90998217-11.38050813j, -29.7044211  +0.j        ])
    

    Assigning Teams to their respected eigenvalues and sorting those eigenvalues:

    ranks = np.argsort(abs(eVecs[:,0])).tolist()
    ranks.reverse()
    [NFL_Costal[i] for i in ranks]
    
    Out: ['LA Rams', 'San Fran 49ers', 'Baltimore Colts', 'ATL Falcons']
    

    The "s" strength of the eigenvalue:

    [abs(eVecs[:,0])[i] for i in ranks]
    
    Out:
    [0.6231054927277032,
    0.5191067170247625,
    0.4622768789711547,
    0.35856359051223774]
    

    Of note: our teams LA, Bal, ATL, and SF placed for the season as 4th, 9th, 10th, 13th, respectively. SF did second best in our list, but placed the lowest. Taking a quick look at their games through the entire season, its evident that they had no trouble putting points on the board, but ended it all with 4 wins 8 losses and 2 ties.

    In terms of just these 4 teams: SF had the second most points between them, but tied for least wins with ATL. Therefore, this particular method is a better representation of a team's ability to score points rather than a win; many more factors need to be accounted for in the matrix formulation to make this a more accurate model.

    mark
  • @dan Love it! Love the pic! Love the coastal! Just love it!

    Not sure what "the Illinois" has to do with it (anti-coastal?) but it's great.

  • I decided to rank the NFC West from 1984, which included the 49er's, the Rams, the Saints, and the Falcons. I gathered the wins and losses from the link below:
    https://www.pro-football-reference.com/years/1984/

    The result (using the order shown above from top to bottom) gave the following matrix:

    $$
    A = \left(
    \begin{array}{cccc}
    0 & 33 & 30 & 14 \\
    0 & 0 & 28 & 28 \\
    20 & 10 & 0 & 281 \\
    5 & 30 & 36 & 0 \\
    \end{array}
    \right).
    $$

    In this matrix, each entry represents the amount of points scored by a team described by a row against a different team in a column. It can be noted that the diagonal is all 0's as a team cannot score against itself. The first step will be calculating the eigenvalues of the matrix:

    import numpy as np
    from scipy.linalg import eig
    
    Scores = [
        [0, 33, 30, 14],
        [0, 0, 28, 28],
        [20, 10, 0, 28],
        [5, 30, 36, 0]
    ]
    
    eig(Scores)
    

    After running eig(), the following results were generated:

    (array([ 63.63096241+0.j       , -14.41539153+6.3070947j,
            -14.41539153-6.3070947j, -34.80017936+0.j       ]),
     array([[-0.56338676+0.j        , -0.28789742-0.33309294j,
             -0.28789742+0.33309294j,  0.30114537+0.j        ],
            [-0.43620806+0.j        ,  0.60887854+0.j        ,
              0.60887854-0.j        ,  0.04991445+0.j        ],
            [-0.47348793+0.j        , -0.59083611+0.06660885j,
             -0.59083611-0.06660885j, -0.70366087+0.j        ],
            [-0.51780988+0.j        ,  0.27736388+0.0705431j ,
              0.27736388-0.0705431j ,  0.64162402+0.j        ]]))
    

    At this point, the command argsort will be used to rank the eigenvalues.

    vals, vecs = eig(Scores)
    vec = abs(vecs[:,0])
    ranking = np.argsort(vec).tolist()
    ranking.reverse()
    ranking
    

    The output, ranking gives the following:

    [0, 3, 2, 1]
    

    This is the ranking of each row based on the eigenvalues calculated earlier. The next step is to substitute in the names of each team and to check the relative strengths of the rankings based on their eigenvectors:

    nfc_west_1984 = ['49ers', 'Rams', 'Saints', 'Falcons']
    
    [nfc_west_1984[i] for i in ranking]
    [vec[i] for i in ranking]
    

    Which gives the outputs:

    ['49ers', 'Falcons', 'Saints', 'Rams']
    
    [0.5633867633255967,
     0.5178098778092245,
     0.47348792594239686,
     0.4362080573986129]
    
    mark
  • I've grown up watching the NYJ, never watching them win. So decided to go back to 1968, when they beat the Raiders in the Super Bowl 16-7.

    This is the matrix corresponding to the AFL - East division in 1968 which had the teams ranked from high to low - NYJ, Houston Oilers, Miami Dolphins, Boston Patriots, Buffalo Bills.

    \begin{pmatrix}
    0 & 46 & 66 & 95 & 60 \\
    21 & 0 & 31 & 61 & 65 \\
    24 & 34 & 0 & 72 & 35 \\
    45 & 17 & 17 & 0 & 34\\
    58 & 13 & 31 & 13 & 0
    \end{pmatrix}

    Started by loading in the correct libraries

    import numpy as np
    from scipy.linalg import eig
    

    Then creating the matrix

    M=np.matrix([
    [0,46,66,95,60],
    [21,0,31,61,65],
    [24,34,0,72,35],
    [45,17,17,0,34],
    [58,13,31,13,0]
    ])
    
    AFLEast=['Jets', 'Oilers','Dolphins','Patriots','Bills']
    

    To find the eigen values we use

    vals, vecs=eig(M) 
    

    which produced

    array([162.05232288 +0.j        , -41.83613392+23.30124152j,
       -41.83613392-23.30124152j, -50.06889798 +0.j        ,
       -28.31115705 +0.j        ])
    

    In order to get these ranked we use the follow code,

    vec = abs(vecs[:,0])
    ranking = np.argsort(vec).tolist()
    ranking.reverse()
    ranking
    

    To get an easy read,

    [AFLEast[i] for i in ranking]
    
    ['Jets', 'Oilers', 'Dolphins', 'Bills', 'Patriots']
    

    This is only slightly off from their original rankings, with the swap of the Bills and Patriots

    and for the strength of the values we have

    [vec[i] for i in ranking]
    
    [0.6258922818083602,
    0.434437122915214,
    0.41350196906907133,
    0.3651899298340478,
    0.3393754182755129]
    
    mark
  • edited April 2020

    I'm not much of a sports fan, so I chose to analyze an NFL division. The small division sizes, relatively small number of games played, and the uniform distribution of games within the division makes this analyses easier as compared to other sports. I picked the NFC East division since my dad is a New York Giants (or as he would say, New Jersey Giants) fan. And I went with the the 2011 Season, since that was the last time the Giants won the Super Bowl, which had that totally absurd and hilarious touchdown at the end.

    In order to use eigen-rankings to analyse the data, I first had to add up all the points that each team in the division scored against every other team in the division. The teams are indexed in the following order: New York Giants, Philadelphia Eagles, Dallas Cowboys, and Washington Redskins, which resulted in the matrix,
    $$M=\begin{pmatrix}
    0&39&68&24\\
    33&0&54&54\\
    48&14&0&45\\
    51&23&40&0\end{pmatrix}.$$
    From here, I went over to Python to compute the eigenvalues and eigenvectors of the matrix.
    First, I imported the appropriate libraries,

    import numpy as np
    from scipy.linalg import eig
    

    and entered my matrix,

    M = np.matrix([
        [0, 39, 68, 24],
        [33, 0, 54, 54],
        [48, 14, 0, 45],
        [51, 23, 40, 0]
    ])
    

    Next, I setup the index for the teams,

    NFCE = [
        'New York Giants',
        'Philadelphia Eagles',
        'Dallas Cowboys',
        'Washington Redskins'
    ]
    

    Then, I calculated the the eigenvalues and eigenvectors,

    vals, vecs = eig(M)
    

    which give the following approximate eigenvalues:
    $$121, -46.6+17.8i, -46.6-17.8i, \text{and} -27.9.$$
    Using the argsort()function, and applying the team index to the result,

    vec = abs(vecs[:,0])
    ranking = np.argsort(vec).tolist()
    ranking.reverse()
    [NFCE[i] for i in ranking]
    

    the eigen-ranking for the division is found to be,

    1. Philadelphia Eagles
    2. New York Giants
    3. Washington Redskins
    4. Dallas Cowboys

    Laslty, by reordering the eigenvector used for the ranking,
    [vec[i] for i in ranking]
    

    we find each teams approximate strength according the eigen-ranking,

    1. Philadelphia Eagles = 0.552
    2. New York Giants = 0.522
    3. Washington Redskins = 0.472
    4. Dallas Cowboys = 0.446

    .

    mark
  • BenBen
    edited April 2020

    I know nothing of football. While I do throw a Super-Bowl part every year, it's purely for the opportunity to grill out and watch the flashy half-time show. That said, my brother patiently explained to me about conferences and divisions. I went with a highlight season from his favorite childhood team, NFC East of 1995 champs Dallas Cowboys:

    • Dallas Cowboys
    • Philadelphia Eagles
    • Washington Redskins
    • New York Giants
    • Arizona Cardinals

    Tallying regular season points scored into a matrix, where the Cowboys are team 0, and the Cardinals team 5 (across both axis), we get:
    $$M =
    \begin{pmatrix}
    0 & 51 & 40 & 56 & 71\\
    32 & 0 & 51 & 45 & 52\\
    51 & 41 & 0 & 28 & 47\\
    20 & 33 & 44 & 0 & 37\\
    33 & 39 & 31 & 27 & 0
    \end{pmatrix}
    $$

    While the Cowboys went on to win the playoffs, what would an eigen-ranking tell us about relative team strength during the regular season?

    First, lets setup our environment in python:

    from scipy.linalg import eig
    import numpy as np
    

    And import our team roster and season data:

    nfce = ['Cowboys','Eagles','Redskins','Giants','Cardinals']
    
    ssn = np.matrix([
        [0, 51, 40, 56, 71],
        [32, 0, 51, 45, 52],
        [51, 41, 0, 28, 47],
        [20, 33, 44, 0, 37],
        [33, 39, 31, 27, 0]
        ])
    

    We then process our matrix into eigen-values and eigen-vectors:

    # Returns eigenvalues, matrix whose cols are eigenvecs.
        # +*.j denotes complex part (conjugation).
    vals, vecs = eig(ssn)
    
    vals:
    [ 163.37 + 0.j, -39.71+16.83j, -39.71 - 16.83j, -36.734 + 0.j, -47.22 + 0.j ]
    
    vecs:
    [[ 0.54565269+0.j          0.20425645-0.48380512j  0.20425645+0.48380512j
       0.35525511+0.j         -0.19512309+0.j        ]
     [ 0.46906768+0.j          0.16884321+0.27267399j  0.16884321-0.27267399j
      -0.10408335+0.j         -0.6660705 +0.j        ]
     [ 0.45768678+0.j         -0.60569265+0.j         -0.60569265-0.j
      -0.21773808+0.j          0.32045864+0.j        ]
     [ 0.3686188 +0.j          0.40185389+0.27393317j  0.40185389-0.27393317j
       0.71563527+0.j         -0.2241492 +0.j        ]
     [ 0.36997519+0.j         -0.09658258-0.09292506j -0.09658258+0.09292506j
      -0.55083278+0.j          0.60443401+0.j        ]]
    

    Extract our dominant eigen-vector, and sort our data based on overall eigen-values:

        # Nab the idx of dominant eigVal
        maxIdx = np.where(vals == max(abs(vals)))[0][0]
    
        vec = abs(vecs[:, maxIdx])
        ranking = np.argsort(vec).tolist()
        ranking.reverse()
    
        teams = [nfce[i] for i in ranking]
        ranks = [vec[i] for i in ranking]
    

    This gives us an overall ranking of:

        for i in range(len(teams)):
            if (i == 0): print('\nTeams, ranked:')
            s = "{:<10}{:^4}{}".format(teams[i],':', ranks[i])
            print(s)
    
    Cowboys    :  0.545652687362274
    Eagles     :  0.4690676807605927
    Redskins   :  0.45768678462253326
    Cardinals  :  0.36997519401623846
    Giants     :  0.3686187985368365
    

    By points points scored, this tells us that the Cowboys maintained a significant margin against the other teams in the '95 NFCE. Interestingly, team rankings appear to be [roughly] evenly stratified. This is likely due to football's scheme of translating a single major scoring event (touchdown) into 6 points, and rewarding minor strategic advancement with fewer (1-3) points.

    mark
  • edited April 2020

    I've never been a huge football fan but I hail from western Pennsylvania so I guess my team would have to be the Steelers. I chose to analyze the 1975 AFC Central division, consisting of the following teams.

    • Cincinnati Bengals
    • Cleveland Browns
    • Houston Oilers
    • Pittsburgh Steelers

    Using the same order as above, I constructed the matrix
    $$
    M =
    \begin{bmatrix}
    0 & 47 & 44 & 38\\
    52 & 0 & 20 & 23\\
    38 & 61 & 0 & 28\\
    65 & 73 & 56 & 0
    \end{bmatrix}
    $$
    to represent the points scored by each team.
    Utilizing python

    from numpy import array, argsort
    from scipy.linalg import eig
    
    AFC_Central = ['Cincinnati Bengals ','Cleveland Browns   ','Houston Oilers     ','Pittsburgh Steelers']
    M = array([
        [0,47,44,38],
        [52,0,20,23],
        [38,61,0,28],
        [65,73,56,0]
    ])
    evals, evecs = eig(M)
    print(abs(evals))
    #output:
      [130.04127211  44.75590057  44.75590057  45.70635905]
    

    I found the dominant eigenvalue to be in the $0^{th}$ index. Therefore, the eigenvector that effectively ranks these teams should be the $0^{th}$ column of evec.

    vec = abs(evecs[:,0])
    rank = argsort(vec).tolist()
    rank.reverse()
    team_name = [AFC_Central[i] for i in rank]
    value = [vec[i] for i in rank]
    for i in range(4):
        print(team_name[i],':', value[i])
    #output:
    Pittsburgh Steelers : 0.6477938222363908
    Cincinnati Bengals  : 0.4798329213464976
    Houston Oilers      : 0.45636684389545046
    Cleveland Browns    : 0.37663355567097123
    
    mark
Sign In or Register to comment.