Understanding , an exercise in Data Science

Dr. Chris Edwards, January 31, 2020

The website retrosheet.org has downloads of play-by-play information for over 100 years of baseball seasons. The information can be used to learn about the game of baseball, at least as it is played at the Major League level. My interest in the data is in creating a method to simulate how the game plays. There are many table-top baseball games (Strat-o-Matic, APBA, Statis Pro, among others) which allow fans to recreate games, or seasons, or even conduct hypothetical matchups between Babe Ruth and Nolan Ryan, for instance. The game of baseball is rich in record keeping, and this is the appeal to many fans.

Other uses of this data could include learning strategies to win more games. Do managers use the proper tactics? Could we improve how the game is managed by learning what actions in the past produced success? Without being able to properly experiment, we are forced to rely on the record of observational data. However, if we can discover patterns, they may be helpful in answering some of these questions on strategy.

Initial data gathering

The appendix to this document contains a sample game of www.retrosheet.org data. This particular game is from April 14, 1978 and featured the visiting against the home team Padres. This game’s data file consists of 158 rows of information, some of it data about the game, some of it data about the players participating, and some of it about events that occurred during the game. (In this game, there were 101 events during the game, which is the bulk of the data one would analyze for trends about how the game plays.) The data exists in a CSV (comma separated variables) format, so it is quite easy for various computer applications to read and store. Given that each team is scheduled to play 162 games in a season, and there are presently 30 teams, there are a tremendous number of lines of data if one were to look at an entire season. For example, in the 1978 season, there are over 300,000 lines of information.

Loading all of the game data into a computer isn’t the challenging part of this exercise; rather, the fact that the play-by-play data is simply represented as a code without context is what makes the following analyses challenging. For example, in our sample game, the first ’s information is coded thus:

play,1,0,hernl001,??,,63 play,1,0,andrr101,??,,53 play,1,0,evand001,??,,6/L play,1,1,richg001,??,,6 play,1,1,thomd001,??,,S4 play,1,1,gambo001,??,,NP sub,heint101,"Tom Heintzelman",0,2,4 com,"Rob Andrews left with split nail on right thumb" play,1,1,gambo001,??,,7 play,1,1,winfd001,??,,5 The first batter was Larry Herndon, and he a ground ball to the , who threw to the first basemen for the first . There are up to 7 pieces of information on each line, some of it missing for some years. The first line of this snippet has 5 pieces of information: The information was a play, in the first inning, 1, with the visiting team batting, 0, the batter was Larry Herndon, hernl001, and the result of the play was 63. There is no record of how many outs, or what the score is, or even whether the actual play resulted in an out or something else. The user is expected to understand baseball enough to puzzle out that information. As such then, this data requires a tremendous amount of interpretation before any meaningful analysis can even be attempted.

Furthermore, we see that sometimes during the game, players are replaced, either for tactical reasons (bringing in a new for example) or for injuries (as in the above snippet, where Rob Andrews was replaced by Tom Heintzelman). Other entries include comments, game info, starters, and end-of-game data. Before any analysis can be attempted, then, this data needs a lot of attention. While it is easy to find out how a particular batter fared over the course of a season by creating a frequency table, the missing context is quite important. For example, we can find that Dave Winfield of the had singles on 18.21% of his appearances in the record. What is missing, however, is how and when these singles happened. Were they against good ? Were they when the game result was in doubt when they would be most useful, or did they happen in blowouts?

> tab( a$, a$Batter == "Dave WinfieldSDP" ) b a FALSE TRUE 0 138803 548 1 26224 122 b a FALSE TRUE 0 0.8411 0.8179 1 0.1589 0.1821

I needed much more information than was presented in the data download. I had to create context as well.

Transformation issues

In addition to the need to create context, there were other problems with the information. There are many examples in this dataset where the coding of the results was inconsistent. For example, “B-1” is used to indicate the batter ended up at first base on the play. However, it is often only used when the play needed some clarity in the explanation. For instance, when a batter gets a single, it is implied that he ended up at first base, so the scorers never include the “B-1”. If there was a fielding by the though, coded as an “E3”, it is not clear in every case where the batter ended up. (Sometimes, when the first baseman makes a fielding error, such as letting the ball go between his legs and the eventually makes the play, the batter ends up on second base, and the scorer would likely have coded it as “E3.B-2”). Other times, the codes are inconsistent between different retrosheet scorers, so that different entries represent the same game outcome. I have often heard that “80 percent of a data scientist’s time is spent cleaning the data”. That was very much true in this project. I needed to use my knowledge of the game of baseball and become a detective to figure out what the various codes really meant. I made copious use of the table() command, and comparisons with known facts about the season in question. Fortunately, for some data, other websites had done aggregated data, so I could check my work against their values.

As mentioned, in addition to the problem of coding the results into something consistent, I also had to create the “game states” that existed before the result, such as number of outs in an inning, the score of the game, and what the bases status was (man on first, bases loaded, etc). One thing I did not have to create was the inning number. Fortunately, that was one of the data values coded. Creating these base states would normally be easy, if only the coding were done consistently. In fact, one of the methods I used to determine if I had covered all coding translations correctly was to compare how many did not have 3 outs by my “Outs” variable. An exception to this check is in the case of the game ending in the middle of an inning, when there may not have yet been three outs. I also kept track of which players were at each of the nine positions defensively, and if there were any assists or putouts during the play. Another consistency check was to then compare if the number of outs calculated was equal to the number of putouts I assigned. For each year of data I examined, making this comparison enlightened me to new anomalies in the coding not yet encountered in the previous data sets.

Other variables created during this “data cleanup” exercise were such things as “Hits”, “Outs”, “ Plays”, and others that I would add as time passed and I had other questions I wanted answered. For example, some time later in my work I realized I wanted to know under what circumstances a might instruct his pitcher to issue an intentional walk. I had to go back and break the “Walks” variable into two variables, one for “Intentional Walks” and one for “Other Walks”. Fortunately, using R to code the transformations meant that I just had to add the new line and rerun the entire exercise. master$W <- ifelse( ( ( substr( master$Result, 1, 1 ) == "W" ) | substr( master$Result, 1, 2 ) == "IW" ) & ( substr( master$Result, 1, 2 ) != "WP" ), 1, 0 ) master$IW <- ifelse( ( substr( master$Result, 1, 2 ) == "IW" ), 1, 0 )

As mentioned, after the massive effort of cleaning up the results codes, and creating the game state variables, I had to perform consistency checks to verify I had reasonable data to continue to analyze. This involved such things as verifying there was a “man on first base” in my variables if there was a “1-2” (which means a man on first base ended up on second base after the play) coded in the result. In some cases, my original coding transformations were miscoding some events, and these consistency checks helped to catch the mistakes.

I also wanted to add some columns of data to the data set representing information about the particular players involved in the current play. There are statistics available for each player, fielder, pitcher, batter, etc. that can be used to understand the play. For example, if a was recorded as the result of the play, could this be because the runner is fast, or because the catcher is unskilled at throwing potential stealers out at second? Thus, I needed to aggregate some statistics by player into a new database, and then go back to the events file and paste in the relevant facts. To make the situation clearer, consider the stolen base situation mentioned earlier. By creating the aggregated data for every batter, I was able to find out how many times each player was on first base with second base empty, and how many times they actually stole second base in those situations. This gave me a percentage which I could use as a measure of how likely a player was to steal second base. Then I went back to the huge events file, and appended a variable indicating each runner’s stolen base percentage, as gleaned from the aggregated file of statistics. I repeated that kind of calculation for a variety of interesting ideas, like speed of runner, arm strength of catchers, ability of pitchers to hold base runners on base, etc. The work is still evolving, but again, because the code was created in R, updates are rather straightforward to implement.

Modeling events

After the 80% of the time spent cleaning the data, I was ready to begin learning some facts about how baseball is played. I was interested in such things as what kind of outs do left handed batters make, or what kind of hits power hitters get, or how often each batter strikes out, and against what kinds of pitchers do all these events occur most often? To shorten the following results discussions, I will just focus on a few of the many questions I asked and explored.

Stolen Base Modeling

One of the main reasons I started down this rabbit hole was to understand how base stealing was done at the Major League level. Base stealing is a talent, and it is based on running speed, reading a pitcher’s habits and moves, and knowing which catcher’s have good or poor throwing arms. Those attributes, however, are quite difficult to measure, so I first had to create surrogate measures. Certainly, my choices for attributes like speed can be criticized; I may not have found the best ways to quantify who is fast and who is slow. But one of my measures of goodness I’m using in this project is predictability, so as long as the results are highly correlated with reality, the surrogate measures become useful. Hence, the players who steal the most bases (as a rate) are assigned a fast “speed value”. Whether they are actually fast is not relevant as long as the model predicts that they will steal more bases in situations where they actually did steal more bases.

For many of my response variables, the outcome was either a success (stolen base) or a failure (caught stealing). This meant that I was really focused on using techniques like logistic regression or decision trees. One popular technique for finding significant variables is to use best subsets, or stepwise regressions. Given the large number of variables I had, and the very real possibility of the existence of interaction effects between variables, I decided to use decision trees as a first step in finding some “good” variables. For the stolen bases model, it turned out that the most important variables were quite logical and involved the speed of the runner, the ability of the pitcher to hold runners, the catcher’s throwing ability, and the like.

The table below shows how the combined 1977-1979 dataset broke down as to when steals occurred. The headings are: 0 = bases empty, 1 = man on third base only, 10 = man on second base only, 11 = men on second and third bases, 100 = man on first base only, 101 = men on first and third bases, 110 = men on first and second bases, and 111 = bases loaded. The variable a was if there was a base-stealing event or not, and b was the base occupancy status. tab(a, b) is a function I wrote to create a frequency table and the associated column fractions by row. Thus, we see that there is a 12.82% chance of a random base runner stealing second base when only first base is occupied, (the column highlighted in red).

> tab( nmaster$Active, nmaster$Base.for.Chart ) b a 0 1 10 11 100 101 110 111 0 264249 15121 43073 8847 83011 15259 32478 10933 1 0 94 738 39 12210 1270 743 36 b a 0 1 10 11 100 101 110 111 0 1.0000 0.9938 0.9832 0.9956 0.8718 0.9232 0.9776 0.9967 1 0.0000 0.0062 0.0168 0.0044 0.1282 0.0768 0.0224 0.0033

My original motivation was to create a method of randomly generating actions in a simulated game setting. So, for example, a particular batter is facing a particular pitcher in the third inning with 1 out and a runner on first base. Because of the data preparation, I know the relevant facts about all the players: pitcher’s ability to prevent stealing, catcher’s ability to prevent stealing, runner’s speed, etc. I wanted the simulated game I was playing to generate random events in a reasonable facsimile to real life events. I broke this random event generation into a series of steps, much like a decision tree flowchart. The diagram below demonstrates this for one particular base-stealing situation.

The steps I next took were to fill in what the required random numbers should be at each branching. For example, the first branch in the tree was to see if there was going to be an action of any sort. If not, then we can just proceed to simulate the batter’s result (hit, out, walk, etc.). My next branch was to see if the pitcher or catcher had picked the runner off by throwing him out before he even attempted a steal. The code below shows the output for one decision tree, in this case the first branch decision: Is there a base-stealing event? The variables included were:

GOPerc a measure of the pitcher’s ability to prevent base stealing events POPerc a measure of the pitcher’s ability to pick off runners MOF.SBattPerc a measure of the runner’s ability to steal bases Catcher.ArmRate a measure of the catcher’s ability to throw out base stealers PHand Pitcher’s throwing hand, right or left ModInn inning, with coded as 10 Outs.Before outs before batter bats Tight Is the score tied or within 1 ? Bottom Is it the bottom of the inning? Batter.BA the batter’s season batting average MOF.RSXBT a measure of the runner’s speed Catcher.SB another measure of the catcher’s ability to throw out base stealers Catcher.PO, Pitcher.PO measures of the abilities to pick off runners

> temp <- rpart( Active ~ GOPerc + POPerc + MOF.SBattPerc + Catcher.ArmRate + PHand + ModInn + factor( Outs.Before ) + Tight + Bottom + Batter.BA + MOF.RSXBT + Catcher.SB + Catcher.PO + Pitcher.PO, data = subset, minbucket = 25, cp = 0.002 ) n= 95221 node), split, n, deviance, yval * denotes terminal node

1) root 95221 10644.3400 0.12822800 2) MOF.SBattPerc< 0.09999999 70278 4674.2720 0.07164404 4) MOF.SBattPerc< 0.05154639 47802 1910.8230 0.04171374 8) MOF.SBattPerc< 0.02636544 26808 577.0151 0.02200836 * 9) MOF.SBattPerc>=0.02636544 20994 1310.1060 0.06687625 18) Tight< 0.5 7783 161.5020 0.02120005 * 19) Tight>=0.5 13211 1122.8000 0.09378548 * 5) MOF.SBattPerc>=0.05154639 22476 2629.5530 0.13529990 10) Tight< 0.5 8050 356.6241 0.04645963 * 11) Tight>=0.5 14426 2173.9400 0.18487450 22) GOPerc< 0.07369506 6056 703.1255 0.13408190 * 23) GOPerc>=0.07369506 8370 1443.8860 0.22162490 * 3) MOF.SBattPerc>=0.09999999 24943 5111.0690 0.28765590 6) Tight< 0.5 8202 1028.6730 0.14703730 12) MOF.SBattPerc< 0.2092207 6896 727.0621 0.11977960 * 13) MOF.SBattPerc>=0.2092207 1306 269.4334 0.29096480 * 7) Tight>=0.5 16741 3840.7540 0.35654980 14) MOF.SBattPerc< 0.2 13484 2901.9090 0.31348260 28) GOPerc< 0.09876543 10393 2089.0320 0.27864910 56) GOPerc< 0.06241147 3724 629.8504 0.21562840 * 57) GOPerc>=0.06241147 6669 1436.1330 0.31384020 * 29) GOPerc>=0.09876543 3091 757.8648 0.43060500 * 15) MOF.SBattPerc>=0.2 3257 810.2947 0.53484800 *

We see that only three variables were found to be important in the resulting decision tree: GOPerc, MOF.SBattPerc, and Tight. These seem quite reasonable, as the first variable measures how likely a pitcher is to allow any base stealing event, the second variable measures how likely a base runner is to attempt a steal or get picked off, and the third variable represents how close the score of the game is. An alternate look at the tree is afforded by the package rpart.plot in R.

This view of the tree allows us to see more clearly which variables dominate. The values in the nodes are the overall chance of some base stealing event and the percent of all cases falling in that node. Thus, we see in the first branch node, for the “no” answer, a 29% success rate, accounting for 26% of the cases. Our interpretation is that if a runner attempts steals in more than 10% of his opportunities, he has a 29% chance of some sort of base stealing event, in contrast to the 7.2% chance otherwise. Looking at the bottom row (and the shading helps us see the highest success rates) we see that the highest base stealing attempt rate occurs when the potential base stealer tries in more than 20% of situations and the game score is close (within 1 run); the rate here is 53%. Note that if the score was not close, these frequent base steal attempters only have a 29% chance of an event (the fifth cell from the right in the bottom row). At least for this dataset, we have an indication that managers are more aggressive with their base stealers in close games.

The model appears to be creating effective final nodes, with different success rates, but to compare other methods, I needed an overall indication of goodness. I chose a “sum of squared errors” approach, and the value for this model was 8995. This measure means nothing by itself, but will be more meaningful after examining a competing model. Because the response variable here is binary, logistic regression is an appropriate technique to compare to. After some tweaking with the variables involved, including possible interaction effects, I came up with the following candidate model. > GoFormula <- glm( Active ~ GOPerc * MOF.SBattPerc + Tight, data = subset, family = binomial ) > summary( GoFormula )

Call: glm(formula = Active ~ GOPerc * MOF.SBattPerc + Tight, family = binomial, data = subset)

Deviance Residuals: Min 1Q Median 3Q Max -3.5239 -0.4945 -0.3616 -0.2129 3.0667

Coefficients: Estimate Std. Error z value Pr(>|z|) (Intercept) -5.0427 0.0636 -79.287 < 2e-16 *** GOPerc 11.6409 0.6638 17.537 < 2e-16 *** MOF.SBattPerc 11.6497 0.4645 25.078 < 2e-16 *** Tight 1.3306 0.0283 47.010 < 2e-16 *** GOPerc:MOF.SBattPerc 17.2652 5.3986 3.198 0.00138 ** --- Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

Null deviance: 72940 on 95220 degrees of freedom Residual deviance: 59515 on 95216 degrees of freedom AIC: 59525

Number of Fisher Scoring iterations: 6

> sum( ( temp$y - GoFormula$fitted.values ) ^ 2 ) # SSE calculation [1] 8965.085

This model uses the same three variables found with the decision tree approach but includes an interaction effect. Furthermore, because it is logistic regression, we must take care in interpreting the parameter estimates. Recall that the calculated fitted value is found using the formula � = �!!, where � = −5.0427 + 11.6409 ∗ ������ + 11.6497 ∗ ���. ��������� + 17.2652 ∗ ������ ∗ ���. ��������� + 1.3306 ∗ ���ℎ�. The appropriate interpretation of the coefficients is often phrased in terms of logs of odds ratios. As I wasn’t really interested in the individual variables used, but rather the performance of the model as a whole, I haven’t yet pondered the meaning of the coefficients. Note, however, that all four coefficients are quite statistically significant. Further, the ��� measurement of 8965 compares favorably with the 8995 found using the tree model, which imposed no structure on the variables. I conclude that the logistic model would produce reasonable probabilities of base runners attempting to steal first base. To further interpret the ��� calculations, we find that the overall Sums of Squares (found in the first node of the tree output) is 10644.34. This means our “percent of variation explained” is only 16%, but it should be noted that when watching a baseball game, we don’t see base stealing occur that often. If we can isolate situations where the fitted values are upwards of 50%, then we have successfully targeted likely base stealing situations. The table below also helps put the model’s predictive ability into focus. > tab( subset$Active, cut( GoFormula$fitted.values, cuts ) )

b a (0,0.1] (0.1,0.2] (0.2,0.3] (0.3,0.4] (0.4,0.5] (0.5,0.6] (0.6,0.7] (0.7,0.8] (0.8,0.9] (0.9,1] 0 54151 18187 5499 2538 1202 615 427 227 117 48 1 2280 3605 2169 1416 921 600 499 390 229 101 b a (0,0.1] (0.1,0.2] (0.2,0.3] (0.3,0.4] (0.4,0.5] (0.5,0.6] (0.6,0.7] (0.7,0.8] (0.8,0.9] (0.9,1] 0 0.9596 0.8346 0.7171 0.6419 0.5662 0.5062 0.4611 0.3679 0.3382 0.3221 1 0.0404 0.1654 0.2829 0.3581 0.4338 0.4938 0.5389 0.6321 0.6618 0.6779

The column headings are the model’s predicted values (the fitted values) by decile, and the row variable is whether or not a base-stealing action took place. Again, via a highlighted column, we see that when the fitted value was between 70% and 80%, the model predicted a 63.21% success rate. The progression in the last row shows the increasing chance of success, the higher the predicted value is.

I also ran models to predict when a manager was likely to issue an Intentional Walk, when the manager was likely to have a batter (mostly those with low batting averages, like pitchers) attempt to bunt a runner into scoring position, when a pitcher was likely to throw a or commit a Balk, etc.

Merging years

The first year I studied was 1978, because one of the tabletop games I own was first produced using that season, and so the players are very familiar to me. Eventually, I would like to understand if and how trends have changed over time, so I also loaded and analyzed the 1977 and 1979 seasons. In fact, having three times the data allowed me to refine some of the decision trees into models that had more resolution, but it did reinforce an important consideration in modeling, that of validation. While increasing the sample size did allow me finer models, the question is always whether those models are believable, or perhaps over fit instead. With other years close by in time, I can take a model developed from the 1978 data and see how it performs on the 1979 data, for example. I have not done that analysis yet, but it is on my agenda!

Conclusion

My main purpose in this project was to understand via probabilities what happens in a baseball game, specifically in batter appearances and the events that result. I was hoping to create a sort of “artificial intelligence” mechanism for playing my tabletop baseball game in a solitaire mode. I wanted the simulation to react realistically with such events as intentional walks, stolen base attempts, and sacrifice bunts. If a player with a poor batting average comes to bat with no outs and a runner on first, should that be a situation where they attempt to bunt the runner over to second? My modeling has come up with a means of tackling this question. Through the use of decision trees and logistic regression, I have created flowcharts I can use during the game. By generating a few random numbers, and following the arrows in the chart, I have a mechanism to produce these random events, and in such a way that they mimic what actually happens in real baseball games. Interestingly, I posed such a question to a group of fellow solitaire gamers. I asked “How do you decide when to have your pitcher issue an intentional walk in the games you play?” I was hoping to get some insight into what fellow fans believe a typical “intentional walk” situation is. Unfortunately, the answer I got from some of my fellow gamers was “I use what I know about baseball to make that choice”, as if it’s common knowledge when the intentional walk is appropriate. I think the situation is much more complicated than that, and I want to discover more about the game as my questions to my dataset evolve. For example, because my main motivation is to produce a reasonable simulation to a baseball game, I haven’t explored yet whether baseball managers are doing the right things when they manage. Is it really a good idea to sacrifice bunt a runner from first to second? There are only 3 outs in an inning, and they are therefore precious. While it is far easier for a runner to score from second base than from first base, and it is possible to ground into a with a runner on first base as opposed to second base, is it worth having only 2 outs in the inning instead of 3? When even the best batters fail 7 out of 10 times, asking for several successes before 2 outs occur is often not likely. (This leads one to think of the negative binomial distribution as a way to model just such chances.)

I know there is much analysis to do. I wrote this summary of the project so far to give readers a look into how I used Data Science ideas in my work. Comments and critiques are welcome. I’m always willing to learn new perspectives.

Appendix

Example game log: id,SDN197804140 version,1 info,inputprogvers,"version 7RS(19) of 07/07/92" info,visteam,SFN info,hometeam,SDN info,site,SAN01 info,date,1978/04/14 info,number,0 info,starttime,0:00PM info,daynight,night info,usedh,false info,umphome,davis901 info,ump1b,tatat901 info,ump2b,varge901 info,ump3b,pryop901 info,scorer,"25,29" info,translator,"Comly" info,inputter,"Comly" info,inputtime,1996/08/07 11:41PM info,howscored,park info,pitches,none info,temp,0 info,winddir,unknown info,windspeed,-1 info,fieldcond,unknown info,precip,unknown info,sky,night info,timeofgame,151 info,attendance,45901 info,wp,moffr101 info,lp,fingr001 info,, info,gwrbi, start,hernl001,"Larry Herndon",0,1,8 start,andrr101,"Rob Andrews",0,2,4 start,evand001,"",0,3,5 start,mccow101,"Willie McCovey",0,4,3 start,clarj001,"Jack Clark",0,5,9 start,whitt001,"Terry Whitfield",0,6,7 start,lemaj001,"Johnnie LeMaster",0,7,6 start,sadem101,"Mike Sadek",0,8,2 start,barrj101,"Jim Barr",0,9,1 start,richg001,"Gene Richards",1,1,3 start,thomd001,"Derrell Thomas",1,2,4 start,gambo001,"Oscar Gamble",1,3,7 start,winfd001,"Dave Winfield",1,4,9 start,hendg001,"George Hendrick",1,5,8 start,tenag101,"Gene Tenace",1,6,2 start,almob001,"Bill Almon",1,7,5 start,smito001,"Ozzie Smith",1,8,6 start,joner101,"",1,9,1 play,1,0,hernl001,??,,63 play,1,0,andrr101,??,,53 play,1,0,evand001,??,,6/L play,1,1,richg001,??,,6 play,1,1,thomd001,??,,S4 play,1,1,gambo001,??,,NP sub,heint101,"Tom Heintzelman",0,2,4 com,"Rob Andrews left with split nail on right thumb" play,1,1,gambo001,??,,7 play,1,1,winfd001,??,,5 play,2,0,mccow101,??,,3/G play,2,0,clarj001,??,,D9 play,2,0,whitt001,??,,9.2-3 play,2,0,lemaj001,??,,53 play,2,1,hendg001,??,,63 play,2,1,tenag101,??,,53 play,2,1,almob001,??,,S play,2,1,smito001,??,,S.1-3 play,2,1,joner101,??,,1/L play,3,0,sadem101,??,,53 play,3,0,barrj101,??,,K play,3,0,hernl001,??,,43 play,3,1,richg001,??,,43 play,3,1,thomd001,??,,13 play,3,1,gambo001,??,,S play,3,1,winfd001,??,,13 play,4,0,heint101,??,,S play,4,0,evand001,??,,46(1)3/GDP play,4,0,mccow101,??,,3/G play,4,1,hendg001,??,,3 play,4,1,tenag101,??,,W play,4,1,almob001,??,,S.1-3 play,4,1,smito001,??,,SB2 play,4,1,smito001,??,,13/SH.3-H;2-3 play,4,1,joner101,??,,53 play,5,0,clarj001,??,,NP sub,davib101,"Bob Davis",1,6,2 play,5,0,clarj001,??,,W play,5,0,whitt001,??,,K play,5,0,lemaj001,??,,53.1-2 play,5,0,sadem101,??,,63 play,5,1,richg001,??,,43 play,5,1,thomd001,??,,S play,5,1,gambo001,??,,4 play,5,1,winfd001,??,,43 play,6,0,barrj101,??,,53 play,6,0,hernl001,??,,T9 play,6,0,heint101,??,,K play,6,0,evand001,??,,S9.3-H play,6,0,mccow101,??,,S.1-3 play,6,0,clarj001,??,,NP sub,freid101,"Dave Freisleben",1,9,1 play,6,0,clarj001,??,,K play,6,1,hendg001,??,,HR play,6,1,davib101,??,,7 play,6,1,almob001,??,,S play,6,1,smito001,??,,6(1)3/GDP play,7,0,whitt001,??,,S play,7,0,lemaj001,??,,PO1(E1).1-2 play,7,0,lemaj001,??,,53 play,7,0,sadem101,??,,63(B)5(2)/DP play,7,1,freid101,??,,NP sub,chamm101,"Mike Champion",1,9,11 play,7,1,chamm101,??,,S play,7,1,richg001,??,,NP sub,curtj001,"Jack Curtis",0,9,1 play,7,1,richg001,??,,64(1)3/GDP play,7,1,thomd001,??,,9 play,8,0,curtj001,??,,NP sub,fingr001,"Rollie Fingers",1,9,1 play,8,0,curtj001,??,,NP sub,harrv101,"Vic Harris",0,9,11 play,8,0,harrv101,??,,63 play,8,0,hernl001,??,,K/C play,8,0,heint101,??,,8 play,8,1,gambo001,??,,NP sub,harrv101,"Vic Harris",0,9,6 play,8,1,gambo001,??,,NP sub,moffr101,"Randy Moffitt",0,7,1 play,8,1,gambo001,??,,43 play,8,1,winfd001,??,,8 play,8,1,hendg001,??,,W play,8,1,davib101,??,,64(1)/FO play,9,0,evand001,??,,7 play,9,0,mccow101,??,,E3.B-1 play,9,0,clarj001,??,,NP sub,james101,"Skip James",0,4,12 play,9,0,clarj001,??,,D7.1-H(UR);B-3(THH) play,9,0,whitt001,??,,8/SF.3-H(UR) play,9,0,moffr101,??,,S play,9,0,sadem101,??,,S.1-3 play,9,0,harrv101,??,,9 play,9,1,almob001,??,,NP sub,james101,"Skip James",0,4,3 play,9,1,almob001,??,,53 play,9,1,smito001,??,,NP sub,reynd101,"Don Reynolds",1,8,11 play,9,1,reynd101,??,,5 play,9,1,fingr001,??,,NP sub,turnj101,"Jerry Turner",1,9,11 play,9,1,turnj101,??,,8 data,er,barrj101,2 data,er,curtj001,0 data,er,moffr101,0 data,er,joner101,1 data,er,freid101,0 data,er,fingr001,0