<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Austin Rochford (Posts about NBA)</title><link>https://austinrochford.com/</link><description></description><atom:link href="https://austinrochford.com/tags/nba.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2022 &lt;a href="mailto:austin.rochford@gmail.com"&gt;Austin Rochford&lt;/a&gt; </copyright><lastBuildDate>Mon, 17 Jan 2022 11:51:54 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>An Improved Analysis of NBA Foul Calls with Python</title><link>https://austinrochford.com/posts/2018-02-04-nba-irt-2.html</link><dc:creator>Austin Rochford</dc:creator><description>&lt;p&gt;Last April, I wrote a &lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html"&gt;post&lt;/a&gt; that used Bayesian item-response theory models to analyze NBA foul call data. Last November, I &lt;a href="http://austinrochford.com/talks.html#pydata-nyc"&gt;spoke&lt;/a&gt; about a greatly improved version of these models at &lt;a href="https://pydata.org/nyc2017/"&gt;PyData NYC&lt;/a&gt;. This post is a write-up of the models from that talk.&lt;/p&gt;
&lt;h2 id="last-two-minute-report"&gt;Last Two-minute Report&lt;/h2&gt;
&lt;p&gt;Since late in the 2014-2015 season, the NBA has issued &lt;a href="http://official.nba.com/2017-18-nba-officiating-last-two-minute-reports/"&gt;last two minute reports&lt;/a&gt;. These reports give the league’s assessment of the correctness of foul calls and non-calls in the last two minutes of any game where the score difference was three or fewer points at any point in the last two minutes.&lt;/p&gt;
&lt;p&gt;These reports are notably different from play-by-play logs, in that they include information on non-calls for notable on-court interactions. This non-call information presents a unique opportunity to study the factors that impact foul calls. There is a level of subjectivity inherent in the the NBA’s definition of notable on-court interactions which we attempt to mitigate later using season-specific factors.&lt;/p&gt;
&lt;h3 id="loading-the-data"&gt;Loading the data&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://russellgoldenberg.com/"&gt;Russel Goldenberg&lt;/a&gt; of &lt;a href="https://pudding.cool/"&gt;The Pudding&lt;/a&gt; has been scraping the PDFs that the NBA publishes and transforming them into a CSV for some time. I am grateful for his work, which has enabled this analysis.&lt;/p&gt;
&lt;p&gt;We download the data locally to be kind to GitHub.&lt;/p&gt;
&lt;div class="sourceCode" id="cb1"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb1-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb1-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;%&lt;/span&gt;matplotlib inline&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb2"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb2-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb2-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="im"&gt;import&lt;/span&gt; datetime&lt;/span&gt;
&lt;span id="cb2-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb2-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="im"&gt;from&lt;/span&gt; itertools &lt;span class="im"&gt;import&lt;/span&gt; product&lt;/span&gt;
&lt;span id="cb2-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb2-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="im"&gt;import&lt;/span&gt; logging&lt;/span&gt;
&lt;span id="cb2-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb2-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="im"&gt;import&lt;/span&gt; pickle&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb3"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb3-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb3-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="im"&gt;from&lt;/span&gt; matplotlib &lt;span class="im"&gt;import&lt;/span&gt; pyplot &lt;span class="im"&gt;as&lt;/span&gt; plt&lt;/span&gt;
&lt;span id="cb3-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb3-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="im"&gt;from&lt;/span&gt; matplotlib.offsetbox &lt;span class="im"&gt;import&lt;/span&gt; AnchoredText&lt;/span&gt;
&lt;span id="cb3-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb3-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="im"&gt;from&lt;/span&gt; matplotlib.ticker &lt;span class="im"&gt;import&lt;/span&gt; FuncFormatter, StrMethodFormatter&lt;/span&gt;
&lt;span id="cb3-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb3-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="im"&gt;import&lt;/span&gt; numpy &lt;span class="im"&gt;as&lt;/span&gt; np&lt;/span&gt;
&lt;span id="cb3-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb3-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="im"&gt;import&lt;/span&gt; pandas &lt;span class="im"&gt;as&lt;/span&gt; pd&lt;/span&gt;
&lt;span id="cb3-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb3-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="im"&gt;import&lt;/span&gt; scipy &lt;span class="im"&gt;as&lt;/span&gt; sp&lt;/span&gt;
&lt;span id="cb3-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb3-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="im"&gt;import&lt;/span&gt; seaborn &lt;span class="im"&gt;as&lt;/span&gt; sns&lt;/span&gt;
&lt;span id="cb3-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb3-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="im"&gt;from&lt;/span&gt; sklearn.preprocessing &lt;span class="im"&gt;import&lt;/span&gt; LabelEncoder&lt;/span&gt;
&lt;span id="cb3-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb3-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="im"&gt;from&lt;/span&gt; theano &lt;span class="im"&gt;import&lt;/span&gt; tensor &lt;span class="im"&gt;as&lt;/span&gt; tt&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb4"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb4-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb4-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;pct_formatter &lt;span class="op"&gt;=&lt;/span&gt; StrMethodFormatter(&lt;span class="st"&gt;'&lt;/span&gt;&lt;span class="sc"&gt;{x:.1%}&lt;/span&gt;&lt;span class="st"&gt;'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb4-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb4-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb4-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb4-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;sns.&lt;span class="bu"&gt;set&lt;/span&gt;()&lt;/span&gt;
&lt;span id="cb4-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb4-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;blue, green, &lt;span class="op"&gt;*&lt;/span&gt;_ &lt;span class="op"&gt;=&lt;/span&gt; sns.color_palette()&lt;/span&gt;
&lt;span id="cb4-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb4-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb4-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb4-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;plt.rc(&lt;span class="st"&gt;'figure'&lt;/span&gt;, figsize&lt;span class="op"&gt;=&lt;/span&gt;(&lt;span class="dv"&gt;8&lt;/span&gt;, &lt;span class="dv"&gt;6&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb4-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb4-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb4-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb4-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;LABELSIZE &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="dv"&gt;14&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb4-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;plt.rc(&lt;span class="st"&gt;'axes'&lt;/span&gt;, labelsize&lt;span class="op"&gt;=&lt;/span&gt;LABELSIZE)&lt;/span&gt;
&lt;span id="cb4-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb4-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;plt.rc(&lt;span class="st"&gt;'axes'&lt;/span&gt;, titlesize&lt;span class="op"&gt;=&lt;/span&gt;LABELSIZE)&lt;/span&gt;
&lt;span id="cb4-11"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb4-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;plt.rc(&lt;span class="st"&gt;'figure'&lt;/span&gt;, titlesize&lt;span class="op"&gt;=&lt;/span&gt;LABELSIZE)&lt;/span&gt;
&lt;span id="cb4-12"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb4-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;plt.rc(&lt;span class="st"&gt;'legend'&lt;/span&gt;, fontsize&lt;span class="op"&gt;=&lt;/span&gt;LABELSIZE)&lt;/span&gt;
&lt;span id="cb4-13"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb4-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;plt.rc(&lt;span class="st"&gt;'xtick'&lt;/span&gt;, labelsize&lt;span class="op"&gt;=&lt;/span&gt;LABELSIZE)&lt;/span&gt;
&lt;span id="cb4-14"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb4-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;plt.rc(&lt;span class="st"&gt;'ytick'&lt;/span&gt;, labelsize&lt;span class="op"&gt;=&lt;/span&gt;LABELSIZE)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb5"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb5-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb5-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;SEED &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="dv"&gt;207183&lt;/span&gt; &lt;span class="co"&gt;# from random.org, for reproducibility&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb6"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb6-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb6-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# keep theano from complaining about compile locks for small models&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb6-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(logging.getLogger(&lt;span class="st"&gt;'theano.gof.compilelock'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb6-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb6-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        .setLevel(logging.CRITICAL))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb7"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb7-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb7-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;%%&lt;/span&gt;bash&lt;/span&gt;
&lt;span id="cb7-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb7-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;DATA_URI&lt;span class="op"&gt;=&lt;/span&gt;https:&lt;span class="op"&gt;//&lt;/span&gt;raw.githubusercontent.com&lt;span class="op"&gt;/&lt;/span&gt;polygraph&lt;span class="op"&gt;-&lt;/span&gt;cool&lt;span class="op"&gt;/&lt;/span&gt;last&lt;span class="op"&gt;-&lt;/span&gt;two&lt;span class="op"&gt;-&lt;/span&gt;minute&lt;span class="op"&gt;-&lt;/span&gt;report&lt;span class="op"&gt;/&lt;/span&gt;&lt;span class="dv"&gt;32&lt;/span&gt;&lt;span class="er"&gt;f1c43dfa06c2e7652cc51ea65758007f2a1a01&lt;/span&gt;&lt;span class="op"&gt;/&lt;/span&gt;output&lt;span class="op"&gt;/&lt;/span&gt;all_games.csv&lt;/span&gt;
&lt;span id="cb7-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb7-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;DATA_DEST&lt;span class="op"&gt;=/&lt;/span&gt;tmp&lt;span class="op"&gt;/&lt;/span&gt;all_games.csv&lt;/span&gt;
&lt;span id="cb7-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb7-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb7-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb7-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;if&lt;/span&gt; [[ &lt;span class="op"&gt;!&lt;/span&gt; &lt;span class="op"&gt;-&lt;/span&gt;e $DATA_DEST ]]&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb7-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;then&lt;/span&gt;
&lt;span id="cb7-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb7-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    wget &lt;span class="op"&gt;-&lt;/span&gt;q &lt;span class="op"&gt;-&lt;/span&gt;O $DATA_DEST $DATA_URI&lt;/span&gt;
&lt;span id="cb7-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb7-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;fi&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We use only a subset of the columns in the source data set.&lt;/p&gt;
&lt;div class="sourceCode" id="cb8"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb8-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb8-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;USECOLS &lt;span class="op"&gt;=&lt;/span&gt; [&lt;/span&gt;
&lt;span id="cb8-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb8-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;'period'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb8-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb8-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;'seconds_left'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb8-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb8-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;'call_type'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb8-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb8-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;'committing_player'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb8-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb8-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;'disadvantaged_player'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb8-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb8-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;'review_decision'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb8-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb8-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;'play_id'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb8-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb8-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;'away'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb8-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb8-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;'home'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb8-11"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb8-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;'date'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb8-12"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb8-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;'score_away'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb8-13"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb8-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;'score_home'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb8-14"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb8-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;'disadvantaged_team'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb8-15"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb8-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;'committing_team'&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb8-16"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb8-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb9"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb9-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb9-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;orig_df &lt;span class="op"&gt;=&lt;/span&gt; pd.read_csv(&lt;/span&gt;
&lt;span id="cb9-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb9-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;'/tmp/all_games.csv'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb9-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb9-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    usecols&lt;span class="op"&gt;=&lt;/span&gt;USECOLS,&lt;/span&gt;
&lt;span id="cb9-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb9-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    index_col&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'play_id'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb9-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb9-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    parse_dates&lt;span class="op"&gt;=&lt;/span&gt;[&lt;span class="st"&gt;'date'&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb9-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb9-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The data set contains more than 16,000 plays.&lt;/p&gt;
&lt;div class="sourceCode" id="cb10"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb10-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb10-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;orig_df.shape[&lt;span class="dv"&gt;0&lt;/span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;16300&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each row of the &lt;code&gt;DataFrame&lt;/code&gt; represents a play and each column describes an attrbiute of the play:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;period&lt;/code&gt; is the period of the game,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;seconds_left&lt;/code&gt; is the number of seconds remaining in the game,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;call_type&lt;/code&gt; is the type of call,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;committing_player&lt;/code&gt; and &lt;code&gt;disadvantaged_player&lt;/code&gt; are the names of the players involved in the play,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;review_decision&lt;/code&gt; is the opinion of the league reviewer on whether or not the play was called correctly:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;review_decision = "INC"&lt;/code&gt; means the call was an incorrect noncall,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;review_decision = "CNC"&lt;/code&gt; means the call was an correct noncall,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;review_decision = "IC"&lt;/code&gt; means the call was an incorrect call, and&lt;/li&gt;
&lt;li&gt;&lt;code&gt;review_decision = "CC"&lt;/code&gt; means the call was an correct call,&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;away&lt;/code&gt; and &lt;code&gt;home&lt;/code&gt; are the abbreviations of the teams involved in the game,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;date&lt;/code&gt; is the date on which the game was played,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;score_away&lt;/code&gt; and &lt;code&gt;score_home&lt;/code&gt; are the scores of the &lt;code&gt;away&lt;/code&gt; and &lt;code&gt;home&lt;/code&gt; team during the play, respectively, and&lt;/li&gt;
&lt;li&gt;&lt;code&gt;disadvantaged_team&lt;/code&gt; and &lt;code&gt;committing_team&lt;/code&gt; indicate how each team is involved in the play.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="sourceCode" id="cb12"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb12-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb12-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;orig_df.head(n&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;2&lt;/span&gt;).T&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div&gt;
&lt;style scoped&gt;
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
&lt;/style&gt;
&lt;center&gt;
&lt;table border="1" class="dataframe"&gt;
&lt;thead&gt;
&lt;tr style="text-align: right;"&gt;
&lt;th&gt;
play_id
&lt;/th&gt;
&lt;th&gt;
20150301CLEHOU-0
&lt;/th&gt;
&lt;th&gt;
20150301CLEHOU-1
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;
period
&lt;/th&gt;
&lt;td&gt;
Q4
&lt;/td&gt;
&lt;td&gt;
Q4
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
seconds_left
&lt;/th&gt;
&lt;td&gt;
112
&lt;/td&gt;
&lt;td&gt;
103
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
call_type
&lt;/th&gt;
&lt;td&gt;
Foul: Shooting
&lt;/td&gt;
&lt;td&gt;
Foul: Shooting
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
committing_player
&lt;/th&gt;
&lt;td&gt;
Josh Smith
&lt;/td&gt;
&lt;td&gt;
J.R. Smith
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
disadvantaged_player
&lt;/th&gt;
&lt;td&gt;
Kevin Love
&lt;/td&gt;
&lt;td&gt;
James Harden
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
review_decision
&lt;/th&gt;
&lt;td&gt;
CNC
&lt;/td&gt;
&lt;td&gt;
CC
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
away
&lt;/th&gt;
&lt;td&gt;
CLE
&lt;/td&gt;
&lt;td&gt;
CLE
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
home
&lt;/th&gt;
&lt;td&gt;
HOU
&lt;/td&gt;
&lt;td&gt;
HOU
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
date
&lt;/th&gt;
&lt;td&gt;
2015-03-01 00:00:00
&lt;/td&gt;
&lt;td&gt;
2015-03-01 00:00:00
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
score_away
&lt;/th&gt;
&lt;td&gt;
103
&lt;/td&gt;
&lt;td&gt;
103
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
score_home
&lt;/th&gt;
&lt;td&gt;
105
&lt;/td&gt;
&lt;td&gt;
105
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
disadvantaged_team
&lt;/th&gt;
&lt;td&gt;
CLE
&lt;/td&gt;
&lt;td&gt;
HOU
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
committing_team
&lt;/th&gt;
&lt;td&gt;
HOU
&lt;/td&gt;
&lt;td&gt;
CLE
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/center&gt;&lt;/div&gt;

&lt;h3 id="research-questions"&gt;Research questions&lt;/h3&gt;
&lt;p&gt;In this post, we answer two questions:&lt;/p&gt;
&lt;ol type="1"&gt;
&lt;li&gt;How does game context impact foul calls?&lt;/li&gt;
&lt;li&gt;Is (not) committing and/or drawing fouls a measurable player skill?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The previous post focused on the second question, and gave the first question only a cursory treatment. This post enhances our treatment of the first question, in order to control for non-skill factors influencing foul calls (namely intentional fouls). Controlling for these factors makes our estimates of player skill more realistic.&lt;/p&gt;
&lt;h2 id="exploratory-data-analysis"&gt;Exploratory Data Analysis&lt;/h2&gt;
&lt;p&gt;First we examine the types of calls present in the data set.&lt;/p&gt;
&lt;div class="sourceCode" id="cb13"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb13-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb13-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(orig_df[&lt;span class="st"&gt;'call_type'&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb13-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb13-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        .value_counts()&lt;/span&gt;
&lt;span id="cb13-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb13-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        .head(n&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;15&lt;/span&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;Foul: Personal                     4736
Foul: Shooting                     4201
Foul: Offensive                    2846
Foul: Loose Ball                   1316
Turnover: Traveling                 779
Instant Replay: Support Ruling      607
Foul: Defense 3 Second              277
Instant Replay: Overturn Ruling     191
Foul: Personal Take                 172
Turnover: 3 Second Violation        139
Turnover: 24 Second Violation       126
Turnover: 5 Second Inbound           99
Stoppage: Out-of-Bounds              96
Violation: Lane                      84
Foul: Away from Play                 82
Name: call_type, dtype: int64&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The portion of &lt;code&gt;call_type&lt;/code&gt; before the colon is the general category of the call. We count the occurence of these categories below.&lt;/p&gt;
&lt;div class="sourceCode" id="cb15"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb15-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb15-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(orig_df[&lt;span class="st"&gt;'call_type'&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb15-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb15-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        .&lt;span class="bu"&gt;str&lt;/span&gt;.split(&lt;span class="st"&gt;':'&lt;/span&gt;, expand&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb15-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb15-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        .iloc[:, &lt;span class="dv"&gt;0&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb15-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb15-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        .value_counts()&lt;/span&gt;
&lt;span id="cb15-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb15-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        .plot(&lt;/span&gt;
&lt;span id="cb15-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb15-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            kind&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'bar'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb15-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb15-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            color&lt;span class="op"&gt;=&lt;/span&gt;blue, logy&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;, &lt;/span&gt;
&lt;span id="cb15-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb15-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            title&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;"Call types"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb15-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb15-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        )&lt;/span&gt;
&lt;span id="cb15-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb15-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        .set_ylabel(&lt;span class="st"&gt;"Frequency"&lt;/span&gt;))&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt2/An%20Improved%20Analysis%20of%20NBA%20Foul%20Calls%20with%20Python_21_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;We restrict our attention to foul calls, though other call types would be interesting to study in the future.&lt;/p&gt;
&lt;div class="sourceCode" id="cb16"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb16-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb16-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;foul_df &lt;span class="op"&gt;=&lt;/span&gt; orig_df[&lt;/span&gt;
&lt;span id="cb16-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb16-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    orig_df[&lt;span class="st"&gt;'call_type'&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb16-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb16-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;           .fillna(&lt;span class="st"&gt;"UNKNOWN"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb16-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb16-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;           .&lt;span class="bu"&gt;str&lt;/span&gt;.startswith(&lt;span class="st"&gt;"Foul"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb16-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb16-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We count the foul call types below.&lt;/p&gt;
&lt;div class="sourceCode" id="cb17"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb17-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb17-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(foul_df[&lt;span class="st"&gt;'call_type'&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb17-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb17-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        .&lt;span class="bu"&gt;str&lt;/span&gt;.split(&lt;span class="st"&gt;': '&lt;/span&gt;, expand&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb17-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb17-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        .iloc[:, &lt;span class="dv"&gt;1&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb17-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb17-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        .value_counts()&lt;/span&gt;
&lt;span id="cb17-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb17-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        .plot(&lt;/span&gt;
&lt;span id="cb17-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb17-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            kind&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'bar'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb17-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb17-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            color&lt;span class="op"&gt;=&lt;/span&gt;blue, logy&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb17-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb17-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            title&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;"Foul Types"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb17-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb17-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        )&lt;/span&gt;
&lt;span id="cb17-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb17-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        .set_ylabel(&lt;span class="st"&gt;"Frequency"&lt;/span&gt;))&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt2/An%20Improved%20Analysis%20of%20NBA%20Foul%20Calls%20with%20Python_25_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;We restrict our attention to the five foul types below, which generally involve two players. This subset of fouls allows us to pursue our second research question in the most direct manner.&lt;/p&gt;
&lt;div class="sourceCode" id="cb18"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb18-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb18-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;FOULS &lt;span class="op"&gt;=&lt;/span&gt; [&lt;/span&gt;
&lt;span id="cb18-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb18-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="ss"&gt;f"Foul: &lt;/span&gt;&lt;span class="sc"&gt;{&lt;/span&gt;foul_type&lt;span class="sc"&gt;}&lt;/span&gt;&lt;span class="ss"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb18-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb18-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="cf"&gt;for&lt;/span&gt; foul_type &lt;span class="kw"&gt;in&lt;/span&gt; [&lt;/span&gt;
&lt;span id="cb18-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb18-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            &lt;span class="st"&gt;"Personal"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb18-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb18-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            &lt;span class="st"&gt;"Shooting"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb18-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb18-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            &lt;span class="st"&gt;"Offensive"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb18-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb18-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            &lt;span class="st"&gt;"Loose Ball"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb18-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb18-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            &lt;span class="st"&gt;"Away from Play"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb18-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb18-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        ]&lt;/span&gt;
&lt;span id="cb18-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb18-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="data-transformation"&gt;Data transformation&lt;/h3&gt;
&lt;p&gt;There are a number of misspelled team names in the data, which we correct.&lt;/p&gt;
&lt;div class="sourceCode" id="cb19"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb19-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb19-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;TEAM_MAP &lt;span class="op"&gt;=&lt;/span&gt; {&lt;/span&gt;
&lt;span id="cb19-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb19-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;"NKY"&lt;/span&gt;: &lt;span class="st"&gt;"NYK"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb19-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb19-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;"COS"&lt;/span&gt;: &lt;span class="st"&gt;"BOS"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb19-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb19-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;"SAT"&lt;/span&gt;: &lt;span class="st"&gt;"SAS"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb19-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb19-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;"CHi"&lt;/span&gt;: &lt;span class="st"&gt;"CHI"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb19-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb19-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;"LA)"&lt;/span&gt;: &lt;span class="st"&gt;"LAC"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb19-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb19-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;"AT)"&lt;/span&gt;: &lt;span class="st"&gt;"ATL"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb19-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb19-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;"ARL"&lt;/span&gt;: &lt;span class="st"&gt;"ATL"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb19-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb19-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;}&lt;/span&gt;
&lt;span id="cb19-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb19-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb19-11"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb19-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; correct_team_name(col):&lt;/span&gt;
&lt;span id="cb19-12"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb19-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="kw"&gt;def&lt;/span&gt; _correct_team_name(df):&lt;/span&gt;
&lt;span id="cb19-13"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb19-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="cf"&gt;return&lt;/span&gt; df[col].&lt;span class="bu"&gt;apply&lt;/span&gt;(&lt;span class="kw"&gt;lambda&lt;/span&gt; team_name: TEAM_MAP.get(team_name, team_name))&lt;/span&gt;
&lt;span id="cb19-14"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb19-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb19-15"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb19-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;return&lt;/span&gt; _correct_team_name&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We also convert each game date to an NBA season.&lt;/p&gt;
&lt;div class="sourceCode" id="cb20"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb20-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb20-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; date_to_season(date):&lt;/span&gt;
&lt;span id="cb20-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb20-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;if&lt;/span&gt; date &lt;span class="op"&gt;&amp;gt;=&lt;/span&gt; datetime.datetime(&lt;span class="dv"&gt;2017&lt;/span&gt;, &lt;span class="dv"&gt;10&lt;/span&gt;, &lt;span class="dv"&gt;17&lt;/span&gt;):&lt;/span&gt;
&lt;span id="cb20-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb20-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="cf"&gt;return&lt;/span&gt; &lt;span class="st"&gt;'2017-2018'&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb20-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb20-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;elif&lt;/span&gt; date &lt;span class="op"&gt;&amp;gt;=&lt;/span&gt; datetime.datetime(&lt;span class="dv"&gt;2016&lt;/span&gt;, &lt;span class="dv"&gt;10&lt;/span&gt;, &lt;span class="dv"&gt;25&lt;/span&gt;):&lt;/span&gt;
&lt;span id="cb20-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb20-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="cf"&gt;return&lt;/span&gt; &lt;span class="st"&gt;'2016-2017'&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb20-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb20-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;elif&lt;/span&gt; date &lt;span class="op"&gt;&amp;gt;=&lt;/span&gt; datetime.datetime(&lt;span class="dv"&gt;2015&lt;/span&gt;, &lt;span class="dv"&gt;10&lt;/span&gt;, &lt;span class="dv"&gt;27&lt;/span&gt;):&lt;/span&gt;
&lt;span id="cb20-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb20-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="cf"&gt;return&lt;/span&gt; &lt;span class="st"&gt;'2015-2016'&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb20-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb20-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;else&lt;/span&gt;:&lt;/span&gt;
&lt;span id="cb20-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb20-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="cf"&gt;return&lt;/span&gt; &lt;span class="st"&gt;'2014-2015'&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We clean the data by&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;restricting to plays that occured during the last two minutes of regulation,&lt;/li&gt;
&lt;li&gt;imputing incorrect noncalls when &lt;code&gt;review_decision&lt;/code&gt; is missing,&lt;/li&gt;
&lt;li&gt;correcting team names,&lt;/li&gt;
&lt;li&gt;converting game dates to seasons,&lt;/li&gt;
&lt;li&gt;restricting to the foul types discussed above,&lt;/li&gt;
&lt;li&gt;restricting to the plays that happened during the &lt;a href="https://en.wikipedia.org/wiki/2015%E2%80%9316_NBA_season"&gt;2015-2016&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/2016%E2%80%9317_NBA_season"&gt;2016-2017&lt;/a&gt; regular seasons (those are the only full seasons in the data set as of February 2018), and&lt;/li&gt;
&lt;li&gt;dropping unneeded rows and columns.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="sourceCode" id="cb21"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb21-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb21-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;clean_df &lt;span class="op"&gt;=&lt;/span&gt; (foul_df.where(&lt;span class="kw"&gt;lambda&lt;/span&gt; df: df[&lt;span class="st"&gt;'period'&lt;/span&gt;] &lt;span class="op"&gt;==&lt;/span&gt; &lt;span class="st"&gt;"Q4"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb21-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb21-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                   .where(&lt;span class="kw"&gt;lambda&lt;/span&gt; df: (df[&lt;span class="st"&gt;'date'&lt;/span&gt;].between(datetime.datetime(&lt;span class="dv"&gt;2016&lt;/span&gt;, &lt;span class="dv"&gt;10&lt;/span&gt;, &lt;span class="dv"&gt;25&lt;/span&gt;),&lt;/span&gt;
&lt;span id="cb21-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb21-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                                         datetime.datetime(&lt;span class="dv"&gt;2017&lt;/span&gt;, &lt;span class="dv"&gt;4&lt;/span&gt;, &lt;span class="dv"&gt;12&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb21-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb21-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                     &lt;span class="op"&gt;|&lt;/span&gt; df[&lt;span class="st"&gt;'date'&lt;/span&gt;].between(datetime.datetime(&lt;span class="dv"&gt;2015&lt;/span&gt;, &lt;span class="dv"&gt;10&lt;/span&gt;, &lt;span class="dv"&gt;27&lt;/span&gt;),&lt;/span&gt;
&lt;span id="cb21-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb21-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                                          datetime.datetime(&lt;span class="dv"&gt;2016&lt;/span&gt;, &lt;span class="dv"&gt;5&lt;/span&gt;, &lt;span class="dv"&gt;30&lt;/span&gt;)))&lt;/span&gt;
&lt;span id="cb21-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb21-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                   )&lt;/span&gt;
&lt;span id="cb21-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb21-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                   .assign(&lt;/span&gt;
&lt;span id="cb21-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb21-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                       review_decision&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="kw"&gt;lambda&lt;/span&gt; df: df[&lt;span class="st"&gt;'review_decision'&lt;/span&gt;].fillna(&lt;span class="st"&gt;"INC"&lt;/span&gt;),&lt;/span&gt;
&lt;span id="cb21-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb21-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                       committing_team&lt;span class="op"&gt;=&lt;/span&gt;correct_team_name(&lt;span class="st"&gt;'committing_team'&lt;/span&gt;),&lt;/span&gt;
&lt;span id="cb21-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb21-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                       disadvantged_team&lt;span class="op"&gt;=&lt;/span&gt;correct_team_name(&lt;span class="st"&gt;'disadvantaged_team'&lt;/span&gt;),&lt;/span&gt;
&lt;span id="cb21-11"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb21-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                       away&lt;span class="op"&gt;=&lt;/span&gt;correct_team_name(&lt;span class="st"&gt;'away'&lt;/span&gt;),&lt;/span&gt;
&lt;span id="cb21-12"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb21-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                       home&lt;span class="op"&gt;=&lt;/span&gt;correct_team_name(&lt;span class="st"&gt;'home'&lt;/span&gt;),&lt;/span&gt;
&lt;span id="cb21-13"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb21-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                       season&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="kw"&gt;lambda&lt;/span&gt; df: df[&lt;span class="st"&gt;'date'&lt;/span&gt;].&lt;span class="bu"&gt;apply&lt;/span&gt;(date_to_season)&lt;/span&gt;
&lt;span id="cb21-14"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb21-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                   )&lt;/span&gt;
&lt;span id="cb21-15"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb21-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                   .where(&lt;span class="kw"&gt;lambda&lt;/span&gt; df: df[&lt;span class="st"&gt;'call_type'&lt;/span&gt;].isin(FOULS))&lt;/span&gt;
&lt;span id="cb21-16"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb21-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                   .dropna()&lt;/span&gt;
&lt;span id="cb21-17"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb21-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                   .drop(&lt;span class="st"&gt;'period'&lt;/span&gt;, axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb21-18"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb21-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                   .assign(call_type&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="kw"&gt;lambda&lt;/span&gt; df: (df[&lt;span class="st"&gt;'call_type'&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb21-19"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb21-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                                   .&lt;span class="bu"&gt;str&lt;/span&gt;.split(&lt;span class="st"&gt;': '&lt;/span&gt;, expand&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;)  &lt;/span&gt;
&lt;span id="cb21-20"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb21-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                                   .iloc[:, &lt;span class="dv"&gt;1&lt;/span&gt;])))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;About 55% of the rows in the original data set remain.&lt;/p&gt;
&lt;div class="sourceCode" id="cb22"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb22-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb22-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;clean_df.shape[&lt;span class="dv"&gt;0&lt;/span&gt;] &lt;span class="op"&gt;/&lt;/span&gt; orig_df.shape[&lt;span class="dv"&gt;0&lt;/span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;0.5516564417177914&lt;/code&gt;&lt;/pre&gt;
&lt;div class="sourceCode" id="cb24"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb24-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb24-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;clean_df.head(n&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;2&lt;/span&gt;).T&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div&gt;
&lt;style scoped&gt;
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
&lt;/style&gt;
&lt;center&gt;
&lt;table border="1" class="dataframe"&gt;
&lt;thead&gt;
&lt;tr style="text-align: right;"&gt;
&lt;th&gt;
play_id
&lt;/th&gt;
&lt;th&gt;
20151028INDTOR-1
&lt;/th&gt;
&lt;th&gt;
20151028INDTOR-2
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;
seconds_left
&lt;/th&gt;
&lt;td&gt;
89
&lt;/td&gt;
&lt;td&gt;
73
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
call_type
&lt;/th&gt;
&lt;td&gt;
Shooting
&lt;/td&gt;
&lt;td&gt;
Shooting
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
committing_player
&lt;/th&gt;
&lt;td&gt;
Ian Mahinmi
&lt;/td&gt;
&lt;td&gt;
Bismack Biyombo
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
disadvantaged_player
&lt;/th&gt;
&lt;td&gt;
DeMar DeRozan
&lt;/td&gt;
&lt;td&gt;
Paul George
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
review_decision
&lt;/th&gt;
&lt;td&gt;
CC
&lt;/td&gt;
&lt;td&gt;
IC
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
away
&lt;/th&gt;
&lt;td&gt;
IND
&lt;/td&gt;
&lt;td&gt;
IND
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
home
&lt;/th&gt;
&lt;td&gt;
TOR
&lt;/td&gt;
&lt;td&gt;
TOR
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
date
&lt;/th&gt;
&lt;td&gt;
2015-10-28 00:00:00
&lt;/td&gt;
&lt;td&gt;
2015-10-28 00:00:00
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
score_away
&lt;/th&gt;
&lt;td&gt;
99
&lt;/td&gt;
&lt;td&gt;
99
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
score_home
&lt;/th&gt;
&lt;td&gt;
106
&lt;/td&gt;
&lt;td&gt;
106
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
disadvantaged_team
&lt;/th&gt;
&lt;td&gt;
TOR
&lt;/td&gt;
&lt;td&gt;
IND
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
committing_team
&lt;/th&gt;
&lt;td&gt;
IND
&lt;/td&gt;
&lt;td&gt;
TOR
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
disadvantged_team
&lt;/th&gt;
&lt;td&gt;
TOR
&lt;/td&gt;
&lt;td&gt;
IND
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
season
&lt;/th&gt;
&lt;td&gt;
2015-2016
&lt;/td&gt;
&lt;td&gt;
2015-2016
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/center&gt;&lt;/div&gt;

&lt;p&gt;We use &lt;code&gt;scikit-learn&lt;/code&gt;’s &lt;a href="http://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.LabelEncoder.html"&gt;&lt;code&gt;LabelEncoder&lt;/code&gt;&lt;/a&gt; to transform categorical features (call type, player, and season) to integers.&lt;/p&gt;
&lt;div class="sourceCode" id="cb25"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb25-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb25-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;call_type_enc &lt;span class="op"&gt;=&lt;/span&gt; LabelEncoder().fit(&lt;/span&gt;
&lt;span id="cb25-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb25-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    clean_df[&lt;span class="st"&gt;'call_type'&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb25-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb25-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;/span&gt;
&lt;span id="cb25-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb25-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;n_call_type &lt;span class="op"&gt;=&lt;/span&gt; call_type_enc.classes_.size&lt;/span&gt;
&lt;span id="cb25-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb25-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb25-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb25-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;player_enc &lt;span class="op"&gt;=&lt;/span&gt; LabelEncoder().fit(&lt;/span&gt;
&lt;span id="cb25-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb25-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    np.concatenate((&lt;/span&gt;
&lt;span id="cb25-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb25-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        clean_df[&lt;span class="st"&gt;'committing_player'&lt;/span&gt;],&lt;/span&gt;
&lt;span id="cb25-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb25-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        clean_df[&lt;span class="st"&gt;'disadvantaged_player'&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb25-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb25-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ))&lt;/span&gt;
&lt;span id="cb25-11"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb25-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;/span&gt;
&lt;span id="cb25-12"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb25-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;n_player &lt;span class="op"&gt;=&lt;/span&gt; player_enc.classes_.size&lt;/span&gt;
&lt;span id="cb25-13"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb25-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb25-14"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb25-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;season_enc &lt;span class="op"&gt;=&lt;/span&gt; LabelEncoder().fit(&lt;/span&gt;
&lt;span id="cb25-15"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb25-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    clean_df[&lt;span class="st"&gt;'season'&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb25-16"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb25-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;/span&gt;
&lt;span id="cb25-17"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb25-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;n_season &lt;span class="op"&gt;=&lt;/span&gt; season_enc.classes_.size&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We transform the data by&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;rounding &lt;code&gt;seconds_left&lt;/code&gt; to the nearest second (purely for convenience),&lt;/li&gt;
&lt;li&gt;transforming categorical features to integer ids,&lt;/li&gt;
&lt;li&gt;setting &lt;code&gt;foul_called&lt;/code&gt; equal to one or zero depending on whether or not a foul was called, and&lt;/li&gt;
&lt;li&gt;setting &lt;code&gt;score_committing&lt;/code&gt; and &lt;code&gt;score_disadvantaged&lt;/code&gt; to the score of the committing and disadvantaged teams, respectively.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="sourceCode" id="cb26"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb26-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb26-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;df &lt;span class="op"&gt;=&lt;/span&gt; (clean_df[[&lt;span class="st"&gt;'seconds_left'&lt;/span&gt;]]&lt;/span&gt;
&lt;span id="cb26-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb26-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              .&lt;span class="bu"&gt;round&lt;/span&gt;(&lt;span class="dv"&gt;0&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb26-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb26-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              .assign(&lt;/span&gt;
&lt;span id="cb26-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb26-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                call_type&lt;span class="op"&gt;=&lt;/span&gt;call_type_enc.transform(clean_df[&lt;span class="st"&gt;'call_type'&lt;/span&gt;]),&lt;/span&gt;
&lt;span id="cb26-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb26-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                foul_called&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;1.&lt;/span&gt; &lt;span class="op"&gt;*&lt;/span&gt; clean_df[&lt;span class="st"&gt;'review_decision'&lt;/span&gt;].isin([&lt;span class="st"&gt;'CC'&lt;/span&gt;, &lt;span class="st"&gt;'INC'&lt;/span&gt;]),&lt;/span&gt;
&lt;span id="cb26-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb26-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                player_committing&lt;span class="op"&gt;=&lt;/span&gt;player_enc.transform(clean_df[&lt;span class="st"&gt;'committing_player'&lt;/span&gt;]),&lt;/span&gt;
&lt;span id="cb26-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb26-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                player_disadvantaged&lt;span class="op"&gt;=&lt;/span&gt;player_enc.transform(clean_df[&lt;span class="st"&gt;'disadvantaged_player'&lt;/span&gt;]),&lt;/span&gt;
&lt;span id="cb26-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb26-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                score_committing&lt;span class="op"&gt;=&lt;/span&gt;clean_df[&lt;span class="st"&gt;'score_home'&lt;/span&gt;].where(&lt;/span&gt;
&lt;span id="cb26-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb26-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                    clean_df[&lt;span class="st"&gt;'committing_team'&lt;/span&gt;] &lt;span class="op"&gt;==&lt;/span&gt; clean_df[&lt;span class="st"&gt;'home'&lt;/span&gt;],&lt;/span&gt;
&lt;span id="cb26-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb26-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                    clean_df[&lt;span class="st"&gt;'score_away'&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb26-11"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb26-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                ),&lt;/span&gt;
&lt;span id="cb26-12"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb26-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                score_disadvantaged&lt;span class="op"&gt;=&lt;/span&gt;clean_df[&lt;span class="st"&gt;'score_home'&lt;/span&gt;].where(&lt;/span&gt;
&lt;span id="cb26-13"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb26-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                    clean_df[&lt;span class="st"&gt;'disadvantaged_team'&lt;/span&gt;] &lt;span class="op"&gt;==&lt;/span&gt; clean_df[&lt;span class="st"&gt;'home'&lt;/span&gt;],&lt;/span&gt;
&lt;span id="cb26-14"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb26-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                    clean_df[&lt;span class="st"&gt;'score_away'&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb26-15"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb26-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                ),&lt;/span&gt;
&lt;span id="cb26-16"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb26-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                season&lt;span class="op"&gt;=&lt;/span&gt;season_enc.transform(clean_df[&lt;span class="st"&gt;'season'&lt;/span&gt;])&lt;/span&gt;
&lt;span id="cb26-17"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb26-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              ))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The resulting data is ready for analysis.&lt;/p&gt;
&lt;div class="sourceCode" id="cb27"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb27-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb27-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;df.head(n&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;2&lt;/span&gt;).T&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div&gt;
&lt;style scoped&gt;
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
&lt;/style&gt;
&lt;center&gt;
&lt;table border="1" class="dataframe"&gt;
&lt;thead&gt;
&lt;tr style="text-align: right;"&gt;
&lt;th&gt;
play_id
&lt;/th&gt;
&lt;th&gt;
20151028INDTOR-1
&lt;/th&gt;
&lt;th&gt;
20151028INDTOR-2
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;
seconds_left
&lt;/th&gt;
&lt;td&gt;
89.0
&lt;/td&gt;
&lt;td&gt;
73.0
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
call_type
&lt;/th&gt;
&lt;td&gt;
4.0
&lt;/td&gt;
&lt;td&gt;
4.0
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
foul_called
&lt;/th&gt;
&lt;td&gt;
1.0
&lt;/td&gt;
&lt;td&gt;
0.0
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
player_committing
&lt;/th&gt;
&lt;td&gt;
162.0
&lt;/td&gt;
&lt;td&gt;
36.0
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
player_disadvantaged
&lt;/th&gt;
&lt;td&gt;
98.0
&lt;/td&gt;
&lt;td&gt;
358.0
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
score_committing
&lt;/th&gt;
&lt;td&gt;
99.0
&lt;/td&gt;
&lt;td&gt;
106.0
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
score_disadvantaged
&lt;/th&gt;
&lt;td&gt;
106.0
&lt;/td&gt;
&lt;td&gt;
99.0
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
season
&lt;/th&gt;
&lt;td&gt;
0.0
&lt;/td&gt;
&lt;td&gt;
0.0
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/center&gt;&lt;/div&gt;

&lt;h2 id="modeling"&gt;Modeling&lt;/h2&gt;
&lt;p&gt;We follow George Box’s modeling workflow, as &lt;a href="http://dustintran.com/talks/Tran_Edward.pdf"&gt;summarized&lt;/a&gt; by Dustin Tran:&lt;/p&gt;
&lt;ol type="1"&gt;
&lt;li&gt;build a model of the science,&lt;/li&gt;
&lt;li&gt;infer the model given data, and&lt;/li&gt;
&lt;li&gt;criticize the model given data.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="baseline-model"&gt;Baseline model&lt;/h3&gt;
&lt;h4 id="build-a-model-of-the-science"&gt;Build a model of the science&lt;/h4&gt;
&lt;p&gt;Below we examine the foul call rate by season.&lt;/p&gt;
&lt;div class="sourceCode" id="cb28"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb28-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb28-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; make_foul_rate_yaxis(ax, label&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;"Observed foul call rate"&lt;/span&gt;):&lt;/span&gt;
&lt;span id="cb28-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb28-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.yaxis.set_major_formatter(pct_formatter)&lt;/span&gt;
&lt;span id="cb28-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb28-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.set_ylabel(label)&lt;/span&gt;
&lt;span id="cb28-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb28-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb28-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb28-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;return&lt;/span&gt; ax&lt;/span&gt;
&lt;span id="cb28-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb28-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb28-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb28-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;make_foul_rate_yaxis(&lt;/span&gt;
&lt;span id="cb28-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb28-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    df.pivot_table(&lt;span class="st"&gt;'foul_called'&lt;/span&gt;, &lt;span class="st"&gt;'season'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb28-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb28-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;      .rename(index&lt;span class="op"&gt;=&lt;/span&gt;season_enc.inverse_transform)&lt;/span&gt;
&lt;span id="cb28-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb28-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;      .rename_axis(&lt;span class="st"&gt;"Season"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb28-11"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb28-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;      .plot(kind&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'bar'&lt;/span&gt;, rot&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;, legend&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;False&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb28-12"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb28-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt2/An%20Improved%20Analysis%20of%20NBA%20Foul%20Calls%20with%20Python_45_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;There is a pronounced difference between the foul call rate in the 2015-2016 and 2016-2017 NBA seasons; our first model accounts for this difference.&lt;/p&gt;
&lt;p&gt;We use &lt;a href="http://docs.pymc.io/"&gt;&lt;code&gt;pymc3&lt;/code&gt;&lt;/a&gt; to specify our models. Our first model is given by&lt;/p&gt;
&lt;p&gt;&lt;span class="math display"&gt;\[
\begin{align*}
    \beta^{\textrm{season}}_s 
        &amp;amp; \sim N(0, 5) \\
    \eta^{\textrm{game}}_k
        &amp;amp; = \beta^{\textrm{season}}_{s(k)} \\
    p_k
        &amp;amp; = \textrm{sigm}\left(\eta^{\textrm{game}}_k\right).
\end{align*}
\]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We use a logistic regression model with different factors for each season.&lt;/p&gt;
&lt;div class="sourceCode" id="cb29"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb29-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb29-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="im"&gt;import&lt;/span&gt; pymc3 &lt;span class="im"&gt;as&lt;/span&gt; pm&lt;/span&gt;
&lt;span id="cb29-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb29-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb29-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb29-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;with&lt;/span&gt; pm.Model() &lt;span class="im"&gt;as&lt;/span&gt; base_model:&lt;/span&gt;
&lt;span id="cb29-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb29-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    β_season &lt;span class="op"&gt;=&lt;/span&gt; pm.Normal(&lt;span class="st"&gt;'β_season'&lt;/span&gt;, &lt;span class="fl"&gt;0.&lt;/span&gt;, &lt;span class="fl"&gt;5.&lt;/span&gt;, shape&lt;span class="op"&gt;=&lt;/span&gt;n_season)&lt;/span&gt;
&lt;span id="cb29-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb29-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    p &lt;span class="op"&gt;=&lt;/span&gt; pm.Deterministic(&lt;span class="st"&gt;'p'&lt;/span&gt;, pm.math.sigmoid(β_season))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Foul calls are Bernoulli trials, &lt;span class="math inline"&gt;\(y_k \sim \textrm{Bernoulli}(p_k).\)&lt;/span&gt;&lt;/p&gt;
&lt;div class="sourceCode" id="cb30"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb30-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb30-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;season &lt;span class="op"&gt;=&lt;/span&gt; df[&lt;span class="st"&gt;'season'&lt;/span&gt;].values&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb31"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb31-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb31-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;with&lt;/span&gt; base_model:&lt;/span&gt;
&lt;span id="cb31-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb31-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    y &lt;span class="op"&gt;=&lt;/span&gt; pm.Bernoulli(&lt;/span&gt;
&lt;span id="cb31-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb31-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="st"&gt;'y'&lt;/span&gt;, p[season],&lt;/span&gt;
&lt;span id="cb31-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb31-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        observed&lt;span class="op"&gt;=&lt;/span&gt;df[&lt;span class="st"&gt;'foul_called'&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb31-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb31-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    )&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="infer-the-model-given-data"&gt;Infer the model given data&lt;/h4&gt;
&lt;p&gt;We now sample from the model’s posterior distribution.&lt;/p&gt;
&lt;div class="sourceCode" id="cb32"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb32-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb32-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;NJOBS &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="dv"&gt;3&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb32-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb32-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb32-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb32-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;SAMPLE_KWARGS &lt;span class="op"&gt;=&lt;/span&gt; {&lt;/span&gt;
&lt;span id="cb32-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb32-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;'draws'&lt;/span&gt;: &lt;span class="dv"&gt;1000&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb32-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb32-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;'njobs'&lt;/span&gt;: NJOBS,&lt;/span&gt;
&lt;span id="cb32-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb32-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;'random_seed'&lt;/span&gt;: [&lt;/span&gt;
&lt;span id="cb32-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb32-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        SEED &lt;span class="op"&gt;+&lt;/span&gt; i &lt;span class="cf"&gt;for&lt;/span&gt; i &lt;span class="kw"&gt;in&lt;/span&gt; &lt;span class="bu"&gt;range&lt;/span&gt;(NJOBS)&lt;/span&gt;
&lt;span id="cb32-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb32-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ],&lt;/span&gt;
&lt;span id="cb32-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb32-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;'nuts_kwargs'&lt;/span&gt;: {&lt;/span&gt;
&lt;span id="cb32-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb32-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="st"&gt;'target_accept'&lt;/span&gt;: &lt;span class="fl"&gt;0.9&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb32-11"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb32-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    }&lt;/span&gt;
&lt;span id="cb32-12"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb32-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb33"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb33-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb33-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;with&lt;/span&gt; base_model:&lt;/span&gt;
&lt;span id="cb33-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb33-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    base_trace &lt;span class="op"&gt;=&lt;/span&gt; pm.sample(&lt;span class="op"&gt;**&lt;/span&gt;SAMPLE_KWARGS)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;Auto-assigning NUTS sampler...
Initializing NUTS using jitter+adapt_diag...
Multiprocess sampling (3 chains in 3 jobs)
NUTS: [β_season]
100%|██████████| 1500/1500 [00:07&amp;lt;00:00, 198.65it/s]&lt;/code&gt;&lt;/pre&gt;
&lt;h5 id="convergence-diagnostics"&gt;Convergence diagnostics&lt;/h5&gt;
&lt;p&gt;We rely on three diagnostics to ensure that our samples have converged to the posterior distribution:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Energy plots: if the two distributions in the energy plot differ significantly (espescially in the tails), the sampling was not very efficient.&lt;/li&gt;
&lt;li&gt;Bayesian fraction of missing information (BFMI): BFMI quantifies this difference with a number between zero and one. A BFMI close to (or exceeding) one is preferable, and a BFMI lower than 0.2 is indicative of efficiency issues.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blog.stata.com/2016/05/26/gelman-rubin-convergence-diagnostic-using-multiple-chains/"&gt;Gelman-Rubin statistics&lt;/a&gt;: Gelman-Rubin statistics near one are preferable, and values less than 1.1 are generally taken to indicate convergence.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more information on energy plots and BFMI consult &lt;a href="http://mc-stan.org/users/documentation/case-studies/pystan_workflow.html"&gt;&lt;em&gt;Robust Statistical Workflow with PyStan&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;div class="sourceCode" id="cb35"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb35-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb35-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;bfmi &lt;span class="op"&gt;=&lt;/span&gt; pm.bfmi(base_trace)&lt;/span&gt;
&lt;span id="cb35-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb35-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb35-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb35-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;max_gr &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="bu"&gt;max&lt;/span&gt;(&lt;/span&gt;
&lt;span id="cb35-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb35-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    np.&lt;span class="bu"&gt;max&lt;/span&gt;(gr_stats) &lt;span class="cf"&gt;for&lt;/span&gt; gr_stats &lt;span class="kw"&gt;in&lt;/span&gt; pm.gelman_rubin(base_trace).values()&lt;/span&gt;
&lt;span id="cb35-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb35-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb36"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb36-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb36-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;CONVERGENCE_TITLE &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="kw"&gt;lambda&lt;/span&gt;: &lt;span class="ss"&gt;f"BFMI = &lt;/span&gt;&lt;span class="sc"&gt;{&lt;/span&gt;bfmi&lt;span class="sc"&gt;:.2f}&lt;/span&gt;&lt;span class="ch"&gt;\n&lt;/span&gt;&lt;span class="ss"&gt;Gelman-Rubin = &lt;/span&gt;&lt;span class="sc"&gt;{&lt;/span&gt;max_gr&lt;span class="sc"&gt;:.3f}&lt;/span&gt;&lt;span class="ss"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb37"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb37-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb37-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(pm.energyplot(base_trace, legend&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;False&lt;/span&gt;, figsize&lt;span class="op"&gt;=&lt;/span&gt;(&lt;span class="dv"&gt;6&lt;/span&gt;, &lt;span class="dv"&gt;4&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb37-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb37-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;   .set_title(CONVERGENCE_TITLE()))&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt2/An%20Improved%20Analysis%20of%20NBA%20Foul%20Calls%20with%20Python_57_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;h4 id="criticize-the-model-given-data"&gt;Criticize the model given data&lt;/h4&gt;
&lt;p&gt;We use the samples from &lt;code&gt;p&lt;/code&gt;’s posterior distribution to calculate &lt;a href="https://en.wikipedia.org/wiki/Errors_and_residuals"&gt;residuals&lt;/a&gt;, which we use to criticize our models. These residuals allow us to assess how well our model describes the data-generation process and to discover unmodeled sources of variation.&lt;/p&gt;
&lt;div class="sourceCode" id="cb38"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb38-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb38-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;base_trace[&lt;span class="st"&gt;'p'&lt;/span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;array([[ 0.4052151 ,  0.30696232],
       [ 0.3937377 ,  0.30995026],
       [ 0.39881138,  0.29866616],
       ..., 
       [ 0.40279887,  0.31166828],
       [ 0.4077945 ,  0.30299785],
       [ 0.40207901,  0.29991789]])&lt;/code&gt;&lt;/pre&gt;
&lt;div class="sourceCode" id="cb40"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb40-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb40-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;resid_df &lt;span class="op"&gt;=&lt;/span&gt; (df.assign(p_hat&lt;span class="op"&gt;=&lt;/span&gt;base_trace[&lt;span class="st"&gt;'p'&lt;/span&gt;][:, df[&lt;span class="st"&gt;'season'&lt;/span&gt;]].mean(axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb40-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb40-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              .assign(resid&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="kw"&gt;lambda&lt;/span&gt; df: df[&lt;span class="st"&gt;'foul_called'&lt;/span&gt;] &lt;span class="op"&gt;-&lt;/span&gt; df[&lt;span class="st"&gt;'p_hat'&lt;/span&gt;]))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb41"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb41-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb41-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;resid_df[[&lt;span class="st"&gt;'foul_called'&lt;/span&gt;, &lt;span class="st"&gt;'p_hat'&lt;/span&gt;, &lt;span class="st"&gt;'resid'&lt;/span&gt;]].head()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div&gt;
&lt;style scoped&gt;
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
&lt;/style&gt;
&lt;center&gt;
&lt;table border="1" class="dataframe"&gt;
&lt;thead&gt;
&lt;tr style="text-align: right;"&gt;
&lt;th&gt;
&lt;/th&gt;
&lt;th&gt;
foul_called
&lt;/th&gt;
&lt;th&gt;
p_hat
&lt;/th&gt;
&lt;th&gt;
resid
&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
play_id
&lt;/th&gt;
&lt;th&gt;
&lt;/th&gt;
&lt;th&gt;
&lt;/th&gt;
&lt;th&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;
20151028INDTOR-1
&lt;/th&gt;
&lt;td&gt;
1.0
&lt;/td&gt;
&lt;td&gt;
0.403875
&lt;/td&gt;
&lt;td&gt;
0.596125
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
20151028INDTOR-2
&lt;/th&gt;
&lt;td&gt;
0.0
&lt;/td&gt;
&lt;td&gt;
0.403875
&lt;/td&gt;
&lt;td&gt;
-0.403875
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
20151028INDTOR-3
&lt;/th&gt;
&lt;td&gt;
1.0
&lt;/td&gt;
&lt;td&gt;
0.403875
&lt;/td&gt;
&lt;td&gt;
0.596125
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
20151028INDTOR-4
&lt;/th&gt;
&lt;td&gt;
0.0
&lt;/td&gt;
&lt;td&gt;
0.403875
&lt;/td&gt;
&lt;td&gt;
-0.403875
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
20151028INDTOR-6
&lt;/th&gt;
&lt;td&gt;
0.0
&lt;/td&gt;
&lt;td&gt;
0.403875
&lt;/td&gt;
&lt;td&gt;
-0.403875
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/center&gt;&lt;/div&gt;

&lt;p&gt;The per-season residuals are quite small, which is to be expected.&lt;/p&gt;
&lt;div class="sourceCode" id="cb42"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb42-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb42-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(resid_df.pivot_table(&lt;span class="st"&gt;'resid'&lt;/span&gt;, &lt;span class="st"&gt;'season'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb42-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb42-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;         .rename(index&lt;span class="op"&gt;=&lt;/span&gt;season_enc.inverse_transform))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div&gt;
&lt;style scoped&gt;
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
&lt;/style&gt;
&lt;center&gt;
&lt;table border="1" class="dataframe"&gt;
&lt;thead&gt;
&lt;tr style="text-align: right;"&gt;
&lt;th&gt;
&lt;/th&gt;
&lt;th&gt;
resid
&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
season
&lt;/th&gt;
&lt;th&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;
2015-2016
&lt;/th&gt;
&lt;td&gt;
-0.000162
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
2016-2017
&lt;/th&gt;
&lt;td&gt;
-0.000219
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/center&gt;&lt;/div&gt;

&lt;p&gt;Anyone who has watched a close basketball game will realize that we have neglected an important factor in late game foul calls — &lt;a href="https://en.wikipedia.org/wiki/Flagrant_foul#Game_tactics"&gt;intentional fouls&lt;/a&gt;. Near the end of the game, intentional fouls are used by the losing team when they are on defense to end the leading team’s possession as quickly as possible.&lt;/p&gt;
&lt;p&gt;The influence of intentional fouls in the plot below is shown by the rapidly increasing of the residuals as the number of seconds left in the game decreases.&lt;/p&gt;
&lt;div class="sourceCode" id="cb43"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb43-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb43-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; make_time_axes(ax,&lt;/span&gt;
&lt;span id="cb43-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb43-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                   xlabel&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;"Seconds remaining in game"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb43-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb43-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                   ylabel&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;"Observed foul call rate"&lt;/span&gt;):&lt;/span&gt;
&lt;span id="cb43-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb43-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.invert_xaxis()&lt;/span&gt;
&lt;span id="cb43-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb43-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.set_xlabel(xlabel)&lt;/span&gt;
&lt;span id="cb43-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb43-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb43-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb43-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;return&lt;/span&gt; make_foul_rate_yaxis(ax, label&lt;span class="op"&gt;=&lt;/span&gt;ylabel)&lt;/span&gt;
&lt;span id="cb43-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb43-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb43-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb43-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;make_time_axes(&lt;/span&gt;
&lt;span id="cb43-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb43-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    resid_df.pivot_table(&lt;span class="st"&gt;'resid'&lt;/span&gt;, &lt;span class="st"&gt;'seconds_left'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb43-11"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb43-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            .reset_index()&lt;/span&gt;
&lt;span id="cb43-12"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb43-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            .plot(&lt;span class="st"&gt;'seconds_left'&lt;/span&gt;, &lt;span class="st"&gt;'resid'&lt;/span&gt;, kind&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'scatter'&lt;/span&gt;),&lt;/span&gt;
&lt;span id="cb43-13"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb43-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ylabel&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;"Residual"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb43-14"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb43-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt2/An%20Improved%20Analysis%20of%20NBA%20Foul%20Calls%20with%20Python_65_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;h3 id="possession-model"&gt;Possession model&lt;/h3&gt;
&lt;h4 id="build-a-model-of-the-science-1"&gt;Build a model of the science&lt;/h4&gt;
&lt;p&gt;The following plot illustrates the fact that only the trailing team has any incentive to committ intentional fouls.&lt;/p&gt;
&lt;div class="sourceCode" id="cb44"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb44-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb44-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;df[&lt;span class="st"&gt;'trailing_committing'&lt;/span&gt;] &lt;span class="op"&gt;=&lt;/span&gt; (df[&lt;span class="st"&gt;'score_committing'&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb44-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb44-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                               .lt(df[&lt;span class="st"&gt;'score_disadvantaged'&lt;/span&gt;])&lt;/span&gt;
&lt;span id="cb44-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb44-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                               .mul(&lt;span class="fl"&gt;1.&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb44-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb44-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                               .astype(np.int64))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb45"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb45-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb45-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;make_time_axes(&lt;/span&gt;
&lt;span id="cb45-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb45-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    df.pivot_table(&lt;/span&gt;
&lt;span id="cb45-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb45-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="st"&gt;'foul_called'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb45-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb45-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="st"&gt;'seconds_left'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb45-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb45-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="st"&gt;'trailing_committing'&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb45-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb45-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;      )&lt;/span&gt;
&lt;span id="cb45-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb45-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;      .rolling(&lt;span class="dv"&gt;20&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb45-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb45-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;      .mean()&lt;/span&gt;
&lt;span id="cb45-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb45-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;      .rename(columns&lt;span class="op"&gt;=&lt;/span&gt;{&lt;/span&gt;
&lt;span id="cb45-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb45-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;          &lt;span class="dv"&gt;0&lt;/span&gt;: &lt;span class="st"&gt;"No"&lt;/span&gt;, &lt;span class="dv"&gt;1&lt;/span&gt;: &lt;span class="st"&gt;"Yes"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb45-11"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb45-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;      })&lt;/span&gt;
&lt;span id="cb45-12"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb45-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;      .rename_axis(&lt;/span&gt;
&lt;span id="cb45-13"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb45-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;          &lt;span class="st"&gt;"Committing team is trailing"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb45-14"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb45-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;          axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb45-15"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb45-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;      )&lt;/span&gt;
&lt;span id="cb45-16"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb45-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;      .plot()&lt;/span&gt;
&lt;span id="cb45-17"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb45-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt2/An%20Improved%20Analysis%20of%20NBA%20Foul%20Calls%20with%20Python_68_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;Intentional fouls are only useful when the trailing (and committing) team is on defense. The plot below reflects this fact; shooting and personal fouls are almost always called against the defensive player; we see that they are called at a much higher rate than offensive fouls.&lt;/p&gt;
&lt;div class="sourceCode" id="cb46"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb46-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb46-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax &lt;span class="op"&gt;=&lt;/span&gt; (df.pivot_table(&lt;span class="st"&gt;'foul_called'&lt;/span&gt;, &lt;span class="st"&gt;'call_type'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb46-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb46-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        .rename(index&lt;span class="op"&gt;=&lt;/span&gt;call_type_enc.inverse_transform)&lt;/span&gt;
&lt;span id="cb46-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb46-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        .rename_axis(&lt;span class="st"&gt;"Call type"&lt;/span&gt;, axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb46-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb46-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        .plot(kind&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'barh'&lt;/span&gt;, legend&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;False&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb46-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb46-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb46-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb46-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.xaxis.set_major_formatter(pct_formatter)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb46-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb46-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.set_xlabel(&lt;span class="st"&gt;"Observed foul call rate"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt2/An%20Improved%20Analysis%20of%20NBA%20Foul%20Calls%20with%20Python_70_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;We continue to model the differnce in foul call rates between seasons.&lt;/p&gt;
&lt;div class="sourceCode" id="cb47"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb47-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb47-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;with&lt;/span&gt; pm.Model() &lt;span class="im"&gt;as&lt;/span&gt; poss_model:&lt;/span&gt;
&lt;span id="cb47-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb47-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    β_season &lt;span class="op"&gt;=&lt;/span&gt; pm.Normal(&lt;span class="st"&gt;'β_season'&lt;/span&gt;, &lt;span class="fl"&gt;0.&lt;/span&gt;, &lt;span class="fl"&gt;5.&lt;/span&gt;, shape&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;2&lt;/span&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Throughout this post, we will use &lt;a href="https://en.wikipedia.org/wiki/Multilevel_model"&gt;hierarchical distributions&lt;/a&gt; to model the variation of foul call rates. For much more information on hierarchical models, consult &lt;a href="http://www.stat.columbia.edu/~gelman/arm/"&gt;&lt;em&gt;Data Analysis Using Regression and Multilevel/Hierarchical Models&lt;/em&gt;&lt;/a&gt;. We use the priors&lt;/p&gt;
&lt;p&gt;&lt;span class="math display"&gt;\[
\begin{align*}
    \sigma_{\textrm{call}}
        &amp;amp; \sim \operatorname{HalfNormal}(5) \\
    \beta^{\textrm{call}}_{c}
        &amp;amp; \sim \operatorname{Hierarchical-Normal}(0, \sigma_{\textrm{call}}^2).
\end{align*}
\]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;For sampling efficiency, we use an [non-centered parametrization](http://twiecki.github.io/blog/2017/02/08/bayesian-hierchical-non-centered/#The-Funnel-of-Hell-(and-how-to-escape-it%29) of the hierarchical normal distribution.&lt;/p&gt;
&lt;div class="sourceCode" id="cb48"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb48-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb48-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; hierarchical_normal(name, shape, σ_shape&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;):&lt;/span&gt;
&lt;span id="cb48-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb48-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    Δ &lt;span class="op"&gt;=&lt;/span&gt; pm.Normal(&lt;span class="ss"&gt;f'Δ_&lt;/span&gt;&lt;span class="sc"&gt;{&lt;/span&gt;name&lt;span class="sc"&gt;}&lt;/span&gt;&lt;span class="ss"&gt;'&lt;/span&gt;, &lt;span class="fl"&gt;0.&lt;/span&gt;, &lt;span class="fl"&gt;1.&lt;/span&gt;, shape&lt;span class="op"&gt;=&lt;/span&gt;shape)&lt;/span&gt;
&lt;span id="cb48-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb48-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    σ &lt;span class="op"&gt;=&lt;/span&gt; pm.HalfNormal(&lt;span class="ss"&gt;f'σ_&lt;/span&gt;&lt;span class="sc"&gt;{&lt;/span&gt;name&lt;span class="sc"&gt;}&lt;/span&gt;&lt;span class="ss"&gt;'&lt;/span&gt;, &lt;span class="fl"&gt;5.&lt;/span&gt;, shape&lt;span class="op"&gt;=&lt;/span&gt;σ_shape)&lt;/span&gt;
&lt;span id="cb48-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb48-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb48-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb48-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;return&lt;/span&gt; pm.Deterministic(name, Δ &lt;span class="op"&gt;*&lt;/span&gt; σ)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Each call type has a different foul call rate.&lt;/p&gt;
&lt;div class="sourceCode" id="cb49"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb49-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb49-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;with&lt;/span&gt; poss_model:&lt;/span&gt;
&lt;span id="cb49-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb49-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    β_call &lt;span class="op"&gt;=&lt;/span&gt; hierarchical_normal(&lt;span class="st"&gt;'β_call'&lt;/span&gt;, n_call_type)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We add score difference and the number of possessions by which the committing team is trailing to the &lt;code&gt;DataFrame&lt;/code&gt;.&lt;/p&gt;
&lt;div class="sourceCode" id="cb50"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb50-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb50-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;df[&lt;span class="st"&gt;'score_diff'&lt;/span&gt;] &lt;span class="op"&gt;=&lt;/span&gt; (df[&lt;span class="st"&gt;'score_disadvantaged'&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb50-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb50-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                      .sub(df[&lt;span class="st"&gt;'score_committing'&lt;/span&gt;]))&lt;/span&gt;
&lt;span id="cb50-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb50-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb50-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb50-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;df[&lt;span class="st"&gt;'trailing_poss'&lt;/span&gt;] &lt;span class="op"&gt;=&lt;/span&gt; (df[&lt;span class="st"&gt;'score_diff'&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb50-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb50-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                         .div(&lt;span class="dv"&gt;3&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb50-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb50-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                         .&lt;span class="bu"&gt;apply&lt;/span&gt;(np.ceil))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb51"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb51-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb51-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;trailing_poss_enc &lt;span class="op"&gt;=&lt;/span&gt; LabelEncoder().fit(df[&lt;span class="st"&gt;'trailing_poss'&lt;/span&gt;])&lt;/span&gt;
&lt;span id="cb51-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb51-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;trailing_poss &lt;span class="op"&gt;=&lt;/span&gt; trailing_poss_enc.transform(df[&lt;span class="st"&gt;'trailing_poss'&lt;/span&gt;])&lt;/span&gt;
&lt;span id="cb51-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb51-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;n_trailing_poss &lt;span class="op"&gt;=&lt;/span&gt; trailing_poss_enc.classes_.size&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The plot below shows that the foul call rate (over time) varies based on the score difference (quantized into possessions) between the disadvanted team and the committing team. We assume that at most three points can be scored in a single possession (while this is not quite correct, &lt;a href="https://en.wikipedia.org/wiki/Four-point_play"&gt;four-point plays&lt;/a&gt; are rare enough that we do not account for them in our analysis).&lt;/p&gt;
&lt;div class="sourceCode" id="cb52"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb52-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb52-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;make_time_axes(&lt;/span&gt;
&lt;span id="cb52-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb52-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    df.pivot_table(&lt;/span&gt;
&lt;span id="cb52-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb52-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="st"&gt;'foul_called'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb52-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb52-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="st"&gt;'seconds_left'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb52-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb52-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="st"&gt;'trailing_poss'&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb52-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb52-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;      )&lt;/span&gt;
&lt;span id="cb52-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb52-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;      .loc[:, &lt;span class="dv"&gt;1&lt;/span&gt;:&lt;span class="dv"&gt;3&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb52-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb52-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;      .rolling(&lt;span class="dv"&gt;20&lt;/span&gt;).mean()&lt;/span&gt;
&lt;span id="cb52-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb52-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;      .rename_axis(&lt;/span&gt;
&lt;span id="cb52-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb52-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;          &lt;span class="st"&gt;"Trailing possessions&lt;/span&gt;&lt;span class="ch"&gt;\n&lt;/span&gt;&lt;span class="st"&gt;(committing team)"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb52-11"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb52-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;          axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb52-12"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb52-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;      )&lt;/span&gt;
&lt;span id="cb52-13"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb52-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;      .plot()&lt;/span&gt;
&lt;span id="cb52-14"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb52-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt2/An%20Improved%20Analysis%20of%20NBA%20Foul%20Calls%20with%20Python_81_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;The plot below reflects the fact that intentional fouls are disproportionately personal fouls; the rate at which personal fouls are called increases drastically as the game nears its end.&lt;/p&gt;
&lt;div class="sourceCode" id="cb53"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb53-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb53-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;make_time_axes(&lt;/span&gt;
&lt;span id="cb53-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb53-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    df.pivot_table(&lt;span class="st"&gt;'foul_called'&lt;/span&gt;, &lt;span class="st"&gt;'seconds_left'&lt;/span&gt;, &lt;span class="st"&gt;'call_type'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb53-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb53-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;      .rolling(&lt;span class="dv"&gt;20&lt;/span&gt;).mean()&lt;/span&gt;
&lt;span id="cb53-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb53-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;      .rename(columns&lt;span class="op"&gt;=&lt;/span&gt;call_type_enc.inverse_transform)&lt;/span&gt;
&lt;span id="cb53-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb53-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;      .rename_axis(&lt;span class="va"&gt;None&lt;/span&gt;, axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb53-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb53-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;      .plot()&lt;/span&gt;
&lt;span id="cb53-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb53-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt2/An%20Improved%20Analysis%20of%20NBA%20Foul%20Calls%20with%20Python_83_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;Due to the NBA’s &lt;a href="https://en.wikipedia.org/wiki/Shot_clock"&gt;shot clock&lt;/a&gt;, the natural timescale of a basketball game is possessions, not seconds, remaining.&lt;/p&gt;
&lt;div class="sourceCode" id="cb54"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb54-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb54-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;df[&lt;span class="st"&gt;'remaining_poss'&lt;/span&gt;] &lt;span class="op"&gt;=&lt;/span&gt; (df[&lt;span class="st"&gt;'seconds_left'&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb54-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb54-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                          .floordiv(&lt;span class="dv"&gt;25&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb54-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb54-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                          .add(&lt;span class="dv"&gt;1&lt;/span&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb55"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb55-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb55-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;remaining_poss_enc &lt;span class="op"&gt;=&lt;/span&gt; LabelEncoder().fit(df[&lt;span class="st"&gt;'remaining_poss'&lt;/span&gt;])&lt;/span&gt;
&lt;span id="cb55-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb55-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;remaining_poss &lt;span class="op"&gt;=&lt;/span&gt; remaining_poss_enc.transform(df[&lt;span class="st"&gt;'remaining_poss'&lt;/span&gt;])&lt;/span&gt;
&lt;span id="cb55-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb55-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;n_remaining_poss &lt;span class="op"&gt;=&lt;/span&gt; remaining_poss_enc.classes_.size&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Below we plot the foul call rate across trailing possession/remaining posession pairs. Note that we always calculate trailing possessions (&lt;code&gt;trailing_poss&lt;/code&gt;) from the perspective of the committing team. For instance, &lt;code&gt;trailing_poss = 1&lt;/code&gt; indicates that the committing team is trailing by 1-3 points, whereas &lt;code&gt;trailing_poss = -1&lt;/code&gt; indicates that the committing team is leading by 1-3 points.&lt;/p&gt;
&lt;div class="sourceCode" id="cb56"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb56-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb56-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax &lt;span class="op"&gt;=&lt;/span&gt; sns.heatmap(&lt;/span&gt;
&lt;span id="cb56-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb56-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    df.pivot_table(&lt;/span&gt;
&lt;span id="cb56-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb56-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="st"&gt;'foul_called'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb56-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb56-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="st"&gt;'trailing_poss'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb56-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb56-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="st"&gt;'remaining_poss'&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb56-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb56-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;      )&lt;/span&gt;
&lt;span id="cb56-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb56-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;      .rename_axis(&lt;/span&gt;
&lt;span id="cb56-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb56-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;          &lt;span class="st"&gt;"Trailing possessions&lt;/span&gt;&lt;span class="ch"&gt;\n&lt;/span&gt;&lt;span class="st"&gt;(committing team)"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb56-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb56-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;          axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb56-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb56-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;      )&lt;/span&gt;
&lt;span id="cb56-11"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb56-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;      .rename_axis(&lt;span class="st"&gt;"Remaining possessions"&lt;/span&gt;, axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;),&lt;/span&gt;
&lt;span id="cb56-12"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb56-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    cmap&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'seismic'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb56-13"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb56-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    cbar_kws&lt;span class="op"&gt;=&lt;/span&gt;{&lt;span class="st"&gt;'format'&lt;/span&gt;: pct_formatter}&lt;/span&gt;
&lt;span id="cb56-14"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb56-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;/span&gt;
&lt;span id="cb56-15"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb56-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb56-16"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb56-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.invert_yaxis()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb56-17"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb56-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.set_title(&lt;span class="st"&gt;"Observed foul call rate"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt2/An%20Improved%20Analysis%20of%20NBA%20Foul%20Calls%20with%20Python_88_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;The heatmap above shows that the foul call rate increases significantly when the committing team is trailing by more than the number of possessions remaining in the game. That is, teams resort to intentional fouls only when the opposing team can run out the clock and guarantee a win. (Since we have quantized the score difference and time into posessions, this conclusion is not entirely correct; it is, however, correct enough for our purposes.)&lt;/p&gt;
&lt;div class="sourceCode" id="cb57"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb57-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb57-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;call_name_df &lt;span class="op"&gt;=&lt;/span&gt; df.assign(&lt;/span&gt;
&lt;span id="cb57-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb57-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    call_type&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="kw"&gt;lambda&lt;/span&gt; df: call_type_enc.inverse_transform(&lt;/span&gt;
&lt;span id="cb57-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb57-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        df[&lt;span class="st"&gt;'call_type'&lt;/span&gt;].values&lt;/span&gt;
&lt;span id="cb57-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb57-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    )&lt;/span&gt;
&lt;span id="cb57-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb57-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;/span&gt;
&lt;span id="cb57-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb57-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb57-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb57-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;diff_df &lt;span class="op"&gt;=&lt;/span&gt; (pd.merge(&lt;/span&gt;
&lt;span id="cb57-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb57-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                call_name_df,&lt;/span&gt;
&lt;span id="cb57-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb57-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                call_name_df.groupby(&lt;span class="st"&gt;'call_type'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb57-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb57-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                            [&lt;span class="st"&gt;'foul_called'&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb57-11"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb57-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                            .mean()&lt;/span&gt;
&lt;span id="cb57-12"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb57-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                            .rename(&lt;span class="st"&gt;'avg_foul_called'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb57-13"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb57-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                            .reset_index()&lt;/span&gt;
&lt;span id="cb57-14"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb57-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;             )&lt;/span&gt;
&lt;span id="cb57-15"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb57-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;             .assign(diff&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="kw"&gt;lambda&lt;/span&gt; df: df[&lt;span class="st"&gt;'foul_called'&lt;/span&gt;] &lt;span class="op"&gt;-&lt;/span&gt; df[&lt;span class="st"&gt;'avg_foul_called'&lt;/span&gt;]))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The heatmaps below are broken out by call type, and show the difference between the foul call rate for each trailing/remaining possession combination and the overall foul call rate for the call type in question&lt;/p&gt;
&lt;div class="sourceCode" id="cb58"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb58-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb58-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; plot_foul_diff_heatmap(&lt;span class="op"&gt;*&lt;/span&gt;_, data&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;None&lt;/span&gt;, &lt;span class="op"&gt;**&lt;/span&gt;kwargs):&lt;/span&gt;
&lt;span id="cb58-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb58-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax &lt;span class="op"&gt;=&lt;/span&gt; plt.gca()&lt;/span&gt;
&lt;span id="cb58-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb58-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb58-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb58-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    sns.heatmap(&lt;/span&gt;
&lt;span id="cb58-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb58-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        data.pivot_table(&lt;/span&gt;
&lt;span id="cb58-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb58-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            &lt;span class="st"&gt;'diff'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb58-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb58-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            &lt;span class="st"&gt;'trailing_poss'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb58-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb58-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            &lt;span class="st"&gt;'remaining_poss'&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb58-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb58-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        ),&lt;/span&gt;
&lt;span id="cb58-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb58-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        cmap&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'seismic'&lt;/span&gt;, robust&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb58-11"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb58-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        cbar_kws&lt;span class="op"&gt;=&lt;/span&gt;{&lt;span class="st"&gt;'format'&lt;/span&gt;: pct_formatter}&lt;/span&gt;
&lt;span id="cb58-12"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb58-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    )&lt;/span&gt;
&lt;span id="cb58-13"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb58-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb58-14"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb58-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.invert_yaxis()&lt;/span&gt;
&lt;span id="cb58-15"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb58-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.set_title(&lt;span class="st"&gt;"Observed foul call rate"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb58-16"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb58-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb58-17"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb58-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(sns.FacetGrid(diff_df, col&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'call_type'&lt;/span&gt;, col_wrap&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;3&lt;/span&gt;, aspect&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;1.5&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb58-18"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb58-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    .map_dataframe(plot_foul_diff_heatmap)&lt;/span&gt;
&lt;span id="cb58-19"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb58-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    .set_axis_labels(&lt;/span&gt;
&lt;span id="cb58-20"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb58-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="st"&gt;"Remaining possessions"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb58-21"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb58-21" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="st"&gt;"Trailing possessions&lt;/span&gt;&lt;span class="ch"&gt;\n&lt;/span&gt;&lt;span class="st"&gt;(committing team)"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb58-22"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb58-22" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    )&lt;/span&gt;
&lt;span id="cb58-23"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb58-23" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    .set_titles(&lt;span class="st"&gt;"&lt;/span&gt;&lt;span class="sc"&gt;{col_name}&lt;/span&gt;&lt;span class="st"&gt;"&lt;/span&gt;))&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt2/An%20Improved%20Analysis%20of%20NBA%20Foul%20Calls%20with%20Python_92_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;These plots confirm that most intentional fouls are personal fouls. They also show that the three-way interaction between trailing possesions, remaining possessions, and call type are important to model foul call rates.&lt;/p&gt;
&lt;p&gt;&lt;span class="math display"&gt;\[
\begin{align*}
    \sigma_{\textrm{poss}, c}
            &amp;amp; \sim \operatorname{HalfNormal}(5) \\
    \beta^{\textrm{poss}}_{t, r, c}
        &amp;amp; \sim \operatorname{Hierarchical-Normal}(0, \sigma_{\textrm{poss}, c}^2)
\end{align*}    
\]&lt;/span&gt;&lt;/p&gt;
&lt;div class="sourceCode" id="cb59"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb59-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb59-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;with&lt;/span&gt; poss_model:&lt;/span&gt;
&lt;span id="cb59-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb59-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    β_poss &lt;span class="op"&gt;=&lt;/span&gt; hierarchical_normal(&lt;/span&gt;
&lt;span id="cb59-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb59-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="st"&gt;'β_poss'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb59-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb59-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        (n_trailing_poss, n_remaining_poss, n_call_type),&lt;/span&gt;
&lt;span id="cb59-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb59-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        σ_shape&lt;span class="op"&gt;=&lt;/span&gt;(&lt;span class="dv"&gt;1&lt;/span&gt;, &lt;span class="dv"&gt;1&lt;/span&gt;, n_call_type)&lt;/span&gt;
&lt;span id="cb59-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb59-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    )&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The foul call rate is a combination of season, call type, and possession factors.&lt;/p&gt;
&lt;p&gt;&lt;span class="math display"&gt;\[\eta^{\textrm{game}}_k = \beta^{\textrm{season}}_{s(k)} + \beta^{\textrm{call}}_{c(k)} + \beta^{\textrm{poss}}_{t(k),r(k),c(k)}\]&lt;/span&gt;&lt;/p&gt;
&lt;div class="sourceCode" id="cb60"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb60-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb60-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;call_type &lt;span class="op"&gt;=&lt;/span&gt; df[&lt;span class="st"&gt;'call_type'&lt;/span&gt;].values&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb61"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb61-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb61-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;with&lt;/span&gt; poss_model:&lt;/span&gt;
&lt;span id="cb61-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb61-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    η_game &lt;span class="op"&gt;=&lt;/span&gt; β_season[season] &lt;span class="op"&gt;\&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb61-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb61-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                &lt;span class="op"&gt;+&lt;/span&gt; β_call[call_type] &lt;span class="op"&gt;\&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb61-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb61-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                &lt;span class="op"&gt;+&lt;/span&gt; β_poss[&lt;/span&gt;
&lt;span id="cb61-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb61-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                    trailing_poss,&lt;/span&gt;
&lt;span id="cb61-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb61-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                    remaining_poss,&lt;/span&gt;
&lt;span id="cb61-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb61-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                    call_type&lt;/span&gt;
&lt;span id="cb61-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb61-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                ]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span class="math display"&gt;\[
\begin{align*}
p_k
    &amp;amp; = \operatorname{sigm}\left(\eta^{\textrm{game}}_k\right)
\end{align*}
\]&lt;/span&gt;&lt;/p&gt;
&lt;div class="sourceCode" id="cb62"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb62-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb62-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;with&lt;/span&gt; poss_model:&lt;/span&gt;
&lt;span id="cb62-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb62-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    p &lt;span class="op"&gt;=&lt;/span&gt; pm.Deterministic(&lt;span class="st"&gt;'p'&lt;/span&gt;, pm.math.sigmoid(η_game))&lt;/span&gt;
&lt;span id="cb62-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb62-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    y &lt;span class="op"&gt;=&lt;/span&gt; pm.Bernoulli(&lt;span class="st"&gt;'y'&lt;/span&gt;, p, observed&lt;span class="op"&gt;=&lt;/span&gt;df[&lt;span class="st"&gt;'foul_called'&lt;/span&gt;])&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="infer-the-model-given-data-1"&gt;Infer the model given data&lt;/h4&gt;
&lt;p&gt;Again, we sample from the model’s posterior distribution.&lt;/p&gt;
&lt;div class="sourceCode" id="cb63"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb63-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb63-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;with&lt;/span&gt; poss_model:&lt;/span&gt;
&lt;span id="cb63-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb63-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    poss_trace &lt;span class="op"&gt;=&lt;/span&gt; pm.sample(&lt;span class="op"&gt;**&lt;/span&gt;SAMPLE_KWARGS)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;Auto-assigning NUTS sampler...
Initializing NUTS using jitter+adapt_diag...
Multiprocess sampling (3 chains in 3 jobs)
NUTS: [σ_β_poss_log__, Δ_β_poss, σ_β_call_log__, Δ_β_call, β_season]
100%|██████████| 1500/1500 [07:56&amp;lt;00:00,  3.15it/s]
There were 5 divergences after tuning. Increase `target_accept` or reparameterize.
There were 10 divergences after tuning. Increase `target_accept` or reparameterize.
There were 9 divergences after tuning. Increase `target_accept` or reparameterize.
The number of effective samples is smaller than 25% for some parameters.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The BFMI and Gelman-Rubin statistics for this model indicate no problems with sampling and good convergence.&lt;/p&gt;
&lt;div class="sourceCode" id="cb65"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb65-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb65-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;bfmi &lt;span class="op"&gt;=&lt;/span&gt; pm.bfmi(poss_trace)&lt;/span&gt;
&lt;span id="cb65-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb65-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb65-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb65-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;max_gr &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="bu"&gt;max&lt;/span&gt;(&lt;/span&gt;
&lt;span id="cb65-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb65-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    np.&lt;span class="bu"&gt;max&lt;/span&gt;(gr_stats) &lt;span class="cf"&gt;for&lt;/span&gt; gr_stats &lt;span class="kw"&gt;in&lt;/span&gt; pm.gelman_rubin(poss_trace).values()&lt;/span&gt;
&lt;span id="cb65-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb65-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb66"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb66-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb66-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(pm.energyplot(poss_trace, legend&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;False&lt;/span&gt;, figsize&lt;span class="op"&gt;=&lt;/span&gt;(&lt;span class="dv"&gt;6&lt;/span&gt;, &lt;span class="dv"&gt;4&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb66-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb66-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;   .set_title(CONVERGENCE_TITLE()))&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt2/An%20Improved%20Analysis%20of%20NBA%20Foul%20Calls%20with%20Python_105_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;h4 id="criticize-the-model-given-data-1"&gt;Criticize the model given data&lt;/h4&gt;
&lt;p&gt;Again, we calculate residuals.&lt;/p&gt;
&lt;div class="sourceCode" id="cb67"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb67-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb67-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;resid_df &lt;span class="op"&gt;=&lt;/span&gt; (df.assign(p_hat&lt;span class="op"&gt;=&lt;/span&gt;poss_trace[&lt;span class="st"&gt;'p'&lt;/span&gt;].mean(axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb67-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb67-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              .assign(resid&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="kw"&gt;lambda&lt;/span&gt; df: df.foul_called &lt;span class="op"&gt;-&lt;/span&gt; df.p_hat))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The following plots show that, grouped various ways, the residuals for this model are relatively well-distributed.&lt;/p&gt;
&lt;div class="sourceCode" id="cb68"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb68-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb68-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax &lt;span class="op"&gt;=&lt;/span&gt; sns.heatmap(&lt;/span&gt;
&lt;span id="cb68-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb68-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    resid_df.pivot_table(&lt;/span&gt;
&lt;span id="cb68-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb68-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                &lt;span class="st"&gt;'resid'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb68-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb68-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                &lt;span class="st"&gt;'trailing_poss'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb68-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb68-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                &lt;span class="st"&gt;'remaining_poss'&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb68-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb68-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            )&lt;/span&gt;
&lt;span id="cb68-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb68-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            .rename_axis(&lt;/span&gt;
&lt;span id="cb68-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb68-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                &lt;span class="st"&gt;"Trailing possessions&lt;/span&gt;&lt;span class="ch"&gt;\n&lt;/span&gt;&lt;span class="st"&gt;(committing team)"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb68-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb68-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb68-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb68-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            )&lt;/span&gt;
&lt;span id="cb68-11"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb68-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            .rename_axis(&lt;/span&gt;
&lt;span id="cb68-12"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb68-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                &lt;span class="st"&gt;"Remaining possessions"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb68-13"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb68-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb68-14"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb68-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            )&lt;/span&gt;
&lt;span id="cb68-15"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb68-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            .loc[&lt;span class="op"&gt;-&lt;/span&gt;&lt;span class="dv"&gt;3&lt;/span&gt;:&lt;span class="dv"&gt;3&lt;/span&gt;],&lt;/span&gt;
&lt;span id="cb68-16"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb68-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    cmap&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'seismic'&lt;/span&gt;, &lt;/span&gt;
&lt;span id="cb68-17"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb68-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    cbar_kws&lt;span class="op"&gt;=&lt;/span&gt;{&lt;span class="st"&gt;'format'&lt;/span&gt;: pct_formatter}&lt;/span&gt;
&lt;span id="cb68-18"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb68-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;/span&gt;
&lt;span id="cb68-19"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb68-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb68-20"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb68-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.invert_yaxis()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb68-21"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb68-21" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.set_title(&lt;span class="st"&gt;"Observed foul call rate"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt2/An%20Improved%20Analysis%20of%20NBA%20Foul%20Calls%20with%20Python_109_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;div class="sourceCode" id="cb69"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb69-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb69-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;N_BIN &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="dv"&gt;20&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb69-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb69-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb69-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb69-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;bin_ix, bins &lt;span class="op"&gt;=&lt;/span&gt; pd.qcut(&lt;/span&gt;
&lt;span id="cb69-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb69-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    resid_df.p_hat, N_BIN,&lt;/span&gt;
&lt;span id="cb69-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb69-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    labels&lt;span class="op"&gt;=&lt;/span&gt;np.arange(N_BIN),&lt;/span&gt;
&lt;span id="cb69-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb69-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    retbins&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb69-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb69-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb70"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb70-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb70-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax &lt;span class="op"&gt;=&lt;/span&gt; (resid_df.groupby(bins[bin_ix])&lt;/span&gt;
&lt;span id="cb70-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb70-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              .resid.mean()&lt;/span&gt;
&lt;span id="cb70-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb70-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              .rename_axis(&lt;span class="st"&gt;'p_hat'&lt;/span&gt;, axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb70-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb70-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              .reset_index()&lt;/span&gt;
&lt;span id="cb70-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb70-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              .plot(&lt;span class="st"&gt;'p_hat'&lt;/span&gt;, &lt;span class="st"&gt;'resid'&lt;/span&gt;, kind&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'scatter'&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb70-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb70-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb70-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb70-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.xaxis.set_major_formatter(pct_formatter)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb70-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb70-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.set_xlabel(&lt;span class="vs"&gt;r"Binned $\hat&lt;/span&gt;&lt;span class="sc"&gt;{p}&lt;/span&gt;&lt;span class="vs"&gt;$"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb70-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb70-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb70-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb70-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;make_foul_rate_yaxis(ax, label&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;"Residual"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt2/An%20Improved%20Analysis%20of%20NBA%20Foul%20Calls%20with%20Python_111_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;div class="sourceCode" id="cb71"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb71-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb71-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax &lt;span class="op"&gt;=&lt;/span&gt; (resid_df.groupby(&lt;span class="st"&gt;'seconds_left'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb71-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb71-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              .resid.mean()&lt;/span&gt;
&lt;span id="cb71-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb71-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              .reset_index()&lt;/span&gt;
&lt;span id="cb71-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb71-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              .plot(&lt;span class="st"&gt;'seconds_left'&lt;/span&gt;, &lt;span class="st"&gt;'resid'&lt;/span&gt;, kind&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'scatter'&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb71-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb71-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;make_time_axes(ax, ylabel&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;"Residual"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt2/An%20Improved%20Analysis%20of%20NBA%20Foul%20Calls%20with%20Python_112_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;h4 id="model-selection"&gt;Model selection&lt;/h4&gt;
&lt;p&gt;Now that we have two models, we can engage in &lt;a href="https://en.wikipedia.org/wiki/Model_selection"&gt;model selection&lt;/a&gt;. We use the &lt;a href="http://www.jmlr.org/papers/volume14/watanabe13a/watanabe13a.pdf"&gt;widely applicable Bayesian information criterion&lt;/a&gt; (&lt;a href="http://www.stat.columbia.edu/~gelman/research/published/waic_understand3.pdf"&gt;WAIC&lt;/a&gt;) for model selection.&lt;/p&gt;
&lt;div class="sourceCode" id="cb72"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb72-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb72-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;MODEL_NAME_MAP &lt;span class="op"&gt;=&lt;/span&gt; {&lt;/span&gt;
&lt;span id="cb72-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb72-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="dv"&gt;0&lt;/span&gt;: &lt;span class="st"&gt;"Base"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb72-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb72-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="dv"&gt;1&lt;/span&gt;: &lt;span class="st"&gt;"Possession"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb72-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb72-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb73"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb73-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb73-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;comp_df &lt;span class="op"&gt;=&lt;/span&gt; (pm.compare(&lt;/span&gt;
&lt;span id="cb73-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb73-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                (base_trace, poss_trace),&lt;/span&gt;
&lt;span id="cb73-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb73-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                (base_model, poss_model)&lt;/span&gt;
&lt;span id="cb73-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb73-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;             )&lt;/span&gt;
&lt;span id="cb73-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb73-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;             .rename(index&lt;span class="op"&gt;=&lt;/span&gt;MODEL_NAME_MAP)&lt;/span&gt;
&lt;span id="cb73-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb73-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;             .loc[MODEL_NAME_MAP.values()])&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Since smaller WAICs are better, the possession model clearly outperforms the base model.&lt;/p&gt;
&lt;div class="sourceCode" id="cb74"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb74-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb74-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;comp_df&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div&gt;
&lt;style scoped&gt;
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
&lt;/style&gt;
&lt;center&gt;
&lt;table border="1" class="dataframe"&gt;
&lt;thead&gt;
&lt;tr style="text-align: right;"&gt;
&lt;th&gt;
&lt;/th&gt;
&lt;th&gt;
WAIC
&lt;/th&gt;
&lt;th&gt;
pWAIC
&lt;/th&gt;
&lt;th&gt;
dWAIC
&lt;/th&gt;
&lt;th&gt;
weight
&lt;/th&gt;
&lt;th&gt;
SE
&lt;/th&gt;
&lt;th&gt;
dSE
&lt;/th&gt;
&lt;th&gt;
var_warn
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;
Base
&lt;/th&gt;
&lt;td&gt;
11610.1
&lt;/td&gt;
&lt;td&gt;
2.11
&lt;/td&gt;
&lt;td&gt;
1541.98
&lt;/td&gt;
&lt;td&gt;
0
&lt;/td&gt;
&lt;td&gt;
56.9
&lt;/td&gt;
&lt;td&gt;
73.43
&lt;/td&gt;
&lt;td&gt;
0
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
Possession
&lt;/th&gt;
&lt;td&gt;
10068.1
&lt;/td&gt;
&lt;td&gt;
82.93
&lt;/td&gt;
&lt;td&gt;
0
&lt;/td&gt;
&lt;td&gt;
1
&lt;/td&gt;
&lt;td&gt;
88.05
&lt;/td&gt;
&lt;td&gt;
0
&lt;/td&gt;
&lt;td&gt;
0
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/center&gt;
&lt;/div&gt;
&lt;div class="sourceCode" id="cb75"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb75-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb75-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;fig, ax &lt;span class="op"&gt;=&lt;/span&gt; plt.subplots()&lt;/span&gt;
&lt;span id="cb75-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb75-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb75-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb75-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.errorbar(&lt;/span&gt;
&lt;span id="cb75-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb75-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    np.arange(&lt;span class="bu"&gt;len&lt;/span&gt;(MODEL_NAME_MAP)),&lt;/span&gt;
&lt;span id="cb75-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb75-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    comp_df.WAIC,&lt;/span&gt;
&lt;span id="cb75-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb75-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    yerr&lt;span class="op"&gt;=&lt;/span&gt;comp_df.SE, fmt&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'o'&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb75-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb75-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb75-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb75-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb75-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb75-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.set_xticks(np.arange(&lt;span class="bu"&gt;len&lt;/span&gt;(MODEL_NAME_MAP)))&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb75-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb75-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.set_xticklabels(comp_df.index)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb75-11"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb75-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.set_xlabel(&lt;span class="st"&gt;"Model"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb75-12"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb75-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb75-13"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb75-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.set_ylabel(&lt;span class="st"&gt;"WAIC"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt2/An%20Improved%20Analysis%20of%20NBA%20Foul%20Calls%20with%20Python_118_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;h3 id="player-item-response-theory-model"&gt;Player item-response theory model&lt;/h3&gt;
&lt;h4 id="build-a-model-of-the-science-2"&gt;Build a model of the science&lt;/h4&gt;
&lt;p&gt;We now turn to the question of whether or not committing and/or drawing fouls is a measurable skill. We use an &lt;a href="https://en.wikipedia.org/wiki/Item_response_theory"&gt;item-response theory&lt;/a&gt; (IRT) model to study this question. For more information on Bayesian item-response models, consult the following references.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.stat.columbia.edu/~gelman/research/published/171.pdf"&gt;&lt;em&gt;Practical Issues in Implementing and Understanding Bayesian Ideal Point Estimation&lt;/em&gt;&lt;/a&gt; is an excellent introduction to applied Bayesian IRT models and has inspired much of this work.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.springer.com/us/book/9781441907417"&gt;&lt;em&gt;Bayesian Item Response Modeling — Theory and Applications&lt;/em&gt;&lt;/a&gt; is a comprehensive mathematical overview of Bayesien IRT modeling.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The item-response theory model includes the season, call type, and possession terms of the previous models.&lt;/p&gt;
&lt;div class="sourceCode" id="cb76"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb76-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb76-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;with&lt;/span&gt; pm.Model() &lt;span class="im"&gt;as&lt;/span&gt; irt_model:&lt;/span&gt;
&lt;span id="cb76-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb76-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    β_season &lt;span class="op"&gt;=&lt;/span&gt; pm.Normal(&lt;span class="st"&gt;'β_season'&lt;/span&gt;, &lt;span class="fl"&gt;0.&lt;/span&gt;, &lt;span class="fl"&gt;5.&lt;/span&gt;, shape&lt;span class="op"&gt;=&lt;/span&gt;n_season)&lt;/span&gt;
&lt;span id="cb76-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb76-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    β_call &lt;span class="op"&gt;=&lt;/span&gt; hierarchical_normal(&lt;span class="st"&gt;'β_call'&lt;/span&gt;, n_call_type)&lt;/span&gt;
&lt;span id="cb76-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb76-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    β_poss &lt;span class="op"&gt;=&lt;/span&gt; hierarchical_normal(&lt;/span&gt;
&lt;span id="cb76-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb76-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="st"&gt;'β_poss'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb76-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb76-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        (n_trailing_poss, n_remaining_poss, n_call_type),&lt;/span&gt;
&lt;span id="cb76-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb76-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        σ_shape&lt;span class="op"&gt;=&lt;/span&gt;(&lt;span class="dv"&gt;1&lt;/span&gt;, &lt;span class="dv"&gt;1&lt;/span&gt;, n_call_type)&lt;/span&gt;
&lt;span id="cb76-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb76-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    )&lt;/span&gt;
&lt;span id="cb76-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb76-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb76-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb76-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    η_game &lt;span class="op"&gt;=&lt;/span&gt; β_season[season] &lt;span class="op"&gt;\&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb76-11"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb76-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                &lt;span class="op"&gt;+&lt;/span&gt; β_call[call_type] &lt;span class="op"&gt;\&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb76-12"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb76-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                &lt;span class="op"&gt;+&lt;/span&gt; β_poss[&lt;/span&gt;
&lt;span id="cb76-13"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb76-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                    trailing_poss,&lt;/span&gt;
&lt;span id="cb76-14"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb76-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                    remaining_poss,&lt;/span&gt;
&lt;span id="cb76-15"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb76-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                    call_type&lt;/span&gt;
&lt;span id="cb76-16"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb76-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                ]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Each disadvantaged player has an ideal point (per season).&lt;/p&gt;
&lt;p&gt;&lt;span class="math display"&gt;\[
\begin{align*}
    \sigma_{\theta}
        &amp;amp; \sim \operatorname{HalfNormal}(5) \\
    \theta^{\textrm{player}}_{i, s}
        &amp;amp; \sim \operatorname{Hierarchical-Normal}(0, \sigma_{\theta}^2)
\end{align*}
\]&lt;/span&gt;&lt;/p&gt;
&lt;div class="sourceCode" id="cb77"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb77-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb77-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;player_disadvantaged &lt;span class="op"&gt;=&lt;/span&gt; df[&lt;span class="st"&gt;'player_disadvantaged'&lt;/span&gt;].values&lt;/span&gt;
&lt;span id="cb77-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb77-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;n_player &lt;span class="op"&gt;=&lt;/span&gt; player_enc.classes_.size&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb78"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb78-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb78-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;with&lt;/span&gt; irt_model:&lt;/span&gt;
&lt;span id="cb78-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb78-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    θ_player &lt;span class="op"&gt;=&lt;/span&gt; hierarchical_normal(&lt;/span&gt;
&lt;span id="cb78-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb78-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="st"&gt;'θ_player'&lt;/span&gt;, (n_player, n_season)&lt;/span&gt;
&lt;span id="cb78-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb78-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    )&lt;/span&gt;
&lt;span id="cb78-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb78-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    θ &lt;span class="op"&gt;=&lt;/span&gt; θ_player[player_disadvantaged, season]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Each committing player has an ideal point (per season).&lt;/p&gt;
&lt;p&gt;&lt;span class="math display"&gt;\[
\begin{align*}
    \sigma_{b}
        &amp;amp; \sim \operatorname{HalfNormal}(5) \\
    b^{\textrm{player}}_{j, s}
        &amp;amp; \sim \operatorname{Hierarchical-Normal}(0, \sigma_{b}^2)
\end{align*}    
\]&lt;/span&gt;&lt;/p&gt;
&lt;div class="sourceCode" id="cb79"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb79-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb79-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;player_committing &lt;span class="op"&gt;=&lt;/span&gt; df[&lt;span class="st"&gt;'player_committing'&lt;/span&gt;].values&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb80"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb80-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb80-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;with&lt;/span&gt; irt_model:&lt;/span&gt;
&lt;span id="cb80-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb80-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    b_player &lt;span class="op"&gt;=&lt;/span&gt; hierarchical_normal(&lt;/span&gt;
&lt;span id="cb80-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb80-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="st"&gt;'b_player'&lt;/span&gt;, (n_player, n_season)&lt;/span&gt;
&lt;span id="cb80-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb80-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    )&lt;/span&gt;
&lt;span id="cb80-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb80-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    b &lt;span class="op"&gt;=&lt;/span&gt; b_player[player_committing, season]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Players affect the foul call rate through the difference in their ideal points.&lt;/p&gt;
&lt;p&gt;&lt;span class="math display"&gt;\[\eta^{\textrm{player}}_k = \theta_k - b_k\]&lt;/span&gt;&lt;/p&gt;
&lt;div class="sourceCode" id="cb81"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb81-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb81-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;with&lt;/span&gt; irt_model:&lt;/span&gt;
&lt;span id="cb81-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb81-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    η_player &lt;span class="op"&gt;=&lt;/span&gt; θ &lt;span class="op"&gt;-&lt;/span&gt; b&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The sum of the game and player effects determines the foul call probability.&lt;/p&gt;
&lt;p&gt;&lt;span class="math display"&gt;\[\eta_k = \eta^{\textrm{game}}_k + \eta^{\textrm{player}}_k\]&lt;/span&gt;&lt;/p&gt;
&lt;div class="sourceCode" id="cb82"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb82-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb82-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;with&lt;/span&gt; irt_model:&lt;/span&gt;
&lt;span id="cb82-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb82-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    η &lt;span class="op"&gt;=&lt;/span&gt; η_game &lt;span class="op"&gt;+&lt;/span&gt; η_player&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb83"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb83-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb83-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;with&lt;/span&gt; irt_model:&lt;/span&gt;
&lt;span id="cb83-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb83-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    p &lt;span class="op"&gt;=&lt;/span&gt; pm.Deterministic(&lt;span class="st"&gt;'p'&lt;/span&gt;, pm.math.sigmoid(η))&lt;/span&gt;
&lt;span id="cb83-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb83-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    y &lt;span class="op"&gt;=&lt;/span&gt; pm.Bernoulli(&lt;/span&gt;
&lt;span id="cb83-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb83-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="st"&gt;'y'&lt;/span&gt;, p,&lt;/span&gt;
&lt;span id="cb83-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb83-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        observed&lt;span class="op"&gt;=&lt;/span&gt;df[&lt;span class="st"&gt;'foul_called'&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb83-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb83-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    )&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="infer-the-model-given-data-2"&gt;Infer the model given data&lt;/h4&gt;
&lt;p&gt;Again, we sample from the model’s posterior distribution.&lt;/p&gt;
&lt;div class="sourceCode" id="cb84"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb84-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb84-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;with&lt;/span&gt; irt_model:&lt;/span&gt;
&lt;span id="cb84-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb84-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    irt_trace &lt;span class="op"&gt;=&lt;/span&gt; pm.sample(&lt;span class="op"&gt;**&lt;/span&gt;SAMPLE_KWARGS)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;Auto-assigning NUTS sampler...
Initializing NUTS using jitter+adapt_diag...
Multiprocess sampling (3 chains in 3 jobs)
NUTS: [σ_b_player_log__, Δ_b_player, σ_θ_player_log__, Δ_θ_player, σ_β_poss_log__, Δ_β_poss, σ_β_call_log__, Δ_β_call, β_season]
100%|██████████| 1500/1500 [13:55&amp;lt;00:00,  1.80it/s]
There were 3 divergences after tuning. Increase `target_accept` or reparameterize.
There were 1 divergences after tuning. Increase `target_accept` or reparameterize.
There were 4 divergences after tuning. Increase `target_accept` or reparameterize.
The estimated number of effective samples is smaller than 200 for some parameters.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;None of the sampling diagnostics indicate problems with convergence.&lt;/p&gt;
&lt;div class="sourceCode" id="cb86"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb86-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb86-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;bfmi &lt;span class="op"&gt;=&lt;/span&gt; pm.bfmi(irt_trace)&lt;/span&gt;
&lt;span id="cb86-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb86-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb86-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb86-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;max_gr &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="bu"&gt;max&lt;/span&gt;(&lt;/span&gt;
&lt;span id="cb86-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb86-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    np.&lt;span class="bu"&gt;max&lt;/span&gt;(gr_stats) &lt;span class="cf"&gt;for&lt;/span&gt; gr_stats &lt;span class="kw"&gt;in&lt;/span&gt; pm.gelman_rubin(irt_trace).values()&lt;/span&gt;
&lt;span id="cb86-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb86-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb87"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb87-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb87-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(pm.energyplot(irt_trace, legend&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;False&lt;/span&gt;, figsize&lt;span class="op"&gt;=&lt;/span&gt;(&lt;span class="dv"&gt;6&lt;/span&gt;, &lt;span class="dv"&gt;4&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb87-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb87-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;   .set_title(CONVERGENCE_TITLE()))&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt2/An%20Improved%20Analysis%20of%20NBA%20Foul%20Calls%20with%20Python_139_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;h3 id="criticize-the-model-given-data-take-three"&gt;Criticize the model given data, take three&lt;/h3&gt;
&lt;p&gt;The binned residuals for this model are more asymmetric than for the previous models, but still not too bad.&lt;/p&gt;
&lt;div class="sourceCode" id="cb88"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb88-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb88-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;resid_df &lt;span class="op"&gt;=&lt;/span&gt; (df.assign(p_hat&lt;span class="op"&gt;=&lt;/span&gt;irt_trace[&lt;span class="st"&gt;'p'&lt;/span&gt;].mean(axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb88-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb88-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              .assign(resid&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="kw"&gt;lambda&lt;/span&gt; df: df[&lt;span class="st"&gt;'foul_called'&lt;/span&gt;] &lt;span class="op"&gt;-&lt;/span&gt; df[&lt;span class="st"&gt;'p_hat'&lt;/span&gt;]))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb89"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb89-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb89-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;N_BIN &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="dv"&gt;50&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb89-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb89-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb89-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb89-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;bin_ix, bins &lt;span class="op"&gt;=&lt;/span&gt; pd.qcut(&lt;/span&gt;
&lt;span id="cb89-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb89-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    resid_df.p_hat, N_BIN,&lt;/span&gt;
&lt;span id="cb89-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb89-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    labels&lt;span class="op"&gt;=&lt;/span&gt;np.arange(N_BIN),&lt;/span&gt;
&lt;span id="cb89-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb89-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    retbins&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb89-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb89-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb90"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb90-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb90-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax &lt;span class="op"&gt;=&lt;/span&gt; (resid_df.groupby(bins[bin_ix])&lt;/span&gt;
&lt;span id="cb90-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb90-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              .resid.mean()&lt;/span&gt;
&lt;span id="cb90-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb90-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              .rename_axis(&lt;span class="st"&gt;'p_hat'&lt;/span&gt;, axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb90-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb90-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              .reset_index()&lt;/span&gt;
&lt;span id="cb90-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb90-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              .plot(&lt;span class="st"&gt;'p_hat'&lt;/span&gt;, &lt;span class="st"&gt;'resid'&lt;/span&gt;, kind&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'scatter'&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb90-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb90-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb90-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb90-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.xaxis.set_major_formatter(pct_formatter)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb90-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb90-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.set_xlabel(&lt;span class="vs"&gt;r"Binned $\hat&lt;/span&gt;&lt;span class="sc"&gt;{p}&lt;/span&gt;&lt;span class="vs"&gt;$"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb90-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb90-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb90-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb90-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;make_foul_rate_yaxis(ax, label&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;"Residual"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt2/An%20Improved%20Analysis%20of%20NBA%20Foul%20Calls%20with%20Python_144_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;div class="sourceCode" id="cb91"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb91-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb91-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax &lt;span class="op"&gt;=&lt;/span&gt; (resid_df.groupby(&lt;span class="st"&gt;'seconds_left'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb91-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb91-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              .resid.mean()&lt;/span&gt;
&lt;span id="cb91-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb91-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              .reset_index()&lt;/span&gt;
&lt;span id="cb91-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb91-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              .plot(&lt;span class="st"&gt;'seconds_left'&lt;/span&gt;, &lt;span class="st"&gt;'resid'&lt;/span&gt;, kind&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'scatter'&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb91-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb91-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;make_time_axes(ax, ylabel&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;"Residual"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt2/An%20Improved%20Analysis%20of%20NBA%20Foul%20Calls%20with%20Python_145_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;h4 id="model-selection-1"&gt;Model selection&lt;/h4&gt;
&lt;p&gt;The IRT model is a marginal improvement over the possession model in terms of WAIC.&lt;/p&gt;
&lt;div class="sourceCode" id="cb92"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb92-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb92-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;MODEL_NAME_MAP[&lt;span class="dv"&gt;2&lt;/span&gt;] &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="st"&gt;"IRT"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb92-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb92-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb92-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb92-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;comp_df &lt;span class="op"&gt;=&lt;/span&gt; (pm.compare(&lt;/span&gt;
&lt;span id="cb92-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb92-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                (base_trace, poss_trace, irt_trace),&lt;/span&gt;
&lt;span id="cb92-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb92-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                (base_model, poss_model, irt_model)&lt;/span&gt;
&lt;span id="cb92-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb92-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;             )&lt;/span&gt;
&lt;span id="cb92-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb92-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;             .rename(index&lt;span class="op"&gt;=&lt;/span&gt;MODEL_NAME_MAP)&lt;/span&gt;
&lt;span id="cb92-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb92-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;             .loc[MODEL_NAME_MAP.values()])&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb93"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb93-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb93-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;comp_df&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div&gt;
&lt;style scoped&gt;
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
&lt;/style&gt;
&lt;center&gt;
&lt;table border="1" class="dataframe"&gt;
&lt;thead&gt;
&lt;tr style="text-align: right;"&gt;
&lt;th&gt;
&lt;/th&gt;
&lt;th&gt;
WAIC
&lt;/th&gt;
&lt;th&gt;
pWAIC
&lt;/th&gt;
&lt;th&gt;
dWAIC
&lt;/th&gt;
&lt;th&gt;
weight
&lt;/th&gt;
&lt;th&gt;
SE
&lt;/th&gt;
&lt;th&gt;
dSE
&lt;/th&gt;
&lt;th&gt;
var_warn
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;
Base
&lt;/th&gt;
&lt;td&gt;
11610.1
&lt;/td&gt;
&lt;td&gt;
2.11
&lt;/td&gt;
&lt;td&gt;
1566.92
&lt;/td&gt;
&lt;td&gt;
0
&lt;/td&gt;
&lt;td&gt;
56.9
&lt;/td&gt;
&lt;td&gt;
74.03
&lt;/td&gt;
&lt;td&gt;
0
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
Possession
&lt;/th&gt;
&lt;td&gt;
10068.1
&lt;/td&gt;
&lt;td&gt;
82.93
&lt;/td&gt;
&lt;td&gt;
24.94
&lt;/td&gt;
&lt;td&gt;
0.08
&lt;/td&gt;
&lt;td&gt;
88.05
&lt;/td&gt;
&lt;td&gt;
10.99
&lt;/td&gt;
&lt;td&gt;
0
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
IRT
&lt;/th&gt;
&lt;td&gt;
10043.2
&lt;/td&gt;
&lt;td&gt;
216.6
&lt;/td&gt;
&lt;td&gt;
0
&lt;/td&gt;
&lt;td&gt;
0.91
&lt;/td&gt;
&lt;td&gt;
88.47
&lt;/td&gt;
&lt;td&gt;
0
&lt;/td&gt;
&lt;td&gt;
0
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/center&gt;
&lt;/div&gt;
&lt;div class="sourceCode" id="cb94"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb94-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb94-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;fig, ax &lt;span class="op"&gt;=&lt;/span&gt; plt.subplots()&lt;/span&gt;
&lt;span id="cb94-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb94-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.errorbar(&lt;/span&gt;
&lt;span id="cb94-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb94-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    np.arange(&lt;span class="bu"&gt;len&lt;/span&gt;(MODEL_NAME_MAP)), comp_df.WAIC,&lt;/span&gt;
&lt;span id="cb94-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb94-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    yerr&lt;span class="op"&gt;=&lt;/span&gt;comp_df.SE, fmt&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'o'&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb94-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb94-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb94-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb94-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.set_xticks(np.arange(&lt;span class="bu"&gt;len&lt;/span&gt;(MODEL_NAME_MAP)))&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb94-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb94-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.set_xticklabels(comp_df.index)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb94-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb94-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.set_xlabel(&lt;span class="st"&gt;"Model"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb94-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb94-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.set_ylabel(&lt;span class="st"&gt;"WAIC"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt2/An%20Improved%20Analysis%20of%20NBA%20Foul%20Calls%20with%20Python_149_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;We now produce two &lt;code&gt;DataFrame&lt;/code&gt;s containing the estimated player ideal points per season.&lt;/p&gt;
&lt;div class="sourceCode" id="cb95"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb95-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb95-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; varname_to_param(varname):&lt;/span&gt;
&lt;span id="cb95-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb95-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;return&lt;/span&gt; varname[&lt;span class="dv"&gt;0&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb95-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb95-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb95-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb95-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; varname_to_player(varname):&lt;/span&gt;
&lt;span id="cb95-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb95-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;return&lt;/span&gt; &lt;span class="bu"&gt;int&lt;/span&gt;(varname[&lt;span class="dv"&gt;3&lt;/span&gt;:&lt;span class="op"&gt;-&lt;/span&gt;&lt;span class="dv"&gt;2&lt;/span&gt;])&lt;/span&gt;
&lt;span id="cb95-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb95-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb95-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb95-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; varname_to_season(varname):&lt;/span&gt;
&lt;span id="cb95-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb95-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;return&lt;/span&gt; &lt;span class="bu"&gt;int&lt;/span&gt;(varname[&lt;span class="op"&gt;-&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;])&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb96"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb96-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb96-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;irt_df &lt;span class="op"&gt;=&lt;/span&gt; (pm.trace_to_dataframe(&lt;/span&gt;
&lt;span id="cb96-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb96-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                irt_trace, varnames&lt;span class="op"&gt;=&lt;/span&gt;[&lt;span class="st"&gt;'θ_player'&lt;/span&gt;, &lt;span class="st"&gt;'b_player'&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb96-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb96-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            )&lt;/span&gt;
&lt;span id="cb96-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb96-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            .rename(columns&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="kw"&gt;lambda&lt;/span&gt; col: col.replace(&lt;span class="st"&gt;'_player'&lt;/span&gt;, &lt;span class="st"&gt;''&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb96-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb96-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            .T&lt;/span&gt;
&lt;span id="cb96-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb96-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            .&lt;span class="bu"&gt;apply&lt;/span&gt;(&lt;/span&gt;
&lt;span id="cb96-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb96-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;               &lt;span class="kw"&gt;lambda&lt;/span&gt; s: pd.Series.describe(&lt;/span&gt;
&lt;span id="cb96-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb96-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                   s, percentiles&lt;span class="op"&gt;=&lt;/span&gt;[&lt;span class="fl"&gt;0.055&lt;/span&gt;, &lt;span class="fl"&gt;0.945&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb96-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb96-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;               ),&lt;/span&gt;
&lt;span id="cb96-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb96-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;               axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb96-11"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb96-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            )&lt;/span&gt;
&lt;span id="cb96-12"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb96-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            [[&lt;span class="st"&gt;'mean'&lt;/span&gt;, &lt;span class="st"&gt;'5.5%'&lt;/span&gt;, &lt;span class="st"&gt;'94.5%'&lt;/span&gt;]]&lt;/span&gt;
&lt;span id="cb96-13"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb96-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            .rename(columns&lt;span class="op"&gt;=&lt;/span&gt;{&lt;/span&gt;
&lt;span id="cb96-14"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb96-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                 &lt;span class="st"&gt;'5.5%'&lt;/span&gt;: &lt;span class="st"&gt;'low'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb96-15"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb96-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                 &lt;span class="st"&gt;'94.5%'&lt;/span&gt;: &lt;span class="st"&gt;'high'&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb96-16"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb96-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            })&lt;/span&gt;
&lt;span id="cb96-17"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb96-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            .rename_axis(&lt;span class="st"&gt;'varname'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb96-18"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb96-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            .reset_index()&lt;/span&gt;
&lt;span id="cb96-19"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb96-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            .assign(&lt;/span&gt;
&lt;span id="cb96-20"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb96-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                param&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="kw"&gt;lambda&lt;/span&gt; df: df[&lt;span class="st"&gt;'varname'&lt;/span&gt;].&lt;span class="bu"&gt;apply&lt;/span&gt;(varname_to_param),&lt;/span&gt;
&lt;span id="cb96-21"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb96-21" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                player&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="kw"&gt;lambda&lt;/span&gt; df: df[&lt;span class="st"&gt;'varname'&lt;/span&gt;].&lt;span class="bu"&gt;apply&lt;/span&gt;(varname_to_player),&lt;/span&gt;
&lt;span id="cb96-22"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb96-22" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                season&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="kw"&gt;lambda&lt;/span&gt; df: df[&lt;span class="st"&gt;'varname'&lt;/span&gt;].&lt;span class="bu"&gt;apply&lt;/span&gt;(varname_to_season)&lt;/span&gt;
&lt;span id="cb96-23"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb96-23" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            )&lt;/span&gt;
&lt;span id="cb96-24"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb96-24" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            .drop(&lt;span class="st"&gt;'varname'&lt;/span&gt;, axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb97"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb97-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb97-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;irt_df.head()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div&gt;
&lt;style scoped&gt;
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
&lt;/style&gt;
&lt;center&gt;
&lt;table border="1" class="dataframe"&gt;
&lt;thead&gt;
&lt;tr style="text-align: right;"&gt;
&lt;th&gt;
&lt;/th&gt;
&lt;th&gt;
mean
&lt;/th&gt;
&lt;th&gt;
low
&lt;/th&gt;
&lt;th&gt;
high
&lt;/th&gt;
&lt;th&gt;
param
&lt;/th&gt;
&lt;th&gt;
player
&lt;/th&gt;
&lt;th&gt;
season
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;
0
&lt;/th&gt;
&lt;td&gt;
-0.016516
&lt;/td&gt;
&lt;td&gt;
-0.323127
&lt;/td&gt;
&lt;td&gt;
0.272022
&lt;/td&gt;
&lt;td&gt;
θ
&lt;/td&gt;
&lt;td&gt;
0
&lt;/td&gt;
&lt;td&gt;
0
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
1
&lt;/th&gt;
&lt;td&gt;
0.003845
&lt;/td&gt;
&lt;td&gt;
-0.289842
&lt;/td&gt;
&lt;td&gt;
0.290248
&lt;/td&gt;
&lt;td&gt;
θ
&lt;/td&gt;
&lt;td&gt;
0
&lt;/td&gt;
&lt;td&gt;
1
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
2
&lt;/th&gt;
&lt;td&gt;
0.008519
&lt;/td&gt;
&lt;td&gt;
-0.289715
&lt;/td&gt;
&lt;td&gt;
0.304574
&lt;/td&gt;
&lt;td&gt;
θ
&lt;/td&gt;
&lt;td&gt;
1
&lt;/td&gt;
&lt;td&gt;
0
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
3
&lt;/th&gt;
&lt;td&gt;
0.031367
&lt;/td&gt;
&lt;td&gt;
-0.240853
&lt;/td&gt;
&lt;td&gt;
0.339297
&lt;/td&gt;
&lt;td&gt;
θ
&lt;/td&gt;
&lt;td&gt;
1
&lt;/td&gt;
&lt;td&gt;
1
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
4
&lt;/th&gt;
&lt;td&gt;
-0.037530
&lt;/td&gt;
&lt;td&gt;
-0.320010
&lt;/td&gt;
&lt;td&gt;
0.226110
&lt;/td&gt;
&lt;td&gt;
θ
&lt;/td&gt;
&lt;td&gt;
2
&lt;/td&gt;
&lt;td&gt;
0
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/center&gt;
&lt;/div&gt;
&lt;div class="sourceCode" id="cb98"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb98-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb98-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;player_irt_df &lt;span class="op"&gt;=&lt;/span&gt; irt_df.pivot_table(&lt;/span&gt;
&lt;span id="cb98-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb98-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    index&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'player'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb98-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb98-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    columns&lt;span class="op"&gt;=&lt;/span&gt;[&lt;span class="st"&gt;'param'&lt;/span&gt;, &lt;span class="st"&gt;'season'&lt;/span&gt;],&lt;/span&gt;
&lt;span id="cb98-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb98-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    values&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'mean'&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb98-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb98-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb99"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb99-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb99-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;player_irt_df.head()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div&gt;
&lt;style scoped&gt;
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead tr th {
        text-align: left;
    }

    .dataframe thead tr:last-of-type th {
        text-align: right;
    }
&lt;/style&gt;
&lt;center&gt;
&lt;table border="1" class="dataframe"&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;
param
&lt;/th&gt;
&lt;th colspan="2" halign="left"&gt;
b
&lt;/th&gt;
&lt;th colspan="2" halign="left"&gt;
θ
&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
season
&lt;/th&gt;
&lt;th&gt;
0
&lt;/th&gt;
&lt;th&gt;
1
&lt;/th&gt;
&lt;th&gt;
0
&lt;/th&gt;
&lt;th&gt;
1
&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
player
&lt;/th&gt;
&lt;th&gt;
&lt;/th&gt;
&lt;th&gt;
&lt;/th&gt;
&lt;th&gt;
&lt;/th&gt;
&lt;th&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;
0
&lt;/th&gt;
&lt;td&gt;
-0.069235
&lt;/td&gt;
&lt;td&gt;
0.013339
&lt;/td&gt;
&lt;td&gt;
-0.016516
&lt;/td&gt;
&lt;td&gt;
0.003845
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
1
&lt;/th&gt;
&lt;td&gt;
-0.003003
&lt;/td&gt;
&lt;td&gt;
0.001853
&lt;/td&gt;
&lt;td&gt;
0.008519
&lt;/td&gt;
&lt;td&gt;
0.031367
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
2
&lt;/th&gt;
&lt;td&gt;
0.084515
&lt;/td&gt;
&lt;td&gt;
0.089058
&lt;/td&gt;
&lt;td&gt;
-0.037530
&lt;/td&gt;
&lt;td&gt;
-0.002373
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
3
&lt;/th&gt;
&lt;td&gt;
-0.028946
&lt;/td&gt;
&lt;td&gt;
0.004360
&lt;/td&gt;
&lt;td&gt;
0.003514
&lt;/td&gt;
&lt;td&gt;
-0.000334
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
4
&lt;/th&gt;
&lt;td&gt;
-0.001976
&lt;/td&gt;
&lt;td&gt;
0.280380
&lt;/td&gt;
&lt;td&gt;
0.072932
&lt;/td&gt;
&lt;td&gt;
0.005571
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/center&gt;
&lt;/div&gt;
&lt;p&gt;The following plot shows that the committing skill appears to be somewhat larger than the disadvantaged skill. This difference seems reasonable because most fouls are committed by the player on defense; committing skill is quite likely to be correlated with defensive ability.&lt;/p&gt;
&lt;div class="sourceCode" id="cb100"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb100-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; plot_latent_params(df):&lt;/span&gt;
&lt;span id="cb100-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    fig, ax &lt;span class="op"&gt;=&lt;/span&gt; plt.subplots()&lt;/span&gt;
&lt;span id="cb100-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb100-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    n, _ &lt;span class="op"&gt;=&lt;/span&gt; df.shape&lt;/span&gt;
&lt;span id="cb100-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    y &lt;span class="op"&gt;=&lt;/span&gt; np.arange(n)&lt;/span&gt;
&lt;span id="cb100-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb100-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.errorbar(&lt;/span&gt;
&lt;span id="cb100-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        df[&lt;span class="st"&gt;'mean'&lt;/span&gt;], y,&lt;/span&gt;
&lt;span id="cb100-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        xerr&lt;span class="op"&gt;=&lt;/span&gt;(df[[&lt;span class="st"&gt;'high'&lt;/span&gt;, &lt;span class="st"&gt;'low'&lt;/span&gt;]]&lt;/span&gt;
&lt;span id="cb100-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                .sub(df[&lt;span class="st"&gt;'mean'&lt;/span&gt;], axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb100-11"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                .&lt;span class="bu"&gt;abs&lt;/span&gt;()&lt;/span&gt;
&lt;span id="cb100-12"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                .values.T),&lt;/span&gt;
&lt;span id="cb100-13"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        fmt&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'o'&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb100-14"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    )&lt;/span&gt;
&lt;span id="cb100-15"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb100-16"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.set_yticks(y)&lt;/span&gt;
&lt;span id="cb100-17"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.set_yticklabels(&lt;/span&gt;
&lt;span id="cb100-18"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        player_enc.inverse_transform(df.player)&lt;/span&gt;
&lt;span id="cb100-19"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    )&lt;/span&gt;
&lt;span id="cb100-20"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.set_ylabel(&lt;span class="st"&gt;"Player"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb100-21"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-21" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb100-22"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-22" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;return&lt;/span&gt; fig, ax&lt;/span&gt;
&lt;span id="cb100-23"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-23" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb100-24"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-24" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;fig, axes &lt;span class="op"&gt;=&lt;/span&gt; plt.subplots(&lt;/span&gt;
&lt;span id="cb100-25"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-25" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ncols&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;2&lt;/span&gt;, nrows&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;2&lt;/span&gt;, sharex&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb100-26"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-26" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    figsize&lt;span class="op"&gt;=&lt;/span&gt;(&lt;span class="dv"&gt;16&lt;/span&gt;, &lt;span class="dv"&gt;8&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb100-27"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-27" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;/span&gt;
&lt;span id="cb100-28"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-28" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(θ&lt;span class="dv"&gt;0&lt;/span&gt;&lt;span class="er"&gt;_ax&lt;/span&gt;, θ&lt;span class="dv"&gt;1&lt;/span&gt;&lt;span class="er"&gt;_ax&lt;/span&gt;), (b0_ax, b1_ax) &lt;span class="op"&gt;=&lt;/span&gt; axes&lt;/span&gt;
&lt;span id="cb100-29"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-29" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb100-30"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-30" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;bins &lt;span class="op"&gt;=&lt;/span&gt; np.linspace(&lt;/span&gt;
&lt;span id="cb100-31"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-31" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="fl"&gt;0.9&lt;/span&gt; &lt;span class="op"&gt;*&lt;/span&gt; irt_df[&lt;span class="st"&gt;'mean'&lt;/span&gt;].&lt;span class="bu"&gt;min&lt;/span&gt;(),&lt;/span&gt;
&lt;span id="cb100-32"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-32" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="fl"&gt;1.1&lt;/span&gt; &lt;span class="op"&gt;*&lt;/span&gt; irt_df[&lt;span class="st"&gt;'mean'&lt;/span&gt;].&lt;span class="bu"&gt;max&lt;/span&gt;(),&lt;/span&gt;
&lt;span id="cb100-33"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-33" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="dv"&gt;75&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb100-34"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-34" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;/span&gt;
&lt;span id="cb100-35"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-35" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb100-36"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-36" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;θ&lt;span class="dv"&gt;0&lt;/span&gt;&lt;span class="er"&gt;_ax&lt;/span&gt;.hist(&lt;/span&gt;
&lt;span id="cb100-37"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-37" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    player_irt_df[&lt;span class="st"&gt;'θ'&lt;/span&gt;, &lt;span class="dv"&gt;0&lt;/span&gt;],&lt;/span&gt;
&lt;span id="cb100-38"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-38" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    bins&lt;span class="op"&gt;=&lt;/span&gt;bins, normed&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb100-39"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-39" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb100-40"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-40" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;θ&lt;span class="dv"&gt;1&lt;/span&gt;&lt;span class="er"&gt;_ax&lt;/span&gt;.hist(&lt;/span&gt;
&lt;span id="cb100-41"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-41" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    player_irt_df[&lt;span class="st"&gt;'θ'&lt;/span&gt;, &lt;span class="dv"&gt;1&lt;/span&gt;],&lt;/span&gt;
&lt;span id="cb100-42"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-42" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    bins&lt;span class="op"&gt;=&lt;/span&gt;bins, normed&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb100-43"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-43" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb100-44"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-44" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb100-45"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-45" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;θ&lt;span class="dv"&gt;0&lt;/span&gt;&lt;span class="er"&gt;_ax&lt;/span&gt;.set_yticks([])&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb100-46"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-46" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;θ&lt;span class="dv"&gt;0&lt;/span&gt;&lt;span class="er"&gt;_ax&lt;/span&gt;.set_title(&lt;/span&gt;
&lt;span id="cb100-47"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-47" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="vs"&gt;r"$\hat{\theta}$ ("&lt;/span&gt; &lt;span class="op"&gt;+&lt;/span&gt; season_enc.inverse_transform(&lt;span class="dv"&gt;0&lt;/span&gt;) &lt;span class="op"&gt;+&lt;/span&gt; &lt;span class="st"&gt;")"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb100-48"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-48" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb100-49"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-49" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb100-50"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-50" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;θ&lt;span class="dv"&gt;1&lt;/span&gt;&lt;span class="er"&gt;_ax&lt;/span&gt;.set_yticks([])&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb100-51"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-51" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;θ&lt;span class="dv"&gt;1&lt;/span&gt;&lt;span class="er"&gt;_ax&lt;/span&gt;.set_title(&lt;/span&gt;
&lt;span id="cb100-52"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-52" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="vs"&gt;r"$\hat{\theta}$ ("&lt;/span&gt; &lt;span class="op"&gt;+&lt;/span&gt; season_enc.inverse_transform(&lt;span class="dv"&gt;1&lt;/span&gt;) &lt;span class="op"&gt;+&lt;/span&gt; &lt;span class="st"&gt;")"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb100-53"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-53" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb100-54"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-54" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb100-55"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-55" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;b0_ax.hist(&lt;/span&gt;
&lt;span id="cb100-56"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-56" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    player_irt_df[&lt;span class="st"&gt;'b'&lt;/span&gt;, &lt;span class="dv"&gt;0&lt;/span&gt;],&lt;/span&gt;
&lt;span id="cb100-57"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-57" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    bins&lt;span class="op"&gt;=&lt;/span&gt;bins, normed&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;, color&lt;span class="op"&gt;=&lt;/span&gt;green&lt;/span&gt;
&lt;span id="cb100-58"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-58" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb100-59"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-59" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;b1_ax.hist(&lt;/span&gt;
&lt;span id="cb100-60"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-60" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    player_irt_df[&lt;span class="st"&gt;'b'&lt;/span&gt;, &lt;span class="dv"&gt;1&lt;/span&gt;],&lt;/span&gt;
&lt;span id="cb100-61"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-61" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    bins&lt;span class="op"&gt;=&lt;/span&gt;bins, normed&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;, color&lt;span class="op"&gt;=&lt;/span&gt;green&lt;/span&gt;
&lt;span id="cb100-62"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-62" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb100-63"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-63" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb100-64"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-64" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;b0_ax.set_xlabel(&lt;/span&gt;
&lt;span id="cb100-65"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-65" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="vs"&gt;r"$\hat&lt;/span&gt;&lt;span class="sc"&gt;{b}&lt;/span&gt;&lt;span class="vs"&gt;$ ("&lt;/span&gt; &lt;span class="op"&gt;+&lt;/span&gt; season_enc.inverse_transform(&lt;span class="dv"&gt;0&lt;/span&gt;) &lt;span class="op"&gt;+&lt;/span&gt; &lt;span class="st"&gt;")"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb100-66"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-66" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb100-67"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-67" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb100-68"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-68" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;b0_ax.invert_yaxis()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb100-69"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-69" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;b0_ax.xaxis.tick_top()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb100-70"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-70" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;b0_ax.set_yticks([])&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb100-71"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-71" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb100-72"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-72" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;b1_ax.set_xlabel(&lt;/span&gt;
&lt;span id="cb100-73"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-73" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="vs"&gt;r"$\hat&lt;/span&gt;&lt;span class="sc"&gt;{b}&lt;/span&gt;&lt;span class="vs"&gt;$ ("&lt;/span&gt; &lt;span class="op"&gt;+&lt;/span&gt; season_enc.inverse_transform(&lt;span class="dv"&gt;1&lt;/span&gt;) &lt;span class="op"&gt;+&lt;/span&gt; &lt;span class="st"&gt;")"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb100-74"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-74" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb100-75"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-75" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb100-76"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-76" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;b1_ax.invert_yaxis()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb100-77"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-77" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;b1_ax.xaxis.tick_top()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb100-78"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-78" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;b1_ax.set_yticks([])&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb100-79"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-79" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb100-80"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-80" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;fig.suptitle(&lt;span class="st"&gt;"Disadvantaged skill"&lt;/span&gt;, size&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;18&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb100-81"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-81" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;fig.text(&lt;span class="fl"&gt;0.45&lt;/span&gt;, &lt;span class="fl"&gt;0.02&lt;/span&gt;, &lt;span class="st"&gt;"Committing skill"&lt;/span&gt;, size&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;18&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb100-82"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb100-82" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;fig.tight_layout()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt2/An%20Improved%20Analysis%20of%20NBA%20Foul%20Calls%20with%20Python_157_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;We now examine the top and bottom ten players in each ability, across both seasons.&lt;/p&gt;
&lt;p&gt;The top players in terms of disadvantaged ability tend to be good scorers (Jimmy Butler, Ricky Rubio, John Wall, Andre Iguodala). The presence of DeAndre Jordan in the top ten may to be due to the hack-a-Shaq phenomenon. In future work, it would be interesting to control for the disavantage player’s free throw percentage in order to mitigate the influence of the hack-a-Shaq effect on the measurement of latent skill.&lt;/p&gt;
&lt;p&gt;Interestingly, the bottom players (in terms of disadvantaged ability) include many stars (Pau Gasol, Carmelo Anthony, Kevin Durant, Kawhi Leonard). The presence of these stars in the bottom may somewhat counteract the pervasive narrative that referees favor stars in their foul calls.&lt;/p&gt;
&lt;div class="sourceCode" id="cb101"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb101-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb101-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;top_bot_irt_df &lt;span class="op"&gt;=&lt;/span&gt; (irt_df.groupby(&lt;span class="st"&gt;'param'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb101-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb101-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                    .&lt;span class="bu"&gt;apply&lt;/span&gt;(&lt;/span&gt;
&lt;span id="cb101-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb101-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                        &lt;span class="kw"&gt;lambda&lt;/span&gt; df: pd.concat((&lt;/span&gt;
&lt;span id="cb101-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb101-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                df.nlargest(&lt;span class="dv"&gt;10&lt;/span&gt;, &lt;span class="st"&gt;'mean'&lt;/span&gt;),&lt;/span&gt;
&lt;span id="cb101-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb101-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                df.nsmallest(&lt;span class="dv"&gt;10&lt;/span&gt;, &lt;span class="st"&gt;'mean'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb101-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb101-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                            ),&lt;/span&gt;
&lt;span id="cb101-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb101-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                            axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;, ignore_index&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb101-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb101-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                        )&lt;/span&gt;
&lt;span id="cb101-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb101-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                    )&lt;/span&gt;
&lt;span id="cb101-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb101-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                    .reset_index(drop&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb102"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb102-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb102-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;top_bot_irt_df.head()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div&gt;
&lt;style scoped&gt;
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
&lt;/style&gt;
&lt;center&gt;
&lt;table border="1" class="dataframe"&gt;
&lt;thead&gt;
&lt;tr style="text-align: right;"&gt;
&lt;th&gt;
&lt;/th&gt;
&lt;th&gt;
mean
&lt;/th&gt;
&lt;th&gt;
low
&lt;/th&gt;
&lt;th&gt;
high
&lt;/th&gt;
&lt;th&gt;
param
&lt;/th&gt;
&lt;th&gt;
player
&lt;/th&gt;
&lt;th&gt;
season
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;
0
&lt;/th&gt;
&lt;td&gt;
0.351946
&lt;/td&gt;
&lt;td&gt;
-0.026786
&lt;/td&gt;
&lt;td&gt;
0.762273
&lt;/td&gt;
&lt;td&gt;
b
&lt;/td&gt;
&lt;td&gt;
86
&lt;/td&gt;
&lt;td&gt;
0
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
1
&lt;/th&gt;
&lt;td&gt;
0.320737
&lt;/td&gt;
&lt;td&gt;
-0.027064
&lt;/td&gt;
&lt;td&gt;
0.713128
&lt;/td&gt;
&lt;td&gt;
b
&lt;/td&gt;
&lt;td&gt;
23
&lt;/td&gt;
&lt;td&gt;
0
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
2
&lt;/th&gt;
&lt;td&gt;
0.280380
&lt;/td&gt;
&lt;td&gt;
-0.071020
&lt;/td&gt;
&lt;td&gt;
0.695970
&lt;/td&gt;
&lt;td&gt;
b
&lt;/td&gt;
&lt;td&gt;
4
&lt;/td&gt;
&lt;td&gt;
1
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
3
&lt;/th&gt;
&lt;td&gt;
0.279678
&lt;/td&gt;
&lt;td&gt;
-0.057249
&lt;/td&gt;
&lt;td&gt;
0.647667
&lt;/td&gt;
&lt;td&gt;
b
&lt;/td&gt;
&lt;td&gt;
462
&lt;/td&gt;
&lt;td&gt;
1
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
4
&lt;/th&gt;
&lt;td&gt;
0.271735
&lt;/td&gt;
&lt;td&gt;
-0.106795
&lt;/td&gt;
&lt;td&gt;
0.676231
&lt;/td&gt;
&lt;td&gt;
b
&lt;/td&gt;
&lt;td&gt;
78
&lt;/td&gt;
&lt;td&gt;
0
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/center&gt;
&lt;/div&gt;
&lt;div class="sourceCode" id="cb103"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb103-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb103-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;fig, ax &lt;span class="op"&gt;=&lt;/span&gt; plot_latent_params(&lt;/span&gt;
&lt;span id="cb103-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb103-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    top_bot_irt_df[top_bot_irt_df[&lt;span class="st"&gt;'param'&lt;/span&gt;] &lt;span class="op"&gt;==&lt;/span&gt; &lt;span class="st"&gt;'θ'&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb103-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb103-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                  .sort_values(&lt;span class="st"&gt;'mean'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb103-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb103-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;/span&gt;
&lt;span id="cb103-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb103-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.set_xlabel(&lt;span class="vs"&gt;r"$\hat{\theta}$"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb103-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb103-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.set_title(&lt;span class="st"&gt;"Top and bottom ten"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt2/An%20Improved%20Analysis%20of%20NBA%20Foul%20Calls%20with%20Python_161_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;The top ten players in terms of committing skill include many defensive standouts (Danny Green — twice, Gordon Hayward, Paul George).&lt;/p&gt;
&lt;p&gt;The bottom ten players include many that are known to be defensively challenged (Ricky Rubio and James Harden). Dwight Howard was, at one point, a fierce defender of the rim, but was well past his prime in 2015, when our data set begins. Chris Paul’s presence in the bottom is somewhat surprising.&lt;/p&gt;
&lt;div class="sourceCode" id="cb104"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb104-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb104-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;fig, ax &lt;span class="op"&gt;=&lt;/span&gt; plot_latent_params(&lt;/span&gt;
&lt;span id="cb104-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb104-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    top_bot_irt_df[top_bot_irt_df[&lt;span class="st"&gt;'param'&lt;/span&gt;] &lt;span class="op"&gt;==&lt;/span&gt; &lt;span class="st"&gt;'b'&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb104-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb104-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                  .sort_values(&lt;span class="st"&gt;'mean'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb104-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb104-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;/span&gt;
&lt;span id="cb104-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb104-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.set_xlabel(&lt;span class="vs"&gt;r"$\hat&lt;/span&gt;&lt;span class="sc"&gt;{b}&lt;/span&gt;&lt;span class="vs"&gt;$"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb104-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb104-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.set_title(&lt;span class="st"&gt;"Top and bottom ten"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt2/An%20Improved%20Analysis%20of%20NBA%20Foul%20Calls%20with%20Python_163_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;In the sports analytics community, year-over-year correlation of latent parameters is the test of whether or not a latent quantity truly measures a skill. The following plots show a slight year-over-year correlation in the committing skill, but not much correlation in the disadvantaged skill.&lt;/p&gt;
&lt;div class="sourceCode" id="cb105"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb105-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb105-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; p_val_to_asterisks(p_val):&lt;/span&gt;
&lt;span id="cb105-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb105-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;if&lt;/span&gt; p_val &lt;span class="op"&gt;&amp;lt;&lt;/span&gt; &lt;span class="fl"&gt;0.0001&lt;/span&gt;:&lt;/span&gt;
&lt;span id="cb105-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb105-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="cf"&gt;return&lt;/span&gt; &lt;span class="st"&gt;"****"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb105-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb105-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;elif&lt;/span&gt; p_val &lt;span class="op"&gt;&amp;lt;&lt;/span&gt; &lt;span class="fl"&gt;0.001&lt;/span&gt;:&lt;/span&gt;
&lt;span id="cb105-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb105-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="cf"&gt;return&lt;/span&gt; &lt;span class="st"&gt;"***"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb105-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb105-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;elif&lt;/span&gt; p_val &lt;span class="op"&gt;&amp;lt;&lt;/span&gt; &lt;span class="fl"&gt;0.01&lt;/span&gt;:&lt;/span&gt;
&lt;span id="cb105-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb105-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="cf"&gt;return&lt;/span&gt; &lt;span class="st"&gt;"**"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb105-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb105-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;elif&lt;/span&gt; p_val &lt;span class="op"&gt;&amp;lt;&lt;/span&gt; &lt;span class="fl"&gt;0.05&lt;/span&gt;:&lt;/span&gt;
&lt;span id="cb105-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb105-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="cf"&gt;return&lt;/span&gt; &lt;span class="st"&gt;"*"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb105-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb105-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;else&lt;/span&gt;:&lt;/span&gt;
&lt;span id="cb105-11"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb105-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="cf"&gt;return&lt;/span&gt; &lt;span class="st"&gt;""&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb105-12"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb105-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb105-13"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb105-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; plot_corr(x, y, &lt;span class="op"&gt;**&lt;/span&gt;kwargs):&lt;/span&gt;
&lt;span id="cb105-14"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb105-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    corrcoeff, p_val &lt;span class="op"&gt;=&lt;/span&gt; sp.stats.pearsonr(x, y)&lt;/span&gt;
&lt;span id="cb105-15"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb105-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    asterisks &lt;span class="op"&gt;=&lt;/span&gt; p_val_to_asterisks(p_val)&lt;/span&gt;
&lt;span id="cb105-16"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb105-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb105-17"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb105-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    artist &lt;span class="op"&gt;=&lt;/span&gt; AnchoredText(&lt;/span&gt;
&lt;span id="cb105-18"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb105-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="ss"&gt;f'&lt;/span&gt;&lt;span class="sc"&gt;{&lt;/span&gt;corrcoeff&lt;span class="sc"&gt;:.2f}{&lt;/span&gt;asterisks&lt;span class="sc"&gt;}&lt;/span&gt;&lt;span class="ss"&gt;'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb105-19"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb105-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        loc&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;10&lt;/span&gt;, frameon&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;False&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb105-20"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb105-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        prop&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="bu"&gt;dict&lt;/span&gt;(size&lt;span class="op"&gt;=&lt;/span&gt;LABELSIZE)&lt;/span&gt;
&lt;span id="cb105-21"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb105-21" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    )&lt;/span&gt;
&lt;span id="cb105-22"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb105-22" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    plt.gca().add_artist(artist)&lt;/span&gt;
&lt;span id="cb105-23"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb105-23" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    plt.grid(b&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;False&lt;/span&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb106"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb106-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;PARAM_MAP &lt;span class="op"&gt;=&lt;/span&gt; {&lt;/span&gt;
&lt;span id="cb106-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;'θ'&lt;/span&gt;: &lt;span class="vs"&gt;r"$\hat{\theta}$"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb106-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;'b'&lt;/span&gt;: &lt;span class="vs"&gt;r"$\hat&lt;/span&gt;&lt;span class="sc"&gt;{b}&lt;/span&gt;&lt;span class="vs"&gt;$"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb106-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;}&lt;/span&gt;
&lt;span id="cb106-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb106-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; replace_label(label):&lt;/span&gt;
&lt;span id="cb106-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    param, season &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="bu"&gt;eval&lt;/span&gt;(label)&lt;/span&gt;
&lt;span id="cb106-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb106-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;return&lt;/span&gt; &lt;span class="st"&gt;"&lt;/span&gt;&lt;span class="sc"&gt;{param}&lt;/span&gt;&lt;span class="ch"&gt;\n&lt;/span&gt;&lt;span class="st"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;{season}&lt;/span&gt;&lt;span class="st"&gt;)"&lt;/span&gt;.&lt;span class="bu"&gt;format&lt;/span&gt;(&lt;/span&gt;
&lt;span id="cb106-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        param&lt;span class="op"&gt;=&lt;/span&gt;PARAM_MAP[param],&lt;/span&gt;
&lt;span id="cb106-11"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        season&lt;span class="op"&gt;=&lt;/span&gt;season_enc.inverse_transform(season)&lt;/span&gt;
&lt;span id="cb106-12"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    )&lt;/span&gt;
&lt;span id="cb106-13"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb106-14"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; style_grid(grid):&lt;/span&gt;
&lt;span id="cb106-15"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;for&lt;/span&gt; ax &lt;span class="kw"&gt;in&lt;/span&gt; grid.axes.flat:&lt;/span&gt;
&lt;span id="cb106-16"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        ax.grid(&lt;span class="va"&gt;False&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb106-17"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        ax.set_xticklabels([])&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb106-18"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        ax.set_yticklabels([])&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb106-19"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;/span&gt;
&lt;span id="cb106-20"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="cf"&gt;if&lt;/span&gt; ax.get_xlabel():&lt;/span&gt;
&lt;span id="cb106-21"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-21" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            ax.set_xlabel(replace_label(ax.get_xlabel()))&lt;/span&gt;
&lt;span id="cb106-22"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-22" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb106-23"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-23" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;span class="cf"&gt;if&lt;/span&gt; ax.get_ylabel():&lt;/span&gt;
&lt;span id="cb106-24"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-24" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            ax.set_ylabel(replace_label(ax.get_ylabel()))&lt;/span&gt;
&lt;span id="cb106-25"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-25" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            &lt;/span&gt;
&lt;span id="cb106-26"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb106-26" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;return&lt;/span&gt; grid&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb107"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb107-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb107-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;player_all_season &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="bu"&gt;set&lt;/span&gt;(df.groupby(&lt;span class="st"&gt;'player_disadvantaged'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb107-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb107-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                          .&lt;span class="bu"&gt;filter&lt;/span&gt;(&lt;span class="kw"&gt;lambda&lt;/span&gt; df: df[&lt;span class="st"&gt;'season'&lt;/span&gt;].nunique() &lt;span class="op"&gt;==&lt;/span&gt; n_season)&lt;/span&gt;
&lt;span id="cb107-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb107-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                          [&lt;span class="st"&gt;'player_committing'&lt;/span&gt;]) &lt;span class="op"&gt;\&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb107-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb107-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                        &lt;span class="op"&gt;&amp;amp;&lt;/span&gt; &lt;span class="bu"&gt;set&lt;/span&gt;(df.groupby(&lt;span class="st"&gt;'player_committing'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb107-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb107-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                .&lt;span class="bu"&gt;filter&lt;/span&gt;(&lt;span class="kw"&gt;lambda&lt;/span&gt; df: df[&lt;span class="st"&gt;'season'&lt;/span&gt;].nunique() &lt;span class="op"&gt;==&lt;/span&gt; n_season)&lt;/span&gt;
&lt;span id="cb107-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb107-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                [&lt;span class="st"&gt;'player_committing'&lt;/span&gt;])&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb108"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb108-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb108-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;style_grid(&lt;/span&gt;
&lt;span id="cb108-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb108-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    sns.PairGrid(&lt;/span&gt;
&lt;span id="cb108-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb108-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        player_irt_df.loc[player_all_season],&lt;/span&gt;
&lt;span id="cb108-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb108-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        size&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;1.75&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb108-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb108-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;       )&lt;/span&gt;
&lt;span id="cb108-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb108-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;       .map_upper(plt.scatter, alpha&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;0.5&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb108-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb108-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;       .map_diag(plt.hist)&lt;/span&gt;
&lt;span id="cb108-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb108-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;       .map_lower(plot_corr)&lt;/span&gt;
&lt;span id="cb108-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb108-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt2/An%20Improved%20Analysis%20of%20NBA%20Foul%20Calls%20with%20Python_168_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;Since we can only reasonably estimate the skills of players for which we have sufficient foul call data, we plot the correlations below for players that appeared in at least ten plays in each season.&lt;/p&gt;
&lt;div class="sourceCode" id="cb109"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb109-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb109-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;MIN &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="dv"&gt;10&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb109-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb109-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb109-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb109-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;player_has_min &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="bu"&gt;set&lt;/span&gt;(df.groupby(&lt;span class="st"&gt;'player_disadvantaged'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb109-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb109-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                       .&lt;span class="bu"&gt;filter&lt;/span&gt;(&lt;span class="kw"&gt;lambda&lt;/span&gt; df: (df[&lt;span class="st"&gt;'season'&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb109-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb109-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                             .value_counts()&lt;/span&gt;
&lt;span id="cb109-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb109-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                             .gt(MIN)&lt;/span&gt;
&lt;span id="cb109-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb109-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                             .&lt;span class="bu"&gt;all&lt;/span&gt;()))&lt;/span&gt;
&lt;span id="cb109-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb109-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                       [&lt;span class="st"&gt;'player_committing'&lt;/span&gt;]) &lt;span class="op"&gt;\&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb109-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb109-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                    &lt;span class="op"&gt;&amp;amp;&lt;/span&gt; &lt;span class="bu"&gt;set&lt;/span&gt;(df.groupby(&lt;span class="st"&gt;'player_committing'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb109-10"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb109-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                            .&lt;span class="bu"&gt;filter&lt;/span&gt;(&lt;span class="kw"&gt;lambda&lt;/span&gt; df: (df[&lt;span class="st"&gt;'season'&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb109-11"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb109-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                                  .value_counts()&lt;/span&gt;
&lt;span id="cb109-12"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb109-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                                  .gt(MIN)&lt;/span&gt;
&lt;span id="cb109-13"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb109-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                                  .&lt;span class="bu"&gt;all&lt;/span&gt;()))&lt;/span&gt;
&lt;span id="cb109-14"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb109-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                            [&lt;span class="st"&gt;'player_committing'&lt;/span&gt;])&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb110"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb110-1"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb110-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid &lt;span class="op"&gt;=&lt;/span&gt; style_grid(&lt;/span&gt;
&lt;span id="cb110-2"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb110-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    sns.PairGrid(&lt;/span&gt;
&lt;span id="cb110-3"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb110-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        player_irt_df.loc[player_has_min],&lt;/span&gt;
&lt;span id="cb110-4"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb110-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        size&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;1.75&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb110-5"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb110-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;       )&lt;/span&gt;
&lt;span id="cb110-6"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb110-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;       .map_upper(plt.scatter, alpha&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;0.5&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb110-7"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb110-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;       .map_diag(plt.hist)&lt;/span&gt;
&lt;span id="cb110-8"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb110-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;       .map_lower(plot_corr)&lt;/span&gt;
&lt;span id="cb110-9"&gt;&lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html#cb110-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt2/An%20Improved%20Analysis%20of%20NBA%20Foul%20Calls%20with%20Python_171_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;As expected, the season-over-season latent skill correlations are higher for this subset of players.&lt;/p&gt;
&lt;p&gt;From this figure, it seems that committing skill (&lt;span class="math inline"&gt;\(\hat{b}\)&lt;/span&gt;) exists and is measurable, but is fairly small. It also seems that disadvantaged skill (&lt;span class="math inline"&gt;\(\hat{\theta}\)&lt;/span&gt;), if it exists, is difficult to measure from this data set. Ideally, the NBA would release a foul report for the entirety of every game, but that seems quite unlikely.&lt;/p&gt;
&lt;p&gt;In the future it would be useful to include a correction for the probability that a given game appears in the data set. This correction would help with the sample bias introduced by the fact that only games that are close in the last two minutes are included in the NBA’s reports.&lt;/p&gt;
&lt;p&gt;This post is available as a Jupyter notebook &lt;a href="http://nbviewer.jupyter.org/gist/AustinRochford/a9df849f6a78188dbf9886b2a8a3644b"&gt;here&lt;/a&gt;.&lt;/p&gt;</description><category>Bayesian Statistics</category><category>NBA</category><category>PyMC3</category><guid>https://austinrochford.com/posts/2018-02-04-nba-irt-2.html</guid><pubDate>Sun, 04 Feb 2018 05:00:00 GMT</pubDate></item><item><title>NBA Foul Calls and Bayesian Item Response Theory</title><link>https://austinrochford.com/posts/2017-04-04-nba-irt.html</link><dc:creator>Austin Rochford</dc:creator><description>&lt;p&gt;An improved version of this analysis is available &lt;a href="https://austinrochford.com/posts/2018-02-04-nba-irt-2.html"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;(&lt;strong&gt;Author’s note&lt;/strong&gt;: many thanks to Robert (&lt;span class="citation" data-cites="atlhawksfanatic"&gt;[@atlhawksfanatic]&lt;/span&gt;(https://twitter.com/atlhawksfanatic) on Twitter) for pointing out &lt;a href="https://twitter.com/atlhawksfanatic/status/849685639796850689"&gt;some&lt;/a&gt; &lt;a href="https://twitter.com/atlhawksfanatic/status/849686015753302021"&gt;subtleties&lt;/a&gt; in the data set that I had missed. This post has been revised in line with his feedback. Robert has a very interesting &lt;a href="http://www.peachtreehoops.com/2017/2/17/14638288/nba-last-two-minute-reports-changing"&gt;post&lt;/a&gt; about how last two minute refereeing has changed over the last three years; I highly recommend you read it.)&lt;/p&gt;
&lt;p&gt;I recently found a very interesting &lt;a href="https://github.com/polygraph-cool/last-two-minute-report"&gt;data set&lt;/a&gt; derived from the NBA’s &lt;a href="http://official.nba.com/nba-last-two-minute-reports-archive/"&gt;Last Two Minute Report&lt;/a&gt; by &lt;a href="http://russellgoldenberg.com/"&gt;Russell Goldenberg&lt;/a&gt; of &lt;a href="https://pudding.cool/"&gt;The Pudding&lt;/a&gt;. Since 2015, the NBA has released a report reviewing every call and non-call in the final two minutes of every NBA game where the teams were separated by five points or less with two minutes remaining. This data set has extracted each play from the NBA-distributed PDF and augmented it with information from &lt;a href="https://basketball-reference.com/"&gt;Basketball Reference&lt;/a&gt; to produce a convenient CSV. The Pudding has published two &lt;a href="https://pudding.cool/2017/02/two-minute-report/"&gt;very&lt;/a&gt; &lt;a href="https://pudding.cool/2017/03/home-court/"&gt;interesting&lt;/a&gt; visual essays using this data that you should definitely explore.&lt;/p&gt;
&lt;p&gt;The NBA is certainly marketed as a star-centric league, so this data set presents a fantastic opportunity to understand the extent to which the players involved in a decision impact whether or not a foul is called. We will also explore other factors related to foul calls.&lt;/p&gt;
&lt;div class="sourceCode" id="cb1"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb1-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb1-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;%&lt;/span&gt;matplotlib inline&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb2"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb2-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb2-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="im"&gt;import&lt;/span&gt; datetime&lt;/span&gt;
&lt;span id="cb2-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb2-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="im"&gt;from&lt;/span&gt; warnings &lt;span class="im"&gt;import&lt;/span&gt; filterwarnings&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb3"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb3-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb3-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="im"&gt;from&lt;/span&gt; matplotlib &lt;span class="im"&gt;import&lt;/span&gt; pyplot &lt;span class="im"&gt;as&lt;/span&gt; plt&lt;/span&gt;
&lt;span id="cb3-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb3-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="im"&gt;from&lt;/span&gt; matplotlib.ticker &lt;span class="im"&gt;import&lt;/span&gt; FuncFormatter&lt;/span&gt;
&lt;span id="cb3-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb3-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="im"&gt;import&lt;/span&gt; numpy &lt;span class="im"&gt;as&lt;/span&gt; np&lt;/span&gt;
&lt;span id="cb3-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb3-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="im"&gt;import&lt;/span&gt; pandas &lt;span class="im"&gt;as&lt;/span&gt; pd&lt;/span&gt;
&lt;span id="cb3-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb3-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="im"&gt;import&lt;/span&gt; pymc3 &lt;span class="im"&gt;as&lt;/span&gt; pm&lt;/span&gt;
&lt;span id="cb3-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb3-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="im"&gt;from&lt;/span&gt; scipy.special &lt;span class="im"&gt;import&lt;/span&gt; expit&lt;/span&gt;
&lt;span id="cb3-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb3-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="im"&gt;import&lt;/span&gt; seaborn &lt;span class="im"&gt;as&lt;/span&gt; sns&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb4"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb4-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb4-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;blue, green, red, purple, gold, teal &lt;span class="op"&gt;=&lt;/span&gt; sns.color_palette()&lt;/span&gt;
&lt;span id="cb4-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb4-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb4-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb4-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;million_dollars_formatter &lt;span class="op"&gt;=&lt;/span&gt; FuncFormatter(&lt;span class="kw"&gt;lambda&lt;/span&gt; value, _: &lt;span class="st"&gt;'$&lt;/span&gt;&lt;span class="sc"&gt;{:.1f}&lt;/span&gt;&lt;span class="st"&gt;M'&lt;/span&gt;.&lt;span class="bu"&gt;format&lt;/span&gt;(value &lt;span class="op"&gt;/&lt;/span&gt; &lt;span class="fl"&gt;1e6&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb4-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb4-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;pct_formatter &lt;span class="op"&gt;=&lt;/span&gt; FuncFormatter(&lt;span class="kw"&gt;lambda&lt;/span&gt; prop, _: &lt;span class="st"&gt;"&lt;/span&gt;&lt;span class="sc"&gt;{:.1%}&lt;/span&gt;&lt;span class="st"&gt;"&lt;/span&gt;.&lt;span class="bu"&gt;format&lt;/span&gt;(prop))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb5"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb5-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb5-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;filterwarnings(&lt;span class="st"&gt;'ignore'&lt;/span&gt;, &lt;span class="st"&gt;'findfont'&lt;/span&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="loading-and-preprocessing-the-data"&gt;Loading and preprocessing the data&lt;/h2&gt;
&lt;p&gt;We begin by loading the data set from GitHub. For reproducibility, we load the data from the most recent commit as of the time this post was published.&lt;/p&gt;
&lt;div class="sourceCode" id="cb6"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb6-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb6-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;DATA_URI &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="st"&gt;'https://raw.githubusercontent.com/polygraph-cool/last-two-minute-report/1b89b71df060add5538b70d391d7ad82a4c24db2/output/all_games.csv'&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb6-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb6-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb6-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;raw_df &lt;span class="op"&gt;=&lt;/span&gt; (pd.read_csv(DATA_URI,&lt;/span&gt;
&lt;span id="cb6-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb6-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                      usecols&lt;span class="op"&gt;=&lt;/span&gt;[&lt;span class="st"&gt;'committing_player'&lt;/span&gt;, &lt;span class="st"&gt;'disadvantaged_player'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb6-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb6-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                               &lt;span class="st"&gt;'committing_team'&lt;/span&gt;, &lt;span class="st"&gt;'disadvantaged_team'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb6-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb6-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                               &lt;span class="st"&gt;'seconds_left'&lt;/span&gt;, &lt;span class="st"&gt;'review_decision'&lt;/span&gt;, &lt;span class="st"&gt;'date'&lt;/span&gt;],&lt;/span&gt;
&lt;span id="cb6-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb6-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                      parse_dates&lt;span class="op"&gt;=&lt;/span&gt;[&lt;span class="st"&gt;'date'&lt;/span&gt;])&lt;/span&gt;
&lt;span id="cb6-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb6-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            .where(&lt;span class="kw"&gt;lambda&lt;/span&gt; df: df.date &lt;span class="op"&gt;&amp;gt;=&lt;/span&gt; datetime.datetime(&lt;span class="dv"&gt;2016&lt;/span&gt;, &lt;span class="dv"&gt;10&lt;/span&gt;, &lt;span class="dv"&gt;25&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb6-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb6-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            .dropna(subset&lt;span class="op"&gt;=&lt;/span&gt;[&lt;span class="st"&gt;'date'&lt;/span&gt;])&lt;/span&gt;
&lt;span id="cb6-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb6-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            .drop(&lt;span class="st"&gt;'date'&lt;/span&gt;, axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb6-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb6-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;raw_df[&lt;span class="st"&gt;'review_decision'&lt;/span&gt;] &lt;span class="op"&gt;=&lt;/span&gt; raw_df.review_decision.fillna(&lt;span class="st"&gt;"INC"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb6-12"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb6-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;raw_df &lt;span class="op"&gt;=&lt;/span&gt; (raw_df.dropna()&lt;/span&gt;
&lt;span id="cb6-13"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb6-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                .reset_index(drop&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We restrict our attention to decisions from the &lt;a href="https://en.wikipedia.org/wiki/2016%E2%80%9317_NBA_season"&gt;2016-2017 NBA season&lt;/a&gt;, for which salary information is &lt;a href="http://www.basketball-reference.com/contracts/players.html"&gt;readily available&lt;/a&gt; from &lt;a href="http://www.basketball-reference.com/"&gt;Basketball Reference&lt;/a&gt;.&lt;/p&gt;
&lt;div class="sourceCode" id="cb7"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb7-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb7-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;raw_df.head()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div&gt;
&lt;center&gt;
&lt;table border="1" class="dataframe"&gt;
&lt;thead&gt;
&lt;tr style="text-align: right;"&gt;
&lt;th&gt;
&lt;/th&gt;
&lt;th&gt;
seconds_left
&lt;/th&gt;
&lt;th&gt;
committing_player
&lt;/th&gt;
&lt;th&gt;
disadvantaged_player
&lt;/th&gt;
&lt;th&gt;
review_decision
&lt;/th&gt;
&lt;th&gt;
disadvantaged_team
&lt;/th&gt;
&lt;th&gt;
committing_team
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;
0
&lt;/th&gt;
&lt;td&gt;
102.0
&lt;/td&gt;
&lt;td&gt;
Al-Farouq Aminu
&lt;/td&gt;
&lt;td&gt;
George Hill
&lt;/td&gt;
&lt;td&gt;
CNC
&lt;/td&gt;
&lt;td&gt;
UTA
&lt;/td&gt;
&lt;td&gt;
POR
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
1
&lt;/th&gt;
&lt;td&gt;
98.0
&lt;/td&gt;
&lt;td&gt;
Boris Diaw
&lt;/td&gt;
&lt;td&gt;
Damian Lillard
&lt;/td&gt;
&lt;td&gt;
CC
&lt;/td&gt;
&lt;td&gt;
POR
&lt;/td&gt;
&lt;td&gt;
UTA
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
2
&lt;/th&gt;
&lt;td&gt;
64.0
&lt;/td&gt;
&lt;td&gt;
Ed Davis
&lt;/td&gt;
&lt;td&gt;
George Hill
&lt;/td&gt;
&lt;td&gt;
CNC
&lt;/td&gt;
&lt;td&gt;
UTA
&lt;/td&gt;
&lt;td&gt;
POR
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
3
&lt;/th&gt;
&lt;td&gt;
62.0
&lt;/td&gt;
&lt;td&gt;
Rudy Gobert
&lt;/td&gt;
&lt;td&gt;
CJ McCollum
&lt;/td&gt;
&lt;td&gt;
INC
&lt;/td&gt;
&lt;td&gt;
POR
&lt;/td&gt;
&lt;td&gt;
UTA
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
4
&lt;/th&gt;
&lt;td&gt;
27.1
&lt;/td&gt;
&lt;td&gt;
CJ McCollum
&lt;/td&gt;
&lt;td&gt;
Rodney Hood
&lt;/td&gt;
&lt;td&gt;
CC
&lt;/td&gt;
&lt;td&gt;
UTA
&lt;/td&gt;
&lt;td&gt;
POR
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/center&gt;
&lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;We have only loaded some of the data set’s columns; see the original CSV header for the rest.&lt;/p&gt;
&lt;p&gt;The response variable in our analysis is derived from &lt;code&gt;review_decision&lt;/code&gt;, which contains information about whether the incident was a call or non-call and whether, upon post-game review, the NBA deemed the (non-)call correct or incorrect. Below we show the frequencies of each type of &lt;code&gt;review_decision&lt;/code&gt;.&lt;/p&gt;
&lt;div class="sourceCode" id="cb8"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb8-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb8-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax &lt;span class="op"&gt;=&lt;/span&gt; (raw_df.groupby(&lt;span class="st"&gt;'review_decision'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb8-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb8-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            .size()&lt;/span&gt;
&lt;span id="cb8-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb8-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            .plot(kind&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'bar'&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb8-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb8-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb8-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb8-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.set_ylabel(&lt;span class="st"&gt;"Frequency"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_12_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;The possible values of &lt;code&gt;review_decision&lt;/code&gt; are&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CC&lt;/code&gt; for correct call,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CNC&lt;/code&gt; for correct non-call,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IC&lt;/code&gt; for incorrect call, and&lt;/li&gt;
&lt;li&gt;&lt;code&gt;INC&lt;/code&gt; for incorrect non-call.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While &lt;code&gt;review_decision&lt;/code&gt; decision provides information about both whether or not a foul was called and whether or not a foul was actually committed, this analysis will focus only on whether or not a foul was called. Including whether or not a foul was actually committed in this analysis introduces some subtleties that are best left to a future post.&lt;/p&gt;
&lt;p&gt;In this dataset, the “committing” player is the one that a foul would be called against, if a foul was called on the play, and the other player is “disadvantaged.”&lt;/p&gt;
&lt;p&gt;We now encode the data. Since the committing player on one play may be the disadvantaged player on another play, we &lt;a href="http://pandas.pydata.org/pandas-docs/stable/generated/pandas.melt.html"&gt;&lt;code&gt;melt&lt;/code&gt;&lt;/a&gt; the raw data frame to have one row per player-play combination so that we can encode the players in a way that is consistent across columns.&lt;/p&gt;
&lt;div class="sourceCode" id="cb9"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb9-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb9-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;PLAYER_MAP &lt;span class="op"&gt;=&lt;/span&gt; {&lt;/span&gt;
&lt;span id="cb9-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb9-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;"Jose Juan Barea"&lt;/span&gt;: &lt;span class="st"&gt;"JJ Barea"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb9-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb9-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;"Nene Hilario"&lt;/span&gt;: &lt;span class="st"&gt;"Nene"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb9-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb9-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;"Tim Hardaway"&lt;/span&gt;: &lt;span class="st"&gt;"Tim Hardaway Jr"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb9-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb9-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;"James Ennis"&lt;/span&gt;: &lt;span class="st"&gt;"James Ennis III"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb9-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb9-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;"Kelly Oubre"&lt;/span&gt;: &lt;span class="st"&gt;"Kelly Oubre Jr"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb9-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb9-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;"Taurean Waller-Prince"&lt;/span&gt;: &lt;span class="st"&gt;"Taurean Prince"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb9-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb9-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;"Glenn Robinson"&lt;/span&gt;: &lt;span class="st"&gt;"Glenn Robinson III"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb9-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb9-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;"Otto Porter"&lt;/span&gt;: &lt;span class="st"&gt;"Otto Porter Jr"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb9-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;}&lt;/span&gt;
&lt;span id="cb9-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb9-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb9-12"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb9-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;TEAM_MAP &lt;span class="op"&gt;=&lt;/span&gt; {&lt;/span&gt;
&lt;span id="cb9-13"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb9-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;"NKY"&lt;/span&gt;: &lt;span class="st"&gt;"NYK"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb9-14"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb9-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;"COS"&lt;/span&gt;: &lt;span class="st"&gt;"BOS"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb9-15"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb9-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="st"&gt;"SAT"&lt;/span&gt;: &lt;span class="st"&gt;"SAS"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-16"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb9-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb10"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb10-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb10-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;long_df &lt;span class="op"&gt;=&lt;/span&gt; (pd.melt(&lt;/span&gt;
&lt;span id="cb10-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb10-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                (raw_df.reset_index(drop&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb10-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb10-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                       .rename_axis(&lt;span class="st"&gt;'play_id'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb10-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb10-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                       .reset_index()),&lt;/span&gt;
&lt;span id="cb10-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb10-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                id_vars&lt;span class="op"&gt;=&lt;/span&gt;[&lt;span class="st"&gt;'play_id'&lt;/span&gt;, &lt;span class="st"&gt;'review_decision'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb10-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb10-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                         &lt;span class="st"&gt;'committing_team'&lt;/span&gt;, &lt;span class="st"&gt;'disadvantaged_team'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb10-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb10-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                         &lt;span class="st"&gt;'seconds_left'&lt;/span&gt;],&lt;/span&gt;
&lt;span id="cb10-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb10-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                value_vars&lt;span class="op"&gt;=&lt;/span&gt;[&lt;span class="st"&gt;'committing_player'&lt;/span&gt;, &lt;span class="st"&gt;'disadvantaged_player'&lt;/span&gt;],&lt;/span&gt;
&lt;span id="cb10-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb10-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                var_name&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'player'&lt;/span&gt;, value_name&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'player_name_'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb10-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb10-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;             &lt;span class="co"&gt;# fix inconsistent player names&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb10-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb10-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;             .assign(player_name&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="kw"&gt;lambda&lt;/span&gt; df: (df.player_name_&lt;/span&gt;
&lt;span id="cb10-12"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb10-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                               .&lt;span class="bu"&gt;str&lt;/span&gt;.replace(&lt;span class="st"&gt;'\.'&lt;/span&gt;, &lt;span class="st"&gt;''&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb10-13"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb10-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                               .&lt;span class="bu"&gt;apply&lt;/span&gt;(&lt;span class="kw"&gt;lambda&lt;/span&gt; name: PLAYER_MAP.get(name, name))))&lt;/span&gt;
&lt;span id="cb10-14"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb10-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;             .assign(team_&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="kw"&gt;lambda&lt;/span&gt; df: (df.committing_team&lt;/span&gt;
&lt;span id="cb10-15"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb10-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                         .where(df.player &lt;span class="op"&gt;==&lt;/span&gt; &lt;span class="st"&gt;'committing_player'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb10-16"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb10-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                                df.disadvantaged_team)))&lt;/span&gt;
&lt;span id="cb10-17"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb10-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;             &lt;span class="co"&gt;# fix typos in team names&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb10-18"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb10-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;             .assign(team&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="kw"&gt;lambda&lt;/span&gt; df: df.team_.&lt;span class="bu"&gt;apply&lt;/span&gt;(&lt;span class="kw"&gt;lambda&lt;/span&gt; team: TEAM_MAP.get(team, team)))&lt;/span&gt;
&lt;span id="cb10-19"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb10-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;             .drop([&lt;span class="st"&gt;'committing_team'&lt;/span&gt;, &lt;span class="st"&gt;'disadvantaged_team'&lt;/span&gt;, &lt;span class="st"&gt;'team_'&lt;/span&gt;], axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb10-20"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb10-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb10-21"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb10-21" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;long_df[&lt;span class="st"&gt;'player_id'&lt;/span&gt;], player_map &lt;span class="op"&gt;=&lt;/span&gt; long_df.player_name.factorize()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb11"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb11-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb11-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;long_df.head()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div&gt;
&lt;center&gt;
&lt;table border="1" class="dataframe"&gt;
&lt;thead&gt;
&lt;tr style="text-align: right;"&gt;
&lt;th&gt;
&lt;/th&gt;
&lt;th&gt;
play_id
&lt;/th&gt;
&lt;th&gt;
review_decision
&lt;/th&gt;
&lt;th&gt;
seconds_left
&lt;/th&gt;
&lt;th&gt;
player
&lt;/th&gt;
&lt;th&gt;
player_name_
&lt;/th&gt;
&lt;th&gt;
player_name
&lt;/th&gt;
&lt;th&gt;
team
&lt;/th&gt;
&lt;th&gt;
player_id
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;
0
&lt;/th&gt;
&lt;td&gt;
0
&lt;/td&gt;
&lt;td&gt;
CNC
&lt;/td&gt;
&lt;td&gt;
102.0
&lt;/td&gt;
&lt;td&gt;
committing_player
&lt;/td&gt;
&lt;td&gt;
Al-Farouq Aminu
&lt;/td&gt;
&lt;td&gt;
Al-Farouq Aminu
&lt;/td&gt;
&lt;td&gt;
POR
&lt;/td&gt;
&lt;td&gt;
0
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
1
&lt;/th&gt;
&lt;td&gt;
1
&lt;/td&gt;
&lt;td&gt;
CC
&lt;/td&gt;
&lt;td&gt;
98.0
&lt;/td&gt;
&lt;td&gt;
committing_player
&lt;/td&gt;
&lt;td&gt;
Boris Diaw
&lt;/td&gt;
&lt;td&gt;
Boris Diaw
&lt;/td&gt;
&lt;td&gt;
UTA
&lt;/td&gt;
&lt;td&gt;
1
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
2
&lt;/th&gt;
&lt;td&gt;
2
&lt;/td&gt;
&lt;td&gt;
CNC
&lt;/td&gt;
&lt;td&gt;
64.0
&lt;/td&gt;
&lt;td&gt;
committing_player
&lt;/td&gt;
&lt;td&gt;
Ed Davis
&lt;/td&gt;
&lt;td&gt;
Ed Davis
&lt;/td&gt;
&lt;td&gt;
POR
&lt;/td&gt;
&lt;td&gt;
2
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
3
&lt;/th&gt;
&lt;td&gt;
3
&lt;/td&gt;
&lt;td&gt;
INC
&lt;/td&gt;
&lt;td&gt;
62.0
&lt;/td&gt;
&lt;td&gt;
committing_player
&lt;/td&gt;
&lt;td&gt;
Rudy Gobert
&lt;/td&gt;
&lt;td&gt;
Rudy Gobert
&lt;/td&gt;
&lt;td&gt;
UTA
&lt;/td&gt;
&lt;td&gt;
3
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
4
&lt;/th&gt;
&lt;td&gt;
4
&lt;/td&gt;
&lt;td&gt;
CC
&lt;/td&gt;
&lt;td&gt;
27.1
&lt;/td&gt;
&lt;td&gt;
committing_player
&lt;/td&gt;
&lt;td&gt;
CJ McCollum
&lt;/td&gt;
&lt;td&gt;
CJ McCollum
&lt;/td&gt;
&lt;td&gt;
POR
&lt;/td&gt;
&lt;td&gt;
4
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/center&gt;
&lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;After encoding, we &lt;a href="http://pandas.pydata.org/pandas-docs/stable/generated/pandas.pivot_table.html"&gt;pivot&lt;/a&gt; back to a wide data frame with one row per play.&lt;/p&gt;
&lt;div class="sourceCode" id="cb12"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb12-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb12-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;df &lt;span class="op"&gt;=&lt;/span&gt; (long_df.pivot_table(index&lt;span class="op"&gt;=&lt;/span&gt;[&lt;span class="st"&gt;'play_id'&lt;/span&gt;, &lt;span class="st"&gt;'review_decision'&lt;/span&gt;, &lt;span class="st"&gt;'seconds_left'&lt;/span&gt;],&lt;/span&gt;
&lt;span id="cb12-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb12-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                          columns&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'player'&lt;/span&gt;, values&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'player_id'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb12-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb12-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;             .rename(columns&lt;span class="op"&gt;=&lt;/span&gt;{&lt;/span&gt;
&lt;span id="cb12-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb12-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                 &lt;span class="st"&gt;'committing_player'&lt;/span&gt;: &lt;span class="st"&gt;'committing_id'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb12-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb12-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                 &lt;span class="st"&gt;'disadvantaged_player'&lt;/span&gt;: &lt;span class="st"&gt;'disadvantaged_id'&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb12-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb12-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;             })&lt;/span&gt;
&lt;span id="cb12-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb12-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;             .rename_axis(&lt;span class="st"&gt;''&lt;/span&gt;, axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb12-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb12-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;             .reset_index()&lt;/span&gt;
&lt;span id="cb12-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb12-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;             .assign(foul_called&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="kw"&gt;lambda&lt;/span&gt; df: &lt;span class="dv"&gt;1&lt;/span&gt; &lt;span class="op"&gt;*&lt;/span&gt; (df.review_decision.isin([&lt;span class="st"&gt;'CC'&lt;/span&gt;, &lt;span class="st"&gt;'IC'&lt;/span&gt;])))&lt;/span&gt;
&lt;span id="cb12-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb12-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;             .drop([&lt;span class="st"&gt;'play_id'&lt;/span&gt;, &lt;span class="st"&gt;'review_decision'&lt;/span&gt;],&lt;/span&gt;
&lt;span id="cb12-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb12-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                   axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In addition to encoding the players, we have include a column (&lt;code&gt;foul_called&lt;/code&gt;) that indicates whether or not a foul was called on the play.&lt;/p&gt;
&lt;div class="sourceCode" id="cb13"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb13-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb13-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;df.head()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div&gt;
&lt;center&gt;
&lt;table border="1" class="dataframe"&gt;
&lt;thead&gt;
&lt;tr style="text-align: right;"&gt;
&lt;th&gt;
&lt;/th&gt;
&lt;th&gt;
seconds_left
&lt;/th&gt;
&lt;th&gt;
committing_id
&lt;/th&gt;
&lt;th&gt;
disadvantaged_id
&lt;/th&gt;
&lt;th&gt;
foul_called
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;
0
&lt;/th&gt;
&lt;td&gt;
102.0
&lt;/td&gt;
&lt;td&gt;
0
&lt;/td&gt;
&lt;td&gt;
300
&lt;/td&gt;
&lt;td&gt;
0
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
1
&lt;/th&gt;
&lt;td&gt;
98.0
&lt;/td&gt;
&lt;td&gt;
1
&lt;/td&gt;
&lt;td&gt;
124
&lt;/td&gt;
&lt;td&gt;
1
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
2
&lt;/th&gt;
&lt;td&gt;
64.0
&lt;/td&gt;
&lt;td&gt;
2
&lt;/td&gt;
&lt;td&gt;
300
&lt;/td&gt;
&lt;td&gt;
0
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
3
&lt;/th&gt;
&lt;td&gt;
62.0
&lt;/td&gt;
&lt;td&gt;
3
&lt;/td&gt;
&lt;td&gt;
4
&lt;/td&gt;
&lt;td&gt;
0
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
4
&lt;/th&gt;
&lt;td&gt;
27.1
&lt;/td&gt;
&lt;td&gt;
4
&lt;/td&gt;
&lt;td&gt;
6
&lt;/td&gt;
&lt;td&gt;
1
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/center&gt;
&lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;In order to understand how foul calls vary systematically across players, we will use salary as a proxy for “star power.” The salary data we use was downloaded from &lt;a href="http://www.basketball-reference.com/contracts/players.html"&gt;Basketball Reference&lt;/a&gt;.&lt;/p&gt;
&lt;div class="sourceCode" id="cb14"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb14-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb14-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;SALARY_URI &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="st"&gt;'http://www.austinrochford.com/resources/nba_irt/2016_2017_salaries.csv'&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb14-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb14-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb14-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb14-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;salary_df &lt;span class="op"&gt;=&lt;/span&gt; (pd.read_csv(SALARY_URI, skiprows&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb14-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb14-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                         usecols&lt;span class="op"&gt;=&lt;/span&gt;[&lt;span class="st"&gt;'Player'&lt;/span&gt;, &lt;span class="st"&gt;'2016-17'&lt;/span&gt;])&lt;/span&gt;
&lt;span id="cb14-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb14-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;               .assign(player_name&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="kw"&gt;lambda&lt;/span&gt; df: (df.Player&lt;/span&gt;
&lt;span id="cb14-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb14-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                                 .&lt;span class="bu"&gt;str&lt;/span&gt;.split(&lt;span class="st"&gt;'&lt;/span&gt;&lt;span class="ch"&gt;\\&lt;/span&gt;&lt;span class="st"&gt;'&lt;/span&gt;, expand&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;)[&lt;span class="dv"&gt;0&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb14-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb14-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                                 .&lt;span class="bu"&gt;str&lt;/span&gt;.replace(&lt;span class="st"&gt;'\.'&lt;/span&gt;, &lt;span class="st"&gt;''&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb14-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb14-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                                 &lt;span class="co"&gt;# fix inconsistent player names&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb14-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb14-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                                 .&lt;span class="bu"&gt;apply&lt;/span&gt;(&lt;span class="kw"&gt;lambda&lt;/span&gt; name: PLAYER_MAP.get(name, name))),&lt;/span&gt;
&lt;span id="cb14-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb14-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                       salary&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="kw"&gt;lambda&lt;/span&gt; df: (df[&lt;span class="st"&gt;'2016-17'&lt;/span&gt;].&lt;span class="bu"&gt;str&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb14-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb14-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                                       .lstrip(&lt;span class="st"&gt;'$'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb14-12"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb14-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                                       .astype(np.float64)))&lt;/span&gt;
&lt;span id="cb14-13"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb14-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;               .assign(log_salary&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="kw"&gt;lambda&lt;/span&gt; df: np.log10(df.salary))&lt;/span&gt;
&lt;span id="cb14-14"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb14-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;               .assign(std_log_salary&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="kw"&gt;lambda&lt;/span&gt; df: (df.log_salary &lt;span class="op"&gt;-&lt;/span&gt; df.log_salary.mean()) &lt;span class="op"&gt;/&lt;/span&gt; df.log_salary.std())&lt;/span&gt;
&lt;span id="cb14-15"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb14-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;               .drop([&lt;span class="st"&gt;'Player'&lt;/span&gt;, &lt;span class="st"&gt;'2016-17'&lt;/span&gt;], axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb14-16"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb14-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;               .groupby(&lt;span class="st"&gt;'player_name'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb14-17"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb14-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;               .&lt;span class="bu"&gt;max&lt;/span&gt;()&lt;/span&gt;
&lt;span id="cb14-18"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb14-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;               .select(&lt;span class="kw"&gt;lambda&lt;/span&gt; name: name &lt;span class="kw"&gt;in&lt;/span&gt; player_map)&lt;/span&gt;
&lt;span id="cb14-19"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb14-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;               .assign(player_id&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="kw"&gt;lambda&lt;/span&gt; df: (np.equal&lt;/span&gt;
&lt;span id="cb14-20"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb14-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                               .outer(player_map, df.index)&lt;/span&gt;
&lt;span id="cb14-21"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb14-21" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                               .argmax(axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;)))&lt;/span&gt;
&lt;span id="cb14-22"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb14-22" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;               .reset_index()&lt;/span&gt;
&lt;span id="cb14-23"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb14-23" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;               .set_index(&lt;span class="st"&gt;'player_id'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb14-24"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb14-24" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;               .sort_index())&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Since NBA salaries span many orders of magnitude (LeBron James’ salary is just shy of $31M while the lowest paid player made just more than $200K) we will use log salaries, standardized to have mean zero and standard deviation one in our model.&lt;/p&gt;
&lt;div class="sourceCode" id="cb15"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb15-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb15-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;salary_df.head()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div&gt;
&lt;center&gt;
&lt;table border="1" class="dataframe"&gt;
&lt;thead&gt;
&lt;tr style="text-align: right;"&gt;
&lt;th&gt;
&lt;/th&gt;
&lt;th&gt;
player_name
&lt;/th&gt;
&lt;th&gt;
salary
&lt;/th&gt;
&lt;th&gt;
log_salary
&lt;/th&gt;
&lt;th&gt;
std_log_salary
&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
player_id
&lt;/th&gt;
&lt;th&gt;
&lt;/th&gt;
&lt;th&gt;
&lt;/th&gt;
&lt;th&gt;
&lt;/th&gt;
&lt;th&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;
0
&lt;/th&gt;
&lt;td&gt;
Al-Farouq Aminu
&lt;/td&gt;
&lt;td&gt;
7680965.0
&lt;/td&gt;
&lt;td&gt;
6.885416
&lt;/td&gt;
&lt;td&gt;
0.848869
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
1
&lt;/th&gt;
&lt;td&gt;
Boris Diaw
&lt;/td&gt;
&lt;td&gt;
7000000.0
&lt;/td&gt;
&lt;td&gt;
6.845098
&lt;/td&gt;
&lt;td&gt;
0.797879
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
2
&lt;/th&gt;
&lt;td&gt;
Ed Davis
&lt;/td&gt;
&lt;td&gt;
6666667.0
&lt;/td&gt;
&lt;td&gt;
6.823909
&lt;/td&gt;
&lt;td&gt;
0.771080
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
3
&lt;/th&gt;
&lt;td&gt;
Rudy Gobert
&lt;/td&gt;
&lt;td&gt;
2121288.0
&lt;/td&gt;
&lt;td&gt;
6.326600
&lt;/td&gt;
&lt;td&gt;
0.142129
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
4
&lt;/th&gt;
&lt;td&gt;
CJ McCollum
&lt;/td&gt;
&lt;td&gt;
3219579.0
&lt;/td&gt;
&lt;td&gt;
6.507799
&lt;/td&gt;
&lt;td&gt;
0.371293
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/center&gt;
&lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;We also produce a dataframe associating players to teams, along with some useful per-player summaries.&lt;/p&gt;
&lt;div class="sourceCode" id="cb16"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb16-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb16-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;team_player_map &lt;span class="op"&gt;=&lt;/span&gt; (long_df.groupby(&lt;span class="st"&gt;'team'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb16-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb16-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                          .player_id&lt;/span&gt;
&lt;span id="cb16-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb16-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                          .&lt;span class="bu"&gt;apply&lt;/span&gt;(pd.Series.drop_duplicates)&lt;/span&gt;
&lt;span id="cb16-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb16-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                          .reset_index(level&lt;span class="op"&gt;=-&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;, drop&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb16-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb16-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                          .reset_index()&lt;/span&gt;
&lt;span id="cb16-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb16-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                          .assign(name&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="kw"&gt;lambda&lt;/span&gt; df: player_map[df.player_id],&lt;/span&gt;
&lt;span id="cb16-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb16-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                  disadvantaged_rate&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="kw"&gt;lambda&lt;/span&gt; tpm_df: (df.groupby(&lt;span class="st"&gt;'disadvantaged_id'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb16-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb16-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                                                       .foul_called&lt;/span&gt;
&lt;span id="cb16-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb16-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                                                       .mean()&lt;/span&gt;
&lt;span id="cb16-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb16-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                                                       .ix[tpm_df.player_id]&lt;/span&gt;
&lt;span id="cb16-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb16-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                                                       .values),&lt;/span&gt;
&lt;span id="cb16-12"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb16-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                  disadvantaged_plays&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="kw"&gt;lambda&lt;/span&gt; tpm_df: (df.groupby(&lt;span class="st"&gt;'disadvantaged_id'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb16-13"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb16-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                                                        .size()&lt;/span&gt;
&lt;span id="cb16-14"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb16-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                                                        .ix[tpm_df.player_id]&lt;/span&gt;
&lt;span id="cb16-15"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb16-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                                                                        .values))&lt;/span&gt;
&lt;span id="cb16-16"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb16-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                          .fillna(&lt;span class="dv"&gt;0&lt;/span&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb17"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb17-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb17-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;team_player_map.head()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div&gt;
&lt;center&gt;
&lt;table border="1" class="dataframe"&gt;
&lt;thead&gt;
&lt;tr style="text-align: right;"&gt;
&lt;th&gt;
&lt;/th&gt;
&lt;th&gt;
team
&lt;/th&gt;
&lt;th&gt;
player_id
&lt;/th&gt;
&lt;th&gt;
disadvantaged_plays
&lt;/th&gt;
&lt;th&gt;
disadvantaged_rate
&lt;/th&gt;
&lt;th&gt;
name
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;
0
&lt;/th&gt;
&lt;td&gt;
ATL
&lt;/td&gt;
&lt;td&gt;
114
&lt;/td&gt;
&lt;td&gt;
8.0
&lt;/td&gt;
&lt;td&gt;
0.000000
&lt;/td&gt;
&lt;td&gt;
Kyle Korver
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
1
&lt;/th&gt;
&lt;td&gt;
ATL
&lt;/td&gt;
&lt;td&gt;
115
&lt;/td&gt;
&lt;td&gt;
13.0
&lt;/td&gt;
&lt;td&gt;
0.538462
&lt;/td&gt;
&lt;td&gt;
Dwight Howard
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
2
&lt;/th&gt;
&lt;td&gt;
ATL
&lt;/td&gt;
&lt;td&gt;
116
&lt;/td&gt;
&lt;td&gt;
44.0
&lt;/td&gt;
&lt;td&gt;
0.272727
&lt;/td&gt;
&lt;td&gt;
Paul Millsap
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
3
&lt;/th&gt;
&lt;td&gt;
ATL
&lt;/td&gt;
&lt;td&gt;
117
&lt;/td&gt;
&lt;td&gt;
60.0
&lt;/td&gt;
&lt;td&gt;
0.283333
&lt;/td&gt;
&lt;td&gt;
Dennis Schroder
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;
4
&lt;/th&gt;
&lt;td&gt;
ATL
&lt;/td&gt;
&lt;td&gt;
181
&lt;/td&gt;
&lt;td&gt;
25.0
&lt;/td&gt;
&lt;td&gt;
0.200000
&lt;/td&gt;
&lt;td&gt;
Kent Bazemore
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/center&gt;
&lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h2 id="modeling"&gt;Modeling&lt;/h2&gt;
&lt;p&gt;Throughout this post, we will develop a series of models for understanding how foul calls vary across players, starting with a simple beta-Bernoulli model and working our way up to a hierachical &lt;a href="https://en.wikipedia.org/wiki/Item_response_theory"&gt;item-response theory&lt;/a&gt; regression model.&lt;/p&gt;
&lt;p&gt;Before building models, we must introduce a bit of notation. The index &lt;span class="math inline"&gt;\(i\)&lt;/span&gt; will correspond to a disadvantaged player and the index &lt;span class="math inline"&gt;\(j\)&lt;/span&gt; corresponds to a committing player. The index &lt;span class="math inline"&gt;\(k\)&lt;/span&gt; corresponds to a play. With this notation &lt;span class="math inline"&gt;\(i(k)\)&lt;/span&gt; and &lt;span class="math inline"&gt;\(j(k)\)&lt;/span&gt; are the index of the disadvantaged and committing player involved in play &lt;span class="math inline"&gt;\(k\)&lt;/span&gt;, respectively. The binary variable &lt;span class="math inline"&gt;\(y_k\)&lt;/span&gt; indicates whether or not a foul was called on play &lt;span class="math inline"&gt;\(k\)&lt;/span&gt;. All of our models use the likelihood&lt;/p&gt;
&lt;p&gt;&lt;span class="math display"&gt;\[y_k \sim \textrm{Bernoulli}(p_{i(k), j(k)}).\]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Each model differs in its specification of the probability that a foul is called, &lt;span class="math inline"&gt;\(p_{i, j}\)&lt;/span&gt;.&lt;/p&gt;
&lt;h3 id="beta-bernoulli-model"&gt;Beta-Bernoulli model&lt;/h3&gt;
&lt;p&gt;One of the simplest possible models for this data focuses only on the disadvantaged player, so &lt;span class="math inline"&gt;\(p_{i, j} = p_i\)&lt;/span&gt;, and places independent beta priors on each &lt;span class="math inline"&gt;\(p_i\)&lt;/span&gt;. For simplicity, we begin with uniform priors, &lt;span class="math inline"&gt;\(p_i \sim \textrm{Beta}(1, 1).\)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Even though this model is conjugate, we will use &lt;a href="http://pymc-devs.github.io/pymc3/"&gt;&lt;code&gt;pymc3&lt;/code&gt;&lt;/a&gt; to perform inference with it for consistency with subsequent, non-conjugate models.&lt;/p&gt;
&lt;div class="sourceCode" id="cb18"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb18-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb18-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;n_players &lt;span class="op"&gt;=&lt;/span&gt; player_map.size&lt;/span&gt;
&lt;span id="cb18-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb18-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;disadvantaged_id &lt;span class="op"&gt;=&lt;/span&gt; df.disadvantaged_id.values&lt;/span&gt;
&lt;span id="cb18-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb18-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb18-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb18-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;foul_called &lt;span class="op"&gt;=&lt;/span&gt; df.foul_called.values&lt;/span&gt;
&lt;span id="cb18-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb18-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;obs_rate &lt;span class="op"&gt;=&lt;/span&gt; foul_called.mean()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb19"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb19-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb19-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;with&lt;/span&gt; pm.Model() &lt;span class="im"&gt;as&lt;/span&gt; bb_model:&lt;/span&gt;
&lt;span id="cb19-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb19-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    p &lt;span class="op"&gt;=&lt;/span&gt; pm.Beta(&lt;span class="st"&gt;'p'&lt;/span&gt;, &lt;span class="fl"&gt;1.&lt;/span&gt;, &lt;span class="fl"&gt;1.&lt;/span&gt;, shape&lt;span class="op"&gt;=&lt;/span&gt;n_players)&lt;/span&gt;
&lt;span id="cb19-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb19-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb19-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb19-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    y &lt;span class="op"&gt;=&lt;/span&gt; pm.Bernoulli(&lt;span class="st"&gt;'y_obs'&lt;/span&gt;, p[disadvantaged_id],&lt;/span&gt;
&lt;span id="cb19-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb19-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                     observed&lt;span class="op"&gt;=&lt;/span&gt;foul_called)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Throughout this post, we will use the &lt;a href="https://arxiv.org/abs/1111.4246"&gt;no-U-turn sampler&lt;/a&gt; for inference, tuning the sampler’s hyperparameters for the first two thousand samples and subsequently keeping the next two thousand samples for inference.&lt;/p&gt;
&lt;div class="sourceCode" id="cb20"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb20-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb20-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;N_TUNE &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="dv"&gt;2000&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb20-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb20-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;N_SAMPLES &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="dv"&gt;2000&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb20-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb20-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb20-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb20-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;SEED &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="dv"&gt;506421&lt;/span&gt; &lt;span class="co"&gt;# from random.org, for reproducibility&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We now sample from the beta-Bernoulli model.&lt;/p&gt;
&lt;div class="sourceCode" id="cb21"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb21-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb21-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; sample(model, n_tune, n_samples, seed):&lt;/span&gt;
&lt;span id="cb21-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb21-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;with&lt;/span&gt; model:&lt;/span&gt;
&lt;span id="cb21-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb21-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        full_trace &lt;span class="op"&gt;=&lt;/span&gt; pm.sample(n_tune &lt;span class="op"&gt;+&lt;/span&gt; n_samples, tune&lt;span class="op"&gt;=&lt;/span&gt;n_tune, random_seed&lt;span class="op"&gt;=&lt;/span&gt;seed)&lt;/span&gt;
&lt;span id="cb21-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb21-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;/span&gt;
&lt;span id="cb21-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb21-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;return&lt;/span&gt; full_trace[n_tune:]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb22"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb22-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb22-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;bb_trace &lt;span class="op"&gt;=&lt;/span&gt; sample(bb_model, N_TUNE, N_SAMPLES, SEED)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;Auto-assigning NUTS sampler...
Initializing NUTS using advi...
  2%|▏         | 4793/200000 [00:02&amp;lt;01:47, 1818.84it/s]Median ELBO converged.
Finished [100%]: Average ELBO = -3,554.3

100%|██████████| 4000/4000 [01:03&amp;lt;00:00, 63.38it/s]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We use energy energy plots to diagnose possible problems with our samples.&lt;/p&gt;
&lt;div class="sourceCode" id="cb24"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb24-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb24-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; energy_plot(trace):&lt;/span&gt;
&lt;span id="cb24-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb24-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    energy &lt;span class="op"&gt;=&lt;/span&gt; trace[&lt;span class="st"&gt;'energy'&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb24-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb24-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    energy_diff &lt;span class="op"&gt;=&lt;/span&gt; np.diff(energy)&lt;/span&gt;
&lt;span id="cb24-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb24-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb24-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb24-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    fig, ax &lt;span class="op"&gt;=&lt;/span&gt; plt.subplots(figsize&lt;span class="op"&gt;=&lt;/span&gt;(&lt;span class="dv"&gt;8&lt;/span&gt;, &lt;span class="dv"&gt;6&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb24-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb24-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb24-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb24-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.hist(energy &lt;span class="op"&gt;-&lt;/span&gt; energy.mean(), bins&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;30&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb24-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb24-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            lw&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;, alpha&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;0.5&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb24-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb24-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            label&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;"Energy"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb24-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb24-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.hist(energy_diff, bins&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;30&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb24-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb24-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            lw&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;, alpha&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;0.5&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb24-12"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb24-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            label&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;"Energy difference"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb24-13"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb24-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb24-14"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb24-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.set_xticks([])&lt;/span&gt;
&lt;span id="cb24-15"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb24-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.set_yticks([])&lt;/span&gt;
&lt;span id="cb24-16"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb24-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb24-17"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb24-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.legend()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb25"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb25-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb25-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;energy_plot(bb_trace)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_38_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;Since the energy and energy difference distributions are quite similar, there is no indication from this plot of sampling issues. For an in-depth treatment of Hamiltonian Monte Carlo algorithms and convergence diagnostics, consult &lt;a href="https://betanalpha.github.io/"&gt;Michael Betancourt&lt;/a&gt;’s excellent paper &lt;a href="https://arxiv.org/abs/1701.02434"&gt;&lt;em&gt;A Conceptual Introduction to Hamiltonian Monte Carlo&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We will use the &lt;a href="http://www.stat.columbia.edu/~gelman/research/unpublished/loo_stan.pdf"&gt;widely applicable information criterion&lt;/a&gt; (WAIC) and binned residuals to check and compare our models. WAIC is a Bayesian measure of out-of-sample predictive accuracy based on in-sample data that is quite closely related to [leave-one-out cross-validation](https://en.wikipedia.org/wiki/Cross-validation_(statistics%29#Leave-one-out_cross-validation). It attempts to improve upon known shortcomings of the widely-used &lt;a href="https://en.wikipedia.org/wiki/Deviance_information_criterion"&gt;deviance information criterion&lt;/a&gt;. (See &lt;a href="http://www.stat.columbia.edu/~gelman/research/published/waic_understand3.pdf"&gt;&lt;em&gt;Understanding predictive information criteria for Bayesian models&lt;/em&gt;&lt;/a&gt; for a review and comparison of various information criteria, including DIC and WAIC.) WAIC is easy to calculate with &lt;code&gt;pymc3&lt;/code&gt;.&lt;/p&gt;
&lt;div class="sourceCode" id="cb26"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb26-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb26-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; get_waic_df(model, trace, name):&lt;/span&gt;
&lt;span id="cb26-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb26-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;with&lt;/span&gt; model:&lt;/span&gt;
&lt;span id="cb26-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb26-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        waic &lt;span class="op"&gt;=&lt;/span&gt; pm.waic(trace)&lt;/span&gt;
&lt;span id="cb26-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb26-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb26-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb26-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;return&lt;/span&gt; pd.DataFrame.from_records([waic], index&lt;span class="op"&gt;=&lt;/span&gt;[name], columns&lt;span class="op"&gt;=&lt;/span&gt;waic._fields)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb27"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb27-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb27-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;waic_df &lt;span class="op"&gt;=&lt;/span&gt; get_waic_df(bb_model, bb_trace, &lt;span class="st"&gt;"Beta-Bernoulli"&lt;/span&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;/opt/conda/lib/python3.5/site-packages/pymc3/stats.py:145: UserWarning: For one or more samples the posterior variance of the
        log predictive densities exceeds 0.4. This could be indication of
        WAIC starting to fail see http://arxiv.org/abs/1507.04544 for details
        
  """)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We see that the WAIC calculation indicates difficulties with the beta-Bernoulii model, which we will soon confirm.&lt;/p&gt;
&lt;div class="sourceCode" id="cb29"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb29-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb29-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;waic_df&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div&gt;
&lt;center&gt;
&lt;table border="1" class="dataframe"&gt;
&lt;thead&gt;
&lt;tr style="text-align: right;"&gt;
&lt;th&gt;
&lt;/th&gt;
&lt;th&gt;
WAIC
&lt;/th&gt;
&lt;th&gt;
WAIC_se
&lt;/th&gt;
&lt;th&gt;
p_WAIC
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;
Beta-Bernoulli
&lt;/th&gt;
&lt;td&gt;
6021.491064
&lt;/td&gt;
&lt;td&gt;
66.23822
&lt;/td&gt;
&lt;td&gt;
238.488377
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/center&gt;
&lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;In addition to the WAIC value, we get an estimate of its standard error (&lt;code&gt;WAIC_se&lt;/code&gt;) and the number of effective parameters in the model (&lt;code&gt;p_WAIC&lt;/code&gt;). The number of effective parameters is an indication of model complexity.&lt;/p&gt;
&lt;p&gt;The second diagnostic tool we use on our models are binned residuals, which show how well-calibrated the model’s predicted probabilities are. Intuitively, if our model predicts that an event has a 35% chance of occurring and we can observe many repetitions of that event, we would expect the event to actually occur about 35% of the time. If the observed occurrences of the event differ substantially from the predicted rate, we have reason to doubt the quality of our model. Since we generally can’t observe each event many times, we instead group events into bins by their predicted probability and check that the average predicted probability in each bin is close to the rate at which the events in that bin are observed.&lt;/p&gt;
&lt;p&gt;The binned predictions and residuals for the beta-Bernoulli model are shown below.&lt;/p&gt;
&lt;div class="sourceCode" id="cb30"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb30-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;BINS &lt;span class="op"&gt;=&lt;/span&gt; np.linspace(&lt;span class="dv"&gt;0&lt;/span&gt;, &lt;span class="dv"&gt;1&lt;/span&gt;, &lt;span class="dv"&gt;11&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb30-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;BINS_3D &lt;span class="op"&gt;=&lt;/span&gt; BINS[np.newaxis, np.newaxis]&lt;/span&gt;
&lt;span id="cb30-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb30-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; binned_residuals(y, p):&lt;/span&gt;
&lt;span id="cb30-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    p_3d &lt;span class="op"&gt;=&lt;/span&gt; p[..., np.newaxis]&lt;/span&gt;
&lt;span id="cb30-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb30-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    in_bin &lt;span class="op"&gt;=&lt;/span&gt; (BINS_3D[..., :&lt;span class="op"&gt;-&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;] &lt;span class="op"&gt;&amp;lt;&lt;/span&gt; p_3d) &lt;span class="op"&gt;&amp;amp;&lt;/span&gt; (p_3d &lt;span class="op"&gt;&amp;lt;=&lt;/span&gt; BINS_3D[..., &lt;span class="dv"&gt;1&lt;/span&gt;:])&lt;/span&gt;
&lt;span id="cb30-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    bin_counts &lt;span class="op"&gt;=&lt;/span&gt; in_bin.&lt;span class="bu"&gt;sum&lt;/span&gt;(axis&lt;span class="op"&gt;=&lt;/span&gt;(&lt;span class="dv"&gt;0&lt;/span&gt;, &lt;span class="dv"&gt;1&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb30-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb30-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    p_mean &lt;span class="op"&gt;=&lt;/span&gt; (in_bin &lt;span class="op"&gt;*&lt;/span&gt; p_3d).&lt;span class="bu"&gt;sum&lt;/span&gt;(axis&lt;span class="op"&gt;=&lt;/span&gt;(&lt;span class="dv"&gt;0&lt;/span&gt;, &lt;span class="dv"&gt;1&lt;/span&gt;)) &lt;span class="op"&gt;/&lt;/span&gt; bin_counts&lt;/span&gt;
&lt;span id="cb30-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    y_mean &lt;span class="op"&gt;=&lt;/span&gt; (in_bin &lt;span class="op"&gt;*&lt;/span&gt; y[np.newaxis, :, np.newaxis]).&lt;span class="bu"&gt;sum&lt;/span&gt;(axis&lt;span class="op"&gt;=&lt;/span&gt;(&lt;span class="dv"&gt;0&lt;/span&gt;, &lt;span class="dv"&gt;1&lt;/span&gt;)) &lt;span class="op"&gt;/&lt;/span&gt; bin_counts&lt;/span&gt;
&lt;span id="cb30-12"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb30-13"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;return&lt;/span&gt; y_mean, p_mean, bin_counts&lt;/span&gt;
&lt;span id="cb30-14"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb30-15"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; binned_residual_plot(bin_obs, bin_p, bin_counts):&lt;/span&gt;
&lt;span id="cb30-16"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    fig, (ax, resid_ax) &lt;span class="op"&gt;=&lt;/span&gt; plt.subplots(ncols&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;2&lt;/span&gt;, sharex&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;, figsize&lt;span class="op"&gt;=&lt;/span&gt;(&lt;span class="dv"&gt;16&lt;/span&gt;, &lt;span class="dv"&gt;6&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb30-17"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb30-18"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.scatter(bin_p, bin_obs,&lt;/span&gt;
&lt;span id="cb30-19"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;               s&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;300&lt;/span&gt; &lt;span class="op"&gt;*&lt;/span&gt; np.sqrt(bin_counts &lt;span class="op"&gt;/&lt;/span&gt; bin_counts.&lt;span class="bu"&gt;sum&lt;/span&gt;()),&lt;/span&gt;
&lt;span id="cb30-20"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;               zorder&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;5&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb30-21"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-21" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.plot([&lt;span class="dv"&gt;0&lt;/span&gt;, &lt;span class="dv"&gt;1&lt;/span&gt;], [&lt;span class="dv"&gt;0&lt;/span&gt;, &lt;span class="dv"&gt;1&lt;/span&gt;], &lt;span class="st"&gt;'--'&lt;/span&gt;, c&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'k'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb30-22"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-22" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb30-23"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-23" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.set_xlim(&lt;span class="dv"&gt;0&lt;/span&gt;, &lt;span class="dv"&gt;1&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb30-24"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-24" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.set_xticks(np.linspace(&lt;span class="dv"&gt;0&lt;/span&gt;, &lt;span class="dv"&gt;1&lt;/span&gt;, &lt;span class="dv"&gt;5&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb30-25"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-25" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.xaxis.set_major_formatter(pct_formatter)&lt;/span&gt;
&lt;span id="cb30-26"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-26" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.set_xlabel(&lt;span class="st"&gt;"Mean $p$ (binned)"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb30-27"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-27" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb30-28"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-28" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.set_ylim(&lt;span class="dv"&gt;0&lt;/span&gt;, &lt;span class="dv"&gt;1&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb30-29"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-29" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.set_yticks(np.linspace(&lt;span class="dv"&gt;0&lt;/span&gt;, &lt;span class="dv"&gt;1&lt;/span&gt;, &lt;span class="dv"&gt;5&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb30-30"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-30" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.yaxis.set_major_formatter(pct_formatter)&lt;/span&gt;
&lt;span id="cb30-31"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-31" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.set_ylabel(&lt;span class="st"&gt;"Observed rate (binned)"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb30-32"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-32" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb30-33"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-33" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    resid_ax.scatter(bin_p, bin_obs &lt;span class="op"&gt;-&lt;/span&gt; bin_p,&lt;/span&gt;
&lt;span id="cb30-34"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-34" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                     s&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;300&lt;/span&gt; &lt;span class="op"&gt;*&lt;/span&gt; np.sqrt(bin_counts &lt;span class="op"&gt;/&lt;/span&gt; bin_counts.&lt;span class="bu"&gt;sum&lt;/span&gt;()),&lt;/span&gt;
&lt;span id="cb30-35"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-35" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                     zorder&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;5&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb30-36"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-36" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb30-37"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-37" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    resid_ax.hlines(&lt;span class="dv"&gt;0&lt;/span&gt;, &lt;span class="dv"&gt;0&lt;/span&gt;, &lt;span class="dv"&gt;1&lt;/span&gt;, &lt;span class="st"&gt;'k'&lt;/span&gt;, &lt;span class="st"&gt;'--'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb30-38"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-38" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb30-39"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-39" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    resid_ax.set_xlim(&lt;span class="dv"&gt;0&lt;/span&gt;, &lt;span class="dv"&gt;1&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb30-40"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-40" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    resid_ax.set_xticks(np.linspace(&lt;span class="dv"&gt;0&lt;/span&gt;, &lt;span class="dv"&gt;1&lt;/span&gt;, &lt;span class="dv"&gt;5&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb30-41"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-41" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    resid_ax.xaxis.set_major_formatter(pct_formatter)&lt;/span&gt;
&lt;span id="cb30-42"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-42" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    resid_ax.set_xlabel(&lt;span class="st"&gt;"Mean $p$ (binned)"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb30-43"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-43" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb30-44"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-44" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    resid_ax.yaxis.set_major_formatter(pct_formatter)&lt;/span&gt;
&lt;span id="cb30-45"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb30-45" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    resid_ax.set_ylabel(&lt;span class="st"&gt;"Residual (binned)"&lt;/span&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb31"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb31-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb31-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;bin_obs, bin_p, bin_counts &lt;span class="op"&gt;=&lt;/span&gt; binned_residuals(foul_called, bb_trace[&lt;span class="st"&gt;'p'&lt;/span&gt;][:, disadvantaged_id])&lt;/span&gt;
&lt;span id="cb31-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb31-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb31-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb31-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;binned_residual_plot(bin_obs, bin_p, bin_counts)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_46_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;In these plots, the dashed black lines show how these quantities would be related, for a perfect model. The area of each point is proportional to the number of events whose predicted probability fell in the relevant bin. From these plots, we get further confirmation that our simple beta-Bernoulli model is quite unsatisfactory, as many binned residuals exceed 5% in absolute value.&lt;/p&gt;
&lt;p&gt;Below we plot the posterior mean and 90% credible interval for &lt;span class="math inline"&gt;\(p\)&lt;/span&gt; for each player in the data set (grouped by team, for legibility), along with the player’s observed foul called percentage when disadvantaged. The area of the point for observed foul called percentage is proportional to the number of plays in which the player was disadvantaged.&lt;/p&gt;
&lt;div class="sourceCode" id="cb32"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb32-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb32-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; to_param_df(player_df, trace, varnames):&lt;/span&gt;
&lt;span id="cb32-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb32-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    df &lt;span class="op"&gt;=&lt;/span&gt; player_df&lt;/span&gt;
&lt;span id="cb32-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb32-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb32-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb32-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;for&lt;/span&gt; name &lt;span class="kw"&gt;in&lt;/span&gt; varnames:&lt;/span&gt;
&lt;span id="cb32-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb32-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        mean &lt;span class="op"&gt;=&lt;/span&gt; trace[name].mean(axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb32-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb32-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        low, high &lt;span class="op"&gt;=&lt;/span&gt; np.percentile(trace[name], [&lt;span class="dv"&gt;5&lt;/span&gt;, &lt;span class="dv"&gt;95&lt;/span&gt;], axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb32-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb32-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;/span&gt;
&lt;span id="cb32-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb32-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        df &lt;span class="op"&gt;=&lt;/span&gt; df.assign(&lt;span class="op"&gt;**&lt;/span&gt;{&lt;/span&gt;
&lt;span id="cb32-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb32-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            &lt;span class="st"&gt;'&lt;/span&gt;&lt;span class="sc"&gt;{}&lt;/span&gt;&lt;span class="st"&gt;_mean'&lt;/span&gt;.&lt;span class="bu"&gt;format&lt;/span&gt;(name): mean[df.player_id],&lt;/span&gt;
&lt;span id="cb32-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb32-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            &lt;span class="st"&gt;'&lt;/span&gt;&lt;span class="sc"&gt;{}&lt;/span&gt;&lt;span class="st"&gt;_low'&lt;/span&gt;.&lt;span class="bu"&gt;format&lt;/span&gt;(name): low[df.player_id],&lt;/span&gt;
&lt;span id="cb32-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb32-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            &lt;span class="st"&gt;'&lt;/span&gt;&lt;span class="sc"&gt;{}&lt;/span&gt;&lt;span class="st"&gt;_high'&lt;/span&gt;.&lt;span class="bu"&gt;format&lt;/span&gt;(name): high[df.player_id]&lt;/span&gt;
&lt;span id="cb32-12"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb32-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        })&lt;/span&gt;
&lt;span id="cb32-13"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb32-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;/span&gt;
&lt;span id="cb32-14"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb32-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;return&lt;/span&gt; df&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb33"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb33-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb33-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;bb_df &lt;span class="op"&gt;=&lt;/span&gt; to_param_df(team_player_map, bb_trace, [&lt;span class="st"&gt;'p'&lt;/span&gt;])&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb34"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb34-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; plot_params(mean, interval, names, ax&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;None&lt;/span&gt;, &lt;span class="op"&gt;**&lt;/span&gt;kwargs):&lt;/span&gt;
&lt;span id="cb34-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;if&lt;/span&gt; ax &lt;span class="kw"&gt;is&lt;/span&gt; &lt;span class="va"&gt;None&lt;/span&gt;:&lt;/span&gt;
&lt;span id="cb34-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        fig, ax &lt;span class="op"&gt;=&lt;/span&gt; plt.subplots(figsize&lt;span class="op"&gt;=&lt;/span&gt;(&lt;span class="dv"&gt;8&lt;/span&gt;, &lt;span class="dv"&gt;6&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb34-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb34-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    n_players &lt;span class="op"&gt;=&lt;/span&gt; names.size&lt;/span&gt;
&lt;span id="cb34-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb34-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.errorbar(mean, np.arange(n_players),&lt;/span&gt;
&lt;span id="cb34-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                xerr&lt;span class="op"&gt;=&lt;/span&gt;np.&lt;span class="bu"&gt;abs&lt;/span&gt;(mean &lt;span class="op"&gt;-&lt;/span&gt; interval),&lt;/span&gt;
&lt;span id="cb34-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                fmt&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'o'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb34-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                label&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;"Mean with&lt;/span&gt;&lt;span class="ch"&gt;\n&lt;/span&gt;&lt;span class="st"&gt;90&lt;/span&gt;&lt;span class="sc"&gt;% i&lt;/span&gt;&lt;span class="st"&gt;nterval"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb34-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb34-12"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb34-13"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.set_ylim(&lt;span class="op"&gt;-&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;, n_players)&lt;/span&gt;
&lt;span id="cb34-14"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.set_yticks(np.arange(n_players))&lt;/span&gt;
&lt;span id="cb34-15"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.set_yticklabels(names)&lt;/span&gt;
&lt;span id="cb34-16"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb34-17"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;return&lt;/span&gt; ax&lt;/span&gt;
&lt;span id="cb34-18"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb34-19"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; plot_p_params(rate, n_plays, league_mean, ax&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;None&lt;/span&gt;, &lt;span class="op"&gt;**&lt;/span&gt;kwargs):&lt;/span&gt;
&lt;span id="cb34-20"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;if&lt;/span&gt; ax &lt;span class="kw"&gt;is&lt;/span&gt; &lt;span class="va"&gt;None&lt;/span&gt;:&lt;/span&gt;
&lt;span id="cb34-21"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-21" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        ax &lt;span class="op"&gt;=&lt;/span&gt; plt.gca()&lt;/span&gt;
&lt;span id="cb34-22"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-22" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;/span&gt;
&lt;span id="cb34-23"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-23" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    n_players &lt;span class="op"&gt;=&lt;/span&gt; rate.size&lt;/span&gt;
&lt;span id="cb34-24"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-24" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        &lt;/span&gt;
&lt;span id="cb34-25"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-25" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.scatter(rate, np.arange(n_players),&lt;/span&gt;
&lt;span id="cb34-26"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-26" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;               c&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'k'&lt;/span&gt;, s&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;20&lt;/span&gt; &lt;span class="op"&gt;*&lt;/span&gt; np.sqrt(n_plays),&lt;/span&gt;
&lt;span id="cb34-27"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-27" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;               alpha&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;0.5&lt;/span&gt;, zorder&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;5&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb34-28"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-28" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;               label&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;"Observed"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb34-29"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-29" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb34-30"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-30" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.vlines(league_mean, &lt;span class="op"&gt;-&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;, n_players,&lt;/span&gt;
&lt;span id="cb34-31"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-31" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              &lt;span class="st"&gt;'k'&lt;/span&gt;, &lt;span class="st"&gt;'--'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb34-32"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-32" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              label&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;"League average"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb34-33"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-33" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb34-34"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-34" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; plot_p_helper(mean, low, high, rate, n_plays, names, league_mean&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;None&lt;/span&gt;, ax&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;None&lt;/span&gt;, &lt;span class="op"&gt;**&lt;/span&gt;kwargs):&lt;/span&gt;
&lt;span id="cb34-35"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-35" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;if&lt;/span&gt; ax &lt;span class="kw"&gt;is&lt;/span&gt; &lt;span class="va"&gt;None&lt;/span&gt;:&lt;/span&gt;
&lt;span id="cb34-36"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-36" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        ax &lt;span class="op"&gt;=&lt;/span&gt; plt.gca()&lt;/span&gt;
&lt;span id="cb34-37"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-37" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb34-38"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-38" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    mean &lt;span class="op"&gt;=&lt;/span&gt; mean.values&lt;/span&gt;
&lt;span id="cb34-39"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-39" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    rate &lt;span class="op"&gt;=&lt;/span&gt; rate.values&lt;/span&gt;
&lt;span id="cb34-40"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-40" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    n_plays &lt;span class="op"&gt;=&lt;/span&gt; n_plays.values&lt;/span&gt;
&lt;span id="cb34-41"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-41" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    names &lt;span class="op"&gt;=&lt;/span&gt; names.values&lt;/span&gt;
&lt;span id="cb34-42"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-42" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb34-43"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-43" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    argsorted_ix &lt;span class="op"&gt;=&lt;/span&gt; mean.argsort()&lt;/span&gt;
&lt;span id="cb34-44"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-44" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    interval &lt;span class="op"&gt;=&lt;/span&gt; np.row_stack([low, high])&lt;/span&gt;
&lt;span id="cb34-45"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-45" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb34-46"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-46" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    plot_params(mean[argsorted_ix], interval[:, argsorted_ix], names[argsorted_ix],&lt;/span&gt;
&lt;span id="cb34-47"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-47" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                ax&lt;span class="op"&gt;=&lt;/span&gt;ax, &lt;span class="op"&gt;**&lt;/span&gt;kwargs)&lt;/span&gt;
&lt;span id="cb34-48"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-48" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    plot_p_params(rate[argsorted_ix], n_plays[argsorted_ix], league_mean,&lt;/span&gt;
&lt;span id="cb34-49"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb34-49" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                  ax&lt;span class="op"&gt;=&lt;/span&gt;ax, &lt;span class="op"&gt;**&lt;/span&gt;kwargs)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb35"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb35-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb35-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid &lt;span class="op"&gt;=&lt;/span&gt; sns.FacetGrid(bb_df, col&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'team'&lt;/span&gt;, col_wrap&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;2&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb35-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb35-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                     sharey&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;False&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb35-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb35-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                     size&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;4&lt;/span&gt;, aspect&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;1.5&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb35-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb35-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid.&lt;span class="bu"&gt;map&lt;/span&gt;(plot_p_helper,&lt;/span&gt;
&lt;span id="cb35-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb35-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;         &lt;span class="st"&gt;'p_mean'&lt;/span&gt;, &lt;span class="st"&gt;'p_low'&lt;/span&gt;, &lt;span class="st"&gt;'p_high'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb35-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb35-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;         &lt;span class="st"&gt;'disadvantaged_rate'&lt;/span&gt;, &lt;span class="st"&gt;'disadvantaged_plays'&lt;/span&gt;, &lt;span class="st"&gt;'name'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb35-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb35-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;         league_mean&lt;span class="op"&gt;=&lt;/span&gt;obs_rate)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb35-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb35-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb35-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb35-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid.set_axis_labels(&lt;span class="vs"&gt;r"$p$"&lt;/span&gt;, &lt;span class="st"&gt;"Player"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb35-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb35-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb35-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb35-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;for&lt;/span&gt; ax &lt;span class="kw"&gt;in&lt;/span&gt; grid.axes:&lt;/span&gt;
&lt;span id="cb35-12"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb35-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.set_xticks(np.linspace(&lt;span class="dv"&gt;0&lt;/span&gt;, &lt;span class="dv"&gt;1&lt;/span&gt;, &lt;span class="dv"&gt;5&lt;/span&gt;))&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb35-13"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb35-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.set_xticklabels(ax.get_xticklabels(), visible&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb35-14"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb35-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.xaxis.set_major_formatter(pct_formatter)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb35-15"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb35-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb35-16"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb35-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid.fig.tight_layout()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb35-17"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb35-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid.add_legend()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_51_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;These plots reveal an undesirable property of this model and its inferences. Since the prior distribution on &lt;span class="math inline"&gt;\(p_i\)&lt;/span&gt; is uniform on the interval &lt;span class="math inline"&gt;\([0, 1]\)&lt;/span&gt;, all posterior estimates of &lt;span class="math inline"&gt;\(p_i\)&lt;/span&gt; are pulled towards the prior expected value of 50%. This phenomenon is known as shrinkage. In extreme cases of players that were never disadvantaged, the posterior estimate of &lt;span class="math inline"&gt;\(p_i\)&lt;/span&gt; is quite close to 50%. For these players, the league average foul call rate would seem to be a much more reasonable estimate of &lt;span class="math inline"&gt;\(p_i\)&lt;/span&gt; than 50%. The league average foul call rate is shown as a dotted black line on the charts above.&lt;/p&gt;
&lt;p&gt;There are several possible modifications of the beta-Bernoulli model that can cause shrinkage toward the league average. Perhaps the most straightforward is the &lt;a href="https://en.wikipedia.org/wiki/Empirical_Bayes_method"&gt;empirical Bayesian method&lt;/a&gt; that sets the parameters of the prior distribution on &lt;span class="math inline"&gt;\(p_i\)&lt;/span&gt; using the observed data. In this framework, there are many methods of choosing prior hyperparameters that make the prior expected value equal to the league average, therefore causing shrinkage toward the league average. We do not use empirical Bayesian methods in this post as they make it cumbersome to build the more complex models we want to use to understand the relationship between salary and foul calls. Empirical Bayesian methods are, however, an approximation to the fully hierachical models we begin building in the next section.&lt;/p&gt;
&lt;h3 id="hierarchical-logistic-normal-model"&gt;Hierarchical logistic-normal model&lt;/h3&gt;
&lt;p&gt;A hierarchical &lt;a href="https://en.wikipedia.org/wiki/Logit-normal_distribution"&gt;logistic-normal&lt;/a&gt; model addresses some of the shortcomings of the beta-Bernoulli model. For simplicity, this model focuses exclusively on the disadvantaged player and assumes that the &lt;a href="https://en.wikipedia.org/wiki/Logit"&gt;log-odds&lt;/a&gt; of a foul call for a given disadvantaged player are normally distributed. That is,&lt;/p&gt;
&lt;p&gt;&lt;span class="math display"&gt;\[
\begin{align*}
\log \left(\frac{p_i}{1 - p_i}\right) &amp;amp;
    \sim N(\mu, \sigma^2) \\
\eta_{k}
    &amp;amp; = \log \left(\frac{p_{i(k)}}{1 - p_{i(k)}}\right),
\end{align*}\]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;which is equivalent to&lt;/p&gt;
&lt;p&gt;&lt;span class="math display"&gt;\[p_{i(k)} = \frac{1}{1 + \exp(-\eta_k)}.\]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We address the beta-Bernoulli model’s shrinkage problem by placing a normal hyperprior distribution on &lt;span class="math inline"&gt;\(\mu\)&lt;/span&gt;, &lt;span class="math inline"&gt;\(\mu \sim N(0, 100).\)&lt;/span&gt; This shared hyperprior makes this model hierarchical. To complete the specification of this model, we place a half-&lt;a href="https://en.wikipedia.org/wiki/Cauchy_distribution"&gt;Cauchy&lt;/a&gt; prior on &lt;span class="math inline"&gt;\(\sigma\)&lt;/span&gt;, &lt;span class="math inline"&gt;\(\sigma \sim \textrm{HalfCauchy}(2.5)\)&lt;/span&gt;.&lt;/p&gt;
&lt;div class="sourceCode" id="cb36"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb36-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb36-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;with&lt;/span&gt; pm.Model() &lt;span class="im"&gt;as&lt;/span&gt; ln_model:&lt;/span&gt;
&lt;span id="cb36-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb36-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    μ &lt;span class="op"&gt;=&lt;/span&gt; pm.Normal(&lt;span class="st"&gt;'μ'&lt;/span&gt;, &lt;span class="fl"&gt;0.&lt;/span&gt;, &lt;span class="fl"&gt;10.&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb36-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb36-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    Δ &lt;span class="op"&gt;=&lt;/span&gt; pm.Normal(&lt;span class="st"&gt;'Δ'&lt;/span&gt;, &lt;span class="fl"&gt;0.&lt;/span&gt;, &lt;span class="fl"&gt;1.&lt;/span&gt;, shape&lt;span class="op"&gt;=&lt;/span&gt;n_players)&lt;/span&gt;
&lt;span id="cb36-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb36-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    σ &lt;span class="op"&gt;=&lt;/span&gt; pm.HalfCauchy(&lt;span class="st"&gt;'σ'&lt;/span&gt;, &lt;span class="fl"&gt;2.5&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb36-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb36-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb36-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb36-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    p_player &lt;span class="op"&gt;=&lt;/span&gt; pm.Deterministic(&lt;span class="st"&gt;'p_player'&lt;/span&gt;, pm.math.sigmoid(μ &lt;span class="op"&gt;+&lt;/span&gt; Δ &lt;span class="op"&gt;*&lt;/span&gt; σ))&lt;/span&gt;
&lt;span id="cb36-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb36-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb36-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb36-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    η &lt;span class="op"&gt;=&lt;/span&gt; μ &lt;span class="op"&gt;+&lt;/span&gt; Δ[disadvantaged_id] &lt;span class="op"&gt;*&lt;/span&gt; σ&lt;/span&gt;
&lt;span id="cb36-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb36-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    p &lt;span class="op"&gt;=&lt;/span&gt; pm.Deterministic(&lt;span class="st"&gt;'p'&lt;/span&gt;, pm.math.sigmoid(η))&lt;/span&gt;
&lt;span id="cb36-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb36-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb36-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb36-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    y &lt;span class="op"&gt;=&lt;/span&gt; pm.Bernoulli(&lt;span class="st"&gt;'y_obs'&lt;/span&gt;, p, observed&lt;span class="op"&gt;=&lt;/span&gt;foul_called)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Throughout this post we use an &lt;a href="http://twiecki.github.io/blog/2017/02/08/bayesian-hierchical-non-centered/"&gt;offset parameterization&lt;/a&gt; for hierarchical models that significantly improves sampling efficiency. We now sample from this model.&lt;/p&gt;
&lt;div class="sourceCode" id="cb37"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb37-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb37-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ln_trace &lt;span class="op"&gt;=&lt;/span&gt; sample(ln_model, N_TUNE, N_SAMPLES, SEED)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;Auto-assigning NUTS sampler...
Initializing NUTS using advi...
  9%|▊         | 17001/200000 [00:12&amp;lt;02:13, 1372.87it/s]Median ELBO converged.
Finished [100%]: Average ELBO = -3,520.3

100%|██████████| 4000/4000 [02:27&amp;lt;00:00, 65.27it/s]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The energy plot for this model gives no cause for concern.&lt;/p&gt;
&lt;div class="sourceCode" id="cb39"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb39-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb39-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;energy_plot(ln_trace)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_58_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;We now calculate the WAIC of the logistic-normal model, and compare it to that of the beta-Bernoulli model.&lt;/p&gt;
&lt;div class="sourceCode" id="cb40"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb40-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb40-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;waic_df &lt;span class="op"&gt;=&lt;/span&gt; waic_df.append(get_waic_df(ln_model, ln_trace, &lt;span class="st"&gt;"Logistic-normal"&lt;/span&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb41"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb41-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb41-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; waic_plot(waic_df):&lt;/span&gt;
&lt;span id="cb41-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb41-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    fig, (waic_ax, p_ax) &lt;span class="op"&gt;=&lt;/span&gt; plt.subplots(ncols&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;2&lt;/span&gt;, sharex&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;, figsize&lt;span class="op"&gt;=&lt;/span&gt;(&lt;span class="dv"&gt;16&lt;/span&gt;, &lt;span class="dv"&gt;6&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb41-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb41-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb41-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb41-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    waic_x &lt;span class="op"&gt;=&lt;/span&gt; np.arange(waic_df.shape[&lt;span class="dv"&gt;0&lt;/span&gt;])&lt;/span&gt;
&lt;span id="cb41-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb41-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb41-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb41-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    waic_ax.errorbar(waic_x, waic_df.WAIC,&lt;/span&gt;
&lt;span id="cb41-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb41-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                     yerr&lt;span class="op"&gt;=&lt;/span&gt;waic_df.WAIC_se,&lt;/span&gt;
&lt;span id="cb41-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb41-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                     fmt&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'o'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb41-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb41-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb41-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb41-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    waic_ax.set_xticks(waic_x)&lt;/span&gt;
&lt;span id="cb41-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb41-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    waic_ax.xaxis.grid(&lt;span class="va"&gt;False&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb41-12"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb41-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    waic_ax.set_xticklabels(waic_df.index)&lt;/span&gt;
&lt;span id="cb41-13"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb41-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    waic_ax.set_xlabel(&lt;span class="st"&gt;"Model"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb41-14"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb41-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb41-15"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb41-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    waic_ax.set_ylabel(&lt;span class="st"&gt;"WAIC"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb41-16"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb41-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb41-17"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb41-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    p_ax.bar(waic_x, waic_df.p_WAIC)&lt;/span&gt;
&lt;span id="cb41-18"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb41-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb41-19"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb41-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    p_ax.xaxis.grid(&lt;span class="va"&gt;False&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb41-20"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb41-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    p_ax.set_xlabel(&lt;span class="st"&gt;"Model"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb41-21"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb41-21" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb41-22"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb41-22" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    p_ax.set_ylabel(&lt;span class="st"&gt;"Effective number&lt;/span&gt;&lt;span class="ch"&gt;\n&lt;/span&gt;&lt;span class="st"&gt;of parameters"&lt;/span&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb42"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb42-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb42-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;waic_plot(waic_df)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_62_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;The left-hand plot above shows that the logistic-normal model is a significant improvement in WAIC over the beta-Bernoulli model, which is unsurprising. The right-hand plot shows that the logistic-normal model has roughly 20% the number of effective parameters as the beta-Bernoulli model. This reduction is due to the partial pooling effect of the hierarchical prior. The hyperprior on &lt;span class="math inline"&gt;\(\mu\)&lt;/span&gt; causes observations for one player to impact the estimate of &lt;span class="math inline"&gt;\(p_i\)&lt;/span&gt; for all players; this sharing of information across players is responsible for the large decrease in the number of effective parameters.&lt;/p&gt;
&lt;p&gt;Finally, we examine the binned residuals for the logistic-normal model.&lt;/p&gt;
&lt;div class="sourceCode" id="cb43"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb43-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb43-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;bin_obs, bin_p, bin_counts &lt;span class="op"&gt;=&lt;/span&gt; binned_residuals(foul_called, ln_trace[&lt;span class="st"&gt;'p'&lt;/span&gt;])&lt;/span&gt;
&lt;span id="cb43-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb43-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb43-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb43-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;binned_residual_plot(bin_obs, bin_p, bin_counts)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_64_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;These binned residuals are much smaller than those of the beta-Bernoulli model, which is further confirmation that the logistic-normal model is preferable.&lt;/p&gt;
&lt;p&gt;Below we plot the posterior distribution of &lt;span class="math inline"&gt;\(\operatorname{logit}^{-1}(\mu)\)&lt;/span&gt;, and we see that the observed foul call rate of approximately 25.1% lies within its 90% interval.&lt;/p&gt;
&lt;div class="sourceCode" id="cb44"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb44-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb44-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax, &lt;span class="op"&gt;=&lt;/span&gt; pm.plot_posterior(ln_trace, [&lt;span class="st"&gt;'μ'&lt;/span&gt;],&lt;/span&gt;
&lt;span id="cb44-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb44-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                        alpha_level&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;0.1&lt;/span&gt;, transform&lt;span class="op"&gt;=&lt;/span&gt;expit, ref_val&lt;span class="op"&gt;=&lt;/span&gt;obs_rate,&lt;/span&gt;
&lt;span id="cb44-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb44-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                        lw&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;0.&lt;/span&gt;, alpha&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;0.75&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb44-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb44-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb44-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb44-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.xaxis.set_major_formatter(pct_formatter)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb44-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb44-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb44-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb44-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.set_title(&lt;span class="vs"&gt;r"$\operatorname&lt;/span&gt;&lt;span class="sc"&gt;{logit}&lt;/span&gt;&lt;span class="vs"&gt;^{-1}(\mu)$"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_67_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;With this posterior for &lt;span class="math inline"&gt;\(\operatorname{logit}^{-1}(\mu)\)&lt;/span&gt;, we see the desired posterior shrinkage of each &lt;span class="math inline"&gt;\(p_i\)&lt;/span&gt; toward the observed foul call rate.&lt;/p&gt;
&lt;div class="sourceCode" id="cb45"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb45-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb45-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ln_df &lt;span class="op"&gt;=&lt;/span&gt; to_param_df(team_player_map, ln_trace, [&lt;span class="st"&gt;'p'&lt;/span&gt;])&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb46"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb46-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb46-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid &lt;span class="op"&gt;=&lt;/span&gt; sns.FacetGrid(ln_df, col&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'team'&lt;/span&gt;, col_wrap&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;2&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb46-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb46-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                     sharey&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;False&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb46-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb46-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                     size&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;4&lt;/span&gt;, aspect&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;1.5&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb46-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb46-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid.&lt;span class="bu"&gt;map&lt;/span&gt;(plot_p_helper,&lt;/span&gt;
&lt;span id="cb46-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb46-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;         &lt;span class="st"&gt;'p_mean'&lt;/span&gt;, &lt;span class="st"&gt;'p_low'&lt;/span&gt;, &lt;span class="st"&gt;'p_high'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb46-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb46-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;         &lt;span class="st"&gt;'disadvantaged_rate'&lt;/span&gt;, &lt;span class="st"&gt;'disadvantaged_plays'&lt;/span&gt;, &lt;span class="st"&gt;'name'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb46-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb46-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;         league_mean&lt;span class="op"&gt;=&lt;/span&gt;obs_rate)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb46-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb46-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb46-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb46-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid.set_axis_labels(&lt;span class="vs"&gt;r"$p$"&lt;/span&gt;, &lt;span class="st"&gt;"Player"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb46-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb46-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb46-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb46-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;for&lt;/span&gt; ax &lt;span class="kw"&gt;in&lt;/span&gt; grid.axes:&lt;/span&gt;
&lt;span id="cb46-12"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb46-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.set_xticks(np.linspace(&lt;span class="dv"&gt;0&lt;/span&gt;, &lt;span class="dv"&gt;1&lt;/span&gt;, &lt;span class="dv"&gt;5&lt;/span&gt;))&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb46-13"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb46-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.set_xticklabels(ax.get_xticklabels(), visible&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb46-14"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb46-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    ax.xaxis.set_major_formatter(pct_formatter)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb46-15"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb46-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb46-16"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb46-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid.fig.tight_layout()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb46-17"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb46-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid.add_legend()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_70_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;The inferences in these plots are markedly different from those of the beta-Bernoulli model. Most strikingly, we see that estimates have been shrunk towards the league average foul call rate, and that players that were never disadvantaged have posterior foul call probabilities quite close to that rate. As a consequence of this more reasonable shrinkage, the range of values taken by the posterior &lt;span class="math inline"&gt;\(p_i\)&lt;/span&gt; estimates is much smaller for the logistic-normal model than for the beta-Bernoulli model. Below we plot the top- and bottom-ten players by &lt;span class="math inline"&gt;\(p_i\)&lt;/span&gt;.&lt;/p&gt;
&lt;div class="sourceCode" id="cb47"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb47-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;fig, (top_ax, bottom_ax) &lt;span class="op"&gt;=&lt;/span&gt; plt.subplots(nrows&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;2&lt;/span&gt;, sharex&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;, figsize&lt;span class="op"&gt;=&lt;/span&gt;(&lt;span class="dv"&gt;8&lt;/span&gt;, &lt;span class="dv"&gt;10&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb47-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb47-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;by_p &lt;span class="op"&gt;=&lt;/span&gt; (ln_df.drop_duplicates([&lt;span class="st"&gt;'player_id'&lt;/span&gt;])&lt;/span&gt;
&lt;span id="cb47-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;             .sort_values(&lt;span class="st"&gt;'p_mean'&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb47-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;p_top &lt;span class="op"&gt;=&lt;/span&gt; by_p.iloc[&lt;span class="op"&gt;-&lt;/span&gt;&lt;span class="dv"&gt;10&lt;/span&gt;:]&lt;/span&gt;
&lt;span id="cb47-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb47-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;plot_params(p_top.p_mean.values, p_top[[&lt;span class="st"&gt;'p_low'&lt;/span&gt;, &lt;span class="st"&gt;'p_high'&lt;/span&gt;]].values.T,&lt;/span&gt;
&lt;span id="cb47-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            p_top.name.values,&lt;/span&gt;
&lt;span id="cb47-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            ax&lt;span class="op"&gt;=&lt;/span&gt;top_ax)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb47-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;top_ax.vlines(obs_rate, &lt;span class="op"&gt;-&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;, &lt;span class="dv"&gt;10&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb47-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              &lt;span class="st"&gt;'k'&lt;/span&gt;, &lt;span class="st"&gt;'--'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb47-12"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              label&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="vs"&gt;r"League average"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb47-13"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb47-14"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;top_ax.xaxis.set_major_formatter(pct_formatter)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb47-15"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb47-16"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;top_ax.set_ylabel(&lt;span class="st"&gt;"Player"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb47-17"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb47-18"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;top_ax.set_title(&lt;span class="st"&gt;"Top ten"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb47-19"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb47-20"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;p_bottom &lt;span class="op"&gt;=&lt;/span&gt; by_p.iloc[:&lt;span class="dv"&gt;10&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb47-21"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-21" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb47-22"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-22" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;plot_params(p_bottom.p_mean.values, p_bottom[[&lt;span class="st"&gt;'p_low'&lt;/span&gt;, &lt;span class="st"&gt;'p_high'&lt;/span&gt;]].values.T,&lt;/span&gt;
&lt;span id="cb47-23"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-23" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            p_bottom.name.values,&lt;/span&gt;
&lt;span id="cb47-24"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-24" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;            ax&lt;span class="op"&gt;=&lt;/span&gt;bottom_ax)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb47-25"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-25" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;bottom_ax.vlines(obs_rate, &lt;span class="op"&gt;-&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;, &lt;span class="dv"&gt;10&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb47-26"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-26" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                 &lt;span class="st"&gt;'k'&lt;/span&gt;, &lt;span class="st"&gt;'--'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb47-27"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-27" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                 label&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="vs"&gt;r"League average"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb47-28"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-28" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb47-29"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-29" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;bottom_ax.xaxis.set_major_formatter(pct_formatter)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb47-30"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-30" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;bottom_ax.set_xlabel(&lt;span class="vs"&gt;r"$p$"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb47-31"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-31" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb47-32"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-32" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;bottom_ax.set_ylabel(&lt;span class="st"&gt;"Player"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb47-33"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-33" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb47-34"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-34" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;fig.tight_layout()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb47-35"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-35" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;bottom_ax.legend(loc&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb47-36"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb47-36" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;bottom_ax.set_title(&lt;span class="st"&gt;"Bottom ten"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_72_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;h3 id="item-response-rasch-model"&gt;Item-response (Rasch) model&lt;/h3&gt;
&lt;p&gt;The hierarchical logistic-normal model is certainly an improvement over the beta-Bernoulli model, but both of these models have focused solely on the disadvantaged player. It seems quite important to understand the contribution of not just the disadvantaged player, but also of the committing player in each play to the probability of a foul call. &lt;a href="https://en.wikipedia.org/wiki/Item_response_theory"&gt;Item-response theory&lt;/a&gt; (IRT) provides generalizations the logistic-normal model that can account for the influence of both players involved in a play. IRT originated in psychometrics as a way to simultaneously measure individual aptitude and question difficulty based on test-response data, and has subsequently found many other applications. We use IRT to model foul calls by considering disadvantaged players as analagous to test takers and committing players as analagous to questions. Specifically, we will use the &lt;a href="https://en.wikipedia.org/wiki/Rasch_model"&gt;Rasch model&lt;/a&gt; for the probability &lt;span class="math inline"&gt;\(p_{i, j}\)&lt;/span&gt;, that a foul is called on a play where player &lt;span class="math inline"&gt;\(i\)&lt;/span&gt; is disadvantaged by committing player &lt;span class="math inline"&gt;\(j\)&lt;/span&gt;. This model posits that each player has a latent ability, &lt;span class="math inline"&gt;\(\theta_i\)&lt;/span&gt;, that governs how often fouls are called when they are disadvantaged and a latent difficulty &lt;span class="math inline"&gt;\(b_j\)&lt;/span&gt; that governs how often fouls are not called when they are committing. The probability that a foul is called on a play where player &lt;span class="math inline"&gt;\(i\)&lt;/span&gt; is disadvantaged and player &lt;span class="math inline"&gt;\(j\)&lt;/span&gt; is committing is then a function of the difference between the corresponding latent ability and difficulty parameters,&lt;/p&gt;
&lt;p&gt;&lt;span class="math display"&gt;\[
\begin{align*}
\eta_k
    &amp;amp; = \theta_{i(k)} - b_{j(k)} \\
p_k
    &amp;amp; = \frac{1}{1 + \exp(-\eta_k)}.
\end{align*}
\]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In this model, a player with a large value of &lt;span class="math inline"&gt;\(\theta_i\)&lt;/span&gt; is more likely to get a foul called when they are disadvantaged, and a player with a large value of &lt;span class="math inline"&gt;\(b_j\)&lt;/span&gt; is less likely to have a foul called when they are committing. If &lt;span class="math inline"&gt;\(\theta_{i(k)} = b_{j(k)}\)&lt;/span&gt;, there is a 50% chance a foul is called on that play.&lt;/p&gt;
&lt;p&gt;To complete the specification of this model, we place priors on &lt;span class="math inline"&gt;\(\theta_i\)&lt;/span&gt; and &lt;span class="math inline"&gt;\(b_j\)&lt;/span&gt;. Similarly to &lt;span class="math inline"&gt;\(\eta\)&lt;/span&gt; in the logistic-normal model, we place a hierarchical normal prior on &lt;span class="math inline"&gt;\(\theta_i\)&lt;/span&gt;,&lt;/p&gt;
&lt;p&gt;&lt;span class="math display"&gt;\[
\begin{align*}
\mu_{\theta}
    &amp;amp; \sim N(0, 100) \\
\sigma_{\theta}
    &amp;amp; \sim \textrm{HalfCauchy}(2.5) \\
\theta_i
    &amp;amp; \sim N(\mu_{\theta}, \sigma^2_{\theta}).
\end{align*}
\]&lt;/span&gt;&lt;/p&gt;
&lt;div class="sourceCode" id="cb48"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb48-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb48-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;with&lt;/span&gt; pm.Model() &lt;span class="im"&gt;as&lt;/span&gt; rasch_model:&lt;/span&gt;
&lt;span id="cb48-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb48-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    μ_θ &lt;span class="op"&gt;=&lt;/span&gt; pm.Normal(&lt;span class="st"&gt;'μ_θ'&lt;/span&gt;, &lt;span class="fl"&gt;0.&lt;/span&gt;, &lt;span class="fl"&gt;10.&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb48-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb48-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    Δ_θ &lt;span class="op"&gt;=&lt;/span&gt; pm.Normal(&lt;span class="st"&gt;'Δ_θ'&lt;/span&gt;, &lt;span class="fl"&gt;0.&lt;/span&gt;, &lt;span class="fl"&gt;1.&lt;/span&gt;, shape&lt;span class="op"&gt;=&lt;/span&gt;n_players)&lt;/span&gt;
&lt;span id="cb48-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb48-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    σ_θ &lt;span class="op"&gt;=&lt;/span&gt; pm.HalfCauchy(&lt;span class="st"&gt;'σ_θ'&lt;/span&gt;, &lt;span class="fl"&gt;2.5&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb48-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb48-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    θ &lt;span class="op"&gt;=&lt;/span&gt; pm.Deterministic(&lt;span class="st"&gt;'θ'&lt;/span&gt;, μ_θ &lt;span class="op"&gt;+&lt;/span&gt; Δ_θ &lt;span class="op"&gt;*&lt;/span&gt; σ_θ)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We also place a hierarchical normal prior on &lt;span class="math inline"&gt;\(b_j\)&lt;/span&gt;, though this prior must be subtley different from that on &lt;span class="math inline"&gt;\(\theta_i\)&lt;/span&gt;. Since &lt;span class="math inline"&gt;\(\theta_i\)&lt;/span&gt; and &lt;span class="math inline"&gt;\(b_j\)&lt;/span&gt; are latent variables, there is no natural scale on which they should be measured. If each &lt;span class="math inline"&gt;\(\theta_i\)&lt;/span&gt; and &lt;span class="math inline"&gt;\(b_j\)&lt;/span&gt; are shifted by the same amount, say &lt;span class="math inline"&gt;\(\delta\)&lt;/span&gt;, the likelihood does not change. That is, if &lt;span class="math inline"&gt;\(\tilde{\theta}_i = \theta_i + \delta\)&lt;/span&gt; and &lt;span class="math inline"&gt;\(\tilde{b}_j = b_j + \delta\)&lt;/span&gt;, then&lt;/p&gt;
&lt;p&gt;&lt;span class="math display"&gt;\[
\tilde{\eta}_{i, j} = \tilde{\theta}_i - \tilde{b}_j
    = \theta_i + \delta - (b_j + \delta)
    = \theta_i - b_j
    = \eta_{i, j}.
\]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Therefore, if we allow &lt;span class="math inline"&gt;\(\theta_i\)&lt;/span&gt; and &lt;span class="math inline"&gt;\(\beta_j\)&lt;/span&gt; to be shifted by arbitrary amounts, the Rasch model is not &lt;a href="https://en.wikipedia.org/wiki/Identifiability"&gt;identified&lt;/a&gt;. We identify the Rasch model by constraining the mean of the hyperprior on &lt;span class="math inline"&gt;\(b_j\)&lt;/span&gt; to be zero,&lt;/p&gt;
&lt;p&gt;&lt;span class="math display"&gt;\[
\begin{align*}
\sigma_b
    &amp;amp; \sim \textrm{HalfCauchy}(2.5) \\
b_j
    &amp;amp; \sim N(0, \sigma^2_b).
\end{align*}
\]&lt;/span&gt;&lt;/p&gt;
&lt;div class="sourceCode" id="cb49"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb49-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb49-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;with&lt;/span&gt; rasch_model:&lt;/span&gt;
&lt;span id="cb49-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb49-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    Δ_b &lt;span class="op"&gt;=&lt;/span&gt; pm.Normal(&lt;span class="st"&gt;'Δ_b'&lt;/span&gt;, &lt;span class="fl"&gt;0.&lt;/span&gt;, &lt;span class="fl"&gt;1.&lt;/span&gt;, shape&lt;span class="op"&gt;=&lt;/span&gt;n_players)&lt;/span&gt;
&lt;span id="cb49-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb49-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    σ_b &lt;span class="op"&gt;=&lt;/span&gt; pm.HalfCauchy(&lt;span class="st"&gt;'σ_b'&lt;/span&gt;, &lt;span class="fl"&gt;2.5&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb49-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb49-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    b &lt;span class="op"&gt;=&lt;/span&gt; pm.Deterministic(&lt;span class="st"&gt;'b'&lt;/span&gt;, Δ_b &lt;span class="op"&gt;*&lt;/span&gt; σ_b)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We now specify the Rasch model’s likelihood and sample from it.&lt;/p&gt;
&lt;div class="sourceCode" id="cb50"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb50-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb50-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;committing_id &lt;span class="op"&gt;=&lt;/span&gt; df.committing_id.values&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb51"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb51-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb51-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;with&lt;/span&gt; rasch_model:&lt;/span&gt;
&lt;span id="cb51-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb51-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    η &lt;span class="op"&gt;=&lt;/span&gt; θ[disadvantaged_id] &lt;span class="op"&gt;-&lt;/span&gt; b[committing_id]&lt;/span&gt;
&lt;span id="cb51-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb51-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    p &lt;span class="op"&gt;=&lt;/span&gt; pm.Deterministic(&lt;span class="st"&gt;'p'&lt;/span&gt;, pm.math.sigmoid(η))&lt;/span&gt;
&lt;span id="cb51-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb51-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb51-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb51-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    y &lt;span class="op"&gt;=&lt;/span&gt; pm.Bernoulli(&lt;span class="st"&gt;'y_obs'&lt;/span&gt;, p, observed&lt;span class="op"&gt;=&lt;/span&gt;foul_called)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb52"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb52-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb52-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;rasch_trace &lt;span class="op"&gt;=&lt;/span&gt; sample(rasch_model, N_TUNE, N_SAMPLES, SEED)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;Auto-assigning NUTS sampler...
Initializing NUTS using advi...
Average ELBO = -3,729.5:  11%|█▏        | 22583/200000 [00:19&amp;lt;02:33, 1156.17it/s]Median ELBO converged.
Finished [100%]: Average ELBO = -3,037.1

100%|██████████| 4000/4000 [02:01&amp;lt;00:00, 32.81it/s]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, the energy plot for this model gives no cause for concern.&lt;/p&gt;
&lt;div class="sourceCode" id="cb54"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb54-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb54-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;energy_plot(rasch_trace)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_82_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;Below we show the WAIC of our three models.&lt;/p&gt;
&lt;div class="sourceCode" id="cb55"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb55-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb55-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;waic_df &lt;span class="op"&gt;=&lt;/span&gt; waic_df.append(get_waic_df(rasch_model, rasch_trace, &lt;span class="st"&gt;"Rasch"&lt;/span&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb56"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb56-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb56-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;waic_plot(waic_df)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_85_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;The Rasch model represents a moderate WAIC improvement over the logistic-normal model, and unsurprisingly has many more effective parameters (since it added a nominal parameter, &lt;span class="math inline"&gt;\(b_j\)&lt;/span&gt;, per player).&lt;/p&gt;
&lt;p&gt;The Rasch model also has reasonable binned residuals, with very few events having residuals above 5%.&lt;/p&gt;
&lt;div class="sourceCode" id="cb57"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb57-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb57-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;bin_obs, bin_p, bin_counts &lt;span class="op"&gt;=&lt;/span&gt; binned_residuals(foul_called, rasch_trace[&lt;span class="st"&gt;'p'&lt;/span&gt;])&lt;/span&gt;
&lt;span id="cb57-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb57-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb57-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb57-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;binned_residual_plot(bin_obs, bin_p, bin_counts)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_87_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;For the Rasch model (and subsequent models), we switch from visualizing the per-player call probabilities to the latent parameters &lt;span class="math inline"&gt;\(\theta_i\)&lt;/span&gt; and &lt;span class="math inline"&gt;\(b_j\)&lt;/span&gt;.&lt;/p&gt;
&lt;div class="sourceCode" id="cb58"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb58-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb58-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;μ_θ_mean &lt;span class="op"&gt;=&lt;/span&gt; rasch_trace[&lt;span class="st"&gt;'μ_θ'&lt;/span&gt;].mean()&lt;/span&gt;
&lt;span id="cb58-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb58-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb58-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb58-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;rasch_df &lt;span class="op"&gt;=&lt;/span&gt; to_param_df(team_player_map, rasch_trace, [&lt;span class="st"&gt;'θ'&lt;/span&gt;, &lt;span class="st"&gt;'b'&lt;/span&gt;])&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb59"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb59-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb59-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; plot_params_helper(mean, low, high, names, league_mean&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;None&lt;/span&gt;, league_mean_name&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;None&lt;/span&gt;, ax&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;None&lt;/span&gt;, &lt;span class="op"&gt;**&lt;/span&gt;kwargs):&lt;/span&gt;
&lt;span id="cb59-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb59-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;if&lt;/span&gt; ax &lt;span class="kw"&gt;is&lt;/span&gt; &lt;span class="va"&gt;None&lt;/span&gt;:&lt;/span&gt;
&lt;span id="cb59-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb59-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        ax &lt;span class="op"&gt;=&lt;/span&gt; plt.gca()&lt;/span&gt;
&lt;span id="cb59-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb59-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb59-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb59-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    mean &lt;span class="op"&gt;=&lt;/span&gt; mean.values&lt;/span&gt;
&lt;span id="cb59-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb59-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    names &lt;span class="op"&gt;=&lt;/span&gt; names.values&lt;/span&gt;
&lt;span id="cb59-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb59-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb59-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb59-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    argsorted_ix &lt;span class="op"&gt;=&lt;/span&gt; mean.argsort()&lt;/span&gt;
&lt;span id="cb59-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb59-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    interval &lt;span class="op"&gt;=&lt;/span&gt; np.row_stack([low, high])&lt;/span&gt;
&lt;span id="cb59-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb59-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb59-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb59-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    plot_params(mean[argsorted_ix], interval[:, argsorted_ix], names[argsorted_ix],&lt;/span&gt;
&lt;span id="cb59-12"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb59-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                ax&lt;span class="op"&gt;=&lt;/span&gt;ax, &lt;span class="op"&gt;**&lt;/span&gt;kwargs)&lt;/span&gt;
&lt;span id="cb59-13"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb59-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb59-14"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb59-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="cf"&gt;if&lt;/span&gt; league_mean &lt;span class="kw"&gt;is&lt;/span&gt; &lt;span class="kw"&gt;not&lt;/span&gt; &lt;span class="va"&gt;None&lt;/span&gt;:&lt;/span&gt;
&lt;span id="cb59-15"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb59-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;        ax.vlines(league_mean, &lt;span class="op"&gt;-&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;, names.size,&lt;/span&gt;
&lt;span id="cb59-16"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb59-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                  &lt;span class="st"&gt;'k'&lt;/span&gt;, &lt;span class="st"&gt;'--'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb59-17"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb59-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                  label&lt;span class="op"&gt;=&lt;/span&gt;league_mean_name)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb60"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb60-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb60-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid &lt;span class="op"&gt;=&lt;/span&gt; sns.FacetGrid(rasch_df, col&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'team'&lt;/span&gt;, col_wrap&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;2&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb60-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb60-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                     sharey&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;False&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb60-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb60-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                     size&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;4&lt;/span&gt;, aspect&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;1.5&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb60-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb60-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid.&lt;span class="bu"&gt;map&lt;/span&gt;(plot_params_helper,&lt;/span&gt;
&lt;span id="cb60-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb60-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;         &lt;span class="st"&gt;'θ_mean'&lt;/span&gt;, &lt;span class="st"&gt;'θ_low'&lt;/span&gt;, &lt;span class="st"&gt;'θ_high'&lt;/span&gt;, &lt;span class="st"&gt;'name'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb60-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb60-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;         league_mean&lt;span class="op"&gt;=&lt;/span&gt;μ_θ_mean,&lt;/span&gt;
&lt;span id="cb60-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb60-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;         league_mean_name&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="vs"&gt;r"$\mu_{\theta}$"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb60-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb60-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb60-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb60-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid.set_axis_labels(&lt;span class="vs"&gt;r"$\theta$"&lt;/span&gt;, &lt;span class="st"&gt;"Player"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb60-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb60-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb60-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb60-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid.fig.tight_layout()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb60-12"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb60-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid.add_legend()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_91_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;div class="sourceCode" id="cb61"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb61-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb61-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid &lt;span class="op"&gt;=&lt;/span&gt; sns.FacetGrid(rasch_df, col&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'team'&lt;/span&gt;, col_wrap&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;2&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb61-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb61-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                     sharey&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;False&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb61-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb61-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                     size&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;4&lt;/span&gt;, aspect&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;1.5&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb61-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb61-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid.&lt;span class="bu"&gt;map&lt;/span&gt;(plot_params_helper,&lt;/span&gt;
&lt;span id="cb61-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb61-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;         &lt;span class="st"&gt;'b_mean'&lt;/span&gt;, &lt;span class="st"&gt;'b_low'&lt;/span&gt;, &lt;span class="st"&gt;'b_high'&lt;/span&gt;, &lt;span class="st"&gt;'name'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb61-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb61-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;         league_mean&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;0.&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb61-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb61-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb61-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb61-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid.set_axis_labels(&lt;span class="vs"&gt;r"$b$"&lt;/span&gt;, &lt;span class="st"&gt;"Player"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb61-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb61-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb61-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb61-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid.fig.tight_layout()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb61-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb61-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid.add_legend()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_92_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;Though these plots are voluminuous and therefore difficult to interpret precisely, a few trends are evident. The first is that there is more variation in the committing skill (&lt;span class="math inline"&gt;\(b_j\)&lt;/span&gt;) than in disadvantaged skill (&lt;span class="math inline"&gt;\(\theta_i\)&lt;/span&gt;). This difference is confirmed in the following histograms of the posterior expected values of &lt;span class="math inline"&gt;\(\theta_i\)&lt;/span&gt; and &lt;span class="math inline"&gt;\(b_j\)&lt;/span&gt;.&lt;/p&gt;
&lt;div class="sourceCode" id="cb62"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb62-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; plot_latent_distributions(θ, b):&lt;/span&gt;
&lt;span id="cb62-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    fig, (θ_ax, b_ax) &lt;span class="op"&gt;=&lt;/span&gt; plt.subplots(nrows&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;2&lt;/span&gt;, sharex&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;, figsize&lt;span class="op"&gt;=&lt;/span&gt;(&lt;span class="dv"&gt;8&lt;/span&gt;, &lt;span class="dv"&gt;6&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb62-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb62-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    bins &lt;span class="op"&gt;=&lt;/span&gt; np.linspace(&lt;span class="fl"&gt;0.9&lt;/span&gt; &lt;span class="op"&gt;*&lt;/span&gt; &lt;span class="bu"&gt;min&lt;/span&gt;(θ.&lt;span class="bu"&gt;min&lt;/span&gt;(), b.&lt;span class="bu"&gt;min&lt;/span&gt;()),&lt;/span&gt;
&lt;span id="cb62-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                       &lt;span class="fl"&gt;1.1&lt;/span&gt; &lt;span class="op"&gt;*&lt;/span&gt; &lt;span class="bu"&gt;max&lt;/span&gt;(θ.&lt;span class="bu"&gt;max&lt;/span&gt;(), b.&lt;span class="bu"&gt;max&lt;/span&gt;()),&lt;/span&gt;
&lt;span id="cb62-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                       &lt;span class="dv"&gt;75&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb62-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb62-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    θ_ax.hist(θ, bins&lt;span class="op"&gt;=&lt;/span&gt;bins,&lt;/span&gt;
&lt;span id="cb62-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              alpha&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;0.75&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb62-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb62-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    θ_ax.xaxis.set_label_position(&lt;span class="st"&gt;'top'&lt;/span&gt;) &lt;/span&gt;
&lt;span id="cb62-12"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    θ_ax.set_xlabel(&lt;span class="vs"&gt;r"Posterior expected $\theta$"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb62-13"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb62-14"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    θ_ax.set_yticks([])&lt;/span&gt;
&lt;span id="cb62-15"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    θ_ax.set_ylabel(&lt;span class="st"&gt;"Frequency"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb62-16"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb62-17"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    b_ax.hist(b, bins&lt;span class="op"&gt;=&lt;/span&gt;bins,&lt;/span&gt;
&lt;span id="cb62-18"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;              color&lt;span class="op"&gt;=&lt;/span&gt;green, alpha&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;0.75&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb62-19"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb62-20"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    b_ax.xaxis.tick_top()&lt;/span&gt;
&lt;span id="cb62-21"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-21" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    b_ax.set_xlabel(&lt;span class="vs"&gt;r"Posterior expected $b$"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb62-22"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-22" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb62-23"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-23" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    b_ax.set_yticks([])&lt;/span&gt;
&lt;span id="cb62-24"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-24" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    b_ax.invert_yaxis()&lt;/span&gt;
&lt;span id="cb62-25"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-25" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    b_ax.set_ylabel(&lt;span class="st"&gt;"Frequency"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb62-26"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-26" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb62-27"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb62-27" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    fig.tight_layout()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb63"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb63-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb63-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;plot_latent_distributions(rasch_df.θ_mean, rasch_df.b_mean)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_95_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;The following plots show the top and bottom ten players in terms of both &lt;span class="math inline"&gt;\(\theta_i\)&lt;/span&gt; and &lt;span class="math inline"&gt;\(b_j\)&lt;/span&gt;.&lt;/p&gt;
&lt;div class="sourceCode" id="cb64"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb64-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;def&lt;/span&gt; top_10_plot(trace_df, μ_θ&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;):&lt;/span&gt;
&lt;span id="cb64-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    fig &lt;span class="op"&gt;=&lt;/span&gt; plt.figure(figsize&lt;span class="op"&gt;=&lt;/span&gt;(&lt;span class="dv"&gt;16&lt;/span&gt;, &lt;span class="dv"&gt;10&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb64-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    θ_top_ax &lt;span class="op"&gt;=&lt;/span&gt; fig.add_subplot(&lt;span class="dv"&gt;221&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb64-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    b_top_ax &lt;span class="op"&gt;=&lt;/span&gt; fig.add_subplot(&lt;span class="dv"&gt;222&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb64-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    θ_bottom_ax &lt;span class="op"&gt;=&lt;/span&gt; fig.add_subplot(&lt;span class="dv"&gt;223&lt;/span&gt;, sharex&lt;span class="op"&gt;=&lt;/span&gt;θ_top_ax)&lt;/span&gt;
&lt;span id="cb64-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    b_bottom_ax &lt;span class="op"&gt;=&lt;/span&gt; fig.add_subplot(&lt;span class="dv"&gt;224&lt;/span&gt;, sharex&lt;span class="op"&gt;=&lt;/span&gt;b_top_ax)&lt;/span&gt;
&lt;span id="cb64-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb64-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;span class="co"&gt;# necessary for players that have been on more than one team&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb64-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    trace_df &lt;span class="op"&gt;=&lt;/span&gt; trace_df.drop_duplicates([&lt;span class="st"&gt;'player_id'&lt;/span&gt;])&lt;/span&gt;
&lt;span id="cb64-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb64-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    by_θ &lt;span class="op"&gt;=&lt;/span&gt; trace_df.sort_values(&lt;span class="st"&gt;'θ_mean'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb64-12"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    θ_top &lt;span class="op"&gt;=&lt;/span&gt; by_θ.iloc[&lt;span class="op"&gt;-&lt;/span&gt;&lt;span class="dv"&gt;10&lt;/span&gt;:]&lt;/span&gt;
&lt;span id="cb64-13"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    θ_bottom &lt;span class="op"&gt;=&lt;/span&gt; by_θ.iloc[:&lt;span class="dv"&gt;10&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb64-14"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb64-15"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    plot_params(θ_top.θ_mean.values, θ_top[[&lt;span class="st"&gt;'θ_low'&lt;/span&gt;, &lt;span class="st"&gt;'θ_high'&lt;/span&gt;]].values.T,&lt;/span&gt;
&lt;span id="cb64-16"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                θ_top.name.values,&lt;/span&gt;
&lt;span id="cb64-17"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                ax&lt;span class="op"&gt;=&lt;/span&gt;θ_top_ax)&lt;/span&gt;
&lt;span id="cb64-18"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    θ_top_ax.vlines(μ_θ, &lt;span class="op"&gt;-&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;, &lt;span class="dv"&gt;10&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb64-19"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                    &lt;span class="st"&gt;'k'&lt;/span&gt;, &lt;span class="st"&gt;'--'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb64-20"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                    label&lt;span class="op"&gt;=&lt;/span&gt;(&lt;span class="vs"&gt;r"$\mu_{\theta}$"&lt;/span&gt; &lt;span class="cf"&gt;if&lt;/span&gt; μ_θ &lt;span class="op"&gt;!=&lt;/span&gt; &lt;span class="dv"&gt;0&lt;/span&gt; &lt;span class="cf"&gt;else&lt;/span&gt; &lt;span class="st"&gt;"League average"&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb64-21"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-21" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb64-22"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-22" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    plt.setp(θ_top_ax.get_xticklabels(), visible&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;False&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb64-23"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-23" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb64-24"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-24" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    θ_top_ax.set_ylabel(&lt;span class="st"&gt;"Player"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb64-25"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-25" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb64-26"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-26" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    θ_top_ax.legend(loc&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;2&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb64-27"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-27" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    θ_top_ax.set_title(&lt;span class="st"&gt;"Top ten"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb64-28"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-28" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb64-29"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-29" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    plot_params(θ_bottom.θ_mean.values, θ_bottom[[&lt;span class="st"&gt;'θ_low'&lt;/span&gt;, &lt;span class="st"&gt;'θ_high'&lt;/span&gt;]].values.T,&lt;/span&gt;
&lt;span id="cb64-30"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-30" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                θ_bottom.name.values,&lt;/span&gt;
&lt;span id="cb64-31"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-31" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                ax&lt;span class="op"&gt;=&lt;/span&gt;θ_bottom_ax)&lt;/span&gt;
&lt;span id="cb64-32"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-32" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    θ_bottom_ax.vlines(μ_θ, &lt;span class="op"&gt;-&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;, &lt;span class="dv"&gt;10&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb64-33"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-33" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                       &lt;span class="st"&gt;'k'&lt;/span&gt;, &lt;span class="st"&gt;'--'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb64-34"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-34" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                       label&lt;span class="op"&gt;=&lt;/span&gt;(&lt;span class="vs"&gt;r"$\mu_{\theta}$"&lt;/span&gt; &lt;span class="cf"&gt;if&lt;/span&gt; μ_θ &lt;span class="op"&gt;!=&lt;/span&gt; &lt;span class="dv"&gt;0&lt;/span&gt; &lt;span class="cf"&gt;else&lt;/span&gt; &lt;span class="st"&gt;"League average"&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb64-35"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-35" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb64-36"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-36" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    θ_bottom_ax.set_xlabel(&lt;span class="vs"&gt;r"$\theta$"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb64-37"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-37" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb64-38"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-38" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    θ_bottom_ax.set_ylabel(&lt;span class="st"&gt;"Player"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb64-39"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-39" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb64-40"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-40" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    θ_bottom_ax.set_title(&lt;span class="st"&gt;"Bottom ten"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb64-41"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-41" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb64-42"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-42" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    by_b &lt;span class="op"&gt;=&lt;/span&gt; trace_df.sort_values(&lt;span class="st"&gt;'b_mean'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb64-43"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-43" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    b_top &lt;span class="op"&gt;=&lt;/span&gt; by_b.iloc[&lt;span class="op"&gt;-&lt;/span&gt;&lt;span class="dv"&gt;10&lt;/span&gt;:]&lt;/span&gt;
&lt;span id="cb64-44"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-44" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    b_bottom &lt;span class="op"&gt;=&lt;/span&gt; by_b.iloc[:&lt;span class="dv"&gt;10&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb64-45"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-45" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb64-46"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-46" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    plot_params(b_top.b_mean.values, b_top[[&lt;span class="st"&gt;'b_low'&lt;/span&gt;, &lt;span class="st"&gt;'b_high'&lt;/span&gt;]].values.T,&lt;/span&gt;
&lt;span id="cb64-47"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-47" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                b_top.name.values,&lt;/span&gt;
&lt;span id="cb64-48"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-48" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                ax&lt;span class="op"&gt;=&lt;/span&gt;b_top_ax)&lt;/span&gt;
&lt;span id="cb64-49"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-49" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    b_top_ax.vlines(&lt;span class="dv"&gt;0&lt;/span&gt;, &lt;span class="op"&gt;-&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;, &lt;span class="dv"&gt;10&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb64-50"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-50" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                    &lt;span class="st"&gt;'k'&lt;/span&gt;, &lt;span class="st"&gt;'--'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb64-51"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-51" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                    label&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;"League average"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb64-52"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-52" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb64-53"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-53" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    plt.setp(b_top_ax.get_xticklabels(), visible&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;False&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb64-54"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-54" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb64-55"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-55" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    b_top_ax.legend(loc&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;2&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb64-56"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-56" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    b_top_ax.set_title(&lt;span class="st"&gt;"Top ten"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb64-57"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-57" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb64-58"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-58" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    b_bottom_player_id &lt;span class="op"&gt;=&lt;/span&gt; b.argsort()[:&lt;span class="dv"&gt;10&lt;/span&gt;]&lt;/span&gt;
&lt;span id="cb64-59"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-59" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb64-60"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-60" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    plot_params(b_bottom.b_mean.values, b_bottom[[&lt;span class="st"&gt;'b_low'&lt;/span&gt;, &lt;span class="st"&gt;'b_high'&lt;/span&gt;]].values.T,&lt;/span&gt;
&lt;span id="cb64-61"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-61" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                b_bottom.name.values,&lt;/span&gt;
&lt;span id="cb64-62"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-62" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                ax&lt;span class="op"&gt;=&lt;/span&gt;b_bottom_ax)&lt;/span&gt;
&lt;span id="cb64-63"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-63" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    b_bottom_ax.vlines(&lt;span class="dv"&gt;0&lt;/span&gt;, &lt;span class="op"&gt;-&lt;/span&gt;&lt;span class="dv"&gt;1&lt;/span&gt;, &lt;span class="dv"&gt;10&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb64-64"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-64" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                       &lt;span class="st"&gt;'k'&lt;/span&gt;, &lt;span class="st"&gt;'--'&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb64-65"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-65" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb64-66"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-66" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    b_bottom_ax.set_xlabel(&lt;span class="vs"&gt;r"$b$"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb64-67"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-67" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb64-68"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-68" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    b_bottom_ax.set_title(&lt;span class="st"&gt;"Bottom ten"&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb64-69"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-69" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb64-70"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb64-70" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    fig.tight_layout()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb65"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb65-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb65-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;top_10_plot(rasch_df, μ_θ&lt;span class="op"&gt;=&lt;/span&gt;μ_θ_mean)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_98_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;We focus first on &lt;span class="math inline"&gt;\(\theta_i\)&lt;/span&gt;. Interestingly, the top-ten players for the Rasch model contains many more top-tier stars than the logistic-normal model, including John Wall, Russell Westbrook, and LeBron James. Turning to &lt;span class="math inline"&gt;\(b\)&lt;/span&gt;, it is interesting that the while the top and bottom ten players contain many recognizable names (LaMarcus Aldridge, Harrison Barnes, Kawhi Leonard, and Ricky Rubio) the only truly top-tier player present is Anthony Davis.&lt;/p&gt;
&lt;h3 id="time-remaining-model"&gt;Time remaining model&lt;/h3&gt;
&lt;p&gt;As basketball fans know, there are many factors other than the players involved that influence foul calls. Very often, sufficiently close NBA games end with intentional fouls, as the losing team attempts to stop the clock and force another offensive possesion. Therefore, we expect to see in increase in the foul call probability as the game nears its conclusion.&lt;/p&gt;
&lt;div class="sourceCode" id="cb66"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb66-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb66-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;n_sec &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="dv"&gt;121&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb66-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb66-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;sec &lt;span class="op"&gt;=&lt;/span&gt; (df.seconds_left&lt;/span&gt;
&lt;span id="cb66-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb66-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;         .&lt;span class="bu"&gt;round&lt;/span&gt;(&lt;span class="dv"&gt;0&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb66-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb66-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;         .values&lt;/span&gt;
&lt;span id="cb66-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb66-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;         .astype(np.int64))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb67"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb67-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb67-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;fig, ax &lt;span class="op"&gt;=&lt;/span&gt; plt.subplots(figsize&lt;span class="op"&gt;=&lt;/span&gt;(&lt;span class="dv"&gt;8&lt;/span&gt;, &lt;span class="dv"&gt;6&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb67-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb67-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb67-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb67-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(df.groupby(sec)&lt;/span&gt;
&lt;span id="cb67-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb67-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;   .foul_called&lt;/span&gt;
&lt;span id="cb67-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb67-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;   .mean()&lt;/span&gt;
&lt;span id="cb67-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb67-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;   .plot(c&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'k'&lt;/span&gt;, label&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;"Observed foul call rate"&lt;/span&gt;, ax&lt;span class="op"&gt;=&lt;/span&gt;ax))&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb67-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb67-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb67-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb67-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.set_xticks(np.linspace(&lt;span class="dv"&gt;0&lt;/span&gt;, &lt;span class="dv"&gt;120&lt;/span&gt;, &lt;span class="dv"&gt;5&lt;/span&gt;))&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb67-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb67-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.invert_xaxis()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb67-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb67-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.set_xlabel(&lt;span class="st"&gt;"Seconds remaining in game"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb67-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb67-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb67-12"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb67-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.yaxis.set_major_formatter(pct_formatter)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb67-13"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb67-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.set_ylabel(&lt;span class="st"&gt;"Probability foul is called"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb67-14"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb67-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb67-15"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb67-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;ax.legend(loc&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;2&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_102_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;The plot above confirms this expectation, which we can use to improve our latent skill model. If &lt;span class="math inline"&gt;\(t \in \{0, 1, \ldots, 120\}\)&lt;/span&gt; is the number of seconds remaining in the game, we model the latent contribution of &lt;span class="math inline"&gt;\(t\)&lt;/span&gt; to the logodds that a foul is called with a &lt;a href="https://en.wikipedia.org/wiki/Random_walk#Gaussian_random_walk"&gt;Gaussian random walk&lt;/a&gt;,&lt;/p&gt;
&lt;p&gt;&lt;span class="math display"&gt;\[
\begin{align*}
\lambda_0
    &amp;amp; \sim N(0, 100) \\
\lambda_t
    &amp;amp; \sim N(\lambda_{t - 1}, \tau^{-1}_{\lambda}) \\
\tau_{\lambda}
    &amp;amp; \sim \textrm{Exp}(10^{-4}).
\end{align*}
\]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This prior allows us to flexibly model the shape of the curve shown above. If &lt;span class="math inline"&gt;\(t(k)\)&lt;/span&gt; is the number of seconds remaining during the &lt;span class="math inline"&gt;\(k\)&lt;/span&gt;-th play, we incorporate &lt;span class="math inline"&gt;\(\lambda_{t(k)}\)&lt;/span&gt; into our model with&lt;/p&gt;
&lt;p&gt;&lt;span class="math display"&gt;\[\eta_k = \lambda_{t(k)} + \theta_{i(k)} - b_{j(k)}.\]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This model is not identified until we constrain the mean of &lt;span class="math inline"&gt;\(\theta\)&lt;/span&gt; to be zero, for reasons similar to those discussed above for the Rasch model.&lt;/p&gt;
&lt;div class="sourceCode" id="cb68"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb68-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb68-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;with&lt;/span&gt; pm.Model() &lt;span class="im"&gt;as&lt;/span&gt; time_model:&lt;/span&gt;
&lt;span id="cb68-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb68-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    τ_λ &lt;span class="op"&gt;=&lt;/span&gt; pm.Exponential(&lt;span class="st"&gt;'τ_λ'&lt;/span&gt;, &lt;span class="fl"&gt;1e-4&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb68-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb68-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    λ &lt;span class="op"&gt;=&lt;/span&gt; pm.GaussianRandomWalk(&lt;span class="st"&gt;'λ'&lt;/span&gt;, tau&lt;span class="op"&gt;=&lt;/span&gt;τ_λ,&lt;/span&gt;
&lt;span id="cb68-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb68-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                              init&lt;span class="op"&gt;=&lt;/span&gt;pm.Normal.dist(&lt;span class="fl"&gt;0.&lt;/span&gt;, &lt;span class="fl"&gt;10.&lt;/span&gt;),&lt;/span&gt;
&lt;span id="cb68-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb68-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                              shape&lt;span class="op"&gt;=&lt;/span&gt;n_sec)&lt;/span&gt;
&lt;span id="cb68-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb68-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb68-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb68-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    Δ_θ &lt;span class="op"&gt;=&lt;/span&gt; pm.Normal(&lt;span class="st"&gt;'Δ_θ'&lt;/span&gt;, &lt;span class="fl"&gt;0.&lt;/span&gt;, &lt;span class="fl"&gt;1.&lt;/span&gt;, shape&lt;span class="op"&gt;=&lt;/span&gt;n_players)&lt;/span&gt;
&lt;span id="cb68-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb68-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    σ_θ &lt;span class="op"&gt;=&lt;/span&gt; pm.HalfCauchy(&lt;span class="st"&gt;'σ_θ'&lt;/span&gt;, &lt;span class="fl"&gt;2.5&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb68-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb68-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    θ &lt;span class="op"&gt;=&lt;/span&gt; pm.Deterministic(&lt;span class="st"&gt;'θ'&lt;/span&gt;, Δ_θ &lt;span class="op"&gt;*&lt;/span&gt; σ_θ)&lt;/span&gt;
&lt;span id="cb68-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb68-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb68-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb68-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    Δ_b &lt;span class="op"&gt;=&lt;/span&gt; pm.Normal(&lt;span class="st"&gt;'Δ_b'&lt;/span&gt;, &lt;span class="fl"&gt;0.&lt;/span&gt;, &lt;span class="fl"&gt;1.&lt;/span&gt;, shape&lt;span class="op"&gt;=&lt;/span&gt;n_players)&lt;/span&gt;
&lt;span id="cb68-12"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb68-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    σ_b &lt;span class="op"&gt;=&lt;/span&gt; pm.HalfCauchy(&lt;span class="st"&gt;'σ_b'&lt;/span&gt;, &lt;span class="fl"&gt;2.5&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb68-13"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb68-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    b &lt;span class="op"&gt;=&lt;/span&gt; pm.Deterministic(&lt;span class="st"&gt;'b'&lt;/span&gt;, Δ_b &lt;span class="op"&gt;*&lt;/span&gt; σ_b)&lt;/span&gt;
&lt;span id="cb68-14"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb68-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb68-15"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb68-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    η &lt;span class="op"&gt;=&lt;/span&gt; λ[sec] &lt;span class="op"&gt;+&lt;/span&gt; θ[disadvantaged_id] &lt;span class="op"&gt;-&lt;/span&gt; b[committing_id]&lt;/span&gt;
&lt;span id="cb68-16"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb68-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    p &lt;span class="op"&gt;=&lt;/span&gt; pm.Deterministic(&lt;span class="st"&gt;'p'&lt;/span&gt;, pm.math.sigmoid(η))&lt;/span&gt;
&lt;span id="cb68-17"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb68-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb68-18"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb68-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    y &lt;span class="op"&gt;=&lt;/span&gt; pm.Bernoulli(&lt;span class="st"&gt;'y_obs'&lt;/span&gt;, p, observed&lt;span class="op"&gt;=&lt;/span&gt;foul_called)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We now sample from the model.&lt;/p&gt;
&lt;div class="sourceCode" id="cb69"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb69-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb69-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;time_trace &lt;span class="op"&gt;=&lt;/span&gt; sample(time_model, N_TUNE, N_SAMPLES, SEED)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;Auto-assigning NUTS sampler...
Initializing NUTS using advi...
Average ELBO = -1.0533e+05:  15%|█▌        | 30300/200000 [00:31&amp;lt;02:52, 982.03it/s] Median ELBO converged.
Finished [100%]: Average ELBO = -3,104.5

100%|██████████| 4000/4000 [03:10&amp;lt;00:00, 20.99it/s]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The energy plot for this model is worse than the previous ones, but not too bad.&lt;/p&gt;
&lt;div class="sourceCode" id="cb71"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb71-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb71-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;energy_plot(time_trace)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_108_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;div class="sourceCode" id="cb72"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb72-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb72-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;waic_df &lt;span class="op"&gt;=&lt;/span&gt; waic_df.append(get_waic_df(time_model, time_trace, &lt;span class="st"&gt;"Time"&lt;/span&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb73"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb73-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb73-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;waic_plot(waic_df)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_110_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;We see that the time remaining model represents an appreciable improvement over the Rasch model in terms of WAIC.&lt;/p&gt;
&lt;div class="sourceCode" id="cb74"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb74-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb74-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;bin_obs, bin_p, bin_counts &lt;span class="op"&gt;=&lt;/span&gt; binned_residuals(foul_called, time_trace[&lt;span class="st"&gt;'p'&lt;/span&gt;])&lt;/span&gt;
&lt;span id="cb74-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb74-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb74-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb74-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;binned_residual_plot(bin_obs, bin_p, bin_counts)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_112_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;The binned residuals for this model also look quite good, with very few samples appreciably exceeding a 1% difference.&lt;/p&gt;
&lt;p&gt;We now compare the distribtuions of &lt;span class="math inline"&gt;\(\theta_i\)&lt;/span&gt; and &lt;span class="math inline"&gt;\(b_j\)&lt;/span&gt; for this model with those for the Rasch model.&lt;/p&gt;
&lt;div class="sourceCode" id="cb75"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb75-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb75-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;time_df &lt;span class="op"&gt;=&lt;/span&gt; to_param_df(team_player_map, time_trace, [&lt;span class="st"&gt;'θ'&lt;/span&gt;, &lt;span class="st"&gt;'b'&lt;/span&gt;])&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb76"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb76-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb76-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;plot_latent_distributions(time_df.θ_mean, time_df.b_mean)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_115_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;The effect of constraining the mean of &lt;span class="math inline"&gt;\(\theta\)&lt;/span&gt; to be zero is immediately apparent. Also, the variation in &lt;span class="math inline"&gt;\(\theta\)&lt;/span&gt; remains lower than the variation than &lt;span class="math inline"&gt;\(b\)&lt;/span&gt; in this model. We also see that the top- and bottom-ten players by &lt;span class="math inline"&gt;\(\theta\)&lt;/span&gt;- and &lt;span class="math inline"&gt;\(b\)&lt;/span&gt;-value remain largely unchanged from the Rasch model.&lt;/p&gt;
&lt;div class="sourceCode" id="cb77"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb77-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb77-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;top_10_plot(time_df)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_117_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;Basketball fans may find it amusing that under this model, Dwight Howard has joined the top-ten in terms of &lt;span class="math inline"&gt;\(\theta\)&lt;/span&gt; and Ricky Rubio is no longer the worst player in terms of &lt;span class="math inline"&gt;\(b\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;While this model has not done much to change the rank-ordering of the most- and least-skilled players, it does enable us to plot per-player foul probabilities over time, as below.&lt;/p&gt;
&lt;div class="sourceCode" id="cb78"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb78-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;fig, (θ_ax, b_ax) &lt;span class="op"&gt;=&lt;/span&gt; plt.subplots(ncols&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;2&lt;/span&gt;, sharex&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;, sharey&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;, figsize&lt;span class="op"&gt;=&lt;/span&gt;(&lt;span class="dv"&gt;16&lt;/span&gt;, &lt;span class="dv"&gt;6&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb78-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb78-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(df.groupby(sec)&lt;/span&gt;
&lt;span id="cb78-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;   .foul_called&lt;/span&gt;
&lt;span id="cb78-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;   .mean()&lt;/span&gt;
&lt;span id="cb78-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;   .plot(c&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'k'&lt;/span&gt;, alpha&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;0.5&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb78-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;         label&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;"Observed foul call rate"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb78-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;         ax&lt;span class="op"&gt;=&lt;/span&gt;θ_ax))&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb78-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb78-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;plot_sec &lt;span class="op"&gt;=&lt;/span&gt; np.arange(n_sec)&lt;/span&gt;
&lt;span id="cb78-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb78-12"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;θ_ax.plot(plot_sec, expit(time_trace[&lt;span class="st"&gt;'λ'&lt;/span&gt;].mean(axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;)),&lt;/span&gt;
&lt;span id="cb78-13"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;          c&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'k'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb78-14"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;          label&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;"Average player"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb78-15"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb78-16"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;θ_best_id &lt;span class="op"&gt;=&lt;/span&gt; time_df.ix[time_df.θ_mean.idxmax()].player_id&lt;/span&gt;
&lt;span id="cb78-17"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;θ_ax.plot(plot_sec, expit(time_trace[&lt;span class="st"&gt;'θ'&lt;/span&gt;][:, θ_best_id].mean(axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;) &lt;span class="op"&gt;\&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb78-18"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                          &lt;span class="op"&gt;+&lt;/span&gt; time_trace[&lt;span class="st"&gt;'λ'&lt;/span&gt;].mean(axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;)),&lt;/span&gt;
&lt;span id="cb78-19"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;          label&lt;span class="op"&gt;=&lt;/span&gt;player_map[θ_best_id])&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb78-20"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb78-21"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-21" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;θ_worst_id &lt;span class="op"&gt;=&lt;/span&gt; time_df.ix[time_df.θ_mean.idxmin()].player_id&lt;/span&gt;
&lt;span id="cb78-22"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-22" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;θ_ax.plot(plot_sec, expit(time_trace[&lt;span class="st"&gt;'θ'&lt;/span&gt;][:, θ_worst_id].mean(axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;) &lt;span class="op"&gt;\&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb78-23"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-23" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                          &lt;span class="op"&gt;+&lt;/span&gt; time_trace[&lt;span class="st"&gt;'λ'&lt;/span&gt;].mean(axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;)),&lt;/span&gt;
&lt;span id="cb78-24"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-24" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;          label&lt;span class="op"&gt;=&lt;/span&gt;player_map[θ_worst_id])&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb78-25"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-25" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb78-26"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-26" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb78-27"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-27" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;θ_ax.set_xticks(np.linspace(&lt;span class="dv"&gt;0&lt;/span&gt;, &lt;span class="dv"&gt;120&lt;/span&gt;, &lt;span class="dv"&gt;5&lt;/span&gt;))&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb78-28"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-28" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;θ_ax.invert_xaxis()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb78-29"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-29" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;θ_ax.set_xlabel(&lt;span class="st"&gt;"Seconds remaining in game"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb78-30"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-30" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb78-31"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-31" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;θ_ax.yaxis.set_major_formatter(pct_formatter)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb78-32"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-32" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;θ_ax.set_ylabel(&lt;span class="st"&gt;"Probability foul is called&lt;/span&gt;&lt;span class="ch"&gt;\n&lt;/span&gt;&lt;span class="st"&gt;against average opposing player"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb78-33"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-33" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb78-34"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-34" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;θ_ax.legend(loc&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;2&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb78-35"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-35" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;θ_ax.set_title(&lt;span class="vs"&gt;r"Disadvantaged player ($\theta$)"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb78-36"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-36" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb78-37"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-37" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(df.groupby(sec)&lt;/span&gt;
&lt;span id="cb78-38"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-38" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;   .foul_called&lt;/span&gt;
&lt;span id="cb78-39"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-39" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;   .mean()&lt;/span&gt;
&lt;span id="cb78-40"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-40" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;   .plot(c&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'k'&lt;/span&gt;, alpha&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;0.5&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb78-41"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-41" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;         label&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;"Observed foul call rate"&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb78-42"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-42" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;         ax&lt;span class="op"&gt;=&lt;/span&gt;b_ax))&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb78-43"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-43" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb78-44"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-44" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;plot_sec &lt;span class="op"&gt;=&lt;/span&gt; np.arange(n_sec)&lt;/span&gt;
&lt;span id="cb78-45"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-45" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb78-46"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-46" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;b_ax.plot(plot_sec, expit(time_trace[&lt;span class="st"&gt;'λ'&lt;/span&gt;].mean(axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;)),&lt;/span&gt;
&lt;span id="cb78-47"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-47" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;          c&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'k'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb78-48"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-48" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;          label&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;"Average player"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb78-49"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-49" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb78-50"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-50" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;b_best_id &lt;span class="op"&gt;=&lt;/span&gt; time_df.ix[time_df.b_mean.idxmax()].player_id&lt;/span&gt;
&lt;span id="cb78-51"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-51" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;b_ax.plot(plot_sec, expit(&lt;span class="op"&gt;-&lt;/span&gt;time_trace[&lt;span class="st"&gt;'b'&lt;/span&gt;][:, b_best_id].mean(axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;) &lt;span class="op"&gt;\&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb78-52"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-52" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                          &lt;span class="op"&gt;+&lt;/span&gt; time_trace[&lt;span class="st"&gt;'λ'&lt;/span&gt;].mean(axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;)),&lt;/span&gt;
&lt;span id="cb78-53"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-53" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;          label&lt;span class="op"&gt;=&lt;/span&gt;player_map[b_best_id])&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb78-54"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-54" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb78-55"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-55" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;b_worst_id &lt;span class="op"&gt;=&lt;/span&gt; time_df.ix[time_df.b_mean.idxmin()].player_id&lt;/span&gt;
&lt;span id="cb78-56"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-56" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;b_ax.plot(plot_sec, expit(&lt;span class="op"&gt;-&lt;/span&gt;time_trace[&lt;span class="st"&gt;'b'&lt;/span&gt;][:, b_worst_id].mean(axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;) &lt;span class="op"&gt;\&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb78-57"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-57" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                          &lt;span class="op"&gt;+&lt;/span&gt; time_trace[&lt;span class="st"&gt;'λ'&lt;/span&gt;].mean(axis&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;0&lt;/span&gt;)),&lt;/span&gt;
&lt;span id="cb78-58"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-58" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;          label&lt;span class="op"&gt;=&lt;/span&gt;player_map[b_worst_id])&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb78-59"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-59" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb78-60"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-60" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb78-61"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-61" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;b_ax.set_xticks(np.linspace(&lt;span class="dv"&gt;0&lt;/span&gt;, &lt;span class="dv"&gt;120&lt;/span&gt;, &lt;span class="dv"&gt;5&lt;/span&gt;))&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb78-62"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-62" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;b_ax.invert_xaxis()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb78-63"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-63" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;b_ax.set_xlabel(&lt;span class="st"&gt;"Seconds remaining in game"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb78-64"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-64" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb78-65"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-65" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;b_ax.legend(loc&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;2&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb78-66"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb78-66" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;b_ax.set_title(&lt;span class="vs"&gt;r"Committing player ($b$)"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_119_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;Here we have plotted the probability of a foul call while being opposed by an average player (for both &lt;span class="math inline"&gt;\(\theta\)&lt;/span&gt; and &lt;span class="math inline"&gt;\(b\)&lt;/span&gt;), along with the probability curves for the players with the highest and lowest &lt;span class="math inline"&gt;\(\theta\)&lt;/span&gt; and &lt;span class="math inline"&gt;\(b\)&lt;/span&gt;, respectively. While these plots are quite interesting, one weakness of our model is that the difference between each player’s curve and the league average is constant over time. It would be an interesting and useful to extend this model to allow player offsets to vary over time. Additonally, it would be interesting to understand the influence of the score on the foul-called rate as the game nears its end. It seems quite likely that the winning team is much less likely to commit fouls while the losing team is much more likely to to commit intentional fouls in close games.&lt;/p&gt;
&lt;p&gt;We now plot the per-player values of &lt;span class="math inline"&gt;\(\theta_i\)&lt;/span&gt; and &lt;span class="math inline"&gt;\(b_j\)&lt;/span&gt; under this model.&lt;/p&gt;
&lt;div class="sourceCode" id="cb79"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb79-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb79-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid &lt;span class="op"&gt;=&lt;/span&gt; sns.FacetGrid(time_df, col&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'team'&lt;/span&gt;, col_wrap&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;2&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb79-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb79-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                     sharey&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;False&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb79-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb79-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                     size&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;4&lt;/span&gt;, aspect&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;1.5&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb79-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb79-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid.&lt;span class="bu"&gt;map&lt;/span&gt;(plot_params_helper,&lt;/span&gt;
&lt;span id="cb79-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb79-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;         &lt;span class="st"&gt;'θ_mean'&lt;/span&gt;, &lt;span class="st"&gt;'θ_low'&lt;/span&gt;, &lt;span class="st"&gt;'θ_high'&lt;/span&gt;, &lt;span class="st"&gt;'name'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb79-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb79-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;         league_mean&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;0.&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb79-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb79-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;         league_mean_name&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;"League average"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb79-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb79-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb79-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb79-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid.set_axis_labels(&lt;span class="vs"&gt;r"$\theta$"&lt;/span&gt;, &lt;span class="st"&gt;"Player"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb79-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb79-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb79-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb79-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid.fig.tight_layout()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb79-12"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb79-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid.add_legend()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_121_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;div class="sourceCode" id="cb80"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb80-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb80-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid &lt;span class="op"&gt;=&lt;/span&gt; sns.FacetGrid(time_df, col&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;'team'&lt;/span&gt;, col_wrap&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;2&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb80-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb80-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                     sharey&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;False&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb80-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb80-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                     size&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;4&lt;/span&gt;, aspect&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;1.5&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb80-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb80-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid.&lt;span class="bu"&gt;map&lt;/span&gt;(plot_params_helper,&lt;/span&gt;
&lt;span id="cb80-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb80-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;         &lt;span class="st"&gt;'b_mean'&lt;/span&gt;, &lt;span class="st"&gt;'b_low'&lt;/span&gt;, &lt;span class="st"&gt;'b_high'&lt;/span&gt;, &lt;span class="st"&gt;'name'&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb80-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb80-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;         league_mean&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;0.&lt;/span&gt;,&lt;/span&gt;
&lt;span id="cb80-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb80-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;         league_mean_name&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;"League average"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb80-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb80-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb80-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb80-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid.set_axis_labels(&lt;span class="vs"&gt;r"$b$"&lt;/span&gt;, &lt;span class="st"&gt;"Player"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb80-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb80-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb80-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb80-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid.fig.tight_layout()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb80-12"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb80-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;grid.add_legend()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_122_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;h3 id="salary-model"&gt;Salary model&lt;/h3&gt;
&lt;p&gt;Our final model uses salary as a proxy for “star power” to explore its influence on foul calls. We also (somewhat naively) impute missing salaries to the (log) league average.&lt;/p&gt;
&lt;div class="sourceCode" id="cb81"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb81-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb81-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;std_log_salary &lt;span class="op"&gt;=&lt;/span&gt; (salary_df.ix[np.arange(n_players)]&lt;/span&gt;
&lt;span id="cb81-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb81-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                           .std_log_salary&lt;/span&gt;
&lt;span id="cb81-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb81-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                           .fillna(&lt;span class="dv"&gt;0&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb81-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb81-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                           .values)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With &lt;span class="math inline"&gt;\(s_i\)&lt;/span&gt; denoting the &lt;span class="math inline"&gt;\(i\)&lt;/span&gt;-the player’s standardized log salary, our model becomes&lt;/p&gt;
&lt;p&gt;&lt;span class="math display"&gt;\[
\begin{align*}
\theta_i
    &amp;amp; = \theta_{0, i} + \delta_{\theta} \cdot s_i \\
b_j
    &amp;amp; = b_{0, j} + \delta_b \cdot s_j \\
\eta_k
    &amp;amp; = \lambda_{t(k)} + \theta_{i(k)} - b_{j(k)}.
\end{align*}
\]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In this model, each player’s &lt;span class="math inline"&gt;\(\theta_i\)&lt;/span&gt; and &lt;span class="math inline"&gt;\(b_j\)&lt;/span&gt; parameters are linear functions of their standardized log salary, with (hierarchical) varying intercepts. The varying intercepts &lt;span class="math inline"&gt;\(\theta_{0, i}\)&lt;/span&gt; and &lt;span class="math inline"&gt;\(b_{0, j}\)&lt;/span&gt; are endowed with the same hierarchical normal priors as &lt;span class="math inline"&gt;\(\theta_i\)&lt;/span&gt; and &lt;span class="math inline"&gt;\(b_j\)&lt;/span&gt; had in the previous model. We place normal priors, &lt;span class="math inline"&gt;\(\delta_{\theta} \sim N(0, 100)\)&lt;/span&gt; and &lt;span class="math inline"&gt;\(\delta_b \sim N(0, 100)\)&lt;/span&gt;, on the salary coefficients.&lt;/p&gt;
&lt;div class="sourceCode" id="cb82"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb82-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;with&lt;/span&gt; pm.Model() &lt;span class="im"&gt;as&lt;/span&gt; salary_model:&lt;/span&gt;
&lt;span id="cb82-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    τ_λ &lt;span class="op"&gt;=&lt;/span&gt; pm.Exponential(&lt;span class="st"&gt;'τ_λ'&lt;/span&gt;, &lt;span class="fl"&gt;1e-4&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb82-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    λ &lt;span class="op"&gt;=&lt;/span&gt; pm.GaussianRandomWalk(&lt;span class="st"&gt;'λ'&lt;/span&gt;, tau&lt;span class="op"&gt;=&lt;/span&gt;τ_λ,&lt;/span&gt;
&lt;span id="cb82-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                              init&lt;span class="op"&gt;=&lt;/span&gt;pm.Normal.dist(&lt;span class="fl"&gt;0.&lt;/span&gt;, &lt;span class="fl"&gt;10.&lt;/span&gt;),&lt;/span&gt;
&lt;span id="cb82-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                              shape&lt;span class="op"&gt;=&lt;/span&gt;n_sec)&lt;/span&gt;
&lt;span id="cb82-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb82-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    Δ_θ&lt;span class="dv"&gt;0&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; pm.Normal(&lt;span class="st"&gt;'Δ_θ0'&lt;/span&gt;, &lt;span class="fl"&gt;0.&lt;/span&gt;, &lt;span class="fl"&gt;1.&lt;/span&gt;, shape&lt;span class="op"&gt;=&lt;/span&gt;n_players)&lt;/span&gt;
&lt;span id="cb82-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    σ_θ&lt;span class="dv"&gt;0&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; pm.HalfCauchy(&lt;span class="st"&gt;'σ_θ0'&lt;/span&gt;, &lt;span class="fl"&gt;2.5&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb82-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    θ&lt;span class="dv"&gt;0&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; pm.Deterministic(&lt;span class="st"&gt;'θ0'&lt;/span&gt;, Δ_θ&lt;span class="dv"&gt;0&lt;/span&gt; &lt;span class="op"&gt;*&lt;/span&gt; σ_θ&lt;span class="dv"&gt;0&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb82-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb82-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    δ_θ &lt;span class="op"&gt;=&lt;/span&gt; pm.Normal(&lt;span class="st"&gt;'δ_θ'&lt;/span&gt;, &lt;span class="fl"&gt;0.&lt;/span&gt;, &lt;span class="fl"&gt;10.&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb82-12"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb82-13"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    θ &lt;span class="op"&gt;=&lt;/span&gt; pm.Deterministic(&lt;span class="st"&gt;'θ'&lt;/span&gt;, θ&lt;span class="dv"&gt;0&lt;/span&gt; &lt;span class="op"&gt;+&lt;/span&gt; δ_θ &lt;span class="op"&gt;*&lt;/span&gt; std_log_salary)&lt;/span&gt;
&lt;span id="cb82-14"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb82-15"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    Δ_b0 &lt;span class="op"&gt;=&lt;/span&gt; pm.Normal(&lt;span class="st"&gt;'Δ_b0'&lt;/span&gt;, &lt;span class="fl"&gt;0.&lt;/span&gt;, &lt;span class="fl"&gt;1.&lt;/span&gt;, shape&lt;span class="op"&gt;=&lt;/span&gt;n_players)&lt;/span&gt;
&lt;span id="cb82-16"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    σ_b0 &lt;span class="op"&gt;=&lt;/span&gt; pm.HalfCauchy(&lt;span class="st"&gt;'σ_b0'&lt;/span&gt;, &lt;span class="fl"&gt;2.5&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb82-17"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    b0 &lt;span class="op"&gt;=&lt;/span&gt; pm.Deterministic(&lt;span class="st"&gt;'b0'&lt;/span&gt;, Δ_b0 &lt;span class="op"&gt;*&lt;/span&gt; σ_b0)&lt;/span&gt;
&lt;span id="cb82-18"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb82-19"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    δ_b &lt;span class="op"&gt;=&lt;/span&gt; pm.Normal(&lt;span class="st"&gt;'δ_b'&lt;/span&gt;, &lt;span class="fl"&gt;0.&lt;/span&gt;, &lt;span class="fl"&gt;10.&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb82-20"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb82-21"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-21" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    b &lt;span class="op"&gt;=&lt;/span&gt; pm.Deterministic(&lt;span class="st"&gt;'b'&lt;/span&gt;, b0 &lt;span class="op"&gt;+&lt;/span&gt; δ_b &lt;span class="op"&gt;*&lt;/span&gt; std_log_salary)&lt;/span&gt;
&lt;span id="cb82-22"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-22" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    &lt;/span&gt;
&lt;span id="cb82-23"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-23" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    η &lt;span class="op"&gt;=&lt;/span&gt; λ[sec] &lt;span class="op"&gt;+&lt;/span&gt; θ[disadvantaged_id] &lt;span class="op"&gt;-&lt;/span&gt; b[committing_id]&lt;/span&gt;
&lt;span id="cb82-24"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-24" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    p &lt;span class="op"&gt;=&lt;/span&gt; pm.Deterministic(&lt;span class="st"&gt;'p'&lt;/span&gt;, pm.math.sigmoid(η))&lt;/span&gt;
&lt;span id="cb82-25"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-25" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb82-26"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb82-26" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;    y &lt;span class="op"&gt;=&lt;/span&gt; pm.Bernoulli(&lt;span class="st"&gt;'y_obs'&lt;/span&gt;, p, observed&lt;span class="op"&gt;=&lt;/span&gt;foul_called)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb83"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb83-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb83-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;salary_trace &lt;span class="op"&gt;=&lt;/span&gt; sample(salary_model, N_TUNE, N_SAMPLES, SEED)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;Auto-assigning NUTS sampler...
Initializing NUTS using advi...
Average ELBO = -1.0244e+05:  15%|█▌        | 30998/200000 [00:32&amp;lt;02:57, 952.52it/s]Median ELBO converged.
Finished [100%]: Average ELBO = -2,995.3

100%|██████████| 4000/4000 [03:45&amp;lt;00:00, 17.76it/s]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The energy plot for this model looks a bit worse than that for the time remaining model.&lt;/p&gt;
&lt;div class="sourceCode" id="cb85"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb85-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb85-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;energy_plot(salary_trace)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_129_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;The salary model also appears to be a slight improvement over the time remaining model in terms of WAIC.&lt;/p&gt;
&lt;div class="sourceCode" id="cb86"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb86-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb86-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;waic_df &lt;span class="op"&gt;=&lt;/span&gt; waic_df.append(get_waic_df(salary_model, salary_trace, &lt;span class="st"&gt;"Salary"&lt;/span&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb87"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb87-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb87-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;waic_plot(waic_df)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_132_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;The binned residuals continue to look good for this model.&lt;/p&gt;
&lt;div class="sourceCode" id="cb88"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb88-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb88-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;bin_obs, bin_p, bin_counts &lt;span class="op"&gt;=&lt;/span&gt; binned_residuals(foul_called, salary_trace[&lt;span class="st"&gt;'p'&lt;/span&gt;])&lt;/span&gt;
&lt;span id="cb88-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb88-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb88-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb88-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;binned_residual_plot(bin_obs, bin_p, bin_counts)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_134_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;Based on the posterior distributions of &lt;span class="math inline"&gt;\(\delta_{\theta}\)&lt;/span&gt; and &lt;span class="math inline"&gt;\(\delta_b\)&lt;/span&gt;, we expect to see a fairly strong relationship between a player’s (standardized log) salary and their latent skill parameters.&lt;/p&gt;
&lt;div class="sourceCode" id="cb89"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb89-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb89-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;pm.plot_posterior(salary_trace, [&lt;span class="st"&gt;'δ_θ'&lt;/span&gt;, &lt;span class="st"&gt;'δ_b'&lt;/span&gt;],&lt;/span&gt;
&lt;span id="cb89-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb89-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                  lw&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;0.&lt;/span&gt;, alpha&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;0.75&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_136_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;The following plots confirm this relationship.&lt;/p&gt;
&lt;div class="sourceCode" id="cb90"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb90-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb90-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;salary_df_ &lt;span class="op"&gt;=&lt;/span&gt; to_param_df(team_player_map, salary_trace, [&lt;span class="st"&gt;'θ'&lt;/span&gt;, &lt;span class="st"&gt;'θ0'&lt;/span&gt;, &lt;span class="st"&gt;'b'&lt;/span&gt;, &lt;span class="st"&gt;'b0'&lt;/span&gt;])&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="sourceCode" id="cb91"&gt;&lt;pre class="sourceCode python"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb91-1"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb91-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;fig, (θ_ax, b_ax) &lt;span class="op"&gt;=&lt;/span&gt; plt.subplots(ncols&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="dv"&gt;2&lt;/span&gt;, sharex&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;True&lt;/span&gt;, figsize&lt;span class="op"&gt;=&lt;/span&gt;(&lt;span class="dv"&gt;16&lt;/span&gt;, &lt;span class="dv"&gt;6&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb91-2"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb91-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb91-3"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb91-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;salary &lt;span class="op"&gt;=&lt;/span&gt; (salary_df.ix[np.arange(n_players)]&lt;/span&gt;
&lt;span id="cb91-4"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb91-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                   .salary&lt;/span&gt;
&lt;span id="cb91-5"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb91-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                   .fillna(salary_df.salary.mean())&lt;/span&gt;
&lt;span id="cb91-6"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb91-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;                   .values)&lt;/span&gt;
&lt;span id="cb91-7"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb91-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb91-8"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb91-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;θ_ax.scatter(salary[salary_df_.player_id], salary_df_.θ_mean,&lt;/span&gt;
&lt;span id="cb91-9"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb91-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;             alpha&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;0.75&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb91-10"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb91-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb91-11"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb91-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;θ_ax.xaxis.set_major_formatter(million_dollars_formatter)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb91-12"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb91-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;θ_ax.set_xlabel(&lt;span class="st"&gt;"Salary"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb91-13"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb91-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb91-14"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb91-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;θ_ax.set_ylabel(&lt;span class="vs"&gt;r"$\theta$"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb91-15"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb91-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb91-16"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb91-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;b_ax.scatter(salary[salary_df_.player_id], salary_df_.b_mean,&lt;/span&gt;
&lt;span id="cb91-17"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb91-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;             alpha&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="fl"&gt;0.75&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb91-18"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb91-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb91-19"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb91-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;b_ax.xaxis.set_major_formatter(million_dollars_formatter)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb91-20"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb91-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;b_ax.set_xlabel(&lt;span class="st"&gt;"Salary"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb91-21"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb91-21" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id="cb91-22"&gt;&lt;a href="https://austinrochford.com/posts/2017-04-04-nba-irt.html#cb91-22" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;b_ax.set_ylabel(&lt;span class="vs"&gt;r"$b$"&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;center&gt;
&lt;img src="https://austinrochford.com/resources/nba_irt/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_files/NBA%20Foul%20Calls%20and%20Bayesian%20Item%20Response%20Theory_139_0.png" title="fig:" alt="png"&gt;
&lt;/center&gt;
&lt;p&gt;It is important to note here that these relationships are descriptive, not causal. Our original intent was to use salary as a proxy for the difficult-to-quantify notion of “star power.” These plots suggest that the probability of getting a foul called when disadvanted and not called when committing are both positively related to salary, and therefore (a bit more dubiously) star power.&lt;/p&gt;
&lt;p&gt;Importantly, &lt;span class="math inline"&gt;\(\theta_i\)&lt;/span&gt; and &lt;span class="math inline"&gt;\(b_j\)&lt;/span&gt; should no longer be interpreted directly as measuring latent skill, which is presumably intrinsic to a player and not directly dependent on their salary. In fact, it seems at plausible that NBA scouts, front offices, players, and agents would be somewhat able to appreciate these latent skills, place a value on them, and thereby price them into contracts. It would be interesting future work to refine this model to give an &lt;a href="https://en.wikipedia.org/wiki/Econometrics"&gt;econometric&lt;/a&gt; answer to this question.&lt;/p&gt;
&lt;p&gt;Since we shouldn’t interpret &lt;span class="math inline"&gt;\(\theta_i\)&lt;/span&gt; and &lt;span class="math inline"&gt;\(b_j\)&lt;/span&gt; as measures of latent skill in this model, we will not plot their per-player distributions.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;We set out to explore the relationship between players involved in a play and the probability that a foul is called, along with other factors related to that probability. Through a series of progressively more complex Bayesian item-response models, we have seen that&lt;/p&gt;
&lt;ol type="1"&gt;
&lt;li&gt;foul call probability does vary appreciably across both disadvantaged and committing player,&lt;/li&gt;
&lt;li&gt;there is more variation in the latent skill of the committing player to avoid a foul call than there is in the variation in the latent skill of the disadvantaged player to draw a foul call,&lt;/li&gt;
&lt;li&gt;the amount of time remaining in the game is strongly related to the probability of a foul call, and&lt;/li&gt;
&lt;li&gt;there is a positive relationship between player salary and the probability that a foul is called when they are disadvantaged and not called when they are committing. With a bit of a leap, we can say that the probability a foul is called is at least loosely related to the “star power” of the players involved.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In this post we have only scratched the surface of Bayesian item-response theory. For a more in-depth treatment, consult &lt;a href="http://www.springer.com/us/book/9781441907417"&gt;&lt;em&gt;Bayesian Item Response Modeling&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is is available as a Jupyter notebook &lt;a href="https://gist.github.com/AustinRochford/d1eb474ad4802e1b9edc790c4a9d2e0d"&gt;here&lt;/a&gt;.&lt;/p&gt;</description><category>Bayesian Statistics</category><category>NBA</category><category>PyMC3</category><guid>https://austinrochford.com/posts/2017-04-04-nba-irt.html</guid><pubDate>Tue, 04 Apr 2017 04:00:00 GMT</pubDate></item></channel></rss>