-
Notifications
You must be signed in to change notification settings - Fork 264
Exploring secondary types: FieldPosition, PossessionTime and Clock
In addition to the central types like Game
and Player
provided by nfldb,
there are also several secondary types that describe common data in the
database. Below is a list with a brief description of each and a link to its
API documentation:
-
FieldPosition - This
type is used to describe field position values in the
drive
andplay
tables. This includes knowledge of possession. (i.e., There is a distinction between being on one's own 45 yard line and the opponent's 45 yard line.) -
PossessionTime -
Represents the amount of time that an offense has the ball. This only
describes values in the
drive
table. -
Clock - Represents the phase
and clock time (if applicable) of a point in the game. This includes pregame,
half time, over time, etc. This describes values in the
drive
andplay
tables.
It is worth skimming the API documentation for each of the above types. Most of this article will focus on how to use those types in the query interface.
The representation of the Clock
type is essentially a tuple: the first
element is the phase of the game (Q1
, Half
, Q4
, Final
, etc.) and the
second element is the number of seconds elapsed in the phase. For phases that
have no time component, the number of elapsed seconds is always 0
. Note that
this implies a range of valid values for elapsed seconds: 0 <= s <= 900
where
s
is the number of seconds elapsed.
We can create a new Clock
object with its
constructor method:
c = nfldb.Clock(nfldb.Enums.game_phase.Q2, 61)
print c
And the output is:
[andrew@Liger nflgame] python2 scratch.py
Q2 13:59
We pass a game phase from the game_phase enumeration, which corresponds to a set of values allowed to be a game phase. Any other value will cause an assertion error.
Notice here that 61
seconds corresponds to the number of seconds elapsed
since the start of the quarter. Thus, the game clock should read 13:59
. But
thinking in terms of elapsed seconds is really inconvenient, and writing out
an enumeration value can be a bit painful. Therefore, we can make Clock
values with the more convenient
Clock.from_str
function:
c = nfldb.Clock.from_str('Q2', '13:59')
print c
Which will have the same output as above.
Okay, so how do we use Clock
values in the query interface? It's simple! We
use them like any other value. For example, to find all plays with pass
completions within the last 30 seconds of the 4th quarter in week 1 of the 2013
regular season:
end_4th = nfldb.Clock.from_str('Q4', '0:30')
q = nfldb.Query(db)
q.game(season_year=2013, season_type='Regular', week=1)
q.play(passing_cmp=1, time__ge=end_4th)
for p in q.as_plays():
print p
And the output is:
[andrew@Liger nflgame] python2 scratch.py
(BUF, OWN 20, Q4, 1 and 10) (:05) (Shotgun) E.Manuel pass short middle to F.Jackson to BUF 32 for 12 yards. FUMBLES, recovered by BUF-E.Manuel at BUF 29. E.Manuel to BUF 29 for no gain (A.Dennard).
(NYJ, OWN 20, Q4, 2 and 10) (:29) (Shotgun) G.Smith pass deep middle to K.Winslow to NYJ 45 for 25 yards (M.Barron).
(ARI, OWN 29, Q4, 2 and 16) (:24) C.Palmer pass short right to J.Brown to ARI 43 for 14 yards (J.Laurinaitis).
(GB, OWN 20, Q4, 1 and 10) (:26) (Shotgun) A.Rodgers pass deep right to R.Cobb to SF 42 for 38 yards (D.Whitner). Caught at GB 49. 9-yds YAC
(NYG, OPP 4, Q4, 3 and 1) (:16) (Shotgun) E.Manning pass short middle to B.Myers for 4 yards, TOUCHDOWN.
The nice thing about the Clock
type (and indeed, the other types as well), is
that an ordering for them is established by nfldb. So you can use standard
Python operators to compare Clock
, PossessionTime
or FieldPosition
values. This ordering is also carries over into the PostgreSQL database
automatically, since their representations were carefully chosen to correspond
to a natural ordering.
PossessionTime
objects are fairly straight forward: they represent the time
elapsed by a single drive. The only value that has this type is the
Drive.pos_time
attribute.
Instances of the PossessionTime
class can be instantiated with its
constructor
method.
For example, this represents a possession time of 298
seconds:
p = nfldb.PossessionTime(298)
print p
Which has output:
[andrew@Liger nflgame] python2 scratch.py
04:58
But wouldn't it be nice to be able to just say 04:58
? Just like Clock
has
the Clock.from_str
convenience function, PossessionTime
has the
PossessionTime.from_str
convenience function:
p = nfldb.PossessionTime.from_str('04:58')
print p
Which has the same output as above.
Using possession times in queries is just as easy with clock times. For example, to find all drives longer than 10 minutes in the 2012 season:
long_drive = nfldb.PossessionTime.from_str('10:00')
q = nfldb.Query(db)
q.game(season_year=2012, season_type='Regular')
q.drive(pos_time__ge=long_drive)
for d in q.as_drives():
print d
And the output is:
[andrew@Liger nflgame] python2 scratch.py
[Touchdown ] PIT from OWN 25 to OPP 2 (lasted 10:13 - Q4 13:47 to Q4 03:34)
[Field Goal ] DAL from OWN 8 to OPP 1 (lasted 10:10 - Q1 09:26 to Q2 14:16)
[Downs ] WAS from OWN 20 to OPP 2 (lasted 10:11 - Q1 00:24 to Q2 05:13)
[Field Goal ] GB from OWN 14 to OPP 13 (lasted 11:00 - Q4 15:00 to Q4 04:00)
[Field Goal ] TEN from OWN 20 to OPP 7 (lasted 10:08 - Q3 05:36 to Q4 10:28)
As with the Clock
type, PossessionTime
objects can be compared:
print nfldb.PossessionTime.from_str('04:35') < nfldb.PossessionTime.from_str('05:12')
Output:
[andrew@Liger nflgame] python2 scratch.py
True
The representation of a field position is a single signed integer such that
-50 <= fp <= 50
, where fp
is a field position offset. Negative values
correspond to your own territory, positive values correspond to your
opponent's territory and 0
corresponds to midfield. As 50 - |fp|
gets
smaller (where |fp|
is its absolute value), you get closer to an endzone.
So -45
corresponds to your own 5
yard line while 45
corresponds to your
opponent's 5
yard line.
New instances of FieldPosition
can be created with its
constructor method:
fp = nfldb.FieldPosition(5)
print fp
fp = nfldb.FieldPosition(-13)
print fp
fp = nfldb.FieldPosition(0)
print fp
And the output is:
[andrew@Liger nflgame] python2 scratch.py
OPP 45
OWN 37
MIDFIELD
Note here that a FieldPosition
does not contain contextual information about
which team has possession of the ball, so the field territory is labeled with
OPP
for "opponent" and OWN
for your "own" territory.
Just like with the previous types, thinking about field positions in terms of
offsets is pretty strange. Thus, there is a more convenient
FieldPosition.from_str
function that lets you make FieldPosition
objects from a string:
fp = nfldb.FieldPosition.from_str('OPP 45')
print fp
fp = nfldb.FieldPosition.from_str('OWN 37')
print fp
fp1 = nfldb.FieldPosition.from_str('MIDFIELD')
fp2 = nfldb.FieldPosition.from_str('OWN 50')
fp3 = nfldb.FieldPosition.from_str('OPP 50')
print '%s :: %s :: %s' % (fp1, fp2, fp3)
And the output is:
[andrew@Liger nflgame] python2 scratch.py
OPP 45
OWN 37
MIDFIELD :: MIDFIELD :: MIDFIELD
As with PossessionTime
and Clock
, FieldPosition
objects can be used in
queries. For example, to find all drives that start on your own 1 yard line
from the 2012 regular season:
own1 = nfldb.FieldPosition.from_str('OWN 1')
q = nfldb.Query(db)
q.game(season_year=2012, season_type='Regular')
q.drive(start_field__le=own1)
for d in q.as_drives():
print d
And the output is:
[andrew@Liger nflgame] python2 scratch.py
[Punt ] DEN from OWN 1 to OWN 15 (lasted 02:58 - Q1 02:58 to Q1 00:00)
[Punt ] OAK from OWN 1 to OWN 6 (lasted 01:33 - Q3 11:07 to Q3 09:34)
[Interception] CIN from OWN 1 to OWN 2 (lasted 00:45 - Q1 11:45 to Q1 11:00)
[Punt ] GB from OWN 1 to OWN 16 (lasted 04:28 - Q1 05:59 to Q1 01:31)
[End of Half ] SEA from OWN 1 to OWN 20 (lasted 02:17 - Q2 02:17 to Q2 00:00)
[Field Goal ] ATL from OWN 1 to OPP 22 (lasted 00:54 - Q4 00:59 to Q4 00:05)
[Punt ] BAL from OWN 1 to OWN 6 (lasted 01:41 - Q4 11:56 to Q4 10:15)
[Safety ] SEA from OWN 1 to OWN 18 (lasted 02:48 - Q4 03:47 to Q4 00:59)
[End of Half ] TB from OWN 1 to OWN 14 (lasted 00:52 - Q2 00:52 to Q2 00:00)
[End of Half ] WAS from OWN 1 to OWN 7 (lasted 00:32 - Q2 00:32 to Q2 00:00)
[End of Game ] SF from OWN 1 to OWN 1 (lasted 00:49 - Q4 00:49 to Q4 00:00)
[Interception] DEN from OWN 1 to OWN 3 (lasted 01:16 - Q3 01:17 to Q3 00:01)
[Punt ] NE from OWN 1 to OWN 1 (lasted 00:19 - Q4 09:35 to Q4 09:16)
[Interception] MIA from OWN 1 to OWN 32 (lasted 02:48 - Q4 04:42 to Q4 01:54)
[Safety ] NE from OWN 1 to OWN 1 (lasted 00:05 - Q3 06:52 to Q3 06:47)
[Punt ] BUF from OWN 1 to OWN 16 (lasted 01:55 - Q1 00:17 to Q2 13:22)
[Field Goal ] SF from OWN 1 to OPP 5 (lasted 08:13 - Q3 01:56 to Q4 08:43)
[Punt ] HOU from OWN 1 to OWN 46 (lasted 01:49 - Q2 02:17 to Q2 00:28)
[Punt ] SD from OWN 1 to OWN 5 (lasted 01:02 - Q4 03:35 to Q4 02:33)
[Interception] TEN from OWN 1 to OWN 1 (lasted 00:04 - Q3 05:40 to Q3 05:36)
[Punt ] MIN from OWN 1 to OPP 41 (lasted 06:35 - Q3 00:48 to Q4 09:13)
[Punt ] HOU from OWN 1 to OWN 7 (lasted 01:40 - Q2 10:02 to Q2 08:22)
[Punt ] HOU from OWN 1 to OWN 10 (lasted 01:33 - Q4 12:18 to Q4 10:45)
[Punt ] CHI from OWN 1 to OWN 6 (lasted 01:18 - Q3 01:43 to Q3 00:25)
[Punt ] NE from OWN 1 to MIDFIELD (lasted 03:02 - Q3 04:13 to Q3 01:11)
[Punt ] SF from OWN 1 to OWN 10 (lasted 02:04 - Q1 13:59 to Q1 11:55)
Finally, like the other types, FieldPosition
objects have an ordering defined
on them too. Namely, field positions closer to the goal line are greater than
field positions farther away from the goal line:
print nfldb.FieldPosition.from_str('OWN 1') < nfldb.FieldPosition.from_str('OPP 1')
And the output is:
[andrew@Liger nflgame] python2 scratch.py
True