This is an experiment in using the Kotlin kernel for Jupyter

Note: this notebook was updated July 2021 to point to newer versions of its dependencies, which had become deprecated and were not allowing the notebook to complete successfully. It was also used for a presentation of Kotlin's Jupyter kernel in March 2021, so the 2020 season data, which didn't exist at the time the initial article was written, was added.

In [1]:
// two "supported" packages, we can skip the full dependency & import boilerplate
%use lets-plot, krangl
In [2]:
// csv is courtesy of pro-football-reference: https://www.pro-football-reference.com/years/NFL/passing.htm
val dfPassing = DataFrame.readCSV("nfl_passing.csv")
dfPassing
Out[2]:
RkYearTmsCmpAttCmp%YdsTDTD%IntInt%Y/AAY/AY/CY/GRateSkSkYdsNY/AANY/ASk%
1202032117561801865.21229578714.83952.27.27.211.1240.293.6113575436.426.45.9
2201932113311785363.51203017974.54102.37.27.111.4235.090.4127686106.296.26.7
3201832114621767164.91217378474.84192.47.47.311.4237.892.9128185306.426.36.8
4201732108561748862.11148707414.24302.57.06.811.3224.486.9119578106.155.96.4
5201632115261829563.01236397864.34152.37.27.011.4241.589.3111872256.376.25.8
6201532115271829863.01248438424.64362.47.37.111.5243.890.2118778506.416.36.1
7201432112001787962.61212478074.54502.57.27.011.5236.888.9121276516.356.16.3
8201332111021813661.21206268044.45022.87.16.811.6235.686.0129585516.215.96.7
9201232108331778860.91184187574.34682.67.16.711.6231.385.6116975336.255.96.2
10201132104641741060.11176017454.35062.97.26.712.0229.784.3118877296.325.96.4
11201032104911726960.81134507514.35113.07.06.511.5221.684.1113075146.175.76.1
12200932103721703360.91118517104.25253.17.06.411.5218.583.0110170666.175.66.1
13200832100811652661.01081776463.94652.86.96.511.4211.383.2103665896.165.75.9
14200732104251704561.21097227204.25343.16.96.311.2214.382.6110271526.055.56.1
1520063297961638959.81048616484.05203.26.96.211.5204.880.4116474165.975.46.6
1620053297901646459.51041686443.95063.16.86.211.4203.580.1118275535.905.36.7
1720043297721635459.81077977324.55243.27.16.511.8210.582.8119675416.145.66.8
1820033296951649358.81026286544.05383.36.66.011.3200.478.3109268395.845.26.2
19200232103141729259.61086616944.05283.16.76.111.3212.280.4117575405.885.36.4
2020013195421618159.01020806353.95453.46.86.011.5205.878.5119675595.875.26.9

... only showing top 20 rows

In [3]:
val mapPassing = dfPassing.filter { (it["Year"] lt 2021) AND (it["Year"] gt 1990) }.toMap()
mapPassing
Out[3]:
{Rk=[Ljava.lang.Integer;@2873d672, Year=[Ljava.lang.Integer;@3bc735b3, Tms=[Ljava.lang.Integer;@577f9109, Cmp=[Ljava.lang.Integer;@4303b7f0, Att=[Ljava.lang.Integer;@757529a4, Cmp%=[Ljava.lang.Double;@779de014, Yds=[Ljava.lang.Integer;@5c41d037, TD=[Ljava.lang.Integer;@2234078, TD%=[Ljava.lang.Double;@5ec77191, Int=[Ljava.lang.Integer;@4642b71d, Int%=[Ljava.lang.Double;@1450078a, Y/A=[Ljava.lang.Double;@c68a5f8, AY/A=[Ljava.lang.Double;@69c6161d, Y/C=[Ljava.lang.Double;@3aefae67, Y/G=[Ljava.lang.Double;@2e1792e7, Rate=[Ljava.lang.Double;@6719a5b8, Sk=[Ljava.lang.String;@3eb631b8, SkYds=[Ljava.lang.String;@796d3c9f, NY/A=[Ljava.lang.String;@6bff19ff, ANY/A=[Ljava.lang.String;@41e1455d, Sk%=[Ljava.lang.String;@4e558728}
In [4]:
val p = lets_plot(mapPassing) { x = "Year"; y = "AY/A" } + ggsize(640, 240)
p + geom_bar(stat=Stat.identity) +
    ggtitle("Avg Adjusted Yds/Attempt per NFL regular season")
Out[4]:
In [5]:
val p = lets_plot(mapPassing) { x = "Year"; y = "Rate" } + ggsize(640, 240)
p + geom_bar(stat=Stat.identity) +
    ggtitle("Total Combined Passer Rating per NFL regular season")
Out[5]:
In [6]:
val dfPassingRanges = dfPassing
    .filter { (it["Year"] lt 2021) AND (it["Year"] gt 1990) }
    .addColumn("YearRange") { it["Year"].map<Double>{ floor(it.minus(1).div(5.0)).times(5).plus(1).toInt() }}
    .addColumn("Years") { it["YearRange"].map<Int>{ "$it - ${it + 4}" }}

val mapPassingRanges = dfPassingRanges
    .select({ listOf("Year", "AY/A", "Rate", "YearRange", "Years") })
    .groupBy("YearRange", "Years")
    .summarize(
        "mean_AY/A" to { it["AY/A"].mean(removeNA = true) },
        "mean_Rate" to { it["Rate"].mean(removeNA = true) }
    ).toMap()
    
val xlimits = mapPassingRanges["Years"]?.toSet()?.reversed()?.filterNotNull()
In [7]:
val p = letsPlot(mapPassingRanges) { x = "Years"; y = "mean_AY/A" } + ggsize(720, 240)
p + geomBar(stat=Stat.identity) + scaleXDiscrete(limits = xlimits) +
    scaleYContinuous(limits = Pair(4.0, 8.0)) +
    ggtitle("Average Adjusted Yards/Attempt per NFL regular season")
Out[7]:
In [8]:
val p = letsPlot(mapPassingRanges) { x = "Years"; y = "mean_Rate" } + ggsize(720, 240)
p + geomBar(stat=Stat.identity) + scaleXDiscrete(limits = xlimits) +
    ggtitle("Total Combined Passer Rating per NFL regular season")
Out[8]:
In [ ]: