{"cells":[{"metadata":{"_uuid":"98858a1ed45e940234d8afa926da453cc4e97915"},"cell_type":"markdown","source":"**A Detailed Look at NFL Punts and Player Safety**"},{"metadata":{"_uuid":"2bc9b4b0b3cbe1847fbebefb3ed107aac82d8d6b"},"cell_type":"markdown","source":"**Our View of the Initial Challenge**\n\n* It’s impossible to completely eliminate punt related concussions and keep punting as a valuable part of the game.  Therefor rule changes are recommended to keep both the punts and fake punts part of the game while reducing concussions.  \n* By looking at the challenge data and provided videos it was clear that player velocity in conjunction with returnable longer punts is leading to concussions. \n* How do you slow down the velocity and force of an impact from the punting teams initial lineman/blockers on both the punt and the pursuit of the punt returner, or reduce the number of returns?\n    1. 20 of the 27 (74%) concussions from the punting team come from these 7 position players in blue in the image. \n    2. The velocity of the players in blue is greater due to the amount of time/ground they are allowed to run given their “free” break towards the punt returner.\n    3. Only 2 concussions occurred on punts with a fair catch, so we ideally want more fair catches to reduce concussions. \n\n![Formation](https://i.imgur.com/qHoJYAL.png)"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"markdown","source":"**Finding Correlation Between Time from the Play Snap to the Punt and Subsequent Punt Distance**\n\nA cornerstone of our proposed rule change is that shorter punts produce less concussions.  We found several proofs of this in the data, which I will explain now:\n\n1)  *Proof that longer punts correlate with more concussions*\nThe following JAVA code parses the punter and punt distances from the PlayDescription field and produces a clean CSV with aggregate totals for punt distances.\n\n"},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","collapsed":true,"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":false},"cell_type":"markdown","source":"private static void parsePuntDesc() {\n\t\ttry {\n\t\t\tBufferedReader br = new BufferedReader(new FileReader(\"<HOME>\\\\NFLPunts.txt\"));\n\t\t\tPrintWriter pw = new PrintWriter(\"<HOME>\\\\parsePunts.txt\");\n\t\t\ttry {\n\t\t\t    String line = br.readLine();\n\t\t\t    \n\t\t\t    while (line != null) {\n\t\t\t    \tString[] s = line.split(\" \");\n\t\t\t    \tint puntsToken = -1;\n\t\t\t    \tfor(int i=0;i<s.length-1;i++) {\n\t\t\t    \t\tif(s[i].equals(\"punts\")) {\n\t\t\t    \t\t\tpuntsToken = i;\n\t\t\t    \t\t}\n\t\t\t    \t}\n\t\t\t    \tif(puntsToken==-1) {\n\t\t\t    \t\tString punter = \"NA\";\n\t\t\t\t    \tString yards = \"NA\";\n\t\t\t\t    \tSystem.out.println(\"punter = \" + punter + \", yards = \" + yards);\n\t\t\t\t    \tpw.println(punter + \"\\t\" + yards);\n\t\t\t    \t} else {\n\t\t\t\t    \tString punter = s[puntsToken-1];\n\t\t\t\t    \tString yards = s[puntsToken+1];\n\t\t\t\t    \tSystem.out.println(\"punter = \" + punter + \", yards = \" + yards);\n\t\t\t\t    \tpw.println(punter + \"\\t\" + yards);\n\t\t\t    \t}\n\t\t\t        line = br.readLine();\n\t\t\t    }\n\t\t\t    pw.close();\n\t\t\t} finally {\n\t\t\t    br.close();\n\t\t\t}\n\t\t} catch(Exception e){e.printStackTrace();}\n\t}"},{"metadata":{"_uuid":"93ab3d20532ba6e6c92e6e7be9c718522d7aa192"},"cell_type":"markdown","source":"With the totals from the aggregate punt, we correlated the distance of punts that produced concussions and graphed the information using Excel.\n\n ![ConcurrentGraph](https://i.imgur.com/wHhLhUE.png)\n\nAs you can see, the trend observed by the smoothed moving average predicts a reasonably steady increase of concussive events as punt distances increase.\n\n2)  *Proof that punting the ball more quickly after the play snap ultimately decreases total punt distance.*\n\nFor this, the data from NGS documents in the competition data were loaded into SQL Server.  For each PlayID and GameKey, the amount of time between the snap and the punt can be found with this query:\n\nselect distinct time as punt_time, lag(time) over (order by time) as snap_time, datediff(MILLISECOND, lag(time) over (order by time),time) as punt_to_snap_time, pinfo.gamekey, pinfo.playid, playdescription, event from [NGS-2016-reg-wk1-6] ngs, play_information pinfo where pinfo.GameKey = ngs.GameKey and pinfo.playid= ngs.PlayID and (event = 'ball_snap' or event = 'punt')  order by time desc\n\nAlso note, the below query was used to derive player velocity at any given moment:\n\nselect  time, event, x, lag(x) over (order by time) as previous_x, y, lag(y) over (order by time) as previous_y, SQRT(POWER(cast(x as float)-cast(lag(x) over (order by time) as float),2)+POWER(cast(y as float)-cast(lag(y) over (order by time) as float),2)) * 10.0 as yards_per_second from [NGS-2016-reg-wk1-6] \nwhere GameKey = 144 and PlayID = 2342 and GSISID = 23259 order by time desc\n\nBy utilizing the below code, I've extracted this into the attached \"Time from snap to punt and distance\" data attachment with this Kernel.  In this code, I also correlate the distance of the punts associated with the time before the punter kicks it."},{"metadata":{"_uuid":"cb5e0be6775a87fa42c38774455411e5dd90bf20"},"cell_type":"markdown","source":"public static void main(String[] args) {\n\t\t try\n         {\n\t\t\t PrintWriter pw = new PrintWriter(\"c:\\\\jon\\\\timeAndDist.txt\");\n\t\t\t Class.forName(\"com.microsoft.sqlserver.jdbc.SQLServerDriver\");\n\n             String userName = \"sa\";\n             String password = \"password\";\n             String url = \"jdbc:sqlserver://localhost\\\\sqlexpress:1433;databaseName=NFL\";\n             Connection con = DriverManager.getConnection(url, userName, password);\n             Statement s1 = con.createStatement();\n             ResultSet rs = s1.executeQuery(\"select distinct time as punt_time, lag(time) over (order by time) as snap_time, datediff(MILLISECOND, lag(time) over (order by time),time) as punt_to_snap_time, pinfo.gamekey, pinfo.playid, playdescription, event from [NGS-2016-reg-wk1-6] ngs, play_information pinfo where pinfo.GameKey = ngs.GameKey and pinfo.playid= ngs.PlayID and (event = 'ball_snap' or event = 'punt')  order by time desc\");\n             \n             if(rs!=null){\n                 while (rs.next()){\n                     double puntToSnapTime = rs.getDouble(\"punt_to_snap_time\");\n                     String playDesc = rs.getString(\"playdescription\");\n                     if(puntToSnapTime > 1000 && puntToSnapTime < 5000) {\n                    \t String parsedDesc = parseDesc(playDesc);\n                    \t if(!parsedDesc.contains(\"NA\")) pw.println(parseDesc(playDesc) + \",\" + puntToSnapTime/1000.0 + \",\" + \"5\");\n                     }\n\n                 }\n             }\n             pw.close();\n             //String result = new result[20];\n\n         } catch (Exception e)\n         {\n             e.printStackTrace();\n         }"},{"metadata":{"_uuid":"fae1151811c5bb921ab4dc22eb6b4e406b1d4c33"},"cell_type":"markdown","source":"Now you can run a feedforward regressive model on the aforementioned data to see that the quicker a punter kicks after the snap, the shorter the punt tends to be (it is not a smoothed line...it is really just that linear).  Below is the code for running this in DL4J."},{"metadata":{"_uuid":"95f23ff7fc8bd91831b39dcfd171a42fd9e8ded9"},"cell_type":"markdown","source":"\n\npublic class PuntTimeToDistance {\n\n    public static final int seed = 12345;\n    public static final int nEpochs = 200;\n    public static final int nSamples = 833;\n    public static final int batchSize = 100;\n    public static final double learningRate = 0.01;\n    public static int MIN_RANGE = 0;\n    public static int MAX_RANGE = 100;\n\n    public static final Random rng = new Random(seed);\n\n    public static void main(String[] args){\n\n        //Generate the training data\n        DataSetIterator iterator = getTrainingData(batchSize,rng);\n\n        MultiLayerNetwork net = new MultiLayerNetwork(new NeuralNetConfiguration.Builder()\n                .seed(seed)\n                .weightInit(WeightInit.XAVIER)\n                .updater(new Nesterovs(learningRate, 0.9))\n                .list()\n                .layer(0, new DenseLayer.Builder().nIn(2).nOut(50)\n                        .activation(Activation.SIGMOID)\n                        .build())\n                .layer(1, new DenseLayer.Builder().nIn(50).nOut(5)\n                        .activation(Activation.RELU)\n                        .build())\n                .layer(2, new OutputLayer.Builder(LossFunctions.LossFunction.MSE)\n                        .activation(Activation.IDENTITY)\n                        .nIn(5).nOut(1).build())\n                .build()\n        );\n        net.init();\n        net.setListeners(new ScoreIterationListener(1));\n\n\n        //Train the network on the full data set, and evaluate in periodically\n        for( int i=0; i<nEpochs; i++ ){\n            iterator.reset();\n            net.fit(iterator);\n        }\n\n        double[] xVal = new double[15];\n        double[] yVal = new double[15];\n        int counter = 0;\n        \n        for(double x=1.5; x<=3.0; x += .1) {\n\t        final INDArray input = Nd4j.create(new double[] { x,5 }, new int[] { 1,2 });\n\t        INDArray out = net.output(input, false);\n\t        System.out.println(\"At \" + x + \" seconds, yards = \" + Double.parseDouble(out.toString())*10.0);\n\t        xVal[counter] = x;\n\t        yVal[counter] = Double.parseDouble(out.toString())*10.0;\n\t        counter++;\n        }\n        plot(xVal, yVal);\n        \n\n    }\n\n    private static DataSetIterator getTrainingData(int batchSize, Random rand){\n    \tfinal String filenameTrain  = \"<HOME>\\\\timeAndDist.txt\";\n\n    \tdouble [] yards = new double[nSamples];\n        double [] time = new double[nSamples];\n        double [] offset = new double[nSamples];\n        \n        try {\n        \tBufferedReader br = new BufferedReader(new FileReader(filenameTrain));\n\t\t    String line = br.readLine();\n\t\t    int i = 0;\n\t\t    while (line != null) {\n\t\t    \tString[] s = line.split(\",\");\n\t\t    \t\n\t\t    \ttime[i] = Double.parseDouble( s[1]);\n\t            offset[i] =  Double.parseDouble( s[2]);\n\t            yards[i] = Double.parseDouble( s[0])/10.0;\n\t\t    \t\n\t\t    \ti++;\n\t\t    \tline = br.readLine();\n\t\t    }\n\t\t    br.close();\n        } catch(Exception e) {e.printStackTrace();}\n       \n        INDArray inputNDArray1 = Nd4j.create(time, new int[]{nSamples,1});\n        INDArray inputNDArray2 = Nd4j.create(offset, new int[]{nSamples,1});\n        INDArray inputNDArray = Nd4j.hstack(inputNDArray1,inputNDArray2);\n        INDArray outPut = Nd4j.create(yards, new int[]{nSamples, 1});\n        DataSet dataSet = new DataSet(inputNDArray, outPut);\n        List<DataSet> listDs = dataSet.asList();\n        Collections.shuffle(listDs,rng);\n        return new ListDataSetIterator(listDs,batchSize);\n\n\n    }\n    \n    private static DataSetIterator getTestData(int batchSize, Random rand){\n    \tfinal String filenameTrain  = \"<HOME>\\\\timeAndDist.txt\";\n\n        //Load the training data:\n        RecordReader rr = new CSVRecordReader();\n        DataSetIterator trainIter = null;\n        try {\n        rr.initialize(new FileSplit(new File(filenameTrain)));\n        trainIter = new RecordReaderDataSetIterator(rr,batchSize,0,2);\n        } catch(Exception e) {e.printStackTrace();}\n        return trainIter;\n\n    }\n    \n    private static void plot(final double[] x, final double[] y) {\n        final XYSeriesCollection dataSet = new XYSeriesCollection();\n        final XYSeries s = new XYSeries(\"NGS Data\");\n        for(int i=0;i<x.length;i++) {\n        \ts.add(x[i], y[i]);\n        }\n        dataSet.addSeries(s);\n\n        final JFreeChart chart = ChartFactory.createXYLineChart(\n                \"Punt Length by Time Before Punt\",      // chart title\n                \"Time Between Snap and Punt\",                        // x axis label\n                \"Predicted Distance\", // y axis label\n                dataSet,                    // data\n                PlotOrientation.VERTICAL,\n                true,                       // include legend\n                true,                       // tooltips\n                false                       // urls\n        );\n\n        XYPlot xyPlot = chart.getXYPlot();\n        ValueAxis rangeAxis = xyPlot.getRangeAxis();\n        rangeAxis.setRange(40.0,50.0);\n        final ChartPanel panel = new ChartPanel(chart);\n        \n        XYItemRenderer renderer = xyPlot.getRenderer();\n        BasicStroke stroke = new BasicStroke(3f);\n        renderer.setSeriesStroke(0, stroke);\n\n        final JFrame f = new JFrame();\n        f.add(panel);\n        f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);\n        f.pack();\n\n        f.setVisible(true);\n    }\n    \n    \n    private static void addSeries(final XYSeriesCollection dataSet, final INDArray x, final INDArray y, final String label){\n        final double[] xd = x.data().asDouble();\n        final double[] yd = y.data().asDouble();\n        final XYSeries s = new XYSeries(label);\n        for( int j=0; j<xd.length; j++ ) s.add(xd[j],yd[j]);\n        dataSet.addSeries(s);\n    }\n\n}\n"},{"metadata":{"_uuid":"d0c73c79e0857e81ea77ca1f5bfce11e0c2d0b22"},"cell_type":"markdown","source":"The output of the above code is this chart, in which you can see that punt distances increase as punter-hold times are longer.\n\n![PuntDistance](https://i.imgur.com/BGkb6Pg.png)"},{"metadata":{"_uuid":"f8cff2646dfc853b4a95f6db6b85053f4a36cb4b"},"cell_type":"markdown","source":"**Rule Change Recommendation 1**\n\n*Existing Rule*\n* 9-1-2 : During a kick from scrimmage, only the end men (eligible receivers) on the line of scrimmage at the time of the snap, or an eligible receiver who is aligned or in motion behind the line and is more than one yard outside the end man, are permitted to advance more than one yard beyond the line before the ball is kicked.\n\n*Rule Change*\n* 9-1-2: During a kick from scrimmage, only the two most outer men on both horizontal sides of the ball (4 men total) on the line of scrimmage are considered eligible receivers at the time of the snap, are permitted to advance more than one yard beyond the line before the ball is kicked. Any other player on the line of scrimmage is ineligible and designated as a blocking role, unable to proceed more than 1 yard downfield until the ball is kicked.\n\n*Modification Result*\n* Enables the use of the spread and shield punt formations, putting 4 effective eligible receivers on the line.\n* Consistent blocking down field of these two additional eligible receivers (4 total) will reduce their velocity of producing a damaging block/hit.\n* Figure 3 and Figure 4 in the PPT Presentation show the Shield and Spread Punt formations that would now be acceptable.\n\n\n\n**Rule Change Recommendation 2**\n\n*Punt formation Rule*\n* There is currently no rule for punt formation, so the recommendation is to create a rule that enables more fair catches.\n\n*New Rule*\n* For a punt kick from any formation (traditional, shield or spread), the punter may be no more than 10 yards behind the line of scrimmage.\n\n*Rule Rationale*\n* The data currently shows us that the longer the punts, the greater number of concussions are likely to occur.\n* The data currently shows us that punts with fair catches yield fewer concussions.\n* The data currently shows us that punts from the 5 yard line and in have resulted in 0 concussions.  This is because of the compressed distance of the punter from the line of scrimmage and the incentive to quickly kick the ball.\n\n*Rule Result*\n* Fewer steps for the punter to walk into the punt (videos show between 3-4 steps walk up) will yield reduced punt yardage. (# of punter steps were not part of the dataset)\n* In combination with the Rule 1 modification suggestion, 4 punt team members will be closer to the punt returner.\n* Ultimately more fair catches is the desired result."}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}