BurntSushi / nflgame

An API to retrieve and read NFL Game Center JSON data. It can work with real-time data, which can be used for fantasy football.
http://pdoc.burntsushi.net/nflgame
The Unlicense
1.28k stars 413 forks source link

Find fumble recovered by Special teams while querying defensive stats #259

Open ece70 opened 8 years ago

ece70 commented 8 years ago

In week 5 of the 2016 season between Arizona and SF, ARI kicked off, SF's fumbled and it was recovered by ARI.

In my league, special teams points are credited towards the defense. So while gathering various defensive stats, I find that I'll be short one fumble recovery that is credited towards ARI defense.

When I try to pull various defensive data by the method below, the fumble recovery by ARI is missed:

import nflgame

games = nflgame.games(2016, week=[5], home='ARI', away='ARI')
plays = nflgame.combine_plays(games)

for p in plays.filter(team__ne='ARI', defense_frec__gt=0):
    print p.defense_frec, p.playid 
    for k,v in p.data.iteritems():
        print k,v          

However, we know it happened, the difference being the filter on the team name, ARI is considered to be on offense during the kick off and not defense:

import nflgame
games = nflgame.games(2016, week=[5], home='ARI', away='ARI')
plays = nflgame.combine_plays(games)
for p in plays.filter(team='ARI', defense_frec__gt=0):
    print p.defense_frec, p.playid 
    for k,v in p.data.iteritems():
        print k,v

Yields:

1 2274
ydstogo 0
note FUMBLE
qtr 3
yrdln ARI 35
sp 0
down 0
players {u'00-0029740': [{u'playerName': u'I.Momah', u'clubcode': u'ARI', u'yards': 0, u'statId': 79, u'sequence': 4}, {u'playerName': u'I.Momah', u'clubcode': u'ARI', u'yards': 0, u'statId': 91, u'sequence': 7}, {u'playerName': u'I.Momah', u'clubcode': u'ARI', u'yards': 0, u'statId': 59, u'sequence': 8}], u'00-0030896': [{u'playerName': u'C.Catanzaro', u'clubcode': u'ARI', u'yards': 62, u'statId': 41, u'sequence': 1}, {u'playerName': u'C.Catanzaro', u'clubcode': u'ARI', u'yards': 0, u'statId': 42, u'sequence': 3}], u'00-0030917': [{u'playerName': u'C.Davis', u'clubcode': u'SF', u'yards': 11, u'statId': 45, u'sequence': 2}, {u'playerName': u'C.Davis', u'clubcode': u'SF', u'yards': 0, u'statId': 52, u'sequence': 5}, {u'playerName': u'C.Davis', u'clubcode': u'SF', u'yards': 0, u'statId': 106, u'sequence': 6}, {u'playerName': u'C.Davis', u'clubcode': u'SF', u'yards': 0, u'statId': 79, u'sequence': 9}]}
time 15:00
ydsnet 0
posteam ARI
desc C.Catanzaro kicks 62 yards from ARI 35 to SF 3. C.Davis to SF 14 for 11 yards (I.Momah). FUMBLES (I.Momah), RECOVERED by ARI-I.Momah at SF 14. I.Momah to SF 14 for no gain (C.Davis).

Is there a way to pragmatically distinguish these special teams type events from regular offensive plays and thus allocate the points to a defense vs an offense?

ochawkeye commented 8 years ago

I thought you might be able to take advantage of .kicking() but seems maybe not...

import nflgame

games = nflgame.games(2016, 5)
plays = nflgame.combine_plays(games)

for play in plays.filter(defense_frec__gt=0):
    print play
    if play.players.kicking():
        print '\t', play.players.kicking()
        print '\tThis was a special teams play'
(ARI, ARI 35, Q3) C.Catanzaro kicks 62 yards from ARI 35 to SF 3. C.Davis to SF 14 for 11 yards (I.Momah). FUMBLES (I.Momah), RECOVERED by ARI-I.Momah at SF 14. I.Momah to SF 14 for no gain (C.Davis).
    [C.Catanzaro]
    This was a special teams play
(WAS, WAS 17, Q2, 1 and 10) (6:38) M.Jones up the middle to WAS 16 for -1 yards (Z.Orr). FUMBLES (Z.Orr), RECOVERED by BAL-Z.Orr at WAS 15. Z.Orr to WAS 15 for no gain (M.Jones).
    []
    This was a special teams play
(DET, DET 12, Q3, 2 and 24) (7:57) (Shotgun) M.Stafford sacked at DET 7 for -5 yards. FUMBLES, RECOVERED by PHI-N.Bradham at DET 16. N.Bradham to DET 16 for no gain (T.Swanson).
    []
    This was a special teams play
...

Notice that your play in question had a player in play.players.kicking() and non-special teams have empty lists. Someone more versed in the language would have to explain why [] satisfies the if play.players.kicking() for the non-special teams plays.

If class 'nflgame.seq.GenPlayerStats' had a __len__ method (it doesn't currently) that might be something you could key off of?

ochawkeye commented 8 years ago

A pretty hacky way to do add a length method to class GenPlayerStats, but maybe it will inspire an actual Pythonista to tackle this one.

   # New method in class GenPlayerStats (Gen):
    def len(self):
        return sum(1 for p in self)

Now running the similar code to what was posted previously:

import nflgame

games = nflgame.games(2016, 5)
plays = nflgame.combine_plays(games)

for play in plays.filter(defense_frec__gt=0):
    print play
    if play.players.kicking().len():
        print '\tThis was a special teams play'
(ARI, ARI 35, Q3) C.Catanzaro kicks 62 yards from ARI 35 to SF 3. C.Davis to SF 14 for 11 yards (I.Momah). FUMBLES (I.Momah), RECOVERED by ARI-I.Momah at SF 14. I.Momah to SF 14 for no gain (C.Davis).
    This was a special teams play
(WAS, WAS 17, Q2, 1 and 10) (6:38) M.Jones up the middle to WAS 16 for -1 yards (Z.Orr). FUMBLES (Z.Orr), RECOVERED by BAL-Z.Orr at WAS 15. Z.Orr to WAS 15 for no gain (M.Jones).
(DET, DET 12, Q3, 2 and 24) (7:57) (Shotgun) M.Stafford sacked at DET 7 for -5 yards. FUMBLES, RECOVERED by PHI-N.Bradham at DET 16. N.Bradham to DET 16 for no gain (T.Swanson).
(PHI, PHI 45, Q4, 3 and 2) (2:41) R.Mathews right end to PHI 41 for -4 yards (D.Slay). FUMBLES (D.Slay), RECOVERED by DET-T.Walker at PHI 45. T.Walker to PHI 45 for no gain (N.Agholor). The Replay Official reviewed the fumble ruling, and the play was Upheld. The ruling on the field stands.
(CHI, CHI 15, Q4, 1 and 10) (3:38) (Run formation) B.Hoyer pass short right to C.Meredith to CHI 27 for 12 yards (R.Melvin). FUMBLES (R.Melvin), RECOVERED by IND-D.Jackson at CHI 29. D.Jackson to CHI 29 for no gain (J.Howard). The Replay Official reviewed the pass completion ruling, and the play was Upheld. The ruling on the field stands.
(PIT, NYJ 14, Q4, 2 and 6) (9:44) (No Huddle, Shotgun) B.Roethlisberger sacked at NYJ 22 for -8 yards (L.Williams). FUMBLES (L.Williams) [L.Williams], RECOVERED by NYJ-S.Richardson at NYJ 19. S.Richardson to NYJ 19 for no gain (D.DeCastro). NYJ-S.Richardson was injured during the play.
(ATL, ATL 38, Q3, 3 and 19) (:07) (Shotgun) M.Ryan pass short middle to M.Sanu to 50 for 12 yards (T.Ward). FUMBLES (T.Ward), RECOVERED by DEN-T.Ward at 50. T.Ward to 50 for no gain (A.Levitre).
(DAL, CIN 18, Q3, 3 and 8) (:39) (Shotgun) D.Prescott sacked at CIN 26 for -8 yards (C.Dunlap). FUMBLES (C.Dunlap) [C.Dunlap], touched at CIN 23, RECOVERED by CIN-V.Rey at CIN 31. V.Rey to CIN 41 for 10 yards (T.Frederick).
(LA, BUF 39, Q1, 2 and 19) (:10) (Shotgun) T.Gurley up the middle to BUF 38 for 1 yard (L.McCray). FUMBLES (L.McCray), RECOVERED by BUF-C.Graham at BUF 37. C.Graham to BUF 37 for no gain (L.Kendricks).
(SD, OAK 22, Q1, 3 and 2) (12:09) (Shotgun) P.Rivers pass short middle to A.Gates to OAK 13 for 9 yards (P.Riley). FUMBLES (P.Riley), RECOVERED by OAK-R.Nelson at OAK 11. R.Nelson to OAK 11 for no gain (A.Gates).
(SD, SD 34, Q3, 2 and 1) (4:00) M.Gordon to SD 35 for 1 yard (S.McGee). FUMBLES (S.McGee), RECOVERED by OAK-K.Joseph at SD 38. K.Joseph to SD 38 for no gain (D.Watt).
(NYG, NYG 39, Q2, 2 and 10) (1:22) (No Huddle, Shotgun) E.Manning sacked at NYG 31 for -8 yards (K.Fackrell). FUMBLES (K.Fackrell) [K.Fackrell], RECOVERED by GB-K.Clark at NYG 31. K.Clark to NYG 31 for no gain (J.Pugh).
(TB, TB 23, Q2, 4 and 7) (9:03) B.Anger punts 48 yards to CAR 29, Center-A.DePaola. T.Ginn MUFFS catch, RECOVERED by TB-R.Shepard at CAR 30. R.Shepard to CAR 30 for no gain (T.Ginn).
(CAR, TB 44, Q4, 3 and 4) (14:23) (No Huddle, Shotgun) D.Anderson scrambles right guard to TB 40 for 4 yards (D.Lambert). FUMBLES (D.Lambert), RECOVERED by TB-W.Gholston at TB 40. W.Gholston to TB 40 for no gain (T.Turner).

This looks like it catches fumbles recovered during kickoffs and muffed punts.

(DEN, DEN 20, Q4) R.Dixon kicks 34 yards from DEN 20 to SD 46. K.Wiggins MUFFS catch, RECOVERED by DEN-S.Keo at DEN 49. S.Keo to DEN 49 for no gain (J.Perry).
    This was a special teams play

EDIT: I guess all this could be done without changing seq.py at all

import nflgame

games = nflgame.games(2016, 5)
plays = nflgame.combine_plays(games)

for play in plays.filter(defense_frec__gt=0):
    print play

    if play.players.kicking().len():
        print '\tThis was a special teams play'

    if sum(1 for p in play.players.kicking()):  # This is equivalent to adding my hacky 'len()' method
        print '\tThis was a special teams play'