The fall is a great time of year; the leaves are falling; it is getting colder outside and that means one thing: Football is back in season, specifically college football! College Football fans look forward to the season starting with the inters quad games that happen in the spring semester leading up to the season. One thing that is always important for their team’s success is how efficient their offense is. Without a successful offense, the team will not put up enough points to win or give their defense breathing room throughout the game. The goal of a college team is to earn a bowl game or playoff bid to bring their program and players to the national level to give them exposure before going into the NFL. NFL teams use the offensive efficiency data to see how players perform before they are potentially drafted, and help you see how successful programs were under that player’s leadership. Although the run game is very important in offensive production, unless you have a running back like Saquon Barkley or Lenoard Fornette on your team, the ground game numbers are very similar. For this reason, I wanted to uncover what aspects of a team’s offense air attack impacts how they perform and see how teams with current NFL superstars at the time have performed over the last 10 years.
The data set that I chose to use is the College Football Offensive Stats (2010-2020) data set from Brayden Rogowski:(https://www.kaggle.com/braydenrogowski/college-football-offensive-stats-20102020 ). This came as one singular .csv file filled with the raw data. I plan to use simple group-by and sort commands to make the transformations to the data set that I will need. I used the year column in most of my visualizations to track overall trends and team success in the past ten years. Some other columns that I used were Total Passing Yards: ‘YDS’, number of completions per year per team: ‘CMP’, quarterback rating: ‘RTG’, number of sacks and interceptions that the offense gave up (‘SACKS’,’ INT’) respectively that made up my visualizations.
import os
os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = 'D:/Anaconda/Library/plugins/platforms'
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import warnings
warnings.filterwarnings("ignore")
path = "C:/Users/Noah's PC/OneDrive - Loyola University Maryland/Documents/Fall 2021/IS460/DataFiles/R_datafiles/"
filename = path + 'College-Football-Original-Dataset .csv'
df = pd.read_csv(filename)
df.info
## <bound method DataFrame.info of UniversityName Year TeamID ... TOTAL_PLAYS RUN_PERCENT PASS_PERCENT
## 0 AirForce 2010 AirForce2010 ... 916 81.66% 18.34%
## 1 AirForce 2011 AirForce2011 ... 927 77.99% 22.01%
## 2 AirForce 2012 AirForce2012 ... 955 84.19% 15.81%
## 3 AirForce 2013 AirForce2013 ... 811 78.55% 21.45%
## 4 AirForce 2014 AirForce2014 ... 1002 79.44% 20.56%
## ... ... ... ... ... ... ... ...
## 1367 Wyoming 2016 Wyoming2016 ... 1007 62.36% 37.64%
## 1368 Wyoming 2017 Wyoming2017 ... 794 56.17% 43.83%
## 1369 Wyoming 2018 Wyoming2018 ... 771 64.20% 35.80%
## 1370 Wyoming 2019 Wyoming2019 ... 831 69.19% 30.81%
## 1371 Wyoming 2020 Wyoming2020 ... 409 67.24% 32.76%
##
## [1372 rows x 19 columns]>
td_df = pd.read_csv(filename, usecols =['Year','CMP','TD'])
cmp_td = td_df.groupby(['Year']).agg({'CMP':['sum'], 'TD':['sum']}).reset_index()
cmp_td.columns=['Year','Completions','Touchdowns']
#visualization
plt.figure(figsize=(18,10))
plt.scatter(cmp_td['Year'], cmp_td['Touchdowns'],marker='8',
cmap='viridis', c=cmp_td['Completions'],
s=cmp_td['Touchdowns'],edgecolors ='black')
plt.title('Number of Touchdowns and Completions per Year',fontsize=18)
plt.xlabel('Year', fontsize =14)
plt.xticks([*range(2010,2021)], [*range(2010,2021)], fontsize=14)
## ([<matplotlib.axis.XTick object at 0x000000002E814E20>, <matplotlib.axis.XTick object at 0x000000002E814DF0>, <matplotlib.axis.XTick object at 0x000000002E804940>, <matplotlib.axis.XTick object at 0x0000000030759CA0>, <matplotlib.axis.XTick object at 0x000000003077B130>, <matplotlib.axis.XTick object at 0x000000003077B640>, <matplotlib.axis.XTick object at 0x000000003077BB50>, <matplotlib.axis.XTick object at 0x00000000307820A0>, <matplotlib.axis.XTick object at 0x00000000307825B0>, <matplotlib.axis.XTick object at 0x0000000030782AC0>, <matplotlib.axis.XTick object at 0x000000003077B880>], [Text(2010, 0, '2010'), Text(2011, 0, '2011'), Text(2012, 0, '2012'), Text(2013, 0, '2013'), Text(2014, 0, '2014'), Text(2015, 0, '2015'), Text(2016, 0, '2016'), Text(2017, 0, '2017'), Text(2018, 0, '2018'), Text(2019, 0, '2019'), Text(2020, 0, '2020')])
plt.ylabel('Touchdowns',fontsize=14)
plt.ylim(1800,2900)
## (1800.0, 2900.0)
cbar= plt.colorbar()
cbar.set_label('Completions per Year', rotation=270, fontsize=14, color='black',labelpad=30)
my_colorbar_ticks = [*range(20000,int(cmp_td['Completions'].max()),1000)]
my_colorbar_tick_labels = [*range(20000,int(cmp_td['Completions'].max()),1000)]
my_colorbar_tick_labels = [ '{:,}'.format(each) for each in my_colorbar_tick_labels]
cbar.set_ticks(my_colorbar_ticks)
cbar.set_ticklabels(my_colorbar_tick_labels)
plt.show()
The first visualization is a scatterplot looking at total completions and touchdowns thrown by the offense over the past 10 seasons. As the visualization shows each year sees a general increase in the number of completions and touchdowns thrown. This is most likely due to the big-name quarterbacks that we see leading teams in the NFL currently, and having a highly efficient receiving corps. The interesting outlier on this scatterplot is of course 2020, which is dramatically low for both touchdowns and completions due to the abbreviated COVID season last fall.
#dataframe build
QBR_df=pd.read_csv(filename, usecols =['Year','RTG'])
cmp_percent=pd.read_csv(filename,usecols = ['Year','CMPPercent'])
QBR= QBR_df.groupby(['Year']).agg({'RTG':['mean']}).reset_index()
CMP=cmp_percent.groupby(['Year']).agg({'CMPPercent':['mean']}).reset_index()
CMP.columns=['Year','CMP_avg']
QBR.columns=['Year','QBR_avg']
#visualization
fig=plt.figure(figsize=(18,10))
x = CMP['Year']
y1= CMP['CMP_avg']
y2= QBR['QBR_avg']
x_pos=np.arange(11)
ax1 = fig.add_subplot (1, 1, 1)
ax2 = ax1.twinx()
cmp=ax1.plot(x,y1, linewidth = 2, color = 'Black', marker = '8', markersize= 12, markerfacecolor= 'orange')
qbr= ax2.plot(x,y2,linewidth = 3, marker ='s', markersize = 12, markerfacecolor = 'red')
ax1.set_xlabel('Year', fontsize = 18)
ax1.set_ylabel('Average Completion Percentage', fontsize = 18, labelpad=20)
ax2.set_ylabel('Average Quarterback Rating',fontsize = 18, rotation=270, labelpad=30)
plt.title('Average Quarterback Rating (QBR) and Average Completion Percentage \n (2010-2020)',fontsize=18)
ax1.tick_params(axis='y',labelsize=14)
ax2.tick_params(axis='y',labelsize=14)
plt.xticks([*range(2010,2021)], [*range(2010,2021)], fontsize=14)
## ([<matplotlib.axis.XTick object at 0x000000003080EF10>, <matplotlib.axis.XTick object at 0x000000003080E910>, <matplotlib.axis.XTick object at 0x0000000033B56100>, <matplotlib.axis.XTick object at 0x0000000030862760>, <matplotlib.axis.XTick object at 0x0000000030862BE0>, <matplotlib.axis.XTick object at 0x0000000030867070>, <matplotlib.axis.XTick object at 0x0000000030867580>, <matplotlib.axis.XTick object at 0x00000000308624C0>, <matplotlib.axis.XTick object at 0x0000000030867130>, <matplotlib.axis.XTick object at 0x0000000030867DF0>, <matplotlib.axis.XTick object at 0x0000000030870340>], [Text(2010, 0, '2010'), Text(2011, 0, '2011'), Text(2012, 0, '2012'), Text(2013, 0, '2013'), Text(2014, 0, '2014'), Text(2015, 0, '2015'), Text(2016, 0, '2016'), Text(2017, 0, '2017'), Text(2018, 0, '2018'), Text(2019, 0, '2019'), Text(2020, 0, '2020')])
for x_pos,y_pos in zip(x,y1):
label = "{:.1f}".format(y_pos)
ax1.annotate(label, # this is the text
(x_pos,y_pos), # these are the coordinates to position the label
textcoords="offset points", # how to position the text
xytext=(-10,-27), # distance from text to points (x,y)
fontsize=10,
backgroundcolor ='white',
ha='center',
va='bottom') # horizontal alignment can be left, right or center
for x_pos,y_pos in zip(x,y2):
label = "{:.1f}".format(y_pos)
ax2.annotate(label, # this is the text
(x_pos,y_pos), # these are the coordinates to position the label
textcoords="offset points", # how to position the text
xytext=(-5,15), # distance from text to points (x,y)
fontsize=10,
backgroundcolor ='white',
ha='right',
va='center') # horizontal alignment can be left, right or center
plt.show()
The second visualization is a dual-axis line chart that looks at a quarterback’s completion percentage and quarterback rating or QBR. A quarterbacks rating looks at their completion percentage, average yards per passing attempt, touchdown percentage, and percentage of interceptions thrown ( https://www.sportscasting.com/what-is-an-nfl-passer-rating-and-how-is-it-calculated/ ). Overall we see a general fluctuation in the Quarterback rating while the completion percentage sees a decrease at the beginning of the last decade, with a great improvement at the end towards the end. What was interesting to me when looking at this is that the Quarterback rating declined in 2020 with the COVID season, but the overall completion percentage increased from 2019. This might mean that there is a factor besides completion percentage in the QBR equation such as touchdown percentage or interception percentage that is weighed more.
# dataframe creation
off_line_df = pd.read_csv(filename, usecols = ['Year', 'SACK', 'INT'])
off_line_df.Year.astype(int)
## 0 2010
## 1 2011
## 2 2012
## 3 2013
## 4 2014
## ...
## 1367 2016
## 1368 2017
## 1369 2018
## 1370 2019
## 1371 2020
## Name: Year, Length: 1372, dtype: int32
Off=off_line_df.groupby(['Year']).agg({'SACK':['mean'], 'INT':['mean']})
Off.reset_index(inplace =True ,drop = False)
Off.SACK= round(Off.SACK,2)
Off.INT= round(Off.INT,2)
Off.SACK= Off.SACK.astype(int)
Off.INT= Off.INT.astype(int)
Off.columns=['Year', 'SACK', 'INT']
#visualization creation
def autolabel(these_bars,this_ax,place_of_decimals, symbol):
for each_bar in these_bars:
height = each_bar.get_height()
this_ax.text(each_bar.get_x()+each_bar.get_width()/2, height*1.01, symbol+format(height,place_of_decimals),
fontsize=11, color='black', ha='center', va='bottom')
fig=plt.figure(figsize=(18,10))
ax1 = fig.add_subplot (1, 1, 1)
ax2 = ax1.twinx()
bar_width = 0.4
d1= Off.loc[0:10]
x_pos = np.arange(11)
Sack_bars = ax1.bar(x_pos-(0.5*bar_width), d1.SACK, bar_width, color= 'Navy',edgecolor='black',label='Average Number of Sacks')
Int_bars = ax2.bar(x_pos+(0.5*bar_width),d1.INT, bar_width, color= 'Yellow',edgecolor='black',label='Average Number of Interceptions')
ax1.set_xlabel('Year', fontsize = 18)
ax1.set_ylabel('Average Number of Sacks', fontsize = 18, labelpad=20)
ax2.set_ylabel('Average Number of Interceptions',fontsize = 18, rotation=270, labelpad=20 )
ax1.tick_params(axis='y',labelsize=14)
ax2.tick_params(axis='y',labelsize=14)
plt.title('Average number of sacks and Interceptions per Year\n (2010-2020)',fontsize=18)
ax1.set_xticks(x_pos)
ax1.set_xticklabels(d1.Year, fontsize=14)
count_color,count_label =ax1.get_legend_handles_labels()
fine_color,fine_label= ax2.get_legend_handles_labels()
legend=ax1.legend(count_color + fine_color, count_label +fine_label, loc='upper right',frameon=True, ncol=1,shadow=True,
borderpad=1,fontsize=14)
ax1.set_ylim(0, d1.SACK.max()*1.5)
## (0.0, 40.5)
ax2.set_ylim(0, d1.INT.max()*2)
## (0.0, 24.0)
autolabel(Sack_bars, ax1, '.0f','')
autolabel(Int_bars,ax2, '.0f','')
plt.show()
Next, is a dual-axis bar chart looking at when the offense is throwing interceptions and allowing their quarterback to be sacked each year. From this data, we can see that the average number of sacks per team is almost double the average number of interceptions each year. We also see that while the average number of sacks remains somewhat constant, the average number of interceptions has slightly decreased in recent years. This shows that an offense’s O-line is so crucial to the team’s success because if they do not protect the quarterback the offense is twice as likely to lose yardage and downs due to the sack.
#dataframe creation
top_passing_teams_df =pd.read_csv(filename,usecols = ['UniversityName','YDS'])
c=0
for each in top_passing_teams_df['YDS']:
x = str(each).replace(",","")
top_passing_teams_df.loc[c, 'YDS'] = x
c+=1
#convert YDS to float or int
top_passing_teams_df.YDS = pd.to_numeric(top_passing_teams_df['YDS'])
top_passing_teams_df = top_passing_teams_df.groupby(['UniversityName']).agg({'YDS':['sum']}).reset_index()
top_passing_teams_df.columns =['University_Name','Total_Passing_Yards']
top_passing_teams= top_passing_teams_df
top_passing_teams = top_passing_teams.sort_values('Total_Passing_Yards', ascending = False)
top_passing_teams.reset_index(inplace=True, drop=True)
#mean calculation and color distribution
def pick_colors_according_to_mean_count(top_passing_teams):
colors= []
avg = top_passing_teams.Total_Passing_Yards.mean()
for each in top_passing_teams.Total_Passing_Yards:
if each > avg*1.01:
colors.append('navy')
elif each < avg*0.99:
colors.append('gold')
else:
colors.append('black')
return colors
#Visualization build
import matplotlib.patches as mpatches
bottom3 = 0
top3 = 19
d2 = top_passing_teams
d2 = d2.loc[bottom3:top3]
d2 = d2.sort_values('Total_Passing_Yards', ascending= True)
d2.reset_index(inplace=True, drop=True)
my_colors3 = pick_colors_according_to_mean_count(d2)
Above = mpatches.Patch(color='navy', label= 'Above Average')
At =mpatches.Patch(color='black', label= 'Within 1% of the Average')
Below =mpatches.Patch(color='gold', label= 'Below Average')
fig =plt.figure(figsize=(18,12))
ax1=fig.add_subplot(1, 1, 1)
ax1.barh(d2.University_Name, d2.Total_Passing_Yards, color= my_colors3)
## <BarContainer object of 20 artists>
for row_counter, value_at_row_counter in enumerate(d2.Total_Passing_Yards):
if value_at_row_counter > d2.Total_Passing_Yards.mean()*1.01:
color ='navy'
elif value_at_row_counter < d2.Total_Passing_Yards.mean()*0.99:
color ='gold'
else:
color ='black'
ax1.text(value_at_row_counter+2,row_counter, str(value_at_row_counter), color = color , size='12',
fontweight='bold', ha='left', va='center', backgroundcolor='white')
plt.xlim(0, d2.Total_Passing_Yards.max()*1.20)
## (0.0, 57566.4)
ax1.legend(loc='center right',handles=[Above,At,Below], fontsize=14)
plt.axvline(d2.Total_Passing_Yards.mean(), color='black',linestyle='dashed')
ax1.text(d2.Total_Passing_Yards.mean()+2, 0, 'Mean = ' + str(d2.Total_Passing_Yards.mean()),rotation=0, fontsize=14)
ax1.set_title('Top ' + str(20) + ' Teams with the Most Passing Yards\n (2010-2020)', size=20)
ax1.set_xlabel('Total Passing Yards', fontsize =16)
ax1.set_ylabel('University Name', fontsize = 16)
plt.xticks(fontsize=14)
## (array([ 0., 10000., 20000., 30000., 40000., 50000., 60000.]), [Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, '')])
plt.yticks(fontsize=14)
## ([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], [Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, ''), Text(0, 0, '')])
plt.show()
This horizontal bar chart looks at the top 20 college programs with the most passing yards over the last 10 years. For this visualization, wanted to categorize them as above the average, and below the average number of passing yards over the last 10 years which was around 40,300 yards total. This also can show how teams who are not in the same conference can compete with the more well-known schools. One interesting thing to note from this visualization is how important the quarterback is in the passing game. For example, Patrick Mahomes was the quarterback at Texas Tech from 2014-2016 where he threw for over 11,000 of the 47,000 yards the team had in the past 10 years or almost a quarter of the team’s total yards in the air ( https://www.sports-reference.com/cfb/players/patrick-mahomes-1.html ). As most of us know, Mahomes is now one of the best quarterbacks in the NFL, and this only backs up that statement.
#dataframe creation
big_10 =['Michigan','MichiganState','OhioState','Iowa','Wisconsin','PennState','Minnesota','Purdue','Nebraska','Illinois'
,'Rutgers','Northwestern','Maryland']
year = ['2010','2011','2012','2013','2014','2015','2016',
'2017','2018','2019','2020']
BIG_df= pd.read_csv(filename, usecols = ['Year','UniversityName','YDS'])
#filter on big ten schools only
big_10_df = BIG_df.loc[(BIG_df['UniversityName'] == 'Michigan')|(BIG_df['UniversityName'] =='MichiganState')
|(BIG_df['UniversityName'] =='OhioState')|(BIG_df['UniversityName'] =='Iowa')|(BIG_df['UniversityName'] =='Wisconsin')
|(BIG_df['UniversityName'] =='PennState')|(BIG_df['UniversityName'] =='Minnesota')|(BIG_df['UniversityName'] =='Purdue')
|(BIG_df['UniversityName'] =='Nebraska')|(BIG_df['UniversityName'] =='Illinois')|(BIG_df['UniversityName'] =='Rutgers')|(BIG_df['UniversityName'] =='Northwestern')|(BIG_df['UniversityName'] =='Maryland')]
big_10_df.reset_index(inplace=True,drop=True)
#getting rid of commas in YDS column
c=0
for each in big_10_df['YDS']:
x = str(each).replace(",","")
big_10_df.loc[c, 'YDS'] = x
c+=1
#convert YDS to float or int
big_10_df.YDS =pd.to_numeric(big_10_df['YDS'])
#create pivot table
big_10_bump_df = pd.pivot_table(big_10_df, index='UniversityName', columns='Year', values= 'YDS')
big_10_bump_ranked =big_10_bump_df.rank(0,ascending=False, method='min')
big_10_bump_ranked = big_10_bump_ranked.T
#Visualization Build
fig=plt.figure(figsize=(18,10))
ax= fig.add_subplot(1,1,1)
big_10_bump_ranked.plot(kind='line', ax=ax, marker='o',
markeredgewidth=1, linewidth=6, markersize=18,
markerfacecolor='white')
ax.invert_yaxis()
plt.ylabel('Passing Yards Ranking by University', fontsize=18, labelpad=10)
plt.title('Ranking of Big 10 Teams Passing Yards \n (2010-2020)', fontsize=18, pad=10)
plt.xticks([*range(2010,2021)], [*range(2010,2021)], fontsize=14)
## ([<matplotlib.axis.XTick object at 0x000000003198EE80>, <matplotlib.axis.XTick object at 0x000000003198EE50>, <matplotlib.axis.XTick object at 0x0000000031A46130>, <matplotlib.axis.XTick object at 0x0000000031A6CFA0>, <matplotlib.axis.XTick object at 0x0000000031A754F0>, <matplotlib.axis.XTick object at 0x0000000031A75A00>, <matplotlib.axis.XTick object at 0x0000000031A63850>, <matplotlib.axis.XTick object at 0x0000000031A755B0>, <matplotlib.axis.XTick object at 0x00000000319889D0>, <matplotlib.axis.XTick object at 0x0000000031A03A30>, <matplotlib.axis.XTick object at 0x0000000031A7C370>], [Text(2010, 0, '2010'), Text(2011, 0, '2011'), Text(2012, 0, '2012'), Text(2013, 0, '2013'), Text(2014, 0, '2014'), Text(2015, 0, '2015'), Text(2016, 0, '2016'), Text(2017, 0, '2017'), Text(2018, 0, '2018'), Text(2019, 0, '2019'), Text(2020, 0, '2020')])
plt.yticks([*range(1,14)], [*range(1,14)], fontsize=14)
## ([<matplotlib.axis.YTick object at 0x00000000319B5760>, <matplotlib.axis.YTick object at 0x00000000319B5340>, <matplotlib.axis.YTick object at 0x000000003198E190>, <matplotlib.axis.YTick object at 0x0000000031A7CEE0>, <matplotlib.axis.YTick object at 0x0000000031A84400>, <matplotlib.axis.YTick object at 0x0000000031A848E0>, <matplotlib.axis.YTick object at 0x0000000031A84E20>, <matplotlib.axis.YTick object at 0x0000000031A8A340>, <matplotlib.axis.YTick object at 0x0000000031A84A30>, <matplotlib.axis.YTick object at 0x0000000031A7CB50>, <matplotlib.axis.YTick object at 0x00000000319B5F70>, <matplotlib.axis.YTick object at 0x0000000031A8A6A0>, <matplotlib.axis.YTick object at 0x0000000031A8ABB0>], [Text(0, 1, '1'), Text(0, 2, '2'), Text(0, 3, '3'), Text(0, 4, '4'), Text(0, 5, '5'), Text(0, 6, '6'), Text(0, 7, '7'), Text(0, 8, '8'), Text(0, 9, '9'), Text(0, 10, '10'), Text(0, 11, '11'), Text(0, 12, '12'), Text(0, 13, '13')])
plt.xlabel('Year', fontsize=18, labelpad=10)
handles,labels =ax.get_legend_handles_labels()
handles= [handles[0],handles[1],handles[2],
handles[3],handles[4],handles[5],handles[6],handles[7],handles[8]
,handles[9],handles[10],handles[11],handles[12]]
labels =[labels[0],labels[1],labels[2],labels[3],labels[4],labels[5]
,labels[6],labels[7],labels[8],labels[9],labels[10],labels[11],labels[12]]
ax.legend(handles,labels, bbox_to_anchor=(1.01, 1.01),fontsize=14,
labelspacing=1,
markerscale=.4,
borderpad=1,
handletextpad=0.8)
plt.show()
Finally, I wanted to take a deeper look at a specific conference in division 1 football. I decided to go with the big ten conference which has the long storied teams of Penn State, Michigan, and the Ohio State University. I decided to rank each team’s offense based on their passing ratings as this conference has seen successful quarterbacks such as Drew Brees, Tom Brady, and Justin Fields to name a few currently playing in the NFL, and their team’s air attacks are very similar. From this visualization, you can see that the rivalry of Penn State and Ohio State shines through here as both of them battle for the number one ranking multiple times. One interesting part that I noticed from this visualization is that Rutgers has been consistently near or at the bottom of the ranking since 2016. This is due to their difficulty performing in the Big 10 since joining the conference in 2014, which is where their team began to decline. This is most likely due to their inability to compete with the top-notch caliber teams in the conference.
In conclusion, when looking at college football over the last ten years, we can see how important the air attack is to a team’s ability to be successful while on offense. People see the quarterback as the most important player on the team, and these visualizations back up that statement. More than ever the game of football from college onward is dependent on the success of the passing game. This is why we also see colleges focusing on the air game more as a way to groom their quarterbacks into the future NFL field generals that they aspire to be.