<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>R Langauge on R Views</title>
    <link>https://rviews.rstudio.com/categories/r-langauge/</link>
    <description>Recent content in R Langauge on R Views</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Mon, 21 Feb 2022 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://rviews.rstudio.com/categories/r-langauge/" rel="self" type="application/rss+xml" />
    
    
    
    
    <item>
      <title>Wordle Data Analysis</title>
      <link>https://rviews.rstudio.com/2022/02/21/wordle-data-analysis/</link>
      <pubDate>Mon, 21 Feb 2022 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2022/02/21/wordle-data-analysis/</guid>
      <description>
        
&lt;script src=&#34;/2022/02/21/wordle-data-analysis/index_files/header-attrs/header-attrs.js&#34;&gt;&lt;/script&gt;


&lt;p&gt;&lt;em&gt;Arthur Holtz is a Senior FP&amp;amp;A Manager at Shipt, but don’t let the title mislead you — he spends most of his days buried in databases and R, analyzing data. This post is neither affiliated with nor endorsed by his employer.&lt;/em&gt;&lt;/p&gt;
&lt;div id=&#34;intro&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Intro&lt;/h1&gt;
&lt;p&gt;By now, I’m sure most people are familiar with the viral game &lt;a href=&#34;https://www.nytimes.com/games/wordle/index.html&#34;&gt;Wordle&lt;/a&gt;. If you’ve been living under a rock and have no idea what I’m talking about, I recommend playing it for yourself. The best way I can describe it is: Take the classic board game &lt;a href=&#34;https://en.wikipedia.org/wiki/Mastermind_(board_game)&#34;&gt;Mastermind&lt;/a&gt;, but play with words instead of colored pegs. It’s a fun little game that encourages sharing and comparing scores with your friends – which I think explains a lot of its popularity!&lt;/p&gt;
&lt;p&gt;Speaking of comparing scores, my wife routinely gets a better score than I do, so I’ve been trying to come up with better strategies. A few weeks ago, YouTube recommended &lt;a href=&#34;https://www.youtube.com/watch?v=v68zYyaEmEA&#34;&gt;3Blue1Brown’s fantastic video&lt;/a&gt; on information theory and Wordle strategy. After watching it, I felt inspired. I could do something along those lines! Granted, I’m nowhere near as sophisticated as the creator of that video, but I thought it would be a fun challenge to crack open the game’s script and see what I could do with its data. With all the background out of the way, let’s jump into it!&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;getting-started&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Getting Started&lt;/h1&gt;
&lt;p&gt;First, we will need to include a number of libraries for this analysis. Most of these should be familiar to anyone who works with R regularly so I won’t add more color here.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;suppressMessages({
library(httr)
library(dplyr)
library(stringr)
library(ggplot2)
library(ggthemes)
library(scales)
library(tidyr)})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From the 3B1B video, I found out you can view the code powering Wordle simply by viewing the page source in your browser and finding the JavaScript. As of writing, that code is &lt;a href=&#34;https://www.nytimes.com/games/wordle/main.18637ca1.js&#34;&gt;here&lt;/a&gt;. Now, I’ll admit, I don’t know much of anything about JavaScript, but that doesn’t really matter – the word list is stored in plain text as an array.&lt;/p&gt;
&lt;p&gt;If you look through the code, you might notice there are actually 2 word lists: The first, called &lt;code&gt;Ma&lt;/code&gt;, is a list of 2,309 words that Wordle uses for puzzle solutions (there used to be 2,315 but apparently the &lt;a href=&#34;https://www.gamespot.com/articles/the-nyt-has-now-officially-changed-the-wordle-solution-list/1100-6500735/&#34;&gt;NYT removed a few&lt;/a&gt;). The second list, called &lt;code&gt;Oa&lt;/code&gt;, is a list of words it will accept as valid guesses. Since &lt;code&gt;Oa&lt;/code&gt; has a lot more obscure words that will never show up as the answer, for this exercise, I am focusing entirely on &lt;code&gt;Ma&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The first thing I do is simply download the &lt;code&gt;.js&lt;/code&gt; file and save it as a giant string of text, which I’m calling &lt;code&gt;wordle_script_text&lt;/code&gt;. In the event the script or domain name ever changes, all you need to do is paste in the new URL here.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;url = &amp;quot;https://www.nytimes.com/games/wordle/main.18637ca1.js&amp;quot;
wordle_script_text = GET(url) %&amp;gt;%
  content(as = &amp;quot;text&amp;quot;, encoding = &amp;quot;UTF-8&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;parsing-the-word-list&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Parsing the Word List&lt;/h1&gt;
&lt;p&gt;Next, I’m doing something messy that deserves some more explanation. I couldn’t figure out a good way to programmatically extract the entire word list by looking for the start and end points of the array, so instead, I scrolled through it in a text editor and manually identified the first and last words (&lt;code&gt;cigar&lt;/code&gt; and &lt;code&gt;shave&lt;/code&gt;, respectively). Then I told R to look for those particular words and extract everything between the two. It’s not ideal since rearranging the word list would completely break the rest of my script, so I’m open to any suggestions here!&lt;/p&gt;
&lt;p&gt;Anyway, that left me with a giant string of comma separated words. From there, I removed any escaped quotation marks and separated each word into its own element by splitting the string at each comma. Then I converted all of that to a data frame where each row is a word, renamed the column, and converted every word to upper case. I call this data frame &lt;code&gt;word_list&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;word_list = substr(
  wordle_script_text,
  # cigar is the first word
  str_locate(wordle_script_text, &amp;quot;cigar&amp;quot;)[,&amp;quot;start&amp;quot;],
  # shave is the last word
  str_locate(wordle_script_text, &amp;quot;shave&amp;quot;)[,&amp;quot;end&amp;quot;]) %&amp;gt;%
  str_remove_all(&amp;quot;\&amp;quot;&amp;quot;) %&amp;gt;%
  str_split(&amp;quot;,&amp;quot;) %&amp;gt;%
  data.frame() %&amp;gt;%
  select(word = 1) %&amp;gt;%
  mutate(word = toupper(word))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we have our word list!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;##    word
## 1 CIGAR
## 2 REBUT
## 3 SISSY
## 4 HUMPH
## 5 AWAKE&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;determining-overall-letter-frequency&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Determining Overall Letter Frequency&lt;/h1&gt;
&lt;p&gt;From there, my first idea was to get a frequency diagram of how often each letter appears. After all, intuitively, you wouldn’t expect letters like “X” to show up very often so it doesn’t make a lot of sense to play a word with “X” in it (unless you have a good reason). In the code below, I’m taking &lt;code&gt;word_list&lt;/code&gt; and converting it back to a character vector, splitting every single character into its own element, and converting to a data frame again. I’m calling this data frame &lt;code&gt;letter_list&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;At this point, I should clarify why I did &lt;code&gt;filter(row_number() != 1)&lt;/code&gt;. R added a &lt;code&gt;c()&lt;/code&gt; when I converted to a vector, which I don’t want to count towards the letter counts. This filter removes that extra &lt;code&gt;c&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Continuing with the rest of the code, I renamed the column, filtered for upper-case letters only, and summarized by letter counts in descending order.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;letter_list = word_list %&amp;gt;%
  as.character() %&amp;gt;%
  str_split(&amp;quot;&amp;quot;) %&amp;gt;%
  data.frame() %&amp;gt;%
  filter(row_number() != 1) %&amp;gt;%
  select(letter = 1) %&amp;gt;%
  filter(letter %in% LETTERS) %&amp;gt;%
  group_by(letter) %&amp;gt;%
  summarize(freq = n()) %&amp;gt;%
  arrange(desc(freq))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now we have our frequency by letter!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;## # A tibble: 5 × 2
##   letter  freq
##   &amp;lt;chr&amp;gt;  &amp;lt;int&amp;gt;
## 1 E       1230
## 2 A        975
## 3 R        897
## 4 O        753
## 5 T        729&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But this isn’t very easy to read. Let’s throw it into a plot. A few callouts here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;By default, ggplot sorts the x axis in alphabetical order. &lt;code&gt;reorder&lt;/code&gt; forces ggplot to put the most frequent observations first.&lt;/li&gt;
&lt;li&gt;I used &lt;code&gt;stat = &#34;identity&#34;&lt;/code&gt; since we already summarized &lt;code&gt;letter_list&lt;/code&gt; in the previous section.&lt;/li&gt;
&lt;li&gt;I used &lt;code&gt;expand = c(0,0)&lt;/code&gt; to make the bars stretch all the way to the axis.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;ggplot(letter_list, aes(x = reorder(letter, (-freq)), y = freq)) +
  geom_bar(fill = &amp;quot;lightblue&amp;quot;,
           stat = &amp;quot;identity&amp;quot;) +
  geom_text(aes(x = reorder(letter, (-freq)),
                y = freq,
                label = comma(freq, accuracy = 1),
                vjust = 1),
            size = 2) +
  scale_y_continuous(labels = comma,
                     expand = c(0,0)) +
  theme_clean() +
  xlab(&amp;quot;Letter&amp;quot;) +
  ylab(&amp;quot;Frequency&amp;quot;) +
  labs(caption = &amp;quot;Generated by Arthur Holtz\nlinkedin.com/in/arthur-holtz/&amp;quot;) +
  ggtitle(&amp;quot;Letter Frequency in Wordle&amp;#39;s Official Word List&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;/2022/02/21/wordle-data-analysis/index_files/figure-html/unnamed-chunk-7-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Much better! Looking at this plot, my naive strategy is to open with words containing letters on the left side of the plot. They’re much more likely to show up – but even if they don’t, knowing that is also valuable information. So maybe something like “TEARS” or “IRATE” (my wife’s favorite) is a good starter. One interesting thing to note is that this &lt;strong&gt;doesn’t&lt;/strong&gt; match the &lt;a href=&#34;https://en.wikipedia.org/wiki/Letter_frequency&#34;&gt;overall letter frequency in English&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;determining-letter-frequency-for-each-position&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Determining Letter Frequency for Each Position&lt;/h1&gt;
&lt;p&gt;That’s all good to know, but I want more. Where do we go next? I got to thinking it would be helpful to know which letters are most likely to appear &lt;em&gt;where&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;To that end, I took &lt;code&gt;word_list&lt;/code&gt; and split each letter into its own column. R added an empty column when I tried to do this, so I had to add an extra column (&lt;code&gt;into = as.character(1:6)&lt;/code&gt;) and then remove the empty one and rename the others (the &lt;code&gt;select&lt;/code&gt;). Ugh, whatever. I called this data frame &lt;code&gt;ordered_letter_list&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;ordered_letter_list = word_list %&amp;gt;%
  separate(word,
           sep = &amp;quot;&amp;quot;,
           into = as.character(1:6)) %&amp;gt;%
  select(-1,
         &amp;quot;l_1&amp;quot; = 2,
         &amp;quot;l_2&amp;quot; = 3,
         &amp;quot;l_3&amp;quot; = 4,
         &amp;quot;l_4&amp;quot; = 5,
         &amp;quot;l_5&amp;quot; = 6)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we have a set of data that looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;##   l_1 l_2 l_3 l_4 l_5
## 1   C   I   G   A   R
## 2   R   E   B   U   T
## 3   S   I   S   S   Y
## 4   H   U   M   P   H
## 5   A   W   A   K   E&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I wanted to plot each of these letter positions on its own chart, so I first created a function called &lt;code&gt;build_df&lt;/code&gt; that we can call in a &lt;code&gt;for&lt;/code&gt; loop that only takes one column at a time and summarizes it with the most to least common letters.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;build_df = function(df, col_name) {
  new_df = df %&amp;gt;%
    group_by(letter = eval(sym(col_name))) %&amp;gt;%
    summarize(freq = n()) %&amp;gt;%
    arrange(desc(freq))

  return(new_df)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we plug this function into a &lt;code&gt;for&lt;/code&gt; loop to generate a plot for each letter position. Compared to the single plot before, there are some new things that are worth explaining:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I create an object called &lt;code&gt;letter_#&lt;/code&gt; and I set its value to be &lt;code&gt;ordered_letter_list&lt;/code&gt;, but only including the letter number in question.&lt;/li&gt;
&lt;li&gt;Since we’re inside a &lt;code&gt;for&lt;/code&gt; loop, ggplot won’t normally display plots. I wrap everything in &lt;code&gt;print&lt;/code&gt; to make sure we can see the plots.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;for (i in 1:5) {
  this_df = paste0(&amp;quot;letter_&amp;quot;, i)
  assign(this_df, build_df(ordered_letter_list, paste0(&amp;quot;l_&amp;quot;, i)))

  title = paste0(&amp;quot;Letter # &amp;quot;, i, &amp;quot; Frequency in Wordle&amp;#39;s Official Word List&amp;quot;)

  print(ggplot(eval(sym(this_df)), aes(x = reorder(letter, (-freq)), y = freq)) +
    geom_bar(fill = &amp;quot;lightblue&amp;quot;,
             stat = &amp;quot;identity&amp;quot;) +
    geom_text(aes(x = reorder(letter, (-freq)),
                  y = freq,
                  label = comma(freq, accuracy = 1),
                  vjust = 1),
              size = 2) +
    scale_y_continuous(labels = comma,
                       expand = c(0,0)) +
    theme_clean() +
    xlab(&amp;quot;Letter&amp;quot;) +
    ylab(&amp;quot;Frequency&amp;quot;) +
    labs(caption = &amp;quot;Generated by Arthur Holtz\nlinkedin.com/in/arthur-holtz/&amp;quot;) +
    ggtitle(title))
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;/2022/02/21/wordle-data-analysis/index_files/figure-html/unnamed-chunk-11-1.png&#34; width=&#34;672&#34; /&gt;&lt;img src=&#34;/2022/02/21/wordle-data-analysis/index_files/figure-html/unnamed-chunk-11-2.png&#34; width=&#34;672&#34; /&gt;&lt;img src=&#34;/2022/02/21/wordle-data-analysis/index_files/figure-html/unnamed-chunk-11-3.png&#34; width=&#34;672&#34; /&gt;&lt;img src=&#34;/2022/02/21/wordle-data-analysis/index_files/figure-html/unnamed-chunk-11-4.png&#34; width=&#34;672&#34; /&gt;&lt;img src=&#34;/2022/02/21/wordle-data-analysis/index_files/figure-html/unnamed-chunk-11-5.png&#34; width=&#34;672&#34; /&gt;
With these 5 additional charts in my arsenal, now I know not only which letters are most common in general, but also &lt;em&gt;where&lt;/em&gt; they are most likely to appear. One thing I was really surprised to find was just how uncommon “S” is as the 5th letter — only 36 out of 2,309 words! My best guess is the creators deliberately removed a number of plural words so there wouldn’t be so many words ending in “S.” It’s also neat how the 5 most frequent letters for the 3rd position are the vowels.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;brute-forcing-words-with-the-best-score&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Brute Forcing Words with the Best “Score”&lt;/h1&gt;
&lt;p&gt;After discussing the initial analysis above with my dad, we came up with another idea: What if we took every word and bumped it up against every other word in Wordle’s list, and scored the guesses based on how much information they reveal on average?&lt;/p&gt;
&lt;p&gt;I created a data frame called &lt;code&gt;ordered_letter_list_rev&lt;/code&gt; that’s mostly the same as &lt;code&gt;ordered_letter_list&lt;/code&gt;, except I only took the first 50 words (I’ll explain why later) and I kept the full word as the first column.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;ordered_letter_list_rev = substr(
  wordle_script_text,
  str_locate(wordle_script_text, &amp;quot;cigar&amp;quot;)[,&amp;quot;start&amp;quot;],
  str_locate(wordle_script_text, &amp;quot;shave&amp;quot;)[,&amp;quot;end&amp;quot;]) %&amp;gt;%
  str_remove_all(&amp;quot;\&amp;quot;&amp;quot;) %&amp;gt;%
  str_split(&amp;quot;,&amp;quot;) %&amp;gt;%
  data.frame() %&amp;gt;%
  select(word = 1) %&amp;gt;%
  mutate(word = toupper(word)) %&amp;gt;%
  head(50) %&amp;gt;%
  separate(word,
           sep = &amp;quot;&amp;quot;,
           into = as.character(1:6),
           remove = FALSE) %&amp;gt;%
  select(-2,
         &amp;quot;l_1&amp;quot; = 3,
         &amp;quot;l_2&amp;quot; = 4,
         &amp;quot;l_3&amp;quot; = 5,
         &amp;quot;l_4&amp;quot; = 6,
         &amp;quot;l_5&amp;quot; = 7)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is what it looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;##    word l_1 l_2 l_3 l_4 l_5
## 1 CIGAR   C   I   G   A   R
## 2 REBUT   R   E   B   U   T
## 3 SISSY   S   I   S   S   Y
## 4 HUMPH   H   U   M   P   H
## 5 AWAKE   A   W   A   K   E&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, I created a data frame called &lt;code&gt;cross_joined&lt;/code&gt; where I joined &lt;code&gt;ordered_letter_list_rev&lt;/code&gt; to itself without any matching criteria. This is why I did &lt;code&gt;head(50)&lt;/code&gt; in the previous section — the resulting data frame will square the row count of the original data frame, and I wanted to start with a reasonable-sized subset. If you have access to cloud computing resources, the sheer amount of data is less of a concern, but I’m running this on a personal computer, so bear with me.&lt;/p&gt;
&lt;p&gt;Since this self-join would leave us with duplicate column names, I used &lt;code&gt;suffixes = c(&#34;_guess&#34;,&#34;_target&#34;)&lt;/code&gt; in the &lt;code&gt;merge&lt;/code&gt; function to distinguish the guesses from the target words. I also &lt;code&gt;filter&lt;/code&gt;ed out any rows where the guess is the same as the target since these cases don’t give us any useful information.&lt;/p&gt;
&lt;p&gt;Next came the scoring. My (somewhat arbitrary) idea was to give 3 points for getting the right letter in the right place, 1 point for the right letter in the wrong place, and 0 points for wrong letters. I know I did this part extremely inefficiently since there’s a lot of copy/pasted code. I tried searching Stack Overflow for how to do this properly but eventually realized I had spent more time searching than I would have spent using good ol’ copy/paste in the first place. Again, if you know a better way, I’m wide open to suggestions!&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;cross_joined = merge(ordered_letter_list_rev,
                     ordered_letter_list_rev,
                     by = NULL,
                     suffixes = c(&amp;quot;_guess&amp;quot;,&amp;quot;_target&amp;quot;)) %&amp;gt;%
  filter(word_guess != word_target) %&amp;gt;%
  mutate(
    l_1_score = case_when(
      l_1_guess == l_1_target ~ 3,
      l_1_guess == l_2_target ~ 1,
      l_1_guess == l_3_target ~ 1,
      l_1_guess == l_4_target ~ 1,
      l_1_guess == l_5_target ~ 1,
      TRUE ~ 0),
    l_2_score = case_when(
      l_2_guess == l_2_target ~ 3,
      l_2_guess == l_1_target ~ 1,
      l_2_guess == l_3_target ~ 1,
      l_2_guess == l_4_target ~ 1,
      l_2_guess == l_5_target ~ 1,
      TRUE ~ 0),
    l_3_score = case_when(
      l_3_guess == l_3_target ~ 3,
      l_3_guess == l_1_target ~ 1,
      l_3_guess == l_2_target ~ 1,
      l_3_guess == l_4_target ~ 1,
      l_3_guess == l_5_target ~ 1,
      TRUE ~ 0),
    l_4_score = case_when(
      l_4_guess == l_4_target ~ 3,
      l_4_guess == l_1_target ~ 1,
      l_4_guess == l_2_target ~ 1,
      l_4_guess == l_3_target ~ 1,
      l_4_guess == l_5_target ~ 1,
      TRUE ~ 0),
    l_5_score = case_when(
      l_5_guess == l_5_target ~ 3,
      l_5_guess == l_1_target ~ 1,
      l_5_guess == l_2_target ~ 1,
      l_5_guess == l_3_target ~ 1,
      l_5_guess == l_4_target ~ 1,
      TRUE ~ 0),
    total_score = l_1_score + l_2_score + l_3_score + l_4_score + l_5_score) &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gave me a data frame like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;##   word_guess l_1_guess l_2_guess l_3_guess l_4_guess l_5_guess word_target
## 1      REBUT         R         E         B         U         T       CIGAR
## 2      SISSY         S         I         S         S         Y       CIGAR
## 3      HUMPH         H         U         M         P         H       CIGAR
## 4      AWAKE         A         W         A         K         E       CIGAR
## 5      BLUSH         B         L         U         S         H       CIGAR
##   l_1_target l_2_target l_3_target l_4_target l_5_target l_1_score l_2_score
## 1          C          I          G          A          R         1         0
## 2          C          I          G          A          R         0         3
## 3          C          I          G          A          R         0         0
## 4          C          I          G          A          R         1         0
## 5          C          I          G          A          R         0         0
##   l_3_score l_4_score l_5_score total_score
## 1         0         0         0           1
## 2         0         0         0           3
## 3         0         0         0           0
## 4         1         0         0           2
## 5         0         0         0           0&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While exploring the data here, I noticed something strange. To illustrate, let’s take a look at the score breakdown for guessing &lt;code&gt;MARRY&lt;/code&gt; when the target word is &lt;code&gt;MAJOR&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;##   l_1_score l_2_score l_3_score l_4_score l_5_score total_score
## 1         3         3         1         1         0           8&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice letters 3 and 4 (both &lt;code&gt;R&lt;/code&gt;s) are getting 1 point each. That’s not right! There’s only one &lt;code&gt;R&lt;/code&gt; in &lt;code&gt;MAJOR&lt;/code&gt; so this guess should really only get 7 points. There were a number of other cases like this (&lt;code&gt;ERASE&lt;/code&gt; was a major offender because it has 2 &lt;code&gt;E&lt;/code&gt;s, which you might recall was the most common letter in Wordle overall). The scores for guesses like these are inflated, so I wouldn’t trust this scoring logic.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;conclusion&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;This is where I got stuck. It must be possible to only give points for repeat letters once if there is only one such letter in the answer — but how? I can’t figure it out without making a super-complicated &lt;code&gt;case_when&lt;/code&gt; statement. If someone has a brilliant idea, I’d be thrilled to expand upon this analysis in a future update.&lt;/p&gt;
&lt;p&gt;And where would you go next? Assuming I can solve the scoring problem, I’d also like to use the entire &lt;code&gt;Oa&lt;/code&gt; word list as my guessing pool so I can find the most useful guesses, even if they won’t necessarily be the target word.&lt;/p&gt;
&lt;p&gt;I hope you’ve enjoyed walking through Wordle’s code and word list with me! Even though I couldn’t complete all the analysis I originally set out to do, I’m just hoping the overall and by-location frequency analysis give me enough of an edge to do better than my wife!&lt;/p&gt;
&lt;/div&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2022/02/21/wordle-data-analysis/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>Fake Survival Data for the Disease Progression Model</title>
      <link>https://rviews.rstudio.com/2020/10/08/fake-data-for-the-illness-death-model/</link>
      <pubDate>Thu, 08 Oct 2020 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2020/10/08/fake-data-for-the-illness-death-model/</guid>
      <description>
        


&lt;p&gt;In a &lt;a href=&#34;https://rviews.rstudio.com/2020/09/09/fake-data-with-r/&#34;&gt;previous post&lt;/a&gt;, I showed some examples of simulating fake data from a few packages that are useful for common simulation tasks and indicated that I would be following up with a look at simulating survival data. A tremendous amount of work in survival analysis has been done in R&lt;sup&gt;1&lt;/sup&gt; and it will take some time to explore what’s out there. In this first post, I am just going to jump into the ocean of ideas and see if I can fish out and interesting example.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://link.springer.com/article/10.2165/00019053-199813040-00003&#34;&gt;Markov models&lt;/a&gt; are commonly used in Health Care Economics to model the progression of a disease, and the efficacy and potential benefits of various treatments. One popular approach is to consider cohorts of patients who move through the three states of being &lt;em&gt;healthy&lt;/em&gt; (no disease progression), &lt;em&gt;diseased&lt;/em&gt; (some level of disease progression) and &lt;em&gt;dead&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The following figure illustrates the process. (I will explain the labeling on the arrows below).&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/post/2020-10-02-fake-data-for-the-illness-death-model/index_files/figure-html/unnamed-chunk-2-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;These kinds of models are commonly called multi-state models in the survival literature. In the simplest case, disease progression might be modeled as a discrete time &lt;a href=&#34;https://www.dartmouth.edu/~chance/teaching_aids/books_articles/probability_book/Chapter11.pdf&#34;&gt;Markov chain&lt;/a&gt; where patients move from state-to-state according to a matrix of transition probabilities which govern how the process develops at discrete time intervals. However, for many studies, limiting transitions to discrete, uniform intervals is a little too simplistic. For example, in most cases, the exact time when a patient “progresses” from healthy to deceased is not observed. To account for this, modelers frequently consider &lt;a href=&#34;http://u.math.biu.ac.il/~amirgi/CTMCnotes.pdf&#34;&gt;Continuous Time Markov Chain&lt;/a&gt; which allow modeling the distribution of time spent in each state as well as the state-to-state transitions.&lt;/p&gt;
&lt;p&gt;One way to define a continuous time Markov chain is as a continuous time process that takes values in a discrete state space and obeys the Markov property where the transition to a future state depends only on the present and not on the past.&lt;/p&gt;
&lt;p&gt;A continuous-time stochastic process &lt;span class=&#34;math inline&#34;&gt;\(X_{t}, t \geq 0\)&lt;/span&gt; with discrete state space S is a continuous-time Markov chain if:
&lt;span class=&#34;math display&#34;&gt;\[P(X_{t+s}=j \:|\: X_{s}=i), X_u = x_u, 0 \leq u &amp;lt; s) = P(X_{t+s}=j \: | \: X_{s}=i)\]&lt;/span&gt; &lt;span class=&#34;math display&#34;&gt;\[ \forall s,t \geq 0 \:, i, j, x_{u} \in S, \: 0 \leq u &amp;lt; s \]&lt;/span&gt;
If the process does not depend on the the particular value of &lt;em&gt;s&lt;/em&gt; (the time when the process is in state &lt;em&gt;i&lt;/em&gt;) then it is said to be &lt;em&gt;time homogeneous&lt;/em&gt;. For a very readable account of how the definition above along with the assumption of time homogeneity ensure both the Markov property and that the time the process spends in the various states will be exponentially distributed, see Chapter 7 of &lt;a href=&#34;https://www.amazon.com/Introduction-Stochastic-Processes-Robert-Dobrow/dp/1118740653/ref=sr_1_1?dchild=1&amp;amp;keywords=stochastic+processes+in+r&amp;amp;qid=1601771046&amp;amp;s=books&amp;amp;sr=1-1&#34;&gt;Dobrow (2016)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One more bit of theory before we get to the example: unlike discrete time Markov chains, the development of a continuous time process is not driven by a transition matrix. Instead, state transition probabilities are generated by a matrix, &lt;em&gt;Q&lt;/em&gt;, that gives the instantaneous rates of going from one state to another. Transition probabilities for any time, &lt;em&gt;t&lt;/em&gt;, are then calculated from &lt;em&gt;Q&lt;/em&gt; using &lt;a href=&#34;https://cran.r-project.org/web/packages/expm/index.html&#34;&gt;matrix exponentiation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span class=&#34;math display&#34;&gt;\[P(t)=e^Q\]&lt;/span&gt;
The following is the &lt;em&gt;Q&lt;/em&gt; matrix for our three state disease progression model. Notice, that this is not a stochastic matrix: the rows sum to 0 not to 1. The basic idea is that the rate of flow into a state &lt;em&gt;i&lt;/em&gt; is equal to the flow out of &lt;em&gt;i&lt;/em&gt;. The final row is all zeroes in our &lt;em&gt;Q&lt;/em&gt; matrix because death is an &lt;em&gt;absorbing state&lt;/em&gt; and there are not transitions back to &lt;em&gt;healthy&lt;/em&gt; from &lt;em&gt;diseased&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span class=&#34;math display&#34;&gt;\[Q = \begin{pmatrix}
        \ -(q_{12} + q_{13}) &amp;amp; q_{12} &amp;amp; q_{13}) \\ 
        \ 0 &amp;amp; -q_{23} &amp;amp; q_{23} \\
        \  0 &amp;amp; 0 &amp;amp; 0          
     \end{pmatrix} \]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Armed with a little bit of theory, let’s see how continuous time Markov chains can be used both to simulate survival data and also to fit a model to the fake data.&lt;/p&gt;
&lt;div id=&#34;generating-simulated-survival-data&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Generating Simulated Survival Data&lt;/h3&gt;
&lt;p&gt;The following is essentially the example on page 12 of the pdf for the &lt;a href=&#34;https://CRAN.R-project.org/package=genSurv&#34;&gt;genSurv&lt;/a&gt; package&lt;sup&gt;2&lt;/sup&gt; listed in the CRAN Survival Task View. This shows how to use the &lt;code&gt;genTHMM()&lt;/code&gt; function to simulate data from a time homogeneous, continuous time Markov Chain. In the code below, the &lt;code&gt;model.cens&lt;/code&gt; parameter indicates that censoring is accomplished via a uniform distribution over the interval [0, &lt;code&gt;cens.par&lt;/code&gt;]. A covariate is generated by a uniform distribution over the interval [0, &lt;code&gt;covar&lt;/code&gt;] and enters the model through the equation:&lt;/p&gt;
&lt;p&gt;&lt;span class=&#34;math display&#34;&gt;\[q_{i,j} = \lambda_{i,j} exp(\beta_{i,j} \cdot v)\]&lt;/span&gt;
where &lt;span class=&#34;math inline&#34;&gt;\(\lambda_{i,j}\)&lt;/span&gt; is the base rate, parameter &lt;code&gt;rate&lt;/code&gt; for the &lt;code&gt;genTHMM()&lt;/code&gt; function and &lt;span class=&#34;math inline&#34;&gt;\(\beta_{i,j}\)&lt;/span&gt; are the regression coefficients, &lt;code&gt;beta&lt;/code&gt; in the function. In the code below, we use the &lt;code&gt;covariate&lt;/code&gt; output to create a &lt;code&gt;sex&lt;/code&gt; covariate.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;set.seed(1234)
thmmdata &amp;lt;- genTHMM( n=100, model.cens=&amp;quot;uniform&amp;quot;, # censorship model
                     cens.par = 20, 
                     beta = c(0.01,0.08,0.05),
                     covar = 1, 
                     rate = c(0.1,0.05,0.08) )
                     
df &amp;lt;- thmmdata %&amp;gt;% mutate(sex = if_else(covariate &amp;lt;= .5,0,1 ))
df &amp;lt;- df %&amp;gt;% mutate_if(is.numeric, round, 3)
head(df,11)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##    PTNUM   time state covariate sex
## 1      1  0.000     1     0.114   0
## 2      1  2.183     2     0.114   0
## 3      1  2.265     3     0.114   0
## 4      2  0.000     1     0.233   0
## 5      2  0.284     2     0.233   0
## 6      2  1.396     3     0.233   0
## 7      3  0.000     1     0.283   0
## 8      3  8.600     2     0.283   0
## 9      3 18.469     2     0.283   0
## 10     4  0.000     1     0.267   0
## 11     4  3.734     1     0.267   0&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For more on the theory underlying the &lt;code&gt;genSurv&lt;/code&gt; package have a look at the paper &lt;a href=&#34;https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2692556/&#34;&gt;Meira-Mechado et al. (2009)&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;fitting-the-survival-model&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Fitting the Survival Model&lt;/h3&gt;
&lt;p&gt;The code in this section fits a continuous time, Markov chain survival model to the data generated above using the &lt;a href=&#34;https://cran.r-project.org/package=msm&#34;&gt;&lt;code&gt;msm&lt;/code&gt;&lt;/a&gt; package&lt;sup&gt;3&lt;/sup&gt; and indicates how one might go about examining the output.&lt;/p&gt;
&lt;p&gt;First, let’s look at the transitions between states that occurred for the simulated patients.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;st &amp;lt;- statetable.msm(state, PTNUM,data = thmmdata)
st&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##     to
## from  1  2  3
##    1 28 50 22
##    2  0 25 25&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We see, for example, 50 progressed to the diseased state and 22 patient went directly from being &lt;em&gt;healthy&lt;/em&gt; to &lt;em&gt;dead&lt;/em&gt;. 25 patients who progressed to disease, subsequently died.&lt;/p&gt;
&lt;p&gt;Next, we set up the Q matrix of instantaneous transition rates described above,&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;Q &amp;lt;- matrix(c(0, 1, 1, 0, 0 , 1, 0, 0 , 0), nrow = 3, byrow = TRUE)
rownames(Q) &amp;lt;- c(&amp;quot;S1&amp;quot;, &amp;quot;S2&amp;quot;, &amp;quot;S3&amp;quot;)
colnames(Q)  &amp;lt;- c(&amp;quot;S1&amp;quot;, &amp;quot;S2&amp;quot;, &amp;quot;S3&amp;quot;)
Q&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##    S1 S2 S3
## S1  0  1  1
## S2  0  0  1
## S3  0  0  0&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;fit the model, and plot the survival curves for states &lt;em&gt;S1&lt;/em&gt; and &lt;em&gt;S2&lt;/em&gt; using the “old school” pre-built plot method.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;fit &amp;lt;- msm( state ~ time, subject=PTNUM, data = df, 
            qmatrix = Q, gen.inits = TRUE, covariates = ~ sex)
plot(fit)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;/post/2020-10-02-fake-data-for-the-illness-death-model/index_files/figure-html/unnamed-chunk-6-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The default print method for the mode fit shows the transition intensities with the hazard ratio of the covariate.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;fit&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## 
## Call:
## msm(formula = state ~ time, subject = PTNUM, data = df, qmatrix = Q,     gen.inits = TRUE, covariates = ~sex)
## 
## Maximum likelihood estimates
## Baselines are with covariates set to their means
## 
## Transition intensities with hazard ratios for each covariate
##         Baseline                    sex                   
## S1 - S1 -0.28724 (-0.37477,-0.2202)                       
## S1 - S2  0.24020 ( 0.17891, 0.3225) 0.8992 (0.49769,1.625)
## S1 - S3  0.04703 ( 0.02057, 0.1076) 0.2106 (0.04132,1.073)
## S2 - S2 -0.09756 (-0.14312,-0.0665)                       
## S2 - S3  0.09756 ( 0.06650, 0.1431) 0.6558 (0.30473,1.411)
## 
## -2 * log-likelihood:  413.3 
## [Note, to obtain old print format, use &amp;quot;printold.msm&amp;quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can get the transition rates for sex = 0,&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;qmatrix.msm(fit, covariates = list(sex = 0))&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##    S1                          S2                         
## S1 -0.3583 (-0.51092,-0.25130)  0.2537 ( 0.16167, 0.39804)
## S2 0                           -0.1211 (-0.20856,-0.07037)
## S3 0                           0                          
##    S3                         
## S1  0.1046 ( 0.04986, 0.21962)
## S2  0.1211 ( 0.07037, 0.20856)
## S3 0&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and for sex = 1.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;qmatrix.msm(fit, covariates = list(sex = 1))&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##    S1                            S2                           
## S1 -0.25013 (-0.357720,-0.17490)  0.22810 ( 0.155472, 0.33464)
## S2 0                             -0.07945 (-0.136411,-0.04627)
## S3 0                             0                            
##    S3                           
## S1  0.02204 ( 0.005169, 0.09396)
## S2  0.07945 ( 0.046269, 0.13641)
## S3 0&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and &lt;code&gt;msm&lt;/code&gt; also allows us to calculate the transition function &lt;span class=&#34;math inline&#34;&gt;\(P(t)\)&lt;/span&gt; for arbitrary times.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;pmatrix.msm(fit, t= 13.3)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##         S1     S2     S3
## S1 0.02192 0.3182 0.6599
## S2 0.00000 0.2732 0.7268
## S3 0.00000 0.0000 1.0000&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, we look at the mean sojourn times for patients in the &lt;em&gt;healthy&lt;/em&gt; and &lt;em&gt;diseased&lt;/em&gt; states. Normally, for a process that can transition in and out of states this means the average time spent in the state each time it is visited. For our model, patients, only go forward through the chain, there is no getting better, so the sojourn for S2 is essentially the average amount of time patients spent in the &lt;em&gt;diseased&lt;/em&gt; state.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;sojourn.msm(fit)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##    estimates     SE     L      U
## S1     3.481 0.4725 2.668  4.542
## S2    10.251 2.0045 6.987 15.038&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;a-few-remarks&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;A Few Remarks&lt;/h3&gt;
&lt;p&gt;&lt;sup&gt;1&lt;/sup&gt;The work done in R on survival analysis, and partially embodied in the two hundred thirty-three packages listed in the CRAN &lt;a href=&#34;https://cran.r-project.org/web/views/Survival.html&#34;&gt;Survival Analysis Task View&lt;/a&gt;, constitutes a fundamental contribution to statistics. There is enough material here for a lifetime of study. Even confining oneself to a tour of the eleven packages listed in the simulation section would be a significant undertaking.&lt;/p&gt;
&lt;p&gt;&lt;sup&gt;2&lt;/sup&gt; &lt;code&gt;genSurv&lt;/code&gt; is a pretty bare bones package having just seven functions and little explanatory text. If it was not listed on the CRAN Task View, it would have been easy pass by. Nevertheless, I have only shown a small portion of what it can do.&lt;/p&gt;
&lt;p&gt;&lt;sup&gt;3&lt;/sup&gt;&lt;code&gt;msm&lt;/code&gt; is an example of why R is a treasury of statistical knowledge. Not only does the package offer an impressive array of capabilities for analyzing multi-state Markov models in continuous time, the basic documentation, the package’s pdf, includes references to quite a few of the fundamental papers.&lt;/p&gt;
&lt;/div&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2020/10/08/fake-data-for-the-illness-death-model/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>November 2019: &#34;Top 40&#34; New R Packages</title>
      <link>https://rviews.rstudio.com/2019/12/20/november-2019-top-40-new-r-packages/</link>
      <pubDate>Fri, 20 Dec 2019 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2019/12/20/november-2019-top-40-new-r-packages/</guid>
      <description>
        

&lt;p&gt;One hundred forty-four new packages made it to CRAN in November. Here are my picks for the &amp;ldquo;Top 40&amp;rdquo; in eight categories: Computational Methods, Data, Genomics, Machine Learning, Statistics, Time Series, Utilities, and Visualization.&lt;/p&gt;

&lt;h3 id=&#34;computational-methods&#34;&gt;Computational Methods&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=calculus&#34;&gt;calculus&lt;/a&gt; v0.1.1: Provides C++ optimized functions for numerical and symbolic calculus including symbolic arithmetic, tensor calculus, Einstein summation convention, Taylor series expansion, multivariate Hermite polynomials and much more.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=Jaya&#34;&gt;Jaya&lt;/a&gt; v0.1.9: Implements a gradient-free algorithm, without hyperparameters, for solving both constrained and unconstrained optimization problems. See &lt;a href=&#34;doi:10.5267/j.ijiec.2015.8.004&#34;&gt;Rao (2016)&lt;/a&gt; for details and the &lt;a href=&#34;https://cran.r-project.org/web/packages/Jaya/vignettes/A_guide_to_JA.html&#34;&gt;vignette&lt;/a&gt; for how to use the package.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=treenomial&#34;&gt;treenomial&lt;/a&gt; v1.1.1: Provides functions for creating and comparing polynomials that uniquely describe trees as introduced in &lt;a href=&#34;arXiv:1904.03332&#34;&gt;Liu (2019)&lt;/a&gt;. See &lt;a href=&#34;https://cran.r-project.org/web/packages/treenomial/readme/README.html&#34;&gt;README&lt;/a&gt; for information.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;treenomial.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

&lt;h3 id=&#34;data&#34;&gt;Data&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=eudract&#34;&gt;eudract&lt;/a&gt; v0.9.0: Provides access to the  European Clinical Trials Data Base ( &lt;a href=&#34;https://eudract.ema.europa.eu/&#34;&gt;EudraCT&lt;/a&gt;), which summarizes of all registered clinical trial results. The intent is to prevent non-reporting of negative results and provide open-access to results to inform future research. There is a &lt;a href=&#34;https://cran.r-project.org/web/packages/eudract/vignettes/eudract.html&#34;&gt;vignette&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=ozmaps&#34;&gt;ozmaps&lt;/a&gt; v0.2.0: Provides maps of Australian coastline and administrative regions as well as simple functions for country or state maps of Australia, and in-built data sets of administrative regions from the &lt;a href=&#34;https://www.abs.gov.au/&#34;&gt;Australian Bureau of Statistics&lt;/a&gt;. See &lt;a href=&#34;https://cran.r-project.org/web/packages/ozmaps/readme/README.html&#34;&gt;README&lt;/a&gt; for examples.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;ozmaps.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=presentes&#34;&gt;presentes&lt;/a&gt; v0.1.0: Provides a compilation and digitization of the official registry of victims of state terrorism in Argentina during the last military coup. The original data comes from &lt;a href=&#34;https://www.argentina.gob.ar/sitiosdememoria/ruvte/informe&#34;&gt;RUVTE-ILID (2019)&lt;/a&gt; research and the &lt;a href=&#34;http://basededatos.parquedelamemoria.org.ar/registros/&#34;&gt;List of Victims&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=VancouvR&#34;&gt;VancouvR&lt;/a&gt; : Provides a wrapper for the &lt;a href=&#34;https://opendata.vancouver.ca/api/v2/console&#34;&gt;City of Vancouver Open Data API&lt;/a&gt;. There is an &lt;a href=&#34;https://cran.r-project.org/web/packages/VancouvR/vignettes/Demo.html&#34;&gt;Introduction&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=wiesbaden&#34;&gt;wiesbaden&lt;/a&gt; v1.2.0: Implements an interface to retrieve and import data from different databases of the Federal Statistical Office of Germany (&lt;a href=&#34;https://www.destatis.de/EN/Home/_node.html&#34;&gt;DESTATIS&lt;/a&gt;). There is a &lt;a href=&#34;https://cran.r-project.org/web/packages/wiesbaden/vignettes/using-wiesbaden.html&#34;&gt;vignette&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&#34;genomics&#34;&gt;Genomics&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=biocompute&#34;&gt;biocompute&lt;/a&gt; v1.0.3: Provides tools to create, validate, and export &lt;a href=&#34;https://biocomputeobject.org/about.html&#34;&gt;BioCompute Objects&lt;/a&gt; as described in &lt;a href=&#34;doi:10.17605/osf.io/h59uh&#34;&gt;King et al. (2019)&lt;/a&gt;. There is an &lt;a href=&#34;https://cran.r-project.org/web/packages/biocompute/vignettes/intro.html&#34;&gt;Introduction&lt;/a&gt; and a vignette on &lt;a href=&#34;https://cran.r-project.org/web/packages/biocompute/vignettes/case-study.html&#34;&gt;Authoring Biocompute Objects&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=diem&#34;&gt;diem&lt;/a&gt; v1.0: Implements a novel semi-supervised machine learning classier &lt;a href=&#34;https://www.biorxiv.org/content/10.1101/786285v2&#34;&gt;DIEM&lt;/a&gt;, Debris Identification using Expectation Maximization, to identify  debris-containing droplets from a droplet-based single cell/nucleus RNA-seq. See the &lt;a href=&#34;https://cran.r-project.org/web/packages/diem/vignettes/diem.html&#34;&gt;vignette&lt;/a&gt; for details.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;diem.png&#34; height = &#34;200&#34; width=&#34;400&#34;&gt;&lt;/p&gt;

&lt;h3 id=&#34;machine-learning&#34;&gt;Machine Learning&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=azuremlsdk&#34;&gt;azuremlsdk&lt;/a&gt; v0.5.7: Implements an interface to the &lt;a href=&#34;https://docs.microsoft.com/en-us/python/api/overview/azure/ml/intro?view=azure-ml-py&#34;&gt;Azure Machine Learning Software Development Kit&lt;/a&gt; enabling data scientists to train, deploy, automate, and manage machine learning models on the &lt;a href=&#34;https://docs.microsoft.com/en-us/azure/machine-learning/service/overview-what-is-azure-ml&#34;&gt;Azure Machine Learning service&lt;/a&gt;. There are vignettes on &lt;a href=&#34;https://cran.r-project.org/web/packages/azuremlsdk/vignettes/configuration.html&#34;&gt;Setup&lt;/a&gt; and &lt;a href=&#34;https://cran.r-project.org/web/packages/azuremlsdk/vignettes/installation.html&#34;&gt;Installation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=hereR&#34;&gt;hereR&lt;/a&gt; v0.2.0: Implements an interface to the &lt;a href=&#34;https://developer.here.com/develop/rest-apis&#34;&gt;HERE REST APIs&lt;/a&gt; which provide information on geocoding, routing directions, traffic flow, and  weather forecasts. There are vignettes on &lt;a href=&#34;https://cran.r-project.org/web/packages/hereR/vignettes/authentication.html&#34;&gt;Authentication&lt;/a&gt;, the &lt;a href=&#34;https://cran.r-project.org/web/packages/hereR/vignettes/geocoder.html&#34;&gt;Geocoder API&lt;/a&gt;, the &lt;a href=&#34;https://cran.r-project.org/web/packages/hereR/vignettes/routing.html&#34;&gt;Routing API&lt;/a&gt;, the &lt;a href=&#34;https://cran.r-project.org/web/packages/hereR/vignettes/traffic.html&#34;&gt;Traffic API&lt;/a&gt;, and the &lt;a href=&#34;https://cran.r-project.org/web/packages/hereR/vignettes/weather.html&#34;&gt;Weather API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;hereR.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=hilbertSimilarity&#34;&gt;hilbertSimilarity&lt;/a&gt; v0.4.3: Uses &lt;a href=&#34;https://en.wikipedia.org/wiki/Hilbert_curve&#34;&gt;Hilbert Curves&lt;/a&gt; to develop the notion of Hilbert Similarity to quantify the similarity between samples in high dimensional data. There are vignettes on &lt;a href=&#34;https://cran.r-project.org/web/packages/hilbertSimilarity/vignettes/comparing_samples.html&#34;&gt;Comparing Samples&lt;/a&gt; and &lt;a href=&#34;https://cran.r-project.org/web/packages/hilbertSimilarity/vignettes/identifying_effects.html&#34;&gt;Identifying Treatment Effects&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;hilbertSimilarity.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=orf&#34;&gt;orf&lt;/a&gt; v0.1.2: Implements the Ordered Forest estimator as developed in &lt;a href=&#34;arXiv:1907.02436&#34;&gt;Lechner &amp;amp; Okasa (2019)&lt;/a&gt; to estimate the conditional probabilities of models with ordered categorical outcome (ordered choice models). See the &lt;a href=&#34;https://cran.r-project.org/web/packages/orf/vignettes/orf_vignette.html&#34;&gt;vignette&lt;/a&gt; for details.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;orf.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=RPEClust&#34;&gt;RPEClust&lt;/a&gt; v0.1.0: Implements the random projection ensemble clustering algorithm described in &lt;a href=&#34;arXiv:1909.10832&#34;&gt;Anderlucci et al.(2019)&lt;/a&gt; and  &lt;a href=&#34;doi:10.1198/016214506000000113&#34;&gt;Raftery and Dean (2006)&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&#34;statistics&#34;&gt;Statistics&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=DiffXTables&#34;&gt;DiffXTables&lt;/a&gt; v0.0.2: Provides functions for statistical hypothesis testing of pattern heterogeneity via differences in underlying distributions across two or more contingency tables. It includes the comparative chi-squared test, the Sharma-Song test, and the heterogeneity test. See the &lt;a href=&#34;https://cran.r-project.org/web/packages/DiffXTables/vignettes/DiffXTables.html&#34;&gt;vignette&lt;/a&gt; for details.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;DiffXTables.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=effectsize&#34;&gt;effectsize&lt;/a&gt; v0.0.1: Provides functions to work with indices of effect size and standardized parameters for a wide variety of models (See &lt;a href=&#34;doi:10.21105/joss.01412&#34;&gt;Lüdecke, Waggoner &amp;amp; Makowski (2019)&lt;/a&gt;.) There are vignettes on &lt;a href=&#34;https://cran.r-project.org/web/packages/effectsize/vignettes/bayesian_models.html&#34;&gt;Bayesian Models&lt;/a&gt;, &lt;a href=&#34;https://cran.r-project.org/web/packages/effectsize/vignettes/convert.html&#34;&gt;Converting Between Incices&lt;/a&gt;, &lt;a href=&#34;https://cran.r-project.org/web/packages/effectsize/vignettes/interpret.html&#34;&gt;Automated Interpretation of Indices&lt;/a&gt;, &lt;a href=&#34;https://cran.r-project.org/web/packages/effectsize/vignettes/standardize_data.html&#34;&gt;Data Standardization&lt;/a&gt; and &lt;a href=&#34;https://cran.r-project.org/web/packages/effectsize/vignettes/standardize_parameters.html&#34;&gt;Parameter Standardization&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=exPrior&#34;&gt;exPrior&lt;/a&gt; v1.0.1: Provides practitioners of statistics in geology, hydrology, etc. with a tool for deriving prior distributions for Bayesian inference. See the &lt;a href=&#34;https://cran.r-project.org/web/packages/exPrior/vignettes/using_genExPrior.html&#34;&gt;vignette&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;exPrior.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=fitHeavyTail&#34;&gt;fitHeavyTail&lt;/a&gt; v0.1.1: Implements robust estimation methods for the mean vector and covariance matrix from data (possibly containing NAs) under multivariate heavy-tailed distributions such as angular Gaussian, Cauchy, and Student&amp;rsquo;s t. See &lt;a href=&#34;doi:10.1109/TSP.2014.2348944&#34;&gt;Sun et al. (2014)&lt;/a&gt;, &lt;a href=&#34;doi:10.1109/SAM.2014.6882356&#34;&gt;Sun et al. (2015)&lt;/a&gt;, &lt;a href=&#34;https:www3.stat.sinica.edu.tw/statistica/oldpdf/A5n12.pdf&#34;&gt;Liu and Rubin (1995)&lt;/a&gt; and &lt;a href=&#34;arXiv:1909.12530&#34;&gt;Zhou et al. (2015)&lt;/a&gt; for background, and the &lt;a href=&#34;https://cran.r-project.org/web/packages/fitHeavyTail/vignettes/CovarianceEstimationHeavyTail.html&#34;&gt;vignette&lt;/a&gt; for examples.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;fitHeavyTails.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=mixl&#34;&gt;mixl&lt;/a&gt; v1.1: Provides functions for simulated maximum likelihood estimation of multinomial logit models, mixed models, random coefficients and hybrid choice models. See &lt;a href=&#34;doi:10.3929/ethz-b-000334289&#34;&gt;Molloy et al. (2019)&lt;/a&gt; for details and the &lt;a href=&#34;https://cran.r-project.org/web/packages/mixl/vignettes/user-guide.html&#34;&gt;User Guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=MKdescr&#34;&gt;MKdescr&lt;/a&gt; v0.5: Provides functions to compute a  standardized interquartile range (IQR), a Huber-type skipped mean as described in &lt;a href=&#34;doi:10.2307/1268758&#34;&gt;Hampel (1985)&lt;/a&gt;, a robust coefficient of variation as described in &lt;a href=&#34;arXiv:1907.01110&#34;&gt;Arachchige et al. (2019)&lt;/a&gt;, a robust signal to noise ratio (SNR), and more. See the &lt;a href=&#34;https://cran.r-project.org/web/packages/MKdescr/vignettes/MKdescr.html&#34;&gt;vignette&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;MKdescr.png&#34; height = &#34;200&#34; width=&#34;400&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=MNLpred&#34;&gt;MNLpred&lt;/a&gt; v0.0.1: Provides functions to return simulated predicted probabilities and first differences for multinomial logit models. The methodological approach is based on the principles laid out by &lt;a href=&#34;doi:10.2307/2669316&#34;&gt;King, Tomz, and Wittenberg (2000)&lt;/a&gt;. See the &lt;a href=&#34;https://cran.r-project.org/web/packages/MNLpred/vignettes/OVA_Predictions_For_MNL.html&#34;&gt;vignette&lt;/a&gt; for examples.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=pdqr&#34;&gt;pdqr&lt;/a&gt; v0.2.0: Provides functions to create, transform, and summarize custom discrete and continuous random variables with distribution functions that are analogues of &lt;code&gt;p*()&lt;/code&gt;, &lt;code&gt;d*()&lt;/code&gt;, &lt;code&gt;q*()&lt;/code&gt;, and &lt;code&gt;r*()&lt;/code&gt;. There are vignettes on &lt;a href=&#34;https://cran.r-project.org/web/packages/pdqr/vignettes/pdqr-01-create.html&#34;&gt;Creating&lt;/a&gt;, &lt;a href=&#34;https://cran.r-project.org/web/packages/pdqr/vignettes/pdqr-02-convert.html&#34;&gt;Converting&lt;/a&gt;, &lt;a href=&#34;https://cran.r-project.org/web/packages/pdqr/vignettes/pdqr-03-transform.html&#34;&gt;Transforming&lt;/a&gt;, and &lt;a href=&#34;https://cran.r-project.org/web/packages/pdqr/vignettes/pdqr-04-summarize.html&#34;&gt;Summarizing&lt;/a&gt; pdqr functions.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;pdqr.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=tensorregress&#34;&gt;tensorregression&lt;/a&gt; v1.0: Implements the generalized tensor regression in &lt;a href=&#34;arXiv:1910.09499&#34;&gt;Xu, Hu and Wang (2019)&lt;/a&gt; to solve tensor-response regression given covariates on multiple modes with alternating updating algorithm.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=tidydice&#34;&gt;tidydice&lt;/a&gt;: v0.0.4: Provides functions for basic statistical experiments, that can be used for teaching introductory statistics. See the &lt;a href=&#34;https://cran.r-project.org/web/packages/tidydice/vignettes/tidydice.html&#34;&gt;vignette&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;tidydice.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=tvgeom&#34;&gt;tvgeom&lt;/a&gt; v1.0.1: Implements the probability mass, distribution, quantile, and random number generating functions for the time-varying right-truncated geometric distribution. See the &lt;a href=&#34;https://cran.r-project.org/web/packages/tvgeom/vignettes/introduction.html&#34;&gt;vignette&lt;/a&gt; for background.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;tvgeom.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

&lt;h3 id=&#34;time-series&#34;&gt;Time Series&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=gravitas&#34;&gt;gravitas&lt;/a&gt; v0.1.0: Provides tools for systematically exploring large quantities of temporal data across different temporal granularities (deconstructions of time) by visualizing probability distributions. There are vignettes on exploring probability distributions for &lt;a href=&#34;https://cran.r-project.org/web/packages/gravitas/vignettes/cricket.html&#34;&gt;cricket&lt;/a&gt; and for &lt;a href=&#34;https://cran.r-project.org/web/packages/gravitas/vignettes/gravitas_vignette.html&#34;&gt;bivariate temporal franularities&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;gravitas.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=smoots&#34;&gt;smoots&lt;/a&gt; v1.0.1: Provides nonparametric estimates of trend and its derivatives in equidistant time series  with short-memory stationary errors. See &lt;a href=&#34;http://groups.uni-paderborn.de/wp-wiwi/RePEc/pdf/ciepap/WP102.pdf&#34;&gt;Feng and Gries (2017)&lt;/a&gt; for the methods employed, and see &lt;a href=&#34;https://cran.r-project.org/web/packages/smoots/readme/README.html&#34;&gt;README&lt;/a&gt; for an example.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;smoots.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=tsfgrnn&#34;&gt;tsfgrnn&lt;/a&gt; v0.1.0: Implements a general regression neural network (GRNN), a variant of a radial basis function network, for forecasting time series. See &lt;a href=&#34;doi:10.1007/978-3-030-20521-8_17&#34;&gt;Martinez et al. (2019)&lt;/a&gt; and &lt;a href=&#34;doi:10.1109/TNNLS.2012.2198074&#34;&gt;Yan (2012)&lt;/a&gt; for background and the &lt;a href=&#34;https://cran.r-project.org/web/packages/tsfgrnn/vignettes/tsfgrnn.html&#34;&gt;vignette&lt;/a&gt; for examples.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;tsfgrnn.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

&lt;h3 id=&#34;utilities&#34;&gt;Utilities&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=dipsaus&#34;&gt;dipsaus&lt;/a&gt; v0.0.3: Provides enhancement functions that fall into four categories: &lt;code&gt;shiny&lt;/code&gt; input widgets;  high-performance computing using &lt;code&gt;RcppParallel&lt;/code&gt; and &lt;code&gt;future&lt;/code&gt;; functions to modify R calls and convert numbers, strings, and other objects; and utility functions to get system information such as CPU chipset, memory limit, etc. See the vignettes: &lt;a href=&#34;https://cran.r-project.org/web/packages/dipsaus/vignettes/async_evaluator.html&#34;&gt;Asynchronous Evaluator&lt;/a&gt;, &lt;a href=&#34;https://cran.r-project.org/web/packages/dipsaus/vignettes/r_expr_addons.html&#34;&gt;R Expression Add-ons&lt;/a&gt;, &lt;a href=&#34;https://cran.r-project.org/web/packages/dipsaus/vignettes/shiny_customized_widgets.html&#34;&gt;Shiny Customized Widgets&lt;/a&gt;, and &lt;a href=&#34;https://cran.r-project.org/web/packages/dipsaus/vignettes/utility_functions.html&#34;&gt;Utility Functions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;dipsaus.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=extraoperators&#34;&gt;extraoperators&lt;/a&gt; v0.1.1: Provides operator functions for common tasks such as logical or relational comparisons, finding indices and subsetting. See the &lt;a href=&#34;https://cran.r-project.org/web/packages/extraoperators/vignettes/logicals-vignette.html&#34;&gt;vignette&lt;/a&gt; for details.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=gluedown&#34;&gt;gluedown&lt;/a&gt; v1.0.1: Provides functions to transition between R vectors and markdown text. Users can create vectors in R, glue strings together with the markdown syntax, and print formatted vectors directly to the document. This package primarily uses &lt;a href=&#34;https://github.github.com/gfm/&#34;&gt;GitHub Flavored Markdown&lt;/a&gt;. There is a vignette on &lt;a href=&#34;https://cran.r-project.org/web/packages/gluedown/vignettes/github-spec.html&#34;&gt;GitHub Flavored Markdown&lt;/a&gt; and another on &lt;a href=&#34;https://cran.r-project.org/web/packages/gluedown/vignettes/literal-programming.html&#34;&gt;Printing Markdown&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=googlesheets4&#34;&gt;googlesheets4&lt;/a&gt; v0.1.0: Provides functions for interacting with Google Sheets through the &lt;a href=&#34;https://developers.google.com/sheets/api&#34;&gt;Sheets API v4&lt;/a&gt;. See &lt;a href=&#34;https://cran.r-project.org/web/packages/googlesheets4/readme/README.html&#34;&gt;README&lt;/a&gt; for help.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=hdd&#34;&gt;hdd&lt;/a&gt; v0.1.0: Provides a data class for importing and manipulating out of memory data sets. See the &lt;a href=&#34;https://cran.r-project.org/web/packages/hdd/vignettes/hdd_walkthrough.html&#34;&gt;Introduction&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=RVerbalExpressions&#34;&gt;RVerbalExpressions&lt;/a&gt; v0.1.0: Provides tools to build regular expressions using grammar and functionality inspired by &lt;a href=&#34;https://github.com/VerbalExpressions&#34;&gt;VerbalExpressions&lt;/a&gt;. See the &lt;a href=&#34;https://cran.r-project.org/web/packages/RVerbalExpressions/vignettes/examples.html&#34;&gt;vignette&lt;/a&gt; for examples.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=shinyMobile&#34;&gt;shinyMobile&lt;/a&gt; v0.1.0: Provides tools for building &lt;code&gt;shiny&lt;/code&gt; apps for &lt;code&gt;iOS&lt;/code&gt;, &lt;code&gt;Android&lt;/code&gt;, and desktop computers as well as beautiful &lt;code&gt;shiny&lt;/code&gt; gadgets. &lt;code&gt;shinyMobile&lt;/code&gt; is built on top of the latest &lt;a href=&#34;https://framework7.io&#34;&gt;&amp;lsquo;Framework7&amp;rsquo;&lt;/a&gt; template. There is a &lt;a href=&#34;https://cran.r-project.org/web/packages/shinyMobile/vignettes/getting-started.html&#34;&gt;Getting Started Guide&lt;/a&gt; and vignettes on &lt;a href=&#34;https://cran.r-project.org/web/packages/shinyMobile/vignettes/Dark-Theme.html&#34;&gt;Dark-Theme&lt;/a&gt;, &lt;a href=&#34;https://cran.r-project.org/web/packages/shinyMobile/vignettes/Gadgets.html&#34;&gt;Gadgets&lt;/a&gt;, &lt;a href=&#34;https://cran.r-project.org/web/packages/shinyMobile/vignettes/Single-Layout.html&#34;&gt;Single-Layout&lt;/a&gt;, &lt;a href=&#34;https://cran.r-project.org/web/packages/shinyMobile/vignettes/Split-Layout.html&#34;&gt;Split-Layout&lt;/a&gt;, &lt;a href=&#34;https://cran.r-project.org/web/packages/shinyMobile/vignettes/Tabs-Layout.html&#34;&gt;Tabs-Layout&lt;/a&gt;, and &lt;a href=&#34;https://cran.r-project.org/web/packages/shinyMobile/vignettes/shinyMobile_tools.html&#34;&gt;Tools&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;shinyMobile.png&#34; height = &#34;200&#34; width=&#34;400&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=tidycwl&#34;&gt;tidycwl&lt;/a&gt; v1.0.4: Implements the &lt;a href=&#34;https://www.commonwl.org/&#34;&gt;Common Workflow Language&lt;/a&gt; for describing data analysis workflows. See the &lt;a href=&#34;https://cran.r-project.org/web/packages/tidycwl/vignettes/tidycwl.html&#34;&gt;vignette&lt;/a&gt; for details.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;tidycwl.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

&lt;h3 id=&#34;visualization&#34;&gt;Visualization&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=barplot3d&#34;&gt;barplot3d&lt;/a&gt; v1.0.1: Provides functions for creating 3D plots including sequence context plots used in DNA sequencing analysis. See the &lt;a href=&#34;https://cran.r-project.org/web/packages/barplot3d/vignettes/barplot3d.html&#34;&gt;vignette&lt;/a&gt; for examples.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;barplot3d.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=fplot&#34;&gt;fplot&lt;/a&gt; v0.2.0: Provides functions to plot regular/weighted/conditional distributions by using formulas. See the &lt;a href=&#34;https://cran.r-project.org/web/packages/fplot/vignettes/fplot_walkthrough.html&#34;&gt;vignette&lt;/a&gt; for examples.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;fplot.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=robvis&#34;&gt;robvis&lt;/a&gt;: v0.3.0: Provides functions for visualizing risk-of-bias assessments performed as part of a systematic review, providing tools for randomized controlled trials ( &lt;a href=&#34;doi:10.1136/bmj.l4898&#34;&gt;Sterne et al. (2019)&lt;/a&gt;), non-randomized studies of interventions ( &lt;a href=&#34;doi:10.1136/bmj.i4919&#34;&gt;Sterne et al (2016)&lt;/a&gt;), and diagnostic accuracy studies ( &lt;a href=&#34;doi:10.7326/0003-4819-155-8-201110180-00009&#34;&gt;Whiting et al (2011)&lt;/a&gt;). There is a &lt;a href=&#34;https://cran.r-project.org/web/packages/robvis/vignettes/Introduction_to_robvis.html&#34;&gt;vignette&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;robvis.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2019/12/20/november-2019-top-40-new-r-packages/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>tidyposterior&#39;s Bayesian Approach to Model Comparison</title>
      <link>https://rviews.rstudio.com/2019/12/16/bayesian-model-comparison/</link>
      <pubDate>Mon, 16 Dec 2019 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2019/12/16/bayesian-model-comparison/</guid>
      <description>
        &lt;p&gt;A task common to many machine learning workflows is to compare the performance of several models with respect to some metric such as accuracy or area under the ROC curve. Standard practice is to try out several different algorithms on a training data set and see which works better. Unfortunately, all to often, after this work has been done, model selection comes down to &amp;ldquo;eyeballing&amp;rdquo; several different ROC curves. If you find eyeballing a little too informal, then take a look at the &lt;a href=&#34;https://cran.r-project.org/package=tidyposterior&#34;&gt;&lt;code&gt;tidyposterior&lt;/code&gt;&lt;/a&gt; package (part of the universe of &lt;a href=&#34;https://cran.r-project.org/web/packages/tidymodels/index.html&#34;&gt;&amp;lsquo;tidymodels`&lt;/a&gt;). The &lt;a href=&#34;https://cran.r-project.org/web/packages/tidyposterior/vignettes/Getting_Started.html&#34;&gt;&lt;em&gt;Getting Started&lt;/em&gt;&lt;/a&gt; vignette asks the question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When looking at resampling results, are the differences between the models &amp;ldquo;real&amp;rdquo;?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and lays out a modelling approach to answering it that is grounded in Bayesian theory. This means that all of the information about the differences between two models to be compared will be summed up in a posterior distribution for the differences that will make it possible to calculate probabilities entirely based on the particular data at hand, the information encoded in the various priors, and the effect size that has been chosen because it is considered large enough to make a practical difference. Given an effect size chosen up front to represent a practical difference, the &lt;code&gt;tidyposterior&lt;/code&gt; model will enable conclusions such as; &lt;em&gt;there is a probability of .65 that, for all practical purposes, the two distributions are different&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;There is no magic here. &lt;code&gt;tidyposterior&lt;/code&gt; works through the mechanics of an Bayesian analysis with minimum input from the user (a situation that is possible because of the very constrained nature of comparison problem and the use of cross-validation statistics as input), but the result ultimately comes down to choosing an effect size that makes sense based on domain knowledge of the underlying experiment. For some studies, an effect of .05 might seem to be reasonable. In a situation where having an interpretable is worth more than a black-box prediction modelers may be willing to go higher. And, for studies where models may support life or death decisions something in the 2% range may be required.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;Getting Started&lt;/em&gt; guide provides end-to-end road map on using the &lt;code&gt;tidyposterior&lt;/code&gt; functions to compare models, but it assumes quite a bit of background. Someone just getting started with Bayesian statistics or doing Bayesian statistics in R, might be more comfortable with a little more description of the landscape. For the rest of this post, I&amp;rsquo;ll be the tour guide and provide a little color commentary that you may find helpful as you walk through the vignette.&lt;/p&gt;

&lt;p&gt;The first thing to point out is that the vignette begins after most of the heavy lifting for a model comparison study has already been done, and the information required to compare models has been wrapped up in the data frame &lt;code&gt;precise_example&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As &lt;a href=&#34;https://tidymodels.github.io/tidyposterior/reference/precise_example.html&#34;&gt;website&lt;/a&gt; for &lt;code&gt;tidyposterior&lt;/code&gt; states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;precise_example&lt;/code&gt; contains the results of the classification analysis of a real data set using 10-fold CV. The holdout data sets contained thousands of examples and have precise performance estimates. Three models were fit to the original data and several performance metrics are included.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;precise_example[,-1]
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;## # A tibble: 10 x 28
##    id    glm_Accuracy glm_Kappa glm_ROC glm_Sens glm_Spec glm_PRAUC
##    &amp;lt;chr&amp;gt;        &amp;lt;dbl&amp;gt;     &amp;lt;dbl&amp;gt;   &amp;lt;dbl&amp;gt;    &amp;lt;dbl&amp;gt;    &amp;lt;dbl&amp;gt;     &amp;lt;dbl&amp;gt;
##  1 Fold…        0.722     0.328   0.798    0.729    0.720     0.489
##  2 Fold…        0.696     0.290   0.778    0.720    0.691     0.456
##  3 Fold…        0.701     0.297   0.790    0.723    0.696     0.486
##  4 Fold…        0.704     0.316   0.795    0.763    0.691     0.497
##  5 Fold…        0.721     0.324   0.797    0.722    0.721     0.481
##  6 Fold…        0.711     0.303   0.780    0.706    0.712     0.484
##  7 Fold…        0.702     0.305   0.790    0.739    0.694     0.485
##  8 Fold…        0.718     0.321   0.784    0.729    0.715     0.477
##  9 Fold…        0.720     0.328   0.795    0.739    0.715     0.491
## 10 Fold…        0.719     0.324   0.796    0.728    0.717     0.488
## # … with 21 more variables: glm_Precision &amp;lt;dbl&amp;gt;, glm_Recall &amp;lt;dbl&amp;gt;,
## #   glm_F &amp;lt;dbl&amp;gt;, knn_Accuracy &amp;lt;dbl&amp;gt;, knn_Kappa &amp;lt;dbl&amp;gt;, knn_ROC &amp;lt;dbl&amp;gt;,
## #   knn_Sens &amp;lt;dbl&amp;gt;, knn_Spec &amp;lt;dbl&amp;gt;, knn_PRAUC &amp;lt;dbl&amp;gt;, knn_Precision &amp;lt;dbl&amp;gt;,
## #   knn_Recall &amp;lt;dbl&amp;gt;, knn_F &amp;lt;dbl&amp;gt;, nnet_Accuracy &amp;lt;dbl&amp;gt;, nnet_Kappa &amp;lt;dbl&amp;gt;,
## #   nnet_ROC &amp;lt;dbl&amp;gt;, nnet_Sens &amp;lt;dbl&amp;gt;, nnet_Spec &amp;lt;dbl&amp;gt;, nnet_PRAUC &amp;lt;dbl&amp;gt;,
## #   nnet_Precision &amp;lt;dbl&amp;gt;, nnet_Recall &amp;lt;dbl&amp;gt;, nnet_F &amp;lt;dbl&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The various statistics for the different models have been  &lt;em&gt;matched&lt;/em&gt; by setting a random number seed and using the same random sampling process for each fold. This is the kind of thing that can easily be done with the &lt;a href=&#34;https://cran.r-project.org/package=rsample&#34;&gt;&lt;code&gt;rsample&lt;/code&gt;&lt;/a&gt; or &lt;a href=&#34;https://topepo.github.io/caret/&#34;&gt;&lt;code&gt;caret&lt;/code&gt;&lt;/a&gt; packages, but requires a bit of work otherwise.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;Getting Started&lt;/em&gt; vignette focuses on &lt;code&gt;ROC&lt;/code&gt;, the area under the &lt;a href=&#34;https://rviews.rstudio.com/2019/01/17/roc-curves/&#34;&gt;ROC Curve&lt;/a&gt;. A little bit of preprocessing produces &amp;ldquo;wide&amp;rdquo; and &amp;ldquo;long&amp;rdquo; (or stacked) versions of the ROC statistics. Note that information on the performance statistics contained in the data set can be found on the &lt;a href=&#34;https://tidymodels.github.io/yardstick/reference/index.html&#34;&gt;&lt;code&gt;yardstick&lt;/code&gt; reference page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This first bit of code selects the ROC for the different statistics and creates a stacked version of the data set.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;rocs &amp;lt;- precise_example %&amp;gt;%
  select(id, contains(&amp;quot;ROC&amp;quot;)) %&amp;gt;%
  setNames(tolower(gsub(&amp;quot;_ROC$&amp;quot;, &amp;quot;&amp;quot;, names(.)))) 
rocs
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;## # A tibble: 10 x 4
##    id       glm   knn  nnet
##    &amp;lt;chr&amp;gt;  &amp;lt;dbl&amp;gt; &amp;lt;dbl&amp;gt; &amp;lt;dbl&amp;gt;
##  1 Fold01 0.798 0.753 0.843
##  2 Fold02 0.778 0.744 0.827
##  3 Fold03 0.790 0.743 0.846
##  4 Fold04 0.795 0.755 0.852
##  5 Fold05 0.797 0.760 0.838
##  6 Fold06 0.780 0.747 0.852
##  7 Fold07 0.790 0.757 0.833
##  8 Fold08 0.784 0.754 0.832
##  9 Fold09 0.795 0.764 0.846
## 10 Fold10 0.796 0.748 0.847
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;rocs_stacked &amp;lt;- gather.rset(rocs)
head(rocs_stacked)
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;##       id model statistic
## 1 Fold01   glm    0.7981
## 2 Fold02   glm    0.7779
## 3 Fold03   glm    0.7901
## 4 Fold04   glm    0.7948
## 5 Fold05   glm    0.7972
## 6 Fold06   glm    0.7804
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The first graph of the vignette plots the ROC cross-validation statistics for each fold. The fact that the lines are nearly all parallel indicates that there is probably a resampling effect, but it does look like there is a clear difference in the performance of the three models.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;ggplot(rocs_stacked, aes(x = model, y = statistic, group = id, col = id)) + geom_line(alpha = .75) + theme(legend.position = &amp;quot;none&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;/post/2019-12-02-bayesian-model-comparison/index_files/figure-html/unnamed-chunk-4-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;

&lt;p&gt;The following plot makes the performance differences even more apparent. There is very little overlap of the kernel density estimates of the three distributions.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;ggplot(rocs_stacked, aes(x = statistic, col = model, fill = model)) + 
  geom_histogram(aes(y=..density..), alpha=0.5, position=&amp;quot;identity&amp;quot;, bins = 35)+
  geom_density(alpha=.2) + xlim(min = .73, max = .87) +
  geom_rug()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;/post/2019-12-02-bayesian-model-comparison/index_files/figure-html/unnamed-chunk-5-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Quantifying these differences is the point of our exercise. The strategy will be to fit a Bayesian model that explicitly accounts for the fold effects, and then compare the differences in posterior distributions to calculate the probability that the various distributions are indeed different.&lt;/p&gt;

&lt;p&gt;Here, we fit the model with the &lt;code&gt;tidyposterior&lt;/code&gt; function &lt;code&gt;perf_mod()&lt;/code&gt;. Note that although the only parameter passed to &lt;code&gt;perf_mod()&lt;/code&gt; is to set the seed, other options can be specified. A great deal of attention has been put into setting the defaults. I will explain the model &lt;code&gt;perf_mod()&lt;/code&gt; executes below when we examine the resulting model object.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;roc_model &amp;lt;- perf_mod(rocs, seed = 2824)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I have suppressed the voluminous output generated by fitting the model, but if you run it yourself, you will see that there was a lot going on under the covers. Four independent Markov chains were initiated for the &lt;code&gt;stan&lt;/code&gt; Monte Carlo algorithm used to evaluate the model, each chain going through 1,000 warm up iterations and then another 1,000 to fit the model.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;perf_mod()&lt;/code&gt;is based on the &lt;a href=&#34;https://cran.r-project.org/web/packages/rstanarm/vignettes/glmer.html&#34;&gt;&lt;code&gt;glmer()&lt;/code&gt; function&lt;/a&gt; from the &lt;a href=&#34;https://cran.r-project.org/package=rstanarm&#34;&gt;&lt;code&gt;rstanarm&lt;/code&gt; package&lt;/a&gt;. We can assess the &lt;code&gt;rstanarm&lt;/code&gt; model object which is returned as part of &lt;code&gt;perf_mod&lt;/code&gt; model object.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;summary(roc_model$stan)
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;## 
## Model Info:
##  function:     stan_glmer
##  family:       gaussian [identity]
##  formula:      statistic ~ model + (1 | id)
##  algorithm:    sampling
##  sample:       4000 (posterior sample size)
##  priors:       see help(&#39;prior_summary&#39;)
##  observations: 30
##  groups:       id (10)
## 
## Estimates:
##                                     mean   sd   10%   50%   90%
## (Intercept)                       0.8    0.0  0.8   0.8   0.8  
## modelknn                          0.0    0.0  0.0   0.0   0.0  
## modelnnet                         0.1    0.0  0.0   0.1   0.1  
## b[(Intercept) id:Fold01]          0.0    0.0  0.0   0.0   0.0  
## b[(Intercept) id:Fold02]          0.0    0.0  0.0   0.0   0.0  
## b[(Intercept) id:Fold03]          0.0    0.0  0.0   0.0   0.0  
## b[(Intercept) id:Fold04]          0.0    0.0  0.0   0.0   0.0  
## b[(Intercept) id:Fold05]          0.0    0.0  0.0   0.0   0.0  
## b[(Intercept) id:Fold06]          0.0    0.0  0.0   0.0   0.0  
## b[(Intercept) id:Fold07]          0.0    0.0  0.0   0.0   0.0  
## b[(Intercept) id:Fold08]          0.0    0.0  0.0   0.0   0.0  
## b[(Intercept) id:Fold09]          0.0    0.0  0.0   0.0   0.0  
## b[(Intercept) id:Fold10]          0.0    0.0  0.0   0.0   0.0  
## sigma                             0.0    0.0  0.0   0.0   0.0  
## Sigma[id:(Intercept),(Intercept)] 0.0    0.0  0.0   0.0   0.0  
## 
## Fit Diagnostics:
##            mean   sd   10%   50%   90%
## mean_PPD 0.8    0.0  0.8   0.8   0.8  
## 
## The mean_ppd is the sample average posterior predictive distribution of the outcome variable (for details see help(&#39;summary.stanreg&#39;)).
## 
## MCMC diagnostics
##                                   mcse Rhat n_eff
## (Intercept)                       0.0  1.0  2691 
## modelknn                          0.0  1.0  2977 
## modelnnet                         0.0  1.0  3314 
## b[(Intercept) id:Fold01]          0.0  1.0  3389 
## b[(Intercept) id:Fold02]          0.0  1.0  1162 
## b[(Intercept) id:Fold03]          0.0  1.0  3683 
## b[(Intercept) id:Fold04]          0.0  1.0  2016 
## b[(Intercept) id:Fold05]          0.0  1.0  2888 
## b[(Intercept) id:Fold06]          0.0  1.0  3557 
## b[(Intercept) id:Fold07]          0.0  1.0  3332 
## b[(Intercept) id:Fold08]          0.0  1.0  1952 
## b[(Intercept) id:Fold09]          0.0  1.0  1929 
## b[(Intercept) id:Fold10]          0.0  1.0  3219 
## sigma                             0.0  1.0  1391 
## Sigma[id:(Intercept),(Intercept)] 0.0  1.0  1386 
## mean_PPD                          0.0  1.0  4180 
## log-posterior                     0.2  1.0   688 
## 
## For each parameter, mcse is Monte Carlo standard error, n_eff is a crude measure of effective sample size, and Rhat is the potential scale reduction factor on split chains (at convergence Rhat=1).
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The default model constructed by &lt;code&gt;perf_mod()&lt;/code&gt; specified by the R formula &lt;code&gt;statistic ~ model + (1 | id)&lt;/code&gt; is a Bayesian, multi-level, varying intercept, linear regression model with an intercept, coefficients for the knn and nnet model effects, and an intercept for each of the cross-validation folds. This model might be written as:&lt;/p&gt;

&lt;p&gt;Y&lt;sub&gt;i&lt;/sub&gt; = &lt;code&gt;\(\alpha\)&lt;/code&gt; + &lt;code&gt;\(\beta\)&lt;/code&gt;X&lt;sub&gt;i&lt;/sub&gt; + b&lt;sub&gt;[i]&lt;/sub&gt;&lt;/p&gt;

&lt;p&gt;in the one of the common notations used by Bayesians. (For example, see &lt;a href=&#34;https://books.google.com/books?hl=en&amp;amp;lr=&amp;amp;id=c9xLKzZWoZ4C&amp;amp;oi=fnd&amp;amp;pg=PR17&amp;amp;dq=gelman+and+hill+hierarchical+models&amp;amp;ots=bbT8P1Ksmg&amp;amp;sig=tyBMOIoF2pcSavdePWLUuUDgoiM#v=onepage&amp;amp;q=varying%20intercept%20model&amp;amp;f=false]&#34;&gt;Gelman and Hill (2007)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rstanarm&lt;/code&gt; allows R users to build a wide range of Bayesian regression models using the &lt;a href=&#34;https://mc-stan.org/&#34;&gt;&lt;code&gt;stan&lt;/code&gt; engine&lt;/a&gt; without having to explicitly program in &lt;code&gt;stan&lt;/code&gt;. That&amp;rsquo;s the good news. The bad news is that R&amp;rsquo;s &lt;a href=&#34;https://stat.ethz.ch/R-manual/R-devel/library/stats/html/formula.html&#34;&gt;formula interface&lt;/a&gt; takes some getting used to. A good source for learning the how to interpret formulas for the type of model we are considering here is the &lt;a href=&#34;https://cran.r-project.org/web/packages/lme4/vignettes/lmer.pdf&#34;&gt;vignette&lt;/a&gt; for the &lt;a href=&#34;https://cran.r-project.org/package=lme4&#34;&gt;&lt;code&gt;lme4&lt;/code&gt; package&lt;/a&gt; which implements Frequentist analog of this sort of model. (For an in depth discussion of the pros and cons of R formula interface see the Max Kuhn&amp;rsquo;s two posts on the subject: &lt;a href=&#34;https://rviews.rstudio.com/2017/02/01/the-r-formula-method-the-good-parts/&#34;&gt;pros here&lt;/a&gt; and &lt;a href=&#34;https://rviews.rstudio.com/2017/03/01/the-r-formula-method-the-bad-parts/&#34;&gt;cons here &lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can examine the prior default prior distributions that were selected for this model as follows:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;rstanarm::prior_summary(roc_model$stan)
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;## Priors for model &#39;roc_model$stan&#39; 
## ------
## Intercept (after predictors centered)
##   Specified prior:
##     ~ normal(location = 0, scale = 10)
##   Adjusted prior:
##     ~ normal(location = 0, scale = 0.38)
## 
## Coefficients
##   Specified prior:
##     ~ normal(location = [0,0], scale = [2.5,2.5])
##   Adjusted prior:
##     ~ normal(location = [0,0], scale = [0.094,0.094])
## 
## Auxiliary (sigma)
##   Specified prior:
##     ~ exponential(rate = 1)
##   Adjusted prior:
##     ~ exponential(rate = 26)
## 
## Covariance
##  ~ decov(reg. = 1, conc. = 1, shape = 1, scale = 1)
## ------
## See help(&#39;prior_summary.stanreg&#39;) for more details
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, that the model has been fit, we use the  &lt;code&gt;tidyposterior&lt;/code&gt; function &lt;code&gt;tidy()&lt;/code&gt; to extract the posterior samples from the &lt;code&gt;perf_mod()&lt;/code&gt; model object, examine the first few values and plot the posterior distributions for the three models being compared. (Note that you may also find the &lt;a href=&#34;https://cran.r-project.org/package=tidybayes&#34;&gt;&lt;code&gt;tidybayes&lt;/code&gt;&lt;/a&gt; package helpful in examining draws from the posterior distribution.)&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;roc_post &amp;lt;- tidy(roc_model)
glimpse(roc_post)
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;## Observations: 12,000
## Variables: 2
## $ model     &amp;lt;chr&amp;gt; &amp;quot;glm&amp;quot;, &amp;quot;glm&amp;quot;, &amp;quot;glm&amp;quot;, &amp;quot;glm&amp;quot;, &amp;quot;glm&amp;quot;, &amp;quot;glm&amp;quot;, &amp;quot;glm&amp;quot;, &amp;quot;glm&amp;quot;…
## $ posterior &amp;lt;dbl&amp;gt; 0.7934, 0.7905, 0.7919, 0.7891, 0.7889, 0.7887, 0.7867…
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Also, even though this is a relatively simple ANOVA type model, some model checking ought to be done to determine whether the model is good enough to be interpreted. This kind of checking can be done interactively with the &lt;code&gt;shinystan&lt;/code&gt; application that can be launched with the command:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;launch_shinystan(roc_model$stan)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The following screen capture from &lt;code&gt;shinystan&lt;/code&gt; shows the diagnostics for the intercept for the seventh fold of the cross-validation. Note that the mixing (variability from high to low) in the top left graph which shows a trace of the Markov chain for this parameter looks quite good.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;shinystan.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

&lt;p&gt;The vignette shows violin plots for each of the three models, but in order to make the comparison with the data plot above, I plot the distributions with histograms, kernel density, and rug plots.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;roc_post2 &amp;lt;- as.data.frame(roc_post) # get object of class data.frame
ggplot(roc_post2, aes(x = posterior, col = model, fill = model)) + 
  geom_histogram(aes(y=..density..), alpha=0.5, position=&amp;quot;identity&amp;quot;, bins = 100)+
  geom_density(alpha=.2) + xlim(min = .74, max = .86) +
  geom_rug()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;/post/2019-12-02-bayesian-model-comparison/index_files/figure-html/unnamed-chunk-11-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Notice that the distribution all look like pretty narrow, normal distributions. And although it sure looks like there is quite a bit of difference between the distributions of ROC values for the glm and nnet models, we can quantify the difference by setting up a &lt;em&gt;contrast&lt;/em&gt; computing the posterior difference in RMSE for the two models as &lt;code&gt;nnet&lt;/code&gt; - &lt;code&gt;glm&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;glm_v_nnet &amp;lt;- contrast_models(roc_model, &amp;quot;nnet&amp;quot;, &amp;quot;glm&amp;quot;)
head(glm_v_nnet)
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;##   difference model_1 model_2
## 1    0.04669    nnet     glm
## 2    0.04897    nnet     glm
## 3    0.05226    nnet     glm
## 4    0.05251    nnet     glm
## 5    0.05364    nnet     glm
## 6    0.05583    nnet     glm
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ideally, before beginning a modeling effort, one would have some idea about how different the models have to be in order to make any practical difference. Let&amp;rsquo;s suppose that there needs to be at least a 5% increase in ROC area between the two distributions for there to be a practical difference. We can compute this probability by setting the &lt;code&gt;size&lt;/code&gt; parameter to the &lt;code&gt;summary()&lt;/code&gt; function to 0.05.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;summary(glm_v_nnet, size = 0.05)
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;## # A tibble: 1 x 9
##   contrast probability   mean  lower  upper  size pract_neg pract_equiv
##   &amp;lt;chr&amp;gt;          &amp;lt;dbl&amp;gt;  &amp;lt;dbl&amp;gt;  &amp;lt;dbl&amp;gt;  &amp;lt;dbl&amp;gt; &amp;lt;dbl&amp;gt;     &amp;lt;dbl&amp;gt;       &amp;lt;dbl&amp;gt;
## 1 nnet vs…           1 0.0511 0.0459 0.0563  0.05         0       0.351
## # … with 1 more variable: pract_pos &amp;lt;dbl&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The column labeled &lt;em&gt;probability&lt;/em&gt; indicates that all of the density of the difference is positive. &lt;em&gt;mean&lt;/em&gt;, &lt;em&gt;lower&lt;/em&gt; and &lt;em&gt;upper&lt;/em&gt; give the mean of the posterior difference distribution, and the lower and upper bounds plotted. &lt;em&gt;pract_neg&lt;/em&gt; and &lt;em&gt;prac_pos&lt;/em&gt; report the result of a calculation that uses the &lt;em&gt;Highest Density Interval&lt;/em&gt; &lt;a href=&#34;https://easystats.github.io/bayestestR/reference/hdi.html&#34;&gt;HDI&lt;/a&gt;, the narrowest interval containing some prespecified percentage of the probability density curve, to determine the &lt;em&gt;Region of Practical Equivalence&lt;/em&gt;, &lt;a href=&#34;https://cran.r-project.org/web/packages/bayestestR/vignettes/region_of_practical_equivalence.html&#34;&gt;ROPE&lt;/a&gt;. &lt;em&gt;pract_neg&lt;/em&gt; indicates  that none of the density is below the interval of meaningful difference and &lt;em&gt;prac_pos&lt;/em&gt; indicates that 65% of the density is above the area of practical equivalence. The latter can be interpreted as saying that the probability that the two distributions are different for all practicable purposes is .65. Likewise, &lt;em&gt;pract_equiv&lt;/em&gt; means that there is a 35% chance that the two distributions are practically equivalent.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;ggplot(glm_v_nnet, size = .05)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;/post/2019-12-02-bayesian-model-comparison/index_files/figure-html/unnamed-chunk-14-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;contrast_models()&lt;/code&gt; function is also flexible enough to perform multiple simultaneous contrasts. Here, we set up to contrast both the glm model against the nnet model and the knn model at the 5% level&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;models_contrast &amp;lt;- contrast_models(roc_model, list(&amp;quot;knn&amp;quot;,&amp;quot;nnet&amp;quot;), list(&amp;quot;glm&amp;quot;, &amp;quot;glm&amp;quot;))
summary(models_contrast, size=.05)
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;## # A tibble: 2 x 9
##   contrast probability    mean   lower   upper  size pract_neg pract_equiv
##   &amp;lt;chr&amp;gt;          &amp;lt;dbl&amp;gt;   &amp;lt;dbl&amp;gt;   &amp;lt;dbl&amp;gt;   &amp;lt;dbl&amp;gt; &amp;lt;dbl&amp;gt;     &amp;lt;dbl&amp;gt;       &amp;lt;dbl&amp;gt;
## 1 knn vs …           0 -0.0377 -0.0430 -0.0325  0.05   0.00025       1.000
## 2 nnet vs…           1  0.0511  0.0459  0.0563  0.05   0             0.351
## # … with 1 more variable: pract_pos &amp;lt;dbl&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that first row of the table above and the following plot both indicate that there is no practical difference between the glm and knn models at the 5% level.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;ggplot(models_contrast, size = .05)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;/post/2019-12-02-bayesian-model-comparison/index_files/figure-html/unnamed-chunk-16-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Because the vignette also mentions &lt;a href=&#34;https://developers.google.com/machine-learning/crash-course/classification/accuracy&#34;&gt;accuracy&lt;/a&gt;, the fraction of correct predictions, we briefly repeat the above analysis using accuracy as the performance measure.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;acc &amp;lt;- precise_example %&amp;gt;%
  select(id, contains(&amp;quot;Accuracy&amp;quot;)) %&amp;gt;%
  setNames(tolower(gsub(&amp;quot;_Accuracy$&amp;quot;, &amp;quot;&amp;quot;, names(.)))) 
acc_model &amp;lt;- perf_mod(acc,seed = 2824)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The posterior distributions for accuracy are tightly clustered and indicate that the models yield different levels of performance for this measure.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;acc_post &amp;lt;- as.data.frame(tidy(acc_model)) # get object of class data.frame
ggplot(roc_post2, aes(x = posterior, col = model, fill = model)) + 
  geom_histogram(aes(y=..density..), alpha=0.5, position=&amp;quot;identity&amp;quot;, bins = 50) +
  geom_density(alpha=.2) + xlim(min = .74, max = .86) +
  geom_rug()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;/post/2019-12-02-bayesian-model-comparison/index_files/figure-html/unnamed-chunk-18-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;

&lt;p&gt;And, the &lt;code&gt;contrast_models()&lt;/code&gt; function that confirms the practical difference.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;acc_models_contrast &amp;lt;- contrast_models(acc_model, list(&amp;quot;knn&amp;quot;,&amp;quot;nnet&amp;quot;), list(&amp;quot;glm&amp;quot;, &amp;quot;glm&amp;quot;))
summary(acc_models_contrast, size=.05)
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;## # A tibble: 2 x 9
##   contrast probability    mean   lower   upper  size pract_neg pract_equiv
##   &amp;lt;chr&amp;gt;          &amp;lt;dbl&amp;gt;   &amp;lt;dbl&amp;gt;   &amp;lt;dbl&amp;gt;   &amp;lt;dbl&amp;gt; &amp;lt;dbl&amp;gt;     &amp;lt;dbl&amp;gt;       &amp;lt;dbl&amp;gt;
## 1 knn vs …           0 -0.239  -0.250  -0.229   0.05         1     0      
## 2 nnet vs…           1  0.0687  0.0583  0.0792  0.05         0     0.00175
## # … with 1 more variable: pract_pos &amp;lt;dbl&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;ggplot(acc_models_contrast, size = .05)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;/post/2019-12-02-bayesian-model-comparison/index_files/figure-html/unnamed-chunk-20-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Note that the nnet model comes out on top with respect to both ROC and accuracy, but there appears to be a practical differences in practical differences. A modeler who is primarily interested in accuracy as the performance metric would likely be more confident than a modeler who has selected ROC as the performance metric. This  example should serve as a warning not to engage in &lt;em&gt;performance measure hacking&lt;/em&gt;. Choose your performance measure before you compute anything.&lt;/p&gt;

&lt;p&gt;For additional reading, look here for a &lt;a href=&#34;https://static1.squarespace.com/static/51156277e4b0b8b2ffe11c00/t/5aec85718a922d93cc33bf56/1525450098182/Comparing+Models+Using+Resampling+and+Bayesian+Methods.pdf&#34;&gt;short presentation&lt;/a&gt; by Max Kuhn on the key &lt;code&gt;tidyposterior&lt;/code&gt; ideas, and see the the following papers for an in-depth look at the underlying theory:
&lt;a href=&#34;http://www.jmlr.org/papers/volume18/16-305/16-305.pdf&#34;&gt;Benavoli et al. (2017)&lt;/a&gt; and
&lt;a href=&#34;https://link.springer.com/content/pdf/10.3758%2Fs13423-016-1221-4.pdf&#34;&gt;Kruschke and Liddell (2017)&lt;/a&gt;. For Bayesian analysis in general, and more on the ROPE concept see: Kruschke  (2014). &lt;a href=&#34;https://www.elsevier.com/books/doing-bayesian-data-analysis/kruschke/978-0-12-405888-0&#34;&gt;Doing Bayesian Data Analysis, Second Edition: A Tutorial with R, JAGS, and Stan (2 edition)&lt;/a&gt;.&lt;/p&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2019/12/16/bayesian-model-comparison/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>In-Database Logistic Regression with R</title>
      <link>https://rviews.rstudio.com/2019/12/04/in-database-logisitc-regression-with-r/</link>
      <pubDate>Wed, 04 Dec 2019 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2019/12/04/in-database-logisitc-regression-with-r/</guid>
      <description>
        


&lt;p&gt;&lt;em&gt;Roland Stevenson is a data scientist and consultant who may be reached on &lt;a href=&#34;https://www.linkedin.com/in/roland-stevenson/&#34;&gt;Linkedin&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In a &lt;a href=&#34;https://rviews.rstudio.com/2018/11/07/in-database-xgboost-predictions-with-r/&#34;&gt;previous article&lt;/a&gt; we illustrated how to calculate xgboost model predictions in-database. This was &lt;a href=&#34;https://github.com/tidymodels/tidypredict/issues/40&#34;&gt;referenced&lt;/a&gt; and incorporated into &lt;a href=&#34;https://github.com/tidymodels/tidypredict&#34;&gt;tidypredict&lt;/a&gt;. After learning more about what the tidypredict team is up to, I discovered another tidyverse package called &lt;a href=&#34;https://github.com/tidymodels/modeldb&#34;&gt;modeldb&lt;/a&gt; that fits models in-database. It currently supports linear regression and k-means clustering, so I thought I would provide an example of how to do in-database logistic regression.&lt;/p&gt;
&lt;p&gt;Rather than focusing on the details of logistic regression, we will focus more on how we can use R and some carefully written SQL statements to iteratively minimize a cost function. We will also use the &lt;code&gt;condusco&lt;/code&gt; R package, which allows us to iterate through the results of a query easily.&lt;/p&gt;
&lt;div id=&#34;a-simple-logistic-regression-example&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;A Simple Logistic Regression Example&lt;/h2&gt;
&lt;p&gt;Let’s start with a simple logistic regression example. We’ll simulate an outcome &lt;span class=&#34;math inline&#34;&gt;\(y\)&lt;/span&gt; based on the fact that &lt;span class=&#34;math inline&#34;&gt;\(Pr(y=1) = \frac{e^{\beta x}}{1+e^{\beta x}}\)&lt;/span&gt;. Here &lt;span class=&#34;math inline&#34;&gt;\(\beta\)&lt;/span&gt; is a vector containing the coefficients we will later be estimating (including an intercept term). In the example below, our &lt;span class=&#34;math inline&#34;&gt;\(x\)&lt;/span&gt; values are uniform random values between -1 and 1.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;set.seed(1)

# the number of samples
n &amp;lt;- 1000

# uniform random on (-1,1)
x1 &amp;lt;- 2*runif(n)-1
x2 &amp;lt;- 2*runif(n)-1
x &amp;lt;- cbind(1, x1, x2)

# our betas
beta &amp;lt;- c(-1, -3.0, 5.0)

probs &amp;lt;- exp(beta %*% t(x))/(1+exp(beta %*% t(x)))

y &amp;lt;- rbinom(n,1,probs)

sim &amp;lt;- data.frame(id = seq(1:n), y = y, x1 = x1, x2 = x2)

mylogit &amp;lt;- glm(y ~ x1 + x2, data = sim, family = &amp;quot;binomial&amp;quot;)
summary(mylogit)
mylogit$coefficients&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;SQLA.png&#34; height = &#34;300&#34; width=&#34;100%&#34;&gt;&lt;/p&gt;
&lt;p&gt;As expected, the coefficients of our logistic model successfully approximate the parameters in our &lt;code&gt;beta&lt;/code&gt; vector.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;in-database-logistic-regression&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;In-database Logistic Regression&lt;/h2&gt;
&lt;p&gt;Now, let’s see if we can find a way to calculate these same coefficients in-database. In this example, we’re going to use Google BigQuery as our database, and we’ll use &lt;code&gt;condusco&lt;/code&gt;’s &lt;code&gt;run_pipeline_gbq&lt;/code&gt; function to iteratively run the functions we define later on. To do this, we’ll need to take care of some initial housekeeping:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(bigrquery)
library(whisker)
library(condusco)

# Uncomment and define your own config
# config &amp;lt;- list(
#   project = &amp;#39;&amp;lt;YOUR GBQ PROJECT&amp;gt;&amp;#39;,
#   dataset = &amp;#39;&amp;lt;YOUR GBQ DATASET&amp;gt;&amp;#39;,
#   table_prefix = &amp;#39;&amp;lt;A TABLE_PREFIX TO USE&amp;gt;&amp;#39;
# )

# a simple whisker.render helper function for our use-case
wr &amp;lt;- function(s, params=config){whisker.render(s,params)}

# put the simulated data in GBQ
insert_upload_job(
  project = wr(&amp;#39;{{{project}}}&amp;#39;),
  dataset = wr(&amp;#39;{{{dataset}}}&amp;#39;),
  table = &amp;quot;logreg_sim&amp;quot;,
  values = sim,
  write_disposition = &amp;quot;WRITE_TRUNCATE&amp;quot;
)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;SQL1.png&#34; height = &#34;75&#34; width=&#34;100%&#34;&gt;&lt;/p&gt;
&lt;p&gt;Now, we’ll create the pipelines to do the logistic regression. Please note that the code below is quite verbose. While all of it is needed for the code to work, we’ll just focus on understanding how a couple of steps work. Once we understand one step, the rest is pretty easy. Feel free to skip &lt;a href=&#34;#run-pipeline&#34;&gt;ahead&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;First, we create a pipeline that does two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;create a main table containing all of our global settings&lt;/li&gt;
&lt;li&gt;calls another pipeline (&lt;code&gt;log_reg_stack&lt;/code&gt;) with the global settings as inputs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Importantly, note that all of the parameters (eg. &lt;code&gt;{{{project}}}&lt;/code&gt;) are dynamically swapped out in the query below with the &lt;code&gt;wr&lt;/code&gt; function and the &lt;code&gt;params&lt;/code&gt; variables. So this pipeline dynamically creates a query based on the parameters passed to it. We will call this pipeline later to run the process.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;#
# Pipeline: log_reg
#
log_reg &amp;lt;- function(params){
  
  print (&amp;quot;log_reg&amp;quot;)

  query &amp;lt;- &amp;#39;
    CREATE OR REPLACE TABLE {{{dataset}}}.{{{table_prefix}}}_settings
    AS 
    SELECT
      &amp;quot;{{{project}}}&amp;quot; AS project,
      &amp;quot;{{{dataset}}}&amp;quot; AS dataset,
      &amp;quot;{{{data_table}}}&amp;quot; AS data_table,
      {{{max_steps}}} AS max_steps,
      {{{error_tol}}} AS error_tol,
      {{{learning_rate}}} AS learning_rate,
      &amp;quot;{{{id_column}}}&amp;quot;   AS id_column,
      &amp;quot;{{{label_column}}}&amp;quot; AS label_column,
      &amp;quot;{{{fieldnames}}}&amp;quot; AS fieldnames,
      &amp;quot;{{{constant_id}}}&amp;quot; AS constant_id,
      &amp;quot;{{{table_prefix}}}&amp;quot; AS table_prefix
  &amp;#39;
  
  query_exec(
    project = wr(&amp;#39;{{{project}}}&amp;#39;, params),
    query = wr(query, params),
    use_legacy_sql = FALSE
  )
  
  # Now run the log_reg_stack pipeline and pass the settings to it
  invocation_query &amp;lt;- &amp;#39;
    SELECT *
    FROM {{{dataset}}}.{{table_prefix}}_settings
  &amp;#39;
  run_pipeline_gbq(
    log_reg_stack,
    wr(invocation_query, params),
    wr(&amp;#39;{{{project}}}&amp;#39;, params),
    use_legacy_sql = FALSE
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above pipeline calls another pipeline, &lt;code&gt;log_reg_stack&lt;/code&gt;, which is defined below. &lt;code&gt;log_reg_stack&lt;/code&gt; creates a table with the field names that we will use in the logistic regression and then runs &lt;code&gt;log_reg_stack_field&lt;/code&gt; on each of the field names. Note that the &lt;code&gt;invocation_query&lt;/code&gt; below contains a query that results in one or more rows containing a field name. &lt;code&gt;run_pipeline_gbq&lt;/code&gt; takes the results and iterates over them, calling &lt;code&gt;log_reg_stack_field&lt;/code&gt; on each one. Finally, it creates the &lt;code&gt;_labels&lt;/code&gt; table and calls &lt;code&gt;log_reg_setup&lt;/code&gt;, passing it the results of the global settings query.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;#
# Pipeline: stack variables
#
log_reg_stack &amp;lt;- function(params){
  
  print (&amp;quot;log_reg_stack&amp;quot;)
  
  # Table: _fieldnames 
  query &amp;lt;- &amp;quot;
    CREATE OR REPLACE TABLE {{{dataset}}}.{{{table_prefix}}}_fieldnames
    AS
    SELECT TRIM(fieldname) AS fieldname
    FROM (
      SELECT split(fieldnames,&amp;#39;,&amp;#39;) AS fieldname
      FROM (
          SELECT &amp;#39;{{{fieldnames}}}&amp;#39; AS fieldnames
      )
    ), UNNEST(fieldname) as fieldname
    GROUP BY 1
  &amp;quot;
  
  query_exec(
    project = wr(&amp;#39;{{{project}}}&amp;#39;, params),
    query = wr(query, params),
    use_legacy_sql = FALSE
  )
  
  # Run _stack_field
  query &amp;lt;- &amp;quot;
    DROP TABLE IF EXISTS {{{dataset}}}.{{{table_prefix}}}_stacked
  &amp;quot;
  
  tryCatch({
    query_exec(
      project = wr(&amp;#39;{{{project}}}&amp;#39;, params),
      query = wr(query, params),
      use_legacy_sql = FALSE
    )},
    error = function(e){
      print(e)
  })
    
  invocation_query &amp;lt;- &amp;quot;
    SELECT
      a.fieldname AS fieldname,  
      b.*
    FROM (  
      SELECT fieldname  
      FROM {{{dataset}}}.{{{table_prefix}}}_fieldnames  
      GROUP BY fieldname
    ) a  
    CROSS JOIN (  
      SELECT *  
        FROM {{{dataset}}}.{{{table_prefix}}}_settings
    ) b
  &amp;quot;
  run_pipeline_gbq(
    log_reg_stack_field,
    wr(invocation_query, params),
    wr(&amp;#39;{{{project}}}&amp;#39;, params),
    use_legacy_sql = FALSE
  )
  
  # Table: _labels
  query &amp;lt;- &amp;quot;
    CREATE OR REPLACE TABLE {{{dataset}}}.{{{table_prefix}}}_labels
    AS
    SELECT
      {{{id_column}}} AS id,
      {{{label_column}}} AS label
    FROM {{{data_table}}}
  &amp;quot;
  
  query_exec(
    project = wr(&amp;#39;{{{project}}}&amp;#39;, params),
    query = wr(query, params),
    use_legacy_sql = FALSE
  )
  
  
  # Run _setup
  invocation_query &amp;lt;- &amp;quot;
    SELECT *  
      FROM {{{dataset}}}.{{{table_prefix}}}_settings
  &amp;quot;
  run_pipeline_gbq(
    log_reg_setup,
    wr(invocation_query, params),
    wr(&amp;#39;{{{project}}}&amp;#39;, params),
    use_legacy_sql = FALSE
  )
  
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;log_reg_stack_field&lt;/code&gt; and &lt;code&gt;log_reg_setup&lt;/code&gt; pipelines are not particularly interesting. They do the groundwork needed to allow the &lt;code&gt;log_reg_loop&lt;/code&gt; pipeline to iterate. The &lt;code&gt;_stacked&lt;/code&gt; table contains the feature names and their values, and the &lt;code&gt;_feature_stats&lt;/code&gt; and &lt;code&gt;features_stacked_vni&lt;/code&gt; tables contains normalized values used later. Finally, the &lt;code&gt;_fit_params&lt;/code&gt; table contains the value of the fit parameters that will be updated as we iteratively minimize the cost function in the loop. The &lt;code&gt;log_reg_setup&lt;/code&gt; pipeline ends by calling &lt;code&gt;log_reg_loop&lt;/code&gt;, passing it the results of the global settings query.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;log_reg_stack_field &amp;lt;- function(params){
  
  print (&amp;quot;log_reg_stack_field&amp;quot;)

  destination_table &amp;lt;- &amp;#39;{{{dataset}}}.{{{table_prefix}}}_stacked&amp;#39;

  query &amp;lt;- &amp;quot;
    SELECT {{{id_column}}} AS id,
      LTRIM(&amp;#39;{{{fieldname}}}&amp;#39;) AS feature_name,
      CAST({{{fieldname}}} AS FLOAT64) AS vi
    FROM {{{data_table}}}
  &amp;quot;
  
  query_exec(
    project = wr(&amp;#39;{{{project}}}&amp;#39;, params),
    query = wr(query, params),
    destination_table = wr(destination_table, params),
    use_legacy_sql = FALSE,
    write_disposition = &amp;#39;WRITE_APPEND&amp;#39;,
    create_disposition = &amp;#39;CREATE_IF_NEEDED&amp;#39;
  )
  
}


log_reg_setup &amp;lt;- function(params){
  
  print (&amp;quot;log_reg_setup&amp;quot;)
  
  query &amp;lt;- &amp;quot;
    CREATE OR REPLACE TABLE {{{dataset}}}.{{{table_prefix}}}_feature_stats
    AS
    SELECT feature_name,
      AVG(vi) AS mean,
      STDDEV(vi) AS stddev
    FROM {{{dataset}}}.{{{table_prefix}}}_stacked
    GROUP BY feature_name
  &amp;quot;
  
  query_exec(
    project = wr(&amp;#39;{{{project}}}&amp;#39;, params),
    query = wr(query, params),
    use_legacy_sql = FALSE
  )
  
  query &amp;lt;- &amp;quot;
    CREATE OR REPLACE TABLE {{{dataset}}}.{{{table_prefix}}}_features_stacked_vni
    AS
    SELECT
      a.id AS id,
      a.feature_name AS feature_name,
      CASE
        WHEN b.stddev &amp;gt; 0.0 THEN (vi - b.mean) / b.stddev
        ELSE vi - b.mean
      END AS vni
    FROM {{{dataset}}}.{{{table_prefix}}}_stacked a
    JOIN {{{dataset}}}.{{{table_prefix}}}_feature_stats b
      ON a.feature_name = b.feature_name
  &amp;quot;
  
  query_exec(
    project = wr(&amp;#39;{{{project}}}&amp;#39;, params),
    query = wr(query, params),
    use_legacy_sql = FALSE
  )
  
  query &amp;lt;- &amp;quot;
    INSERT INTO {{{dataset}}}.{{{table_prefix}}}_features_stacked_vni (id, feature_name, vni)     
    SELECT
      id,
      &amp;#39;{{{constant_id}}}&amp;#39; as feature_name,
      1.0 as vni
    FROM {{{dataset}}}.{{{table_prefix}}}_stacked
    GROUP BY 1,2,3
  &amp;quot;
  
  query_exec(
    project = wr(&amp;#39;{{{project}}}&amp;#39;, params),
    query = wr(query, params),
    use_legacy_sql = FALSE
  )
  
  query &amp;lt;- &amp;quot;
    CREATE OR REPLACE TABLE {{{dataset}}}.{{{table_prefix}}}_fit_params
    AS
    SELECT
      step,
      param_id,
      param_value,
      cost,
      stop,
      message
    FROM (
      SELECT 1 as step,
      feature_name as param_id,
      0.0 as param_value,
      1e6 as cost,
      false as stop,
      &amp;#39;&amp;#39; as message
      FROM {{{dataset}}}.{{{table_prefix}}}_stacked
      GROUP BY param_id
    ) UNION ALL (
      SELECT 1 as step,
      &amp;#39;{{{constant_id}}}&amp;#39; as param_id,
      0.0 as param_value,
      1e6 as cost,
      false as stop,
      &amp;#39;&amp;#39; as message
    )
  &amp;quot;
  
  query_exec(
    project = wr(&amp;#39;{{{project}}}&amp;#39;, params),
    query = wr(query, params),
    use_legacy_sql = FALSE
  )
  
  # Run _loop
  invocation_query &amp;lt;- &amp;quot;
    SELECT *  
      FROM {{{dataset}}}.{{{table_prefix}}}_settings
  &amp;quot;
  run_pipeline_gbq(
    log_reg_loop,
    wr(invocation_query, params),
    wr(&amp;#39;{{{project}}}&amp;#39;, params),
    use_legacy_sql = FALSE
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we’ll create a loop pipeline that will iteratively calculate the cost function and update the &lt;code&gt;_fit_params&lt;/code&gt; table with the latest update.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;#
# Pipeline: loop
#
log_reg_loop &amp;lt;- function(params){
  
  print (&amp;quot;log_reg_loop&amp;quot;)
  
  query &amp;lt;- &amp;quot;
    CREATE OR REPLACE TABLE {{{dataset}}}.{{{table_prefix}}}_x_dot_beta_i
    AS 
    SELECT
      a.id AS id,
      SUM(a.vni * b.param_value) AS x_dot_beta_i
    FROM {{{dataset}}}.{{{table_prefix}}}_features_stacked_vni a  
    RIGHT JOIN (
      SELECT param_id, param_value    
      FROM {{{dataset}}}.{{{table_prefix}}}_fit_params    
      WHERE STEP = (SELECT max(step) FROM {{{dataset}}}.{{{table_prefix}}}_fit_params)
    ) b
    ON a.feature_name = b.param_id
    GROUP BY 1
  &amp;quot;
  
  query_exec(
    project = wr(&amp;#39;{{{project}}}&amp;#39;, params),
    query = wr(query, params),
    use_legacy_sql = FALSE
  )
  
  query &amp;lt;- &amp;#39;
  INSERT INTO {{{dataset}}}.{{{table_prefix}}}_fit_params (step, param_id, param_value, cost, stop, message)
  SELECT  
    b.step + 1 as step,  
    b.param_id as param_id,  
    b.param_value - {{{learning_rate}}} * err as param_value,
    -1.0 * a.cost as cost,
    CASE
      WHEN ( abs((b.cost-(-1.0*a.cost))/b.cost) &amp;lt; {{{error_tol}}} ) OR (step+1 &amp;gt; {{{max_steps}}})  
        THEN true
      ELSE false  
      END AS stop,  
    CONCAT( &amp;quot;cost: &amp;quot;, CAST(abs((b.cost-(-1.0*a.cost))/b.cost) AS STRING), &amp;quot; error_tol: &amp;quot;, CAST({{{error_tol}}} AS STRING)) as message  
  FROM (  
    SELECT  
      param_id,  
      avg(err) as err,  
      avg(cost) as cost  
    FROM (  
      SELECT  
        a.id,
        param_id,
        (1.0/(1.0 + EXP(-1.0 * (c.x_dot_beta_i))) - CAST(label AS FLOAT64)) * vni as err,
        CAST(label AS FLOAT64) * LOG( 1.0/(1.0 + EXP(-1.0 * (c.x_dot_beta_i))) )   
          + (1.0-CAST(label AS FLOAT64))*(log(1.0 - (1.0/(1.0 + EXP(-1.0 * (c.x_dot_beta_i))))))  as cost
      FROM (  
        SELECT a.id as id,  
        b.param_id as param_id,
        a.vni as vni,
        b.param_value as param_value
        FROM {{{dataset}}}.{{{table_prefix}}}_features_stacked_vni a  
        JOIN (
          SELECT param_id, param_value    
          FROM {{{dataset}}}.{{{table_prefix}}}_fit_params    
          WHERE STEP = (SELECT max(step) FROM {{{dataset}}}.{{{table_prefix}}}_fit_params)
        ) b
        ON a.feature_name = b.param_id
        GROUP BY 1,2,3,4
      ) a
      JOIN {{{dataset}}}.{{{table_prefix}}}_labels b  
      ON a.id = b.id
      JOIN {{{dataset}}}.{{{table_prefix}}}_x_dot_beta_i c
      ON a.id = c.id
    )  
    GROUP BY param_id
  ) a
  JOIN (
    SELECT *
    FROM {{{dataset}}}.{{{table_prefix}}}_fit_params
    WHERE STEP = (SELECT max(step) FROM {{{dataset}}}.{{{table_prefix}}}_fit_params)
  ) b
  ON a.param_id = b.param_id
  &amp;#39;
  
  query_exec(
    project = wr(&amp;#39;{{{project}}}&amp;#39;, params),
    query = wr(query, params),
    use_legacy_sql = FALSE
  )
  
  
  # Loop or stop
  query &amp;lt;- &amp;quot;
      SELECT stop  AS stop
      FROM (
        SELECT *
          FROM {{{dataset}}}.{{{table_prefix}}}_fit_params
        ORDER BY step DESC
        LIMIT 1
      )
  &amp;quot;
  
  res &amp;lt;- query_exec(
    wr(query, params),
    wr(&amp;#39;{{{project}}}&amp;#39;, params),
    use_legacy_sql = FALSE
  )
  
  if(res$stop == FALSE){
    print(&amp;quot;stop == FALSE&amp;quot;)
    invocation_query &amp;lt;- &amp;#39;
      SELECT *
      FROM {{{dataset}}}.{{table_prefix}}_settings
    &amp;#39;
    run_pipeline_gbq(
      log_reg_loop,
      wr(invocation_query,  params),
      wr(&amp;#39;{{{project}}}&amp;#39;, params),
      use_legacy_sql = FALSE
    )
  }
  else {
    print(&amp;quot;stop == TRUE&amp;quot;)
    invocation_query &amp;lt;- &amp;#39;
      SELECT *
      FROM {{{dataset}}}.{{table_prefix}}_settings
    &amp;#39;
    run_pipeline_gbq(
      log_reg_done,
      wr(invocation_query,  params),
      wr(&amp;#39;{{{project}}}&amp;#39;, params),
      use_legacy_sql = FALSE
    )
  }
  
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And finally, a &lt;code&gt;log_reg_done&lt;/code&gt; pipeline that outputs the results:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;#
# Pipeline: done
#
log_reg_done &amp;lt;- function(params){
  
  print (&amp;quot;log_reg_done&amp;quot;)
  
  # Display results in norm&amp;#39;d coords
  query &amp;lt;- &amp;#39;
    SELECT &amp;quot;normalized coords parameters&amp;quot; as message,
      step,  
      param_id,  
      param_value 
    FROM {{{dataset}}}.{{{table_prefix}}}_fit_params
    WHERE step = (SELECT max(step) from {{{dataset}}}.{{{table_prefix}}}_fit_params)
  &amp;#39;
  
  res &amp;lt;- query_exec(
    wr(query, params),
    wr(&amp;#39;{{{project}}}&amp;#39;, params),
    use_legacy_sql = FALSE
  )
  
  print(res)
  
  # Display results in original coords
  query &amp;lt;- &amp;quot;
    CREATE OR REPLACE TABLE {{{dataset}}}.{{{table_prefix}}}_model_params_stacked
    AS 
    SELECT
      param_id,
      param_value_rescaled
    FROM (
      SELECT
        a.param_id AS param_id,
        a.param_value + b.constant_offset AS param_value_rescaled
      FROM (
        SELECT
          step,
          param_id,
          param_value
        FROM {{{dataset}}}.{{{table_prefix}}}_fit_params
        WHERE step = (SELECT max(step) from {{{dataset}}}.{{{table_prefix}}}_fit_params)
        AND param_id = &amp;#39;CONSTANT&amp;#39;
      ) a
      JOIN (
        SELECT
          step,
          &amp;#39;CONSTANT&amp;#39; as param_id,
          sum(-1.0*param_value*mean/stddev) as constant_offset
        FROM {{{dataset}}}.{{{table_prefix}}}_fit_params a
        JOIN {{{dataset}}}.{{{table_prefix}}}_feature_stats b
          ON a.param_id = b.feature_name
        WHERE step = (SELECT max(step) FROM {{{dataset}}}.{{{table_prefix}}}_fit_params)
        GROUP BY 1,2
      ) b
      ON a.param_id = b.param_id
    ) UNION ALL (
      SELECT
        param_id,
        param_value/stddev as param_value_rescaled
      FROM {{{dataset}}}.{{{table_prefix}}}_fit_params a
      JOIN {{{dataset}}}.{{{table_prefix}}}_feature_stats b
      ON a.param_id = b.feature_name
      WHERE step = (SELECT max(step) FROM {{{dataset}}}.{{{table_prefix}}}_fit_params)
      GROUP BY 1,2
    )
  &amp;quot;
  
  res &amp;lt;- query_exec(
    wr(query, params),
    wr(&amp;#39;{{{project}}}&amp;#39;, params),
    use_legacy_sql = FALSE
  )
  
  print(res)
  
  
  # transpose the _model_params_stacked table
  invocation_query &amp;lt;- &amp;#39;
    SELECT
      a.list,
      b.*
    FROM (
      SELECT CONCAT(&amp;quot;[&amp;quot;, STRING_AGG(CONCAT(&amp;quot;{\\&amp;quot;val\\&amp;quot;: \\&amp;quot;&amp;quot;,TRIM(fieldname), &amp;quot;\\&amp;quot;}&amp;quot;)), &amp;quot;]&amp;quot;) AS list
      FROM rstevenson.indb_logreg_001_fieldnames
    ) a
    CROSS JOIN (
      SELECT *
        FROM rstevenson.indb_logreg_001_settings
    ) b
  &amp;#39;
  
  run_pipeline_gbq(
    log_reg_model_params,
    wr(invocation_query, config),
    wr(&amp;#39;{{{project}}}&amp;#39;, config),
    use_legacy_sql = FALSE
  )
  
  print(&amp;quot;DONE&amp;quot;)
  
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our last pipeline, called at the end of the above pipeline, will transpose the stacked model params. In other words, it will output the parameters of the model in separate columns:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;log_reg_model_params &amp;lt;- function(params){
  
  query &amp;lt;- &amp;quot;
    CREATE OR REPLACE TABLE {{{dataset}}}.{{{table_prefix}}}_model_params
    AS 
    SELECT
    {{#list}}
      MAX(CASE WHEN param_id=&amp;#39;{{val}}&amp;#39; THEN param_value_rescaled END ) AS {{val}},
    {{/list}}
    MAX(CASE WHEN param_id=&amp;#39;{{constant_id}}&amp;#39; THEN param_value_rescaled END ) AS {{constant_id}}
    FROM {{{dataset}}}.{{{table_prefix}}}_model_params_stacked
  ;&amp;quot;

  res &amp;lt;- query_exec(
    wr(query, params),
    wr(&amp;#39;{{{project}}}&amp;#39;, params),
    use_legacy_sql = FALSE
  )
  
  print(res)
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;run-pipeline&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Running the pipeline&lt;/h2&gt;
&lt;p&gt;We are now ready to run the &lt;code&gt;log_reg&lt;/code&gt; pipeline. We’ll set up the invocation query with all of our global parameters. These will be stored in the &lt;code&gt;_settings&lt;/code&gt; table and then, after stacking and setup, the pipeline will iterate through the loop to calculate the logistic regression coefficients.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# Run the log_reg pipeline with the following params (2D test)
invocation_query &amp;lt;- &amp;#39;
  SELECT
  &amp;quot;{{{project}}}&amp;quot; as project,
  &amp;quot;{{{dataset}}}&amp;quot; as dataset,
  &amp;quot;{{{table_prefix}}}&amp;quot; as table_prefix,
  &amp;quot;{{{dataset}}}.logreg_sim&amp;quot; as data_table,        
  &amp;quot;25&amp;quot;  as max_steps,
  &amp;quot;1e-6&amp;quot; as error_tol,
  &amp;quot;6.0&amp;quot;  as learning_rate,
  &amp;quot;id&amp;quot;   as id_column,
  &amp;quot;y&amp;quot;  as label_column,
  &amp;quot;x1, x2&amp;quot;  as fieldnames,
  &amp;quot;CONSTANT&amp;quot; as constant_id
&amp;#39;

cat(wr(invocation_query, config))

query_exec(wr(invocation_query, config), project=config$project, use_legacy_sql = FALSE)

run_pipeline_gbq(
  log_reg,
  wr(invocation_query, config),
  project = wr(&amp;#39;{{{project}}}&amp;#39;, config),
  use_legacy_sql = FALSE
)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After running the above, we should be able to query the table that holds the fitted parameters:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;  query &amp;lt;- &amp;quot;
    SELECT *
    FROM {{{dataset}}}.{{{table_prefix}}}_model_params
  ;&amp;quot;

  query_exec(
    wr(query),
    wr(&amp;#39;{{{project}}}&amp;#39;),
    use_legacy_sql = FALSE
  )&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;SQL2.png&#34; height = &#34;75&#34; width=&#34;100%&#34;&gt;&lt;/p&gt;
&lt;p&gt;As expected, these results are pretty close to our original &lt;code&gt;beta&lt;/code&gt; values.&lt;/p&gt;
&lt;p&gt;Please keep in mind that this is not ready to be released into the wild. Further improvements include modifications to deal with categorical variables, output describing whether a logistic fit is statistically significant for a particular parameter, and options for controlling step-sizes. But it does show the concept of how an iterative process like logistic regression can be done while using the database to maintain state.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;prediction&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Prediction&lt;/h2&gt;
&lt;p&gt;Now that we have fit the logistic regression model and the model is stored in the database, we can predict values using the model. We just need a prediction pipeline:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;#
# Pipeline: predict
#
log_reg_predict &amp;lt;- function(params){
  
  query &amp;lt;- &amp;#39;
  SELECT
    1/(1+exp(-1.0*(CONSTANT + {{#list}}a.{{val}}*b.{{val}} + {{/list}} + 0))) as probability
  FROM {{{dataset}}}.{{{table_prefix}}}_model_params a
  CROSS JOIN {{{data_table}}} b
  ORDER BY {{{id_column}}}
  &amp;#39;
  
  res &amp;lt;- query_exec(
    wr(query, params),
    wr(&amp;#39;{{{project}}}&amp;#39;, params),
    use_legacy_sql = FALSE
  )
  
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that the above uses &lt;code&gt;whisker&lt;/code&gt; to calculate the dot product &lt;span class=&#34;math inline&#34;&gt;\(x\beta\)&lt;/span&gt; by expanding a JSON-formatted array of field names into &lt;code&gt;{{#list}}a.{{val}}*b.{{val}} + {{/list}}&lt;/code&gt; code. In the code below, we will create a JSON-formatted array of field names. Now let’s run the predictions:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# Run the prediction pipeline with the following params
invocation_query &amp;lt;- &amp;#39;
  SELECT
    &amp;quot;{{{project}}}&amp;quot; as project,
    &amp;quot;{{{dataset}}}&amp;quot; as dataset,
    &amp;quot;{{{table_prefix}}}&amp;quot; as table_prefix,
    &amp;quot;{{{dataset}}}.logreg_sim&amp;quot; as data_table,
    &amp;quot;id&amp;quot; as id_column,
    CONCAT(&amp;quot;[&amp;quot;, STRING_AGG(CONCAT(&amp;quot;{\\&amp;quot;val\\&amp;quot;: \\&amp;quot;&amp;quot;,TRIM(fieldname), &amp;quot;\\&amp;quot;}&amp;quot;)), &amp;quot;]&amp;quot;) AS list
  FROM {{{dataset}}}.{{{table_prefix}}}_fieldnames
&amp;#39;

predictions &amp;lt;- run_pipeline_gbq(
  log_reg_predict,
  wr(invocation_query, config),
  project = wr(&amp;#39;{{{project}}}&amp;#39;, config),
  use_legacy_sql = FALSE
)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s test the rounded predictions to see how well they approximate the outcomes:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# inspect first 5 true probs vs. predicted probabilities
head(probs[1:5])
head(predictions[[1]]$probability[1:5])

# mean relative error between true probs and predicted probabilities 
mean((abs(probs-predictions[[1]]$probability))/probs)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;SQL3.png&#34; height = &#34;200&#34; width=&#34;100%&#34;&gt;&lt;/p&gt;
&lt;p&gt;Our model-based logistic regression model predicts the true probabilities with a mean relative error of about 7%.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;next-steps&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Next steps&lt;/h2&gt;
&lt;p&gt;We have shown how to train and store a logistic regression model in a database. We can then predict outcomes given features that are also stored in the database without having to move data back and forth to a prediction server. In this particular example, it would likely be much faster to move the data to a computer and run the predictions there. However, certain use cases exist where in-database modeling could be an avenue for consideration. Further, since logistic models are fundamental to many types of tree and forest predictors, in-database logistic regression would be a necessary step in developing in-database tree methods. It remains to be seen if this approach can be easily translated into the tidyverse modeldb package.&lt;/p&gt;
&lt;/div&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2019/12/04/in-database-logisitc-regression-with-r/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>A comparison of methods for predicting clothing classes using the Fashion MNIST dataset in RStudio and Python (Part 1)</title>
      <link>https://rviews.rstudio.com/2019/11/11/a-comparison-of-methods-for-predicting-clothing-classes-using-the-fashion-mnist-dataset-in-rstudio-and-python-part-1/</link>
      <pubDate>Mon, 11 Nov 2019 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2019/11/11/a-comparison-of-methods-for-predicting-clothing-classes-using-the-fashion-mnist-dataset-in-rstudio-and-python-part-1/</guid>
      <description>
        


&lt;p&gt;&lt;em&gt;Florianne Verkroost is a PhD candidate at Nuffield College at the University of Oxford. With a passion for data science and a background in mathematics and econometrics. She applies her interdisciplinary knowledge to computationally address societal problems of inequality.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In this series of blog posts, I will compare different machine and deep learning methods to predict clothing categories from images using the Fashion MNIST data. In this first blog of the series, we will explore and prepare the data for analysis. I will also show you how to predict the clothing categories of the Fashion MNIST data using my go-to model: an artificial neural network. To show you how to use one of RStudio’s incredible features to run Python from RStudio, I build my neural network in Python using the code in &lt;a href=&#34;https://github.com/fverkroost/RStudio-Blogs/blob/master/simple_neural_network_fashion_mnist.py&#34;&gt;this Python script&lt;/a&gt; or &lt;a href=&#34;https://github.com/fverkroost/RStudio-Blogs/blob/master/simple_neural_network_fashion_mnist.ipynb&#34;&gt;this Jupyter notebook&lt;/a&gt; on my Github. In the &lt;a href=&#34;https://github.com/fverkroost/RStudio-Blogs/blob/master/machine_learning_fashion_mnist_post2.Rmd&#34;&gt;second blog post&lt;/a&gt;, we will experiment with tree-based methods (single tree, random forests and boosting) and support vector machines to see whether we can beat the neural network in terms of performance. As Python cannot be run in this blog post, I will walk you through the results from this script produced earlier, but if you would also like to see how to embed Python code and results in R Markdown files, check out &lt;a href=&#34;https://github.com/fverkroost/RStudio-Blogs/blob/master/machine_learning_fashion_mnist_post1_embedded.Rmd&#34;&gt;this Markdown file on my Github&lt;/a&gt;! The R code used for this blog is also included on my &lt;a href=&#34;https://github.com/fverkroost/RStudio-Blogs/blob/master/machine_learning_fashion_mnist_post1.R&#34;&gt;Github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To start, we first set our seed to make sure the results are reproducible.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;set.seed(1234)&lt;/code&gt;&lt;/pre&gt;
&lt;div id=&#34;importing-and-exploring-the-data&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Importing and exploring the data&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;keras&lt;/code&gt; package contains the Fashion MNIST data, so we can easily import the data into RStudio from this package directly after installing it from Github and loading it.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(devtools)
install.packages(&amp;quot;keras&amp;quot;)
#devtools::install_github(&amp;quot;rstudio/keras&amp;quot;)
library(keras)        
install_keras()  
fashion_mnist &amp;lt;- keras::dataset_fashion_mnist()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The resulting object named &lt;code&gt;fashion_mnist&lt;/code&gt; is a nested list, consisting of lists &lt;code&gt;train&lt;/code&gt; and &lt;code&gt;test&lt;/code&gt;. Each of these lists in turn consists of arrays &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt;. To look at the dimensions of these elements, we recursively apply the &lt;code&gt;dim()&lt;/code&gt; function to the &lt;code&gt;fashion_mnist&lt;/code&gt; list.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;rapply(fashion_mnist, dim)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;train.x1 train.x2 train.x3  train.y  test.x1  test.x2  test.x3   test.y 
   60000       28       28    60000    10000       28       28    10000 &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From the result, we observe that the &lt;code&gt;x&lt;/code&gt; array in the training data contains 28 matrices each of 60000 rows and 28 columns, or in other words 60000 images each of 28 by 28 pixels. The &lt;code&gt;y&lt;/code&gt; array in the training data contains 60000 labels for each of the images in the &lt;code&gt;x&lt;/code&gt; array of the training data. The test data has a similar structure but only contains 10000 images rather than 60000. For simplicity, we rename these lists elements to something more intuitive (where &lt;code&gt;x&lt;/code&gt; now represents images and &lt;code&gt;y&lt;/code&gt; represents labels):&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;c(train.images, train.labels) %&amp;lt;-% fashion_mnist$train
c(test.images, test.labels) %&amp;lt;-% fashion_mnist$test&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every image is captured by a 28 by 28 matrix, where entry [i, j] represents the opacity of that pixel on an integer scale from 0 (white) to 255 (black). The labels consist of integers between zero and nine, each representing a unique clothing category. As the category names are not contained in the data itself, we have to store and add them manually. Note that the categories are evenly distributed in the data.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;cloth_cats = data.frame(category = c(&amp;#39;Top&amp;#39;, &amp;#39;Trouser&amp;#39;, &amp;#39;Pullover&amp;#39;, &amp;#39;Dress&amp;#39;, &amp;#39;Coat&amp;#39;,  
                                     &amp;#39;Sandal&amp;#39;, &amp;#39;Shirt&amp;#39;, &amp;#39;Sneaker&amp;#39;, &amp;#39;Bag&amp;#39;, &amp;#39;Boot&amp;#39;), 
                                     label = seq(0, 9))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To get an idea of what the data entail and look like, we plot the first ten images of the test data. To do so, we first need to reshape the data slightly such that it becomes compatible with &lt;code&gt;ggplot2&lt;/code&gt;. We select the first ten test images, convert them to data frames, rename the columns into digits 1 to 28, create a variable named &lt;code&gt;y&lt;/code&gt; with digits 1 to 28 and then we melt by variable &lt;code&gt;y&lt;/code&gt;. We need package &lt;code&gt;reshape2&lt;/code&gt; to access the &lt;code&gt;melt()&lt;/code&gt; function. This results in a 28 times 28 equals 784 by 3 (y pixels (= y), x pixels (= variable) and the opacity (= value)) data frame. We bind these all together by rows using the &lt;code&gt;rbind.fill()&lt;/code&gt; function from the &lt;code&gt;plyr&lt;/code&gt; package and add a variable &lt;code&gt;Image&lt;/code&gt;, which is a unique string repeated 784 times for each of the nine images containing the image number and corresponding test set label.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(reshape2)
library(plyr)
subarray &amp;lt;- apply(test.images[1:10, , ], 1, as.data.frame)
subarray &amp;lt;- lapply(subarray, function(df){
  colnames(df) &amp;lt;- seq_len(ncol(df))
  df[&amp;#39;y&amp;#39;] &amp;lt;- seq_len(nrow(df))
  df &amp;lt;- melt(df, id = &amp;#39;y&amp;#39;)
  return(df)
})
plotdf &amp;lt;- rbind.fill(subarray)
first_ten_labels &amp;lt;- cloth_cats$category[match(test.labels[1:10], cloth_cats$label)]
first_ten_categories &amp;lt;- paste0(&amp;#39;Image &amp;#39;, 1:10, &amp;#39;: &amp;#39;, first_ten_labels)
plotdf[&amp;#39;Image&amp;#39;] &amp;lt;- factor(rep(first_ten_categories, unlist(lapply(subarray, nrow))), 
                          levels = unique(first_ten_categories))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We then plot these first ten test images using package &lt;code&gt;ggplot2&lt;/code&gt;. Note that we reverse the scale of the y-axis because the original dataset contains the images upside-down. We further remove the legend and axis labels and change the tick labels.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(ggplot2)&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;ggplot() + 
  geom_raster(data = plotdf, aes(x = variable, y = y, fill = value)) + 
  facet_wrap(~ Image, nrow = 2, ncol = 5) + 
  scale_fill_gradient(low = &amp;quot;white&amp;quot;, high = &amp;quot;black&amp;quot;, na.value = NA) + 
  theme(aspect.ratio = 1, legend.position = &amp;quot;none&amp;quot;) + 
  labs(x = NULL, y = NULL) + 
  scale_x_discrete(breaks = seq(0, 28, 7), expand = c(0, 0)) + 
  scale_y_reverse(breaks = seq(0, 28, 7), expand = c(0, 0))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;/post/2019-10-31-a-comparison-of-methods-for-predicting-clothing-classes-using-the-fashion-mnist-dataset-in-rstudio-and-python-part-1/index_files/figure-html/unnamed-chunk-8-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;data-preparation&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Data Preparation&lt;/h2&gt;
&lt;p&gt;Next, it’s time to start the more technical work of predicting the labels from the image data. First, we need to reshape our data to convert it from a multidimensional array into a two-dimensional matrix. To do so, we vectorize each 28 by 28 matrix into a column of length 784, and then we bind the columns for all images on top of each other, finally taking the transpose of the resulting matrix. This way, we can convert a 28 by 28 by 60000 array into a 60000 by 784 matrix. We also normalize the data by dividing between the maximum opacity of 255.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;train.images &amp;lt;- data.frame(t(apply(train.images, 1, c))) / max(fashion_mnist$train$x)
test.images &amp;lt;- data.frame(t(apply(test.images, 1, c))) / max(fashion_mnist$train$x)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We also create two data frames that include all training and test data (images and labels), respectively.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;pixs &amp;lt;- 1:ncol(fashion_mnist$train$x)^2
names(train.images) &amp;lt;- names(test.images) &amp;lt;- paste0(&amp;#39;pixel&amp;#39;, pixs)
train.labels &amp;lt;- data.frame(label = factor(train.labels))
test.labels &amp;lt;- data.frame(label = factor(test.labels))
train.data &amp;lt;- cbind(train.labels, train.images)
test.data &amp;lt;- cbind(test.labels, test.images)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;artificial-neural-network&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Artificial Neural Network&lt;/h2&gt;
&lt;p&gt;Now, let’s continue by building a simple neural network model to predict our clothing categories. Neural networks are artificial computing systems that were built with human neural networks in mind. Neural networks contain nodes, which transmit signals amongst one another. Usually the input at each node is a number, which is transformed according to a non-linear function of the input and weights, the latter being the parameters that are tuned while training the model. Sets of neurons are collected in different layers; neural networks are referred to as ‘deep’ when they contain at least two hidden layers. If you’re not familiar with artificial neural networks, then &lt;a href=&#34;http://neuralnetworksanddeeplearning.com/index.html&#34;&gt;this free online book&lt;/a&gt; is a good source to start learning about them.&lt;/p&gt;
&lt;p&gt;In this post, I will show you how artificial neural networks with different numbers of hidden layers compare, and I will also compare these networks to a convolutional network, which often performs better in the case of visual imagery. I will show you some basic models and how to code these, but will not spend too much time on tuning neural networks, for example when it comes to choosing the right amount of hidden layers or the number of nodes in each hidden layer. In essence, what it comes down to is that these parameters largely depend on your data structure, magnitude and complexity. The more hidden layers one adds, the more complex non-linear relationships can be modelled. Often, in my experience, adding hidden layers to a neural network increases their performance up to a certain number of layers, after which the increase becomes non-significant while the computational requirements and interpretation become more infeasible. It is up to you to play around a bit with your specific data and test how this trade-off works.&lt;/p&gt;
&lt;p&gt;Although neural networks can easily built in RStudio using TensorFlow and Keras, I really want to show you one of the incredible features of RStudio where you can run Python in RStudio. This can be done in two ways: either we choose “Terminal” on the top of the output console in RStudio and run Python via Terminal, or we use the base &lt;code&gt;system2()&lt;/code&gt; function to run Python in RStudio.&lt;/p&gt;
&lt;p&gt;For the second option, to use the &lt;code&gt;system2()&lt;/code&gt; command, it’s important to first check what version of Python should be used. You can check which versions of Python are installed on your machine by running &lt;code&gt;python --version&lt;/code&gt; in Terminal. Note that with RStudio 1.1 (1.1.383 or higher), you can run in Terminal directly from RStudio on the “Terminal” tab. You can also run &lt;code&gt;python3 --version&lt;/code&gt; to check if you have Python version 3 installed. On my machine, &lt;code&gt;python --version&lt;/code&gt; and &lt;code&gt;python3 --version&lt;/code&gt; return Python 2.7.16 and Python 3.7.0, respectively. You can then run &lt;code&gt;which python&lt;/code&gt; (or &lt;code&gt;which python3&lt;/code&gt; if you have Python version 3 installed) in Terminal, which will return the path where Python is installed. In my case, these respective commands return &lt;code&gt;/usr/bin/python&lt;/code&gt; and &lt;code&gt;/Library/Frameworks/Python.framework/Versions/3.7/bin/python3&lt;/code&gt;. As I will make use of Python version 3, I specify the latter as the path to Python in the &lt;code&gt;use_python()&lt;/code&gt; function from the &lt;code&gt;reticulate&lt;/code&gt; package. We can check whether the desired version of Python is used by using the &lt;code&gt;sys&lt;/code&gt; package from Python. Just make sure to change the path in the code below to what version of Python you desire using and where that version in installed.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(reticulate)
use_python(python = &amp;#39;/Library/Frameworks/Python.framework/Versions/3.7/bin/python3&amp;#39;)&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;sys &amp;lt;- import(&amp;quot;sys&amp;quot;)
sys$version&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we’ve specified the correct version of Python to be used, we can run our Python script from RStudio using the &lt;code&gt;system2()&lt;/code&gt; function. This function also takes an argument for the version of Python used, which in my case is Python version 3. If you are using an older version of Python, make sure to change &lt;code&gt;&amp;quot;python3&amp;quot;&lt;/code&gt; in the command below to &lt;code&gt;&amp;quot;python2&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;python_file &amp;lt;- &amp;quot;simple_neural_network_fashion_mnist.py&amp;quot;
system2(&amp;quot;python3&amp;quot;, args = c(python_file), stdout = NULL, stderr = &amp;quot;&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The source code used to build and fit the neural networks from the above script can be found in &lt;a href=&#34;https://github.com/fverkroost/RStudio-Blogs/blob/master/simple_neural_network_fashion_mnist.py&#34;&gt;this Python script&lt;/a&gt; or &lt;a href=&#34;https://github.com/fverkroost/RStudio-Blogs/blob/master/simple_neural_network_fashion_mnist.ipynb&#34;&gt;this Jupyter notebook&lt;/a&gt; on my Github.. In this post, I will walk you through the results from this script produced earlier, but if you would also like to see how to embed Python code and results in R Markdown files, check out &lt;a href=&#34;https://github.com/fverkroost/RStudio-Blogs/blob/master/machine_learning_fashion_mnist_post1_embedded.Rmd&#34;&gt;this file on my Github&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;I will now guide you step by step through the script called in the command above. First, we load the required packages in Python and set the session seed for replicability.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;screen_shots_python_code/pic1.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;We then load the fashion MNIST data from &lt;code&gt;keras&lt;/code&gt; and we normalize the data by dividing by maximum opacity of 255.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;screen_shots_python_code/pic2.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;screen_shots_python_code/pic3.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;We start by building a simple neural network containing one hidden layer. Note that as here we use the untransformed but normalized data, we need to flatten the 28 by 28 pixels input first. We add one hidden densely-connected layer which performs &lt;code&gt;output = relu(dot(input, kernel) + bias)&lt;/code&gt;, where the rectified linear unit (&lt;code&gt;relu&lt;/code&gt;) activation function has been proven to work well. We set the number of nodes equal to 128, because this seems to work well in our case. The number of nodes could essentially be any of the numbers 32, 64, 128, 256 and 512, as these are in a sequence of multiples between the number of nodes in the output (= 10) and input (= 784) layers. The &lt;code&gt;softmax&lt;/code&gt; layer then assigns predicted probabilities to each of the ten clothing categories, which is also why there are ten nodes in this layer.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;screen_shots_python_code/pic4.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;After building the neural network, we compile it. We specify &lt;code&gt;sparse_categorical_crossentropy&lt;/code&gt; as the loss function, which is suitable for categorical multi-class responses. The optimizer controls the learning rate; &lt;code&gt;adam&lt;/code&gt; (adaptive moment estimation) is similar to classical stochastic gradient descent and usually a safe choice for the optimizer. We set our metric of interest to be the accuracy, or the percentage of correctly classified images. Hereafter, we fit the model onto our training data set using ten iterations through the training data (“epochs”). Here, 70% is used for training and 30% is used for validation.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;screen_shots_python_code/pic5.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Next, we print the results of the model in terms of training and testing loss and accuracy.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;screen_shots_python_code/pic6.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;We can see that the neural network with one hidden layer already performs relatively well with a test accuracy of 87.09%. However, it seems like we are slightly overfitting (i.e. the model is fitted too well to a particular data set and therefore does not well extend to other data sets), as the training set accuracy (88.15%) is slightly higher than the test set accuracy. There are several ways to avoid overfitting in neural networks, such as simplifying our model by reducing the number of hidden layers and neurons, adding dropout layers that randomly remove some of the connections between layers, and early stopping when validation loss starts to increase. Later on in this post, I will demonstrate some of these methods to you. For further reading, I personally like &lt;a href=&#34;https://towardsdatascience.com/dont-overfit-how-to-prevent-overfitting-in-your-deep-learning-models-63274e552323&#34;&gt;this&lt;/a&gt; and &lt;a href=&#34;https://keras.rstudio.com/articles/tutorial_overfit_underfit.html&#34;&gt;this&lt;/a&gt; post showing how to avoid overfitting when building neural networks using &lt;code&gt;keras&lt;/code&gt;. Instead, to see whether a deep neural network performs better at predicting clothing categories, we build a neural network with three hidden layers in a similar way as before.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;screen_shots_python_code/pic7.png&#34; /&gt;
&lt;img src=&#34;screen_shots_python_code/pic8.png&#34; /&gt;
&lt;img src=&#34;screen_shots_python_code/pic9.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;It seems like the model with two additional layers does not perform better than the previous one with only one hidden layer, given that both the training (87.42%) and test set (86.03%) accuracies are lower and the loss (38.49) is higher. Let’s try whether adding another five hidden layers improves model performance, or whether we can include that increasing model complexity does not improve performance.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;screen_shots_python_code/pic10.png&#34; /&gt;
&lt;img src=&#34;screen_shots_python_code/pic11.png&#34; /&gt;
&lt;img src=&#34;screen_shots_python_code/pic12.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The model with eight hidden layers performs best in terms of training (88.21%) and test (87.58%) accuracy as well as loss (36.12). Nevertheless, the difference in performance between the first model with one hidden layer and the current model with eight hidden layers is only quite small. Although it seems that with so many hidden layers, we can model additional complexity that improves the accuracy of the model, we must ask ourselves whether increasing model complexity at the cost of interpretability and computational feasibility is worth this slight improvement in accuracy and loss.&lt;/p&gt;
&lt;p&gt;Now that we have seen how the number of hidden layers affects model performance, let’s try and see whether increasing the number of epochs (i.e. the number of times the model iterates through the training data) from ten to fifty improves the performance of our first neural network with one hidden layer.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;screen_shots_python_code/pic13.png&#34; /&gt;
&lt;img src=&#34;screen_shots_python_code/pic14.png&#34; /&gt;
&lt;img src=&#34;screen_shots_python_code/pic15.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The three-layer model trained with fifty epochs has the highest train (89.32%) and test (88.68%) accuracies we have seen so far. However, the loss (54.73) is also about a third larger than we have seen before. Additionally, the model is also less time-efficient, given that the increase in accuracy is not substantial but the model takes significantly longer to fit. To better understand the trade-off between minimizing loss and maximizing accuracy, we plot model loss and accuracy over the number of epochs for the training and cross-validation data.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;screen_shots_python_code/pic16.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;We observe that for the training data, loss decreases to zero while accuracy increases to one, as a result of overfitting. This is why we also check how the model performs on the cross-validation data, for which we observe that loss increases with the number of epochs while accuracy remains relatively stable. Using this figure, we can select an “optimal” number of epochs such that accuracy is maximized while loss is minimized. Looking at the cross-validation data accuracy, we see that the accuracy peak lays at around 20 epochs, for which loss is approximately 0.4. However, similar accuracies but much lower losses and modelling time are achieved with around 6 and 12 epochs, and so we might rather choose to train our model with around 6 or 20 epochs.&lt;/p&gt;
&lt;p&gt;Regarding the model output, the predictions returned are probabilities per class or clothing category. We can calculate the majority vote by taking class that has the maximum of predicted probabilities of all classes. We can print the first ten elements of the &lt;code&gt;majority_vote&lt;/code&gt; dictionary, which we can obtain as follows:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;screen_shots_python_code/pic17.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;All except the fifth (number 4) prediction are correct. In the fifth prediction, a shirt (category 6) is being misclassified as a top (category 0).&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;convolutional-neural-network&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Convolutional Neural Network&lt;/h2&gt;
&lt;p&gt;I also wanted to show you how to build a convolutional neural network and compare its performance to the neural networks presented earlier, mostly because convolutional neural networks have generally been shown to perform better on visual image data. Essentially, what happens in a convolutional neural network is that a smaller matrix (the “filter matrix” or “kernel”) slides over the full image matrix, moving pixel by pixel, multiplies the filter matrix with the part of the full image matrix covered by the filter matrix on that moment, sums up these values and then repeats this until the full image matrix has been covered. For a more extensive explanation on how convolutional neural networks, I refer you to &lt;a href=&#34;https://towardsdatascience.com/a-comprehensive-guide-to-convolutional-neural-networks-the-eli5-way-3bd2b1164a53&#34;&gt;this page&lt;/a&gt; or &lt;a href=&#34;https://medium.com/@RaghavPrabhu/understanding-of-convolutional-neural-network-cnn-deep-learning-99760835f148&#34;&gt;this page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As we need to prepare our data slightly differently for a convolutional neural network, we reload the data and reshape the images to “flatten” them. The last “1” in the reshape dimensions stand for a greyscale, as we have images on a black-to-white scale. If we would have RGB images, we would change the “1” into a “3”.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;screen_shots_python_code/pic18.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;We make sure the the values of the pixels, ranging from zero to 255, are of the float type and then we normalize the values as before.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;screen_shots_python_code/pic19.png&#34; /&gt;
&lt;img src=&#34;screen_shots_python_code/pic20.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The convolutional neural network cannot deal with categorical labels. Therefore, we transform the labels to binary vectors, where all vectors have length ten (as there are ten categories), a “1” at the index of the category and zeros elsewhere. For example, category 3 and 8 would be coded as [0, 0, 0, 1, 0, 0, 0, 0, 0, 0] and [0, 0, 0, 0, 0, 0, 0, 0, 1, 0], respectively. This transformation is referred to as “one hot encoding”.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;screen_shots_python_code/pic21.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Now, we can start building our convolutional neural network. The first layer &lt;code&gt;Conv2D&lt;/code&gt; is a convolutional layer that takes a 2-dimensional matrix of 28 by 28 pixels in greyscale (1) as input. As before, we use 128 nodes in this layer, as the size of the data is not extremely large and we want to avoid making our model unnecessarily complex. The filter matrix is of size 3 by 3, which is quite standard. As before, we use the rectified linear (“relu”) activation function. The &lt;code&gt;MaxPooling2D&lt;/code&gt; layer reduces the dimensionality (and thus required computational power) by outputting the maximum of the part of the input image that is captured by the filter matrix. The &lt;code&gt;Flatten&lt;/code&gt; layer simply flattens the result from the previous layer into a vector. As we saw before, the &lt;code&gt;softmax&lt;/code&gt; layer then assigns predicted probabilities to each of the ten clothing categories. Note that we use the same optimizer and metric as before, but that we now use “categorical_crossentropy” as the loss function instead of “sparse_categorical_crossentropy”. The reason for this is that the former works for one-hot encoded labels, whereas the other works for categorical labels.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;screen_shots_python_code/pic22.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;We fit our model to the training data, where we set the &lt;code&gt;batch_size&lt;/code&gt; argument equal to the number of neurons in the convolutional layers (= 128).&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;screen_shots_python_code/pic23.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;screen_shots_python_code/pic24.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Although we are still overfitting, we observe that the convolutional neural network performs better than the neural networks we saw earlier, achieving a training set accuracy of 95.16% and a test set accuracy of 90.39%, and a lower loss of 28.70. This was to be expected, because convolutional neural networks have previously been shown to perform well on visual imagery data. Let’s see if we can reduce overfitting by reducing the number of neurons from 128 to 64, adding dropout layers and enabling early stopping. Note that the rate in the &lt;code&gt;Dropout&lt;/code&gt; layer is the percentage of connections between layers that are being removed. the &lt;code&gt;SpatialDropout2D&lt;/code&gt; is a special kind of dropout layer for convolutional neural networks, which drops certain multiplications of the filter matrix with parts of the original image before pooling across all movements over the original image.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;screen_shots_python_code/pic25.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;When fitting our model, we also enable early stopping to reduce overfitting. Instead of going through all epochs specified, early stopping automatically stops the iterations through the epoch once it’s being noticed that the validation loss increases.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;screen_shots_python_code/pic26.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;From the results, we observe that although the training and test accuracies have decreased, they are now much more similar than before. The test accuracy has not decreased substantially, but the training accuracy has, which means that overfitting is much less of a problem than before. Next, we can print the first ten predictions from the model and the first ten actual labels and compare them.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;screen_shots_python_code/pic27.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Comparing these predictions to the first ten labels in the data set, we observe that the first ten predictions are correct!&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;next-up&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Next up…&lt;/h2&gt;
&lt;p&gt;Next up in this series of blog posts, I will experiment with tree-based methods and support vector machines to see if they perform as well as the neural network in predicting clothing categories in the Fashion MNIST data.&lt;/p&gt;
&lt;/div&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2019/11/11/a-comparison-of-methods-for-predicting-clothing-classes-using-the-fashion-mnist-dataset-in-rstudio-and-python-part-1/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>A First Look at Confidence Distributions</title>
      <link>https://rviews.rstudio.com/2019/11/05/a-first-look-at-confidence-distributions/</link>
      <pubDate>Tue, 05 Nov 2019 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2019/11/05/a-first-look-at-confidence-distributions/</guid>
      <description>
        
&lt;script src=&#34;/rmarkdown-libs/htmlwidgets/htmlwidgets.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/jquery/jquery.min.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/proj4js/proj4.js&#34;&gt;&lt;/script&gt;
&lt;link href=&#34;/rmarkdown-libs/highcharts/css/motion.css&#34; rel=&#34;stylesheet&#34; /&gt;
&lt;link href=&#34;/rmarkdown-libs/highcharts/css/htmlwdgtgrid.css&#34; rel=&#34;stylesheet&#34; /&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/highcharts.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/highcharts-3d.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/highcharts-more.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/stock.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/map.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/annotations.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/boost.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/data.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/drag-panes.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/drilldown.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/item-series.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/offline-exporting.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/overlapping-datalabels.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/exporting.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/export-data.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/funnel.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/heatmap.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/treemap.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/sankey.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/solid-gauge.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/streamgraph.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/sunburst.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/vector.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/wordcloud.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/xrange.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/tilemap.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/venn.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/gantt.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/timeline.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/modules/parallel-coordinates.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/plugins/grouped-categories.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/plugins/motion.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/plugins/multicolor_series.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/custom/reset.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/custom/symbols-extra.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highcharts/custom/text-symbols.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/highchart-binding/highchart.js&#34;&gt;&lt;/script&gt;


&lt;p&gt;Using a probability distribution to characterize uncertainty is at the core of statistical inference. So, it seems natural to try to summarize the information about the parameters in statistical models with probability distributions. R. A. Fisher thought so. In fact, he expended a great deal of effort over more than thirty years, and put his professional reputation on the line trying to do so, with only limited success. Fisher’s central difficulty was that, in the Frequentist tradition to which he was committed, parameters are not random variables. They are fixed and immutable constituents of the statistical models describing the behavior of populations, which we must estimate because we generally only have access to samples from populations, not to the full populations themselves. Now Bayesians, of course, characterize parameters with probability distributions from the get-go. Parameters are given prior distributions and combined with the likelihood function generated by the data to produce posterior distributions that characterize the parameters. Fisher wanted the posterior distributions without having to assume the priors. This was a key motivating idea for his work on Fiducial probability.&lt;/p&gt;
&lt;p&gt;A few statisticians apparently quietly worked on this program throughout the twentieth century, even though Fisher’s Fiducial ideas were mostly forgotten and not part of the mainstream statistical current. D. R. Cox (&lt;a href=&#34;https://projecteuclid.org/download/pdf_1/euclid.aoms/1177706618&#34;&gt;Cox (1958)&lt;/a&gt;), for example, pioneered the idea of constructing &lt;em&gt;confidence distributions&lt;/em&gt; from confidence intervals, and Bradley Efron (&lt;a href=&#34;https://projecteuclid.org/download/pdf_1/euclid.ss/1028905930&#34;&gt;Efron (1998)&lt;/a&gt;) expressed great optimism that Fisher’s work in this area would become important in the twenty-first century. (Efron’s paper is a masterpiece that summarizes a good bit of twentieth-century statistical research.)&lt;/p&gt;
&lt;p&gt;Recently, however, there seems to have been a resurgence of Fisher’s ideas among statisticians interested in chasing the idea of an orthodox Frequentist view of parameter distributions. The 2013 paper of &lt;a href=&#34;https://www.stat.rutgers.edu/home/mxie/RCPapers/insr.12000.pdf&#34;&gt;Xie and Singh&lt;/a&gt; lays out the modern theory of confidence distributions as a fundamental idea that organizes a great deal of statistical practice. In the initial Summary, the authors write:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;. . .the concept of a confidence distribution subsumes and unifies a wide range of
examples, from regular parametric (fiducial distribution) examples to bootstrap distributions,
significance (p-value) functions, normalized likelihood functions, and, in some cases, Bayesian priors and posteriors.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Later in the paper, they go on to define a confidence distribution as:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A function H&lt;sub&gt;n&lt;/sub&gt;(·) = H&lt;sub&gt;n&lt;/sub&gt;(x, ·) on &lt;strong&gt;X&lt;/strong&gt; × &lt;span class=&#34;math inline&#34;&gt;\(\Theta\)&lt;/span&gt; → [0, 1] is called a confidence distribution (CD) for a parameter &lt;span class=&#34;math inline&#34;&gt;\(\theta\)&lt;/span&gt;, if&lt;br /&gt;
* R1) For each given x ∈ &lt;strong&gt;X&lt;/strong&gt; , H&lt;sub&gt;n&lt;/sub&gt;(·) is a cumulative distribution function on &lt;span class=&#34;math inline&#34;&gt;\(\Theta\)&lt;/span&gt;;&lt;br /&gt;
* R2) At the true parameter value &lt;span class=&#34;math inline&#34;&gt;\(\theta\)&lt;/span&gt; = &lt;span class=&#34;math inline&#34;&gt;\(\theta\)&lt;/span&gt;&lt;sub&gt;0&lt;/sub&gt;, H&lt;sub&gt;n&lt;/sub&gt;(&lt;span class=&#34;math inline&#34;&gt;\(\theta\)&lt;/span&gt;&lt;sub&gt;0&lt;/sub&gt;) ≡ H&lt;sub&gt;n&lt;/sub&gt;(x, &lt;span class=&#34;math inline&#34;&gt;\(\theta\)&lt;/span&gt;&lt;sub&gt;0&lt;/sub&gt;), as a function of the sample&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The simplest example of a confidence distribution I could find that is adequate to illustrate some of the key concepts comes from the book &lt;a href=&#34;https://www.cambridge.org/core/books/confidence-likelihood-probability/143A34F11FB3D6F611F78E27C6D2CA5A&#34;&gt;Confidence, Likelihood, Probability: Statistical Inference with Confidence Distributions&lt;/a&gt; by Schweder and Hjort. On page 62, the authors point out that the distribution of the p-value for the parameter &lt;span class=&#34;math inline&#34;&gt;\(\theta\)&lt;/span&gt; describing the probability of success for a binomial trial can be considered as an approximate confidence distribution for &lt;span class=&#34;math inline&#34;&gt;\(\theta\)&lt;/span&gt;. The distribution is approximate because the distribution is discrete and the “half-correction” is used to improve the approximation. Note that because the &lt;a href=&#34;https://www.youtube.com/watch?v=UPQtjahe3j4&#34;&gt;p-values follow uniform distributions&lt;/a&gt; under the null hypothesis, it should be clear that the assumptions of the definition above are satisfied.&lt;/p&gt;
&lt;p&gt;Suppose Y ~ Bin(n,&lt;span class=&#34;math inline&#34;&gt;\(\theta\)&lt;/span&gt;), then&lt;/p&gt;
&lt;p&gt;C(&lt;span class=&#34;math inline&#34;&gt;\(\theta\)&lt;/span&gt;) = P(Y &amp;gt; y&lt;sub&gt;0&lt;/sub&gt;) + .5 * P(Y = y&lt;sub&gt;0&lt;/sub&gt;)
is a confidence distribution for &lt;span class=&#34;math inline&#34;&gt;\(\theta\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;To illustrate this, we consider the experiment of realizing 8 successes in 20 trials and write a short helper function.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;CD &amp;lt;- function(theta,n=20,y0=8){
            1 - sum(dbinom(x = seq(from = 0, to = y0), size = n, prob = theta)) + 
           .5 * dbinom(x = y0, size = n, prob = theta)}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we compute the CDF and plot it.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(tidyverse)
library(highcharter)
conf_dist &amp;lt;-  
  tibble(theta = seq(0, 1, by = .01)) %&amp;gt;%  
  mutate(probability = map_dbl(theta, CD))

hchart(conf_dist, &amp;quot;line&amp;quot;, hcaes(x = theta, y = probability)) %&amp;gt;%
hc_title(text = &amp;quot;Confidence Distribution for Binomial Model&amp;quot;,
         margin = 20, align = &amp;quot;left&amp;quot;,
         style = list(color = &amp;quot;black&amp;quot;, useHTML = TRUE)) %&amp;gt;%
hc_tooltip(valueDecimals=4, valuePrefix=&amp;quot;cum prob = &amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;div id=&#34;htmlwidget-1&#34; style=&#34;width:100%;height:500px;&#34; class=&#34;highchart html-widget&#34;&gt;&lt;/div&gt;
&lt;script type=&#34;application/json&#34; data-for=&#34;htmlwidget-1&#34;&gt;{&#34;x&#34;:{&#34;hc_opts&#34;:{&#34;title&#34;:{&#34;text&#34;:&#34;Confidence Distribution for Binomial Model&#34;,&#34;margin&#34;:20,&#34;align&#34;:&#34;left&#34;,&#34;style&#34;:{&#34;color&#34;:&#34;black&#34;,&#34;useHTML&#34;:true}},&#34;yAxis&#34;:{&#34;title&#34;:{&#34;text&#34;:&#34;probability&#34;},&#34;type&#34;:&#34;linear&#34;},&#34;credits&#34;:{&#34;enabled&#34;:false},&#34;exporting&#34;:{&#34;enabled&#34;:false},&#34;plotOptions&#34;:{&#34;series&#34;:{&#34;label&#34;:{&#34;enabled&#34;:false},&#34;turboThreshold&#34;:0,&#34;showInLegend&#34;:false},&#34;treemap&#34;:{&#34;layoutAlgorithm&#34;:&#34;squarified&#34;},&#34;scatter&#34;:{&#34;marker&#34;:{&#34;symbol&#34;:&#34;circle&#34;}}},&#34;series&#34;:[{&#34;group&#34;:&#34;group&#34;,&#34;data&#34;:[{&#34;theta&#34;:0,&#34;probability&#34;:0,&#34;x&#34;:0,&#34;y&#34;:0},{&#34;theta&#34;:0.01,&#34;probability&#34;:5.73499566887768e-12,&#34;x&#34;:0.01,&#34;y&#34;:5.73499566887768e-12},{&#34;theta&#34;:0.02,&#34;probability&#34;:1.33572401483433e-09,&#34;x&#34;:0.02,&#34;y&#34;:1.33572401483433e-09},{&#34;theta&#34;:0.03,&#34;probability&#34;:3.11201811492425e-08,&#34;x&#34;:0.03,&#34;y&#34;:3.11201811492425e-08},{&#34;theta&#34;:0.04,&#34;probability&#34;:2.82353239135745e-07,&#34;x&#34;:0.04,&#34;y&#34;:2.82353239135745e-07},{&#34;theta&#34;:0.05,&#34;probability&#34;:1.52740959689047e-06,&#34;x&#34;:0.05,&#34;y&#34;:1.52740959689047e-06},{&#34;theta&#34;:0.06,&#34;probability&#34;:5.95561261361866e-06,&#34;x&#34;:0.06,&#34;y&#34;:5.95561261361866e-06},{&#34;theta&#34;:0.07,&#34;probability&#34;:1.85206634848512e-05,&#34;x&#34;:0.07,&#34;y&#34;:1.85206634848512e-05},{&#34;theta&#34;:0.08,&#34;probability&#34;:4.87954463305065e-05,&#34;x&#34;:0.08,&#34;y&#34;:4.87954463305065e-05},{&#34;theta&#34;:0.09,&#34;probability&#34;:0.000113243868828019,&#34;x&#34;:0.09,&#34;y&#34;:0.000113243868828019},{&#34;theta&#34;:0.1,&#34;probability&#34;:0.000237746775292845,&#34;x&#34;:0.1,&#34;y&#34;:0.000237746775292845},{&#34;theta&#34;:0.11,&#34;probability&#34;:0.000460169436248278,&#34;x&#34;:0.11,&#34;y&#34;:0.000460169436248278},{&#34;theta&#34;:0.12,&#34;probability&#34;:0.000832734447940497,&#34;x&#34;:0.12,&#34;y&#34;:0.000832734447940497},{&#34;theta&#34;:0.13,&#34;probability&#34;:0.0014239652669573,&#34;x&#34;:0.13,&#34;y&#34;:0.0014239652669573},{&#34;theta&#34;:0.14,&#34;probability&#34;:0.00231998890516482,&#34;x&#34;:0.14,&#34;y&#34;:0.00231998890516482},{&#34;theta&#34;:0.15,&#34;probability&#34;:0.00362502697453944,&#34;x&#34;:0.15,&#34;y&#34;:0.00362502697453944},{&#34;theta&#34;:0.16,&#34;probability&#34;:0.00546095711151733,&#34;x&#34;:0.16,&#34;y&#34;:0.00546095711151733},{&#34;theta&#34;:0.17,&#34;probability&#34;:0.00796588655322165,&#34;x&#34;:0.17,&#34;y&#34;:0.00796588655322165},{&#34;theta&#34;:0.18,&#34;probability&#34;:0.0112917413300254,&#34;x&#34;:0.18,&#34;y&#34;:0.0112917413300254},{&#34;theta&#34;:0.19,&#34;probability&#34;:0.0156009338215457,&#34;x&#34;:0.19,&#34;y&#34;:0.0156009338215457},{&#34;theta&#34;:0.2,&#34;probability&#34;:0.0210622247007996,&#34;x&#34;:0.2,&#34;y&#34;:0.0210622247007996},{&#34;theta&#34;:0.21,&#34;probability&#34;:0.0278459398154536,&#34;x&#34;:0.21,&#34;y&#34;:0.0278459398154536},{&#34;theta&#34;:0.22,&#34;probability&#34;:0.0361187364416553,&#34;x&#34;:0.22,&#34;y&#34;:0.0361187364416553},{&#34;theta&#34;:0.23,&#34;probability&#34;:0.0460381355411022,&#34;x&#34;:0.23,&#34;y&#34;:0.0460381355411022},{&#34;theta&#34;:0.24,&#34;probability&#34;:0.0577470468616008,&#34;x&#34;:0.24,&#34;y&#34;:0.0577470468616008},{&#34;theta&#34;:0.25,&#34;probability&#34;:0.0713685123146205,&#34;x&#34;:0.25,&#34;y&#34;:0.0713685123146205},{&#34;theta&#34;:0.26,&#34;probability&#34;:0.087000880960923,&#34;x&#34;:0.26,&#34;y&#34;:0.087000880960923},{&#34;theta&#34;:0.27,&#34;probability&#34;:0.104713607490085,&#34;x&#34;:0.27,&#34;y&#34;:0.104713607490085},{&#34;theta&#34;:0.28,&#34;probability&#34;:0.124543836956545,&#34;x&#34;:0.28,&#34;y&#34;:0.124543836956545},{&#34;theta&#34;:0.29,&#34;probability&#34;:0.146493903594774,&#34;x&#34;:0.29,&#34;y&#34;:0.146493903594774},{&#34;theta&#34;:0.3,&#34;probability&#34;:0.170529832729409,&#34;x&#34;:0.3,&#34;y&#34;:0.170529832729409},{&#34;theta&#34;:0.31,&#34;probability&#34;:0.196580894065467,&#34;x&#34;:0.31,&#34;y&#34;:0.196580894065467},{&#34;theta&#34;:0.32,&#34;probability&#34;:0.224540213842213,&#34;x&#34;:0.32,&#34;y&#34;:0.224540213842213},{&#34;theta&#34;:0.33,&#34;probability&#34;:0.254266414156535,&#34;x&#34;:0.33,&#34;y&#34;:0.254266414156535},{&#34;theta&#34;:0.34,&#34;probability&#34;:0.285586211691276,&#34;x&#34;:0.34,&#34;y&#34;:0.285586211691276},{&#34;theta&#34;:0.35,&#34;probability&#34;:0.318297876353866,&#34;x&#34;:0.35,&#34;y&#34;:0.318297876353866},{&#34;theta&#34;:0.36,&#34;probability&#34;:0.35217542389985,&#34;x&#34;:0.36,&#34;y&#34;:0.35217542389985},{&#34;theta&#34;:0.37,&#34;probability&#34;:0.386973396157569,&#34;x&#34;:0.37,&#34;y&#34;:0.386973396157569},{&#34;theta&#34;:0.38,&#34;probability&#34;:0.422432068373071,&#34;x&#34;:0.38,&#34;y&#34;:0.422432068373071},{&#34;theta&#34;:0.39,&#34;probability&#34;:0.458282915573536,&#34;x&#34;:0.39,&#34;y&#34;:0.458282915573536},{&#34;theta&#34;:0.4,&#34;probability&#34;:0.49425416856512,&#34;x&#34;:0.4,&#34;y&#34;:0.49425416856512},{&#34;theta&#34;:0.41,&#34;probability&#34;:0.530076294873525,&#34;x&#34;:0.41,&#34;y&#34;:0.530076294873525},{&#34;theta&#34;:0.42,&#34;probability&#34;:0.565487250045856,&#34;x&#34;:0.42,&#34;y&#34;:0.565487250045856},{&#34;theta&#34;:0.43,&#34;probability&#34;:0.6002373595472,&#34;x&#34;:0.43,&#34;y&#34;:0.6002373595472},{&#34;theta&#34;:0.44,&#34;probability&#34;:0.63409371017361,&#34;x&#34;:0.44,&#34;y&#34;:0.63409371017361},{&#34;theta&#34;:0.45,&#34;probability&#34;:0.666843951555161,&#34;x&#34;:0.45,&#34;y&#34;:0.666843951555161},{&#34;theta&#34;:0.46,&#34;probability&#34;:0.698299431988929,&#34;x&#34;:0.46,&#34;y&#34;:0.698299431988929},{&#34;theta&#34;:0.47,&#34;probability&#34;:0.728297617569333,&#34;x&#34;:0.47,&#34;y&#34;:0.728297617569333},{&#34;theta&#34;:0.48,&#34;probability&#34;:0.756703768450287,&#34;x&#34;:0.48,&#34;y&#34;:0.756703768450287},{&#34;theta&#34;:0.49,&#34;probability&#34;:0.783411870218361,&#34;x&#34;:0.49,&#34;y&#34;:0.783411870218361},{&#34;theta&#34;:0.5,&#34;probability&#34;:0.808344841003418,&#34;x&#34;:0.5,&#34;y&#34;:0.808344841003418},{&#34;theta&#34;:0.51,&#34;probability&#34;:0.831454055434115,&#34;x&#34;:0.51,&#34;y&#34;:0.831454055434115},{&#34;theta&#34;:0.52,&#34;probability&#34;:0.85271824431402,&#34;x&#34;:0.52,&#34;y&#34;:0.85271824431402},{&#34;theta&#34;:0.53,&#34;probability&#34;:0.872141843535642,&#34;x&#34;:0.53,&#34;y&#34;:0.872141843535642},{&#34;theta&#34;:0.54,&#34;probability&#34;:0.889752876987755,&#34;x&#34;:0.54,&#34;y&#34;:0.889752876987755},{&#34;theta&#34;:0.55,&#34;probability&#34;:0.905600465906429,&#34;x&#34;:0.55,&#34;y&#34;:0.905600465906429},{&#34;theta&#34;:0.56,&#34;probability&#34;:0.91975206126523,&#34;x&#34;:0.56,&#34;y&#34;:0.91975206126523},{&#34;theta&#34;:0.57,&#34;probability&#34;:0.932290496511752,&#34;x&#34;:0.57,&#34;y&#34;:0.932290496511752},{&#34;theta&#34;:0.58,&#34;probability&#34;:0.94331095546382,&#34;x&#34;:0.58,&#34;y&#34;:0.94331095546382},{&#34;theta&#34;:0.59,&#34;probability&#34;:0.952917944802709,&#34;x&#34;:0.59,&#34;y&#34;:0.952917944802709},{&#34;theta&#34;:0.6,&#34;probability&#34;:0.961222352743988,&#34;x&#34;:0.6,&#34;y&#34;:0.961222352743988},{&#34;theta&#34;:0.61,&#34;probability&#34;:0.968338665588788,&#34;x&#34;:0.61,&#34;y&#34;:0.968338665588788},{&#34;theta&#34;:0.62,&#34;probability&#34;:0.974382402457655,&#34;x&#34;:0.62,&#34;y&#34;:0.974382402457655},{&#34;theta&#34;:0.63,&#34;probability&#34;:0.979467816101886,&#34;x&#34;:0.63,&#34;y&#34;:0.979467816101886},{&#34;theta&#34;:0.64,&#34;probability&#34;:0.983705894787928,&#34;x&#34;:0.64,&#34;y&#34;:0.983705894787928},{&#34;theta&#34;:0.65,&#34;probability&#34;:0.987202687353466,&#34;x&#34;:0.65,&#34;y&#34;:0.987202687353466},{&#34;theta&#34;:0.66,&#34;probability&#34;:0.990057961096871,&#34;x&#34;:0.66,&#34;y&#34;:0.990057961096871},{&#34;theta&#34;:0.67,&#34;probability&#34;:0.992364190591014,&#34;x&#34;:0.67,&#34;y&#34;:0.992364190591014},{&#34;theta&#34;:0.68,&#34;probability&#34;:0.994205865151394,&#34;x&#34;:0.68,&#34;y&#34;:0.994205865151394},{&#34;theta&#34;:0.69,&#34;probability&#34;:0.995659093808592,&#34;x&#34;:0.69,&#34;y&#34;:0.995659093808592},{&#34;theta&#34;:0.7,&#34;probability&#34;:0.996791479430329,&#34;x&#34;:0.7,&#34;y&#34;:0.996791479430329},{&#34;theta&#34;:0.71,&#34;probability&#34;:0.997662228223176,&#34;x&#34;:0.71,&#34;y&#34;:0.997662228223176},{&#34;theta&#34;:0.72,&#34;probability&#34;:0.998322457253502,&#34;x&#34;:0.72,&#34;y&#34;:0.998322457253502},{&#34;theta&#34;:0.73,&#34;probability&#34;:0.998815660821706,&#34;x&#34;:0.73,&#34;y&#34;:0.998815660821706},{&#34;theta&#34;:0.74,&#34;probability&#34;:0.999178296394706,&#34;x&#34;:0.74,&#34;y&#34;:0.999178296394706},{&#34;theta&#34;:0.75,&#34;probability&#34;:0.999440452181261,&#34;x&#34;:0.75,&#34;y&#34;:0.999440452181261},{&#34;theta&#34;:0.76,&#34;probability&#34;:0.99962656110662,&#34;x&#34;:0.76,&#34;y&#34;:0.99962656110662},{&#34;theta&#34;:0.77,&#34;probability&#34;:0.999756129655175,&#34;x&#34;:0.77,&#34;y&#34;:0.999756129655175},{&#34;theta&#34;:0.78,&#34;probability&#34;:0.999844454527686,&#34;x&#34;:0.78,&#34;y&#34;:0.999844454527686},{&#34;theta&#34;:0.79,&#34;probability&#34;:0.99990330502023,&#34;x&#34;:0.79,&#34;y&#34;:0.99990330502023},{&#34;theta&#34;:0.8,&#34;probability&#34;:0.999941554197265,&#34;x&#34;:0.8,&#34;y&#34;:0.999941554197265},{&#34;theta&#34;:0.81,&#34;probability&#34;:0.999965747040655,&#34;x&#34;:0.81,&#34;y&#34;:0.999965747040655},{&#34;theta&#34;:0.82,&#34;probability&#34;:0.999980598578908,&#34;x&#34;:0.82,&#34;y&#34;:0.999980598578908},{&#34;theta&#34;:0.83,&#34;probability&#34;:0.999989419342954,&#34;x&#34;:0.83,&#34;y&#34;:0.999989419342954},{&#34;theta&#34;:0.84,&#34;probability&#34;:0.999994469208827,&#34;x&#34;:0.84,&#34;y&#34;:0.999994469208827},{&#34;theta&#34;:0.85,&#34;probability&#34;:0.999997243675202,&#34;x&#34;:0.85,&#34;y&#34;:0.999997243675202},{&#34;theta&#34;:0.86,&#34;probability&#34;:0.999998698837753,&#34;x&#34;:0.86,&#34;y&#34;:0.999998698837753},{&#34;theta&#34;:0.87,&#34;probability&#34;:0.999999422764826,&#34;x&#34;:0.87,&#34;y&#34;:0.999999422764826},{&#34;theta&#34;:0.88,&#34;probability&#34;:0.999999761697516,&#34;x&#34;:0.88,&#34;y&#34;:0.999999761697516},{&#34;theta&#34;:0.89,&#34;probability&#34;:0.999999909577602,&#34;x&#34;:0.89,&#34;y&#34;:0.999999909577602},{&#34;theta&#34;:0.9,&#34;probability&#34;:0.999999968963797,&#34;x&#34;:0.9,&#34;y&#34;:0.999999968963797},{&#34;theta&#34;:0.91,&#34;probability&#34;:0.999999990564098,&#34;x&#34;:0.91,&#34;y&#34;:0.999999990564098},{&#34;theta&#34;:0.92,&#34;probability&#34;:0.999999997530193,&#34;x&#34;:0.92,&#34;y&#34;:0.999999997530193},{&#34;theta&#34;:0.93,&#34;probability&#34;:0.999999999465214,&#34;x&#34;:0.93,&#34;y&#34;:0.999999999465214},{&#34;theta&#34;:0.94,&#34;probability&#34;:0.999999999909645,&#34;x&#34;:0.94,&#34;y&#34;:0.999999999909645},{&#34;theta&#34;:0.95,&#34;probability&#34;:0.99999999998912,&#34;x&#34;:0.95,&#34;y&#34;:0.99999999998912},{&#34;theta&#34;:0.96,&#34;probability&#34;:0.999999999999198,&#34;x&#34;:0.96,&#34;y&#34;:0.999999999999198},{&#34;theta&#34;:0.97,&#34;probability&#34;:0.999999999999973,&#34;x&#34;:0.97,&#34;y&#34;:0.999999999999973},{&#34;theta&#34;:0.98,&#34;probability&#34;:1,&#34;x&#34;:0.98,&#34;y&#34;:1},{&#34;theta&#34;:0.99,&#34;probability&#34;:1,&#34;x&#34;:0.99,&#34;y&#34;:1},{&#34;theta&#34;:1,&#34;probability&#34;:1,&#34;x&#34;:1,&#34;y&#34;:1}],&#34;type&#34;:&#34;line&#34;}],&#34;xAxis&#34;:{&#34;type&#34;:&#34;linear&#34;,&#34;title&#34;:{&#34;text&#34;:&#34;theta&#34;},&#34;categories&#34;:null},&#34;tooltip&#34;:{&#34;valueDecimals&#34;:4,&#34;valuePrefix&#34;:&#34;cum prob = &#34;}},&#34;theme&#34;:{&#34;chart&#34;:{&#34;backgroundColor&#34;:&#34;transparent&#34;}},&#34;conf_opts&#34;:{&#34;global&#34;:{&#34;Date&#34;:null,&#34;VMLRadialGradientURL&#34;:&#34;http =//code.highcharts.com/list(version)/gfx/vml-radial-gradient.png&#34;,&#34;canvasToolsURL&#34;:&#34;http =//code.highcharts.com/list(version)/modules/canvas-tools.js&#34;,&#34;getTimezoneOffset&#34;:null,&#34;timezoneOffset&#34;:0,&#34;useUTC&#34;:true},&#34;lang&#34;:{&#34;contextButtonTitle&#34;:&#34;Chart context menu&#34;,&#34;decimalPoint&#34;:&#34;.&#34;,&#34;downloadJPEG&#34;:&#34;Download JPEG image&#34;,&#34;downloadPDF&#34;:&#34;Download PDF document&#34;,&#34;downloadPNG&#34;:&#34;Download PNG image&#34;,&#34;downloadSVG&#34;:&#34;Download SVG vector image&#34;,&#34;drillUpText&#34;:&#34;Back to {series.name}&#34;,&#34;invalidDate&#34;:null,&#34;loading&#34;:&#34;Loading...&#34;,&#34;months&#34;:[&#34;January&#34;,&#34;February&#34;,&#34;March&#34;,&#34;April&#34;,&#34;May&#34;,&#34;June&#34;,&#34;July&#34;,&#34;August&#34;,&#34;September&#34;,&#34;October&#34;,&#34;November&#34;,&#34;December&#34;],&#34;noData&#34;:&#34;No data to display&#34;,&#34;numericSymbols&#34;:[&#34;k&#34;,&#34;M&#34;,&#34;G&#34;,&#34;T&#34;,&#34;P&#34;,&#34;E&#34;],&#34;printChart&#34;:&#34;Print chart&#34;,&#34;resetZoom&#34;:&#34;Reset zoom&#34;,&#34;resetZoomTitle&#34;:&#34;Reset zoom level 1:1&#34;,&#34;shortMonths&#34;:[&#34;Jan&#34;,&#34;Feb&#34;,&#34;Mar&#34;,&#34;Apr&#34;,&#34;May&#34;,&#34;Jun&#34;,&#34;Jul&#34;,&#34;Aug&#34;,&#34;Sep&#34;,&#34;Oct&#34;,&#34;Nov&#34;,&#34;Dec&#34;],&#34;thousandsSep&#34;:&#34; &#34;,&#34;weekdays&#34;:[&#34;Sunday&#34;,&#34;Monday&#34;,&#34;Tuesday&#34;,&#34;Wednesday&#34;,&#34;Thursday&#34;,&#34;Friday&#34;,&#34;Saturday&#34;]}},&#34;type&#34;:&#34;chart&#34;,&#34;fonts&#34;:[],&#34;debug&#34;:false},&#34;evals&#34;:[],&#34;jsHooks&#34;:[]}&lt;/script&gt;
&lt;p&gt;This is nice, but to really see how computing the confidence distribution might be useful, we compute and plot the confidence curve introduced by Birnbaum in his &lt;a href=&#34;https://projecteuclid.org/download/pdf_1/euclid.aoms/1177705145&#34;&gt;1961 paper&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;conf_curve &amp;lt;-  
  conf_dist %&amp;gt;%  
  mutate(confidence = 2 * abs(.5 - probability))

hchart(conf_curve, &amp;quot;line&amp;quot;, hcaes(x = theta, y = confidence)) %&amp;gt;%
  hc_title(text = &amp;quot;Confidence Curve for Binomial Model&amp;quot;,
           margin = 20, align = &amp;quot;left&amp;quot;,
           style = list(color = &amp;quot;black&amp;quot;, useHTML = TRUE)) %&amp;gt;%
  hc_tooltip(valueDecimals=4, valuePrefix=&amp;quot;conf level = &amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;div id=&#34;htmlwidget-2&#34; style=&#34;width:100%;height:500px;&#34; class=&#34;highchart html-widget&#34;&gt;&lt;/div&gt;
&lt;script type=&#34;application/json&#34; data-for=&#34;htmlwidget-2&#34;&gt;{&#34;x&#34;:{&#34;hc_opts&#34;:{&#34;title&#34;:{&#34;text&#34;:&#34;Confidence Curve for Binomial Model&#34;,&#34;margin&#34;:20,&#34;align&#34;:&#34;left&#34;,&#34;style&#34;:{&#34;color&#34;:&#34;black&#34;,&#34;useHTML&#34;:true}},&#34;yAxis&#34;:{&#34;title&#34;:{&#34;text&#34;:&#34;confidence&#34;},&#34;type&#34;:&#34;linear&#34;},&#34;credits&#34;:{&#34;enabled&#34;:false},&#34;exporting&#34;:{&#34;enabled&#34;:false},&#34;plotOptions&#34;:{&#34;series&#34;:{&#34;label&#34;:{&#34;enabled&#34;:false},&#34;turboThreshold&#34;:0,&#34;showInLegend&#34;:false},&#34;treemap&#34;:{&#34;layoutAlgorithm&#34;:&#34;squarified&#34;},&#34;scatter&#34;:{&#34;marker&#34;:{&#34;symbol&#34;:&#34;circle&#34;}}},&#34;series&#34;:[{&#34;group&#34;:&#34;group&#34;,&#34;data&#34;:[{&#34;theta&#34;:0,&#34;probability&#34;:0,&#34;confidence&#34;:1,&#34;x&#34;:0,&#34;y&#34;:1},{&#34;theta&#34;:0.01,&#34;probability&#34;:5.73499566887768e-12,&#34;confidence&#34;:0.99999999998853,&#34;x&#34;:0.01,&#34;y&#34;:0.99999999998853},{&#34;theta&#34;:0.02,&#34;probability&#34;:1.33572401483433e-09,&#34;confidence&#34;:0.999999997328552,&#34;x&#34;:0.02,&#34;y&#34;:0.999999997328552},{&#34;theta&#34;:0.03,&#34;probability&#34;:3.11201811492425e-08,&#34;confidence&#34;:0.999999937759638,&#34;x&#34;:0.03,&#34;y&#34;:0.999999937759638},{&#34;theta&#34;:0.04,&#34;probability&#34;:2.82353239135745e-07,&#34;confidence&#34;:0.999999435293522,&#34;x&#34;:0.04,&#34;y&#34;:0.999999435293522},{&#34;theta&#34;:0.05,&#34;probability&#34;:1.52740959689047e-06,&#34;confidence&#34;:0.999996945180806,&#34;x&#34;:0.05,&#34;y&#34;:0.999996945180806},{&#34;theta&#34;:0.06,&#34;probability&#34;:5.95561261361866e-06,&#34;confidence&#34;:0.999988088774773,&#34;x&#34;:0.06,&#34;y&#34;:0.999988088774773},{&#34;theta&#34;:0.07,&#34;probability&#34;:1.85206634848512e-05,&#34;confidence&#34;:0.99996295867303,&#34;x&#34;:0.07,&#34;y&#34;:0.99996295867303},{&#34;theta&#34;:0.08,&#34;probability&#34;:4.87954463305065e-05,&#34;confidence&#34;:0.999902409107339,&#34;x&#34;:0.08,&#34;y&#34;:0.999902409107339},{&#34;theta&#34;:0.09,&#34;probability&#34;:0.000113243868828019,&#34;confidence&#34;:0.999773512262344,&#34;x&#34;:0.09,&#34;y&#34;:0.999773512262344},{&#34;theta&#34;:0.1,&#34;probability&#34;:0.000237746775292845,&#34;confidence&#34;:0.999524506449414,&#34;x&#34;:0.1,&#34;y&#34;:0.999524506449414},{&#34;theta&#34;:0.11,&#34;probability&#34;:0.000460169436248278,&#34;confidence&#34;:0.999079661127503,&#34;x&#34;:0.11,&#34;y&#34;:0.999079661127503},{&#34;theta&#34;:0.12,&#34;probability&#34;:0.000832734447940497,&#34;confidence&#34;:0.998334531104119,&#34;x&#34;:0.12,&#34;y&#34;:0.998334531104119},{&#34;theta&#34;:0.13,&#34;probability&#34;:0.0014239652669573,&#34;confidence&#34;:0.997152069466085,&#34;x&#34;:0.13,&#34;y&#34;:0.997152069466085},{&#34;theta&#34;:0.14,&#34;probability&#34;:0.00231998890516482,&#34;confidence&#34;:0.99536002218967,&#34;x&#34;:0.14,&#34;y&#34;:0.99536002218967},{&#34;theta&#34;:0.15,&#34;probability&#34;:0.00362502697453944,&#34;confidence&#34;:0.992749946050921,&#34;x&#34;:0.15,&#34;y&#34;:0.992749946050921},{&#34;theta&#34;:0.16,&#34;probability&#34;:0.00546095711151733,&#34;confidence&#34;:0.989078085776965,&#34;x&#34;:0.16,&#34;y&#34;:0.989078085776965},{&#34;theta&#34;:0.17,&#34;probability&#34;:0.00796588655322165,&#34;confidence&#34;:0.984068226893557,&#34;x&#34;:0.17,&#34;y&#34;:0.984068226893557},{&#34;theta&#34;:0.18,&#34;probability&#34;:0.0112917413300254,&#34;confidence&#34;:0.977416517339949,&#34;x&#34;:0.18,&#34;y&#34;:0.977416517339949},{&#34;theta&#34;:0.19,&#34;probability&#34;:0.0156009338215457,&#34;confidence&#34;:0.968798132356909,&#34;x&#34;:0.19,&#34;y&#34;:0.968798132356909},{&#34;theta&#34;:0.2,&#34;probability&#34;:0.0210622247007996,&#34;confidence&#34;:0.957875550598401,&#34;x&#34;:0.2,&#34;y&#34;:0.957875550598401},{&#34;theta&#34;:0.21,&#34;probability&#34;:0.0278459398154536,&#34;confidence&#34;:0.944308120369093,&#34;x&#34;:0.21,&#34;y&#34;:0.944308120369093},{&#34;theta&#34;:0.22,&#34;probability&#34;:0.0361187364416553,&#34;confidence&#34;:0.927762527116689,&#34;x&#34;:0.22,&#34;y&#34;:0.927762527116689},{&#34;theta&#34;:0.23,&#34;probability&#34;:0.0460381355411022,&#34;confidence&#34;:0.907923728917795,&#34;x&#34;:0.23,&#34;y&#34;:0.907923728917795},{&#34;theta&#34;:0.24,&#34;probability&#34;:0.0577470468616008,&#34;confidence&#34;:0.884505906276798,&#34;x&#34;:0.24,&#34;y&#34;:0.884505906276798},{&#34;theta&#34;:0.25,&#34;probability&#34;:0.0713685123146205,&#34;confidence&#34;:0.857262975370759,&#34;x&#34;:0.25,&#34;y&#34;:0.857262975370759},{&#34;theta&#34;:0.26,&#34;probability&#34;:0.087000880960923,&#34;confidence&#34;:0.825998238078154,&#34;x&#34;:0.26,&#34;y&#34;:0.825998238078154},{&#34;theta&#34;:0.27,&#34;probability&#34;:0.104713607490085,&#34;confidence&#34;:0.790572785019829,&#34;x&#34;:0.27,&#34;y&#34;:0.790572785019829},{&#34;theta&#34;:0.28,&#34;probability&#34;:0.124543836956545,&#34;confidence&#34;:0.75091232608691,&#34;x&#34;:0.28,&#34;y&#34;:0.75091232608691},{&#34;theta&#34;:0.29,&#34;probability&#34;:0.146493903594774,&#34;confidence&#34;:0.707012192810451,&#34;x&#34;:0.29,&#34;y&#34;:0.707012192810451},{&#34;theta&#34;:0.3,&#34;probability&#34;:0.170529832729409,&#34;confidence&#34;:0.658940334541182,&#34;x&#34;:0.3,&#34;y&#34;:0.658940334541182},{&#34;theta&#34;:0.31,&#34;probability&#34;:0.196580894065467,&#34;confidence&#34;:0.606838211869065,&#34;x&#34;:0.31,&#34;y&#34;:0.606838211869065},{&#34;theta&#34;:0.32,&#34;probability&#34;:0.224540213842213,&#34;confidence&#34;:0.550919572315575,&#34;x&#34;:0.32,&#34;y&#34;:0.550919572315575},{&#34;theta&#34;:0.33,&#34;probability&#34;:0.254266414156535,&#34;confidence&#34;:0.49146717168693,&#34;x&#34;:0.33,&#34;y&#34;:0.49146717168693},{&#34;theta&#34;:0.34,&#34;probability&#34;:0.285586211691276,&#34;confidence&#34;:0.428827576617447,&#34;x&#34;:0.34,&#34;y&#34;:0.428827576617447},{&#34;theta&#34;:0.35,&#34;probability&#34;:0.318297876353866,&#34;confidence&#34;:0.363404247292267,&#34;x&#34;:0.35,&#34;y&#34;:0.363404247292267},{&#34;theta&#34;:0.36,&#34;probability&#34;:0.35217542389985,&#34;confidence&#34;:0.295649152200299,&#34;x&#34;:0.36,&#34;y&#34;:0.295649152200299},{&#34;theta&#34;:0.37,&#34;probability&#34;:0.386973396157569,&#34;confidence&#34;:0.226053207684862,&#34;x&#34;:0.37,&#34;y&#34;:0.226053207684862},{&#34;theta&#34;:0.38,&#34;probability&#34;:0.422432068373071,&#34;confidence&#34;:0.155135863253859,&#34;x&#34;:0.38,&#34;y&#34;:0.155135863253859},{&#34;theta&#34;:0.39,&#34;probability&#34;:0.458282915573536,&#34;confidence&#34;:0.083434168852929,&#34;x&#34;:0.39,&#34;y&#34;:0.083434168852929},{&#34;theta&#34;:0.4,&#34;probability&#34;:0.49425416856512,&#34;confidence&#34;:0.0114916628697606,&#34;x&#34;:0.4,&#34;y&#34;:0.0114916628697606},{&#34;theta&#34;:0.41,&#34;probability&#34;:0.530076294873525,&#34;confidence&#34;:0.0601525897470501,&#34;x&#34;:0.41,&#34;y&#34;:0.0601525897470501},{&#34;theta&#34;:0.42,&#34;probability&#34;:0.565487250045856,&#34;confidence&#34;:0.130974500091712,&#34;x&#34;:0.42,&#34;y&#34;:0.130974500091712},{&#34;theta&#34;:0.43,&#34;probability&#34;:0.6002373595472,&#34;confidence&#34;:0.200474719094401,&#34;x&#34;:0.43,&#34;y&#34;:0.200474719094401},{&#34;theta&#34;:0.44,&#34;probability&#34;:0.63409371017361,&#34;confidence&#34;:0.26818742034722,&#34;x&#34;:0.44,&#34;y&#34;:0.26818742034722},{&#34;theta&#34;:0.45,&#34;probability&#34;:0.666843951555161,&#34;confidence&#34;:0.333687903110321,&#34;x&#34;:0.45,&#34;y&#34;:0.333687903110321},{&#34;theta&#34;:0.46,&#34;probability&#34;:0.698299431988929,&#34;confidence&#34;:0.396598863977857,&#34;x&#34;:0.46,&#34;y&#34;:0.396598863977857},{&#34;theta&#34;:0.47,&#34;probability&#34;:0.728297617569333,&#34;confidence&#34;:0.456595235138667,&#34;x&#34;:0.47,&#34;y&#34;:0.456595235138667},{&#34;theta&#34;:0.48,&#34;probability&#34;:0.756703768450287,&#34;confidence&#34;:0.513407536900574,&#34;x&#34;:0.48,&#34;y&#34;:0.513407536900574},{&#34;theta&#34;:0.49,&#34;probability&#34;:0.783411870218361,&#34;confidence&#34;:0.566823740436722,&#34;x&#34;:0.49,&#34;y&#34;:0.566823740436722},{&#34;theta&#34;:0.5,&#34;probability&#34;:0.808344841003418,&#34;confidence&#34;:0.616689682006836,&#34;x&#34;:0.5,&#34;y&#34;:0.616689682006836},{&#34;theta&#34;:0.51,&#34;probability&#34;:0.831454055434115,&#34;confidence&#34;:0.66290811086823,&#34;x&#34;:0.51,&#34;y&#34;:0.66290811086823},{&#34;theta&#34;:0.52,&#34;probability&#34;:0.85271824431402,&#34;confidence&#34;:0.70543648862804,&#34;x&#34;:0.52,&#34;y&#34;:0.70543648862804},{&#34;theta&#34;:0.53,&#34;probability&#34;:0.872141843535642,&#34;confidence&#34;:0.744283687071284,&#34;x&#34;:0.53,&#34;y&#34;:0.744283687071284},{&#34;theta&#34;:0.54,&#34;probability&#34;:0.889752876987755,&#34;confidence&#34;:0.779505753975511,&#34;x&#34;:0.54,&#34;y&#34;:0.779505753975511},{&#34;theta&#34;:0.55,&#34;probability&#34;:0.905600465906429,&#34;confidence&#34;:0.811200931812859,&#34;x&#34;:0.55,&#34;y&#34;:0.811200931812859},{&#34;theta&#34;:0.56,&#34;probability&#34;:0.91975206126523,&#34;confidence&#34;:0.839504122530459,&#34;x&#34;:0.56,&#34;y&#34;:0.839504122530459},{&#34;theta&#34;:0.57,&#34;probability&#34;:0.932290496511752,&#34;confidence&#34;:0.864580993023504,&#34;x&#34;:0.57,&#34;y&#34;:0.864580993023504},{&#34;theta&#34;:0.58,&#34;probability&#34;:0.94331095546382,&#34;confidence&#34;:0.88662191092764,&#34;x&#34;:0.58,&#34;y&#34;:0.88662191092764},{&#34;theta&#34;:0.59,&#34;probability&#34;:0.952917944802709,&#34;confidence&#34;:0.905835889605417,&#34;x&#34;:0.59,&#34;y&#34;:0.905835889605417},{&#34;theta&#34;:0.6,&#34;probability&#34;:0.961222352743988,&#34;confidence&#34;:0.922444705487976,&#34;x&#34;:0.6,&#34;y&#34;:0.922444705487976},{&#34;theta&#34;:0.61,&#34;probability&#34;:0.968338665588788,&#34;confidence&#34;:0.936677331177577,&#34;x&#34;:0.61,&#34;y&#34;:0.936677331177577},{&#34;theta&#34;:0.62,&#34;probability&#34;:0.974382402457655,&#34;confidence&#34;:0.94876480491531,&#34;x&#34;:0.62,&#34;y&#34;:0.94876480491531},{&#34;theta&#34;:0.63,&#34;probability&#34;:0.979467816101886,&#34;confidence&#34;:0.958935632203771,&#34;x&#34;:0.63,&#34;y&#34;:0.958935632203771},{&#34;theta&#34;:0.64,&#34;probability&#34;:0.983705894787928,&#34;confidence&#34;:0.967411789575856,&#34;x&#34;:0.64,&#34;y&#34;:0.967411789575856},{&#34;theta&#34;:0.65,&#34;probability&#34;:0.987202687353466,&#34;confidence&#34;:0.974405374706932,&#34;x&#34;:0.65,&#34;y&#34;:0.974405374706932},{&#34;theta&#34;:0.66,&#34;probability&#34;:0.990057961096871,&#34;confidence&#34;:0.980115922193742,&#34;x&#34;:0.66,&#34;y&#34;:0.980115922193742},{&#34;theta&#34;:0.67,&#34;probability&#34;:0.992364190591014,&#34;confidence&#34;:0.984728381182028,&#34;x&#34;:0.67,&#34;y&#34;:0.984728381182028},{&#34;theta&#34;:0.68,&#34;probability&#34;:0.994205865151394,&#34;confidence&#34;:0.988411730302788,&#34;x&#34;:0.68,&#34;y&#34;:0.988411730302788},{&#34;theta&#34;:0.69,&#34;probability&#34;:0.995659093808592,&#34;confidence&#34;:0.991318187617184,&#34;x&#34;:0.69,&#34;y&#34;:0.991318187617184},{&#34;theta&#34;:0.7,&#34;probability&#34;:0.996791479430329,&#34;confidence&#34;:0.993582958860658,&#34;x&#34;:0.7,&#34;y&#34;:0.993582958860658},{&#34;theta&#34;:0.71,&#34;probability&#34;:0.997662228223176,&#34;confidence&#34;:0.995324456446352,&#34;x&#34;:0.71,&#34;y&#34;:0.995324456446352},{&#34;theta&#34;:0.72,&#34;probability&#34;:0.998322457253502,&#34;confidence&#34;:0.996644914507004,&#34;x&#34;:0.72,&#34;y&#34;:0.996644914507004},{&#34;theta&#34;:0.73,&#34;probability&#34;:0.998815660821706,&#34;confidence&#34;:0.997631321643412,&#34;x&#34;:0.73,&#34;y&#34;:0.997631321643412},{&#34;theta&#34;:0.74,&#34;probability&#34;:0.999178296394706,&#34;confidence&#34;:0.998356592789413,&#34;x&#34;:0.74,&#34;y&#34;:0.998356592789413},{&#34;theta&#34;:0.75,&#34;probability&#34;:0.999440452181261,&#34;confidence&#34;:0.998880904362522,&#34;x&#34;:0.75,&#34;y&#34;:0.998880904362522},{&#34;theta&#34;:0.76,&#34;probability&#34;:0.99962656110662,&#34;confidence&#34;:0.99925312221324,&#34;x&#34;:0.76,&#34;y&#34;:0.99925312221324},{&#34;theta&#34;:0.77,&#34;probability&#34;:0.999756129655175,&#34;confidence&#34;:0.99951225931035,&#34;x&#34;:0.77,&#34;y&#34;:0.99951225931035},{&#34;theta&#34;:0.78,&#34;probability&#34;:0.999844454527686,&#34;confidence&#34;:0.999688909055371,&#34;x&#34;:0.78,&#34;y&#34;:0.999688909055371},{&#34;theta&#34;:0.79,&#34;probability&#34;:0.99990330502023,&#34;confidence&#34;:0.99980661004046,&#34;x&#34;:0.79,&#34;y&#34;:0.99980661004046},{&#34;theta&#34;:0.8,&#34;probability&#34;:0.999941554197265,&#34;confidence&#34;:0.99988310839453,&#34;x&#34;:0.8,&#34;y&#34;:0.99988310839453},{&#34;theta&#34;:0.81,&#34;probability&#34;:0.999965747040655,&#34;confidence&#34;:0.99993149408131,&#34;x&#34;:0.81,&#34;y&#34;:0.99993149408131},{&#34;theta&#34;:0.82,&#34;probability&#34;:0.999980598578908,&#34;confidence&#34;:0.999961197157815,&#34;x&#34;:0.82,&#34;y&#34;:0.999961197157815},{&#34;theta&#34;:0.83,&#34;probability&#34;:0.999989419342954,&#34;confidence&#34;:0.999978838685909,&#34;x&#34;:0.83,&#34;y&#34;:0.999978838685909},{&#34;theta&#34;:0.84,&#34;probability&#34;:0.999994469208827,&#34;confidence&#34;:0.999988938417654,&#34;x&#34;:0.84,&#34;y&#34;:0.999988938417654},{&#34;theta&#34;:0.85,&#34;probability&#34;:0.999997243675202,&#34;confidence&#34;:0.999994487350403,&#34;x&#34;:0.85,&#34;y&#34;:0.999994487350403},{&#34;theta&#34;:0.86,&#34;probability&#34;:0.999998698837753,&#34;confidence&#34;:0.999997397675507,&#34;x&#34;:0.86,&#34;y&#34;:0.999997397675507},{&#34;theta&#34;:0.87,&#34;probability&#34;:0.999999422764826,&#34;confidence&#34;:0.999998845529652,&#34;x&#34;:0.87,&#34;y&#34;:0.999998845529652},{&#34;theta&#34;:0.88,&#34;probability&#34;:0.999999761697516,&#34;confidence&#34;:0.999999523395032,&#34;x&#34;:0.88,&#34;y&#34;:0.999999523395032},{&#34;theta&#34;:0.89,&#34;probability&#34;:0.999999909577602,&#34;confidence&#34;:0.999999819155204,&#34;x&#34;:0.89,&#34;y&#34;:0.999999819155204},{&#34;theta&#34;:0.9,&#34;probability&#34;:0.999999968963797,&#34;confidence&#34;:0.999999937927595,&#34;x&#34;:0.9,&#34;y&#34;:0.999999937927595},{&#34;theta&#34;:0.91,&#34;probability&#34;:0.999999990564098,&#34;confidence&#34;:0.999999981128196,&#34;x&#34;:0.91,&#34;y&#34;:0.999999981128196},{&#34;theta&#34;:0.92,&#34;probability&#34;:0.999999997530193,&#34;confidence&#34;:0.999999995060386,&#34;x&#34;:0.92,&#34;y&#34;:0.999999995060386},{&#34;theta&#34;:0.93,&#34;probability&#34;:0.999999999465214,&#34;confidence&#34;:0.999999998930428,&#34;x&#34;:0.93,&#34;y&#34;:0.999999998930428},{&#34;theta&#34;:0.94,&#34;probability&#34;:0.999999999909645,&#34;confidence&#34;:0.999999999819289,&#34;x&#34;:0.94,&#34;y&#34;:0.999999999819289},{&#34;theta&#34;:0.95,&#34;probability&#34;:0.99999999998912,&#34;confidence&#34;:0.99999999997824,&#34;x&#34;:0.95,&#34;y&#34;:0.99999999997824},{&#34;theta&#34;:0.96,&#34;probability&#34;:0.999999999999198,&#34;confidence&#34;:0.999999999998396,&#34;x&#34;:0.96,&#34;y&#34;:0.999999999998396},{&#34;theta&#34;:0.97,&#34;probability&#34;:0.999999999999973,&#34;confidence&#34;:0.999999999999945,&#34;x&#34;:0.97,&#34;y&#34;:0.999999999999945},{&#34;theta&#34;:0.98,&#34;probability&#34;:1,&#34;confidence&#34;:1,&#34;x&#34;:0.98,&#34;y&#34;:1},{&#34;theta&#34;:0.99,&#34;probability&#34;:1,&#34;confidence&#34;:1,&#34;x&#34;:0.99,&#34;y&#34;:1},{&#34;theta&#34;:1,&#34;probability&#34;:1,&#34;confidence&#34;:1,&#34;x&#34;:1,&#34;y&#34;:1}],&#34;type&#34;:&#34;line&#34;}],&#34;xAxis&#34;:{&#34;type&#34;:&#34;linear&#34;,&#34;title&#34;:{&#34;text&#34;:&#34;theta&#34;},&#34;categories&#34;:null},&#34;tooltip&#34;:{&#34;valueDecimals&#34;:4,&#34;valuePrefix&#34;:&#34;conf level = &#34;}},&#34;theme&#34;:{&#34;chart&#34;:{&#34;backgroundColor&#34;:&#34;transparent&#34;}},&#34;conf_opts&#34;:{&#34;global&#34;:{&#34;Date&#34;:null,&#34;VMLRadialGradientURL&#34;:&#34;http =//code.highcharts.com/list(version)/gfx/vml-radial-gradient.png&#34;,&#34;canvasToolsURL&#34;:&#34;http =//code.highcharts.com/list(version)/modules/canvas-tools.js&#34;,&#34;getTimezoneOffset&#34;:null,&#34;timezoneOffset&#34;:0,&#34;useUTC&#34;:true},&#34;lang&#34;:{&#34;contextButtonTitle&#34;:&#34;Chart context menu&#34;,&#34;decimalPoint&#34;:&#34;.&#34;,&#34;downloadJPEG&#34;:&#34;Download JPEG image&#34;,&#34;downloadPDF&#34;:&#34;Download PDF document&#34;,&#34;downloadPNG&#34;:&#34;Download PNG image&#34;,&#34;downloadSVG&#34;:&#34;Download SVG vector image&#34;,&#34;drillUpText&#34;:&#34;Back to {series.name}&#34;,&#34;invalidDate&#34;:null,&#34;loading&#34;:&#34;Loading...&#34;,&#34;months&#34;:[&#34;January&#34;,&#34;February&#34;,&#34;March&#34;,&#34;April&#34;,&#34;May&#34;,&#34;June&#34;,&#34;July&#34;,&#34;August&#34;,&#34;September&#34;,&#34;October&#34;,&#34;November&#34;,&#34;December&#34;],&#34;noData&#34;:&#34;No data to display&#34;,&#34;numericSymbols&#34;:[&#34;k&#34;,&#34;M&#34;,&#34;G&#34;,&#34;T&#34;,&#34;P&#34;,&#34;E&#34;],&#34;printChart&#34;:&#34;Print chart&#34;,&#34;resetZoom&#34;:&#34;Reset zoom&#34;,&#34;resetZoomTitle&#34;:&#34;Reset zoom level 1:1&#34;,&#34;shortMonths&#34;:[&#34;Jan&#34;,&#34;Feb&#34;,&#34;Mar&#34;,&#34;Apr&#34;,&#34;May&#34;,&#34;Jun&#34;,&#34;Jul&#34;,&#34;Aug&#34;,&#34;Sep&#34;,&#34;Oct&#34;,&#34;Nov&#34;,&#34;Dec&#34;],&#34;thousandsSep&#34;:&#34; &#34;,&#34;weekdays&#34;:[&#34;Sunday&#34;,&#34;Monday&#34;,&#34;Tuesday&#34;,&#34;Wednesday&#34;,&#34;Thursday&#34;,&#34;Friday&#34;,&#34;Saturday&#34;]}},&#34;type&#34;:&#34;chart&#34;,&#34;fonts&#34;:[],&#34;debug&#34;:false},&#34;evals&#34;:[],&#34;jsHooks&#34;:[]}&lt;/script&gt;
&lt;p&gt;Pick a point on the left branch of the curve. The y value gives you the level of confidence and the x value is the lower bound of the corresponding confidence interval. Move horizontally across to the right branch to read off the upper end of the confidence interval. So reading up and down the curve you can read off the confidence intervals for any value of confidence.&lt;/p&gt;
&lt;p&gt;As a check, we compute the 95% confidence interval for &lt;span class=&#34;math inline&#34;&gt;\(\theta\)&lt;/span&gt; using the normal approximation to the binomial.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;ub &amp;lt;- 8 / 20 + (1.96 / 20) * sqrt(8 * 12 / 20)
lb &amp;lt;- 8 / 20 - (1.96 / 20) * sqrt(8 * 12 / 20)
cat(&amp;quot;95% CI = [&amp;quot; , lb , &amp;quot;,&amp;quot; , ub, &amp;quot;]&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## 95% CI = [ 0.1853 , 0.6147 ]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are a Bayesian, there is a really amusing side to confidence distributions. In order to be clear that what they are doing with confidence distributions is in fact different from what Bayesians do when they choose priors, the champions of confidence distributions appeal to &lt;a href=&#34;https://plato.stanford.edu/entries/epistemology/&#34;&gt;epistomology&lt;/a&gt;, the study of knowledge and justified belief. On page (xiv) of their book, Schweder and Hjort write:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The concept of confidence distribution is rather basic, but has proved difficult for statisticians to accept. The main reason is perhaps that confidence distributions represent epistemic probability obtained from the aleatory probability of the statistical model (i.e. the chance variation in nature and society), and to face both types of probability at the same time might be challenging. The traditional Bayesian deals only with subjective probability, which is epistemic when based on knowledge, and the frequentist of the Neyman-Wald school deals only with sampling variability, that is, aleatory probability.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Please indulge me while I unpack this. Aleatory probabilities are what nature and the world give us: the decay times of alpha particles, the valuable behaviors of large populations, etc. Hard-core frequentists will only allow themselves to compute aleatory probabilities. As soon as you compute a confidence distribution or even a confidence interval, you are working with epistemic probabilities: what you believe would be true under repeated sampling that may be impossible to actually carry out. But, you are justified in doing this because these epistemic probabilities are anchored in aleatory probabilities. When Bayesians base their choice of priors on rational beliefs based on plausible evidence, they are on the same epistemic footing as frequentists computing confidence intervals. When Bayesians are capricious in choosing their priors, they are not. Asking most statisticians to think about these things gives them headaches. Thus, quietly, in work that builds bridges, ends the Bayesian vs. Frequentist controversy.&lt;/p&gt;
&lt;p&gt;Notes:&lt;br /&gt;
1. Schweder and Hjolt’s book is really worth owning. Not only does it offer a comprehensive account of confidence distributions and how they may be useful in practice, but it is also a good general reference on statistical inference.&lt;/p&gt;
&lt;ol start=&#34;2&#34; style=&#34;list-style-type: decimal&#34;&gt;
&lt;li&gt;If you are interested in exploring confidence distributions further, have a look at the &lt;a href=&#34;https://CRAN.R-project.org/package=pvaluefunctions&#34;&gt;pvaluefunctions&lt;/a&gt; and &lt;a href=&#34;https://cran.r-project.org/package=gmeta&#34;&gt;gmeta&lt;/a&gt; that are both on CRAN.&lt;/li&gt;
&lt;/ol&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2019/11/05/a-first-look-at-confidence-distributions/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>Building Interactive World Maps in Shiny</title>
      <link>https://rviews.rstudio.com/2019/10/09/building-interactive-world-maps-in-shiny/</link>
      <pubDate>Wed, 09 Oct 2019 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2019/10/09/building-interactive-world-maps-in-shiny/</guid>
      <description>
        


&lt;p&gt;&lt;em&gt;Florianne Verkroost is a PhD candidate at Nuffield College at the University of Oxford. With a passion for data science and a background in mathematics and econometrics. She applies her interdisciplinary knowledge to computationally address societal problems of inequality.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In this post, I will show you how to create interactive world maps and how to show these in the form of an R Shiny app. As the Shiny app cannot be embedded into this blog, I will direct you to the &lt;a href=&#34;https://fverkroost.shinyapps.io/interactive_worldmap_app/&#34;&gt;live app&lt;/a&gt; and show you in &lt;a href=&#34;https://github.com/fverkroost/RStudio-Blogs/blob/master/interactive_worldmap_shiny_embedded.Rmd&#34;&gt;this post&lt;/a&gt; on my GitHub how to embed a Shiny app in your R Markdown files, which is a really cool and innovative way of preparing interactive documents. To show you how to adapt the interface of the app to the choices of the users, we’ll make use of two data sources such that the user can choose what data they want to explore, and that the app adapts the possible input choices to the users’ previous choices. The data sources here are about childlessness and gender inequality, which is the focus of my PhD research, in which I computationally analyse the effects of gender and parental status on socio-economic inequalities.&lt;/p&gt;
&lt;p&gt;We’ll start by loading and cleaning the data, whereafter we will build our interactive world maps in R Shiny. Let’s first load the required packages into RStudio.&lt;/p&gt;
&lt;div id=&#34;section-importing-exploring-and-cleaning-the-data&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Importing, exploring and cleaning the data&lt;/h2&gt;
&lt;p&gt;Now, we can continue with loading our data. As we’ll make world maps, we need a way to map our data sets to geographical data containing coordinates (longitude and latitude). As different data sets have different formats for country names (e.g., “United Kingdom of Great Britain and Northern Ireland” versus “United Kingdom”), we’ll match country names to ISO3 codes to easily merge all data sets later on. Therefore, we first scrape an HTML table of country names, ISO3, ISO2 and UN codes for all countries worldwide. We use the &lt;code&gt;rvest&lt;/code&gt; package using the XPath to indicate what part of the web page contains our table of interest. We use the pipe (%&amp;gt;%) from the &lt;code&gt;magrittr&lt;/code&gt; package to feed our URL of interest into functions that read the HTML table using the XPath and convert that to a data frame in R. One can obtain the XPath by hovering over the HTML table in developer mode in the browser, and having it show the XPath.&lt;/p&gt;
&lt;p&gt;The first element in the resulting list contains our table of interest, and as the first column is empty, we delete it. Also, as you can see from the HTML table in the link, there are some rows that show the letter of the alphabet before starting with a list of countries of which the name starts with that letter. As these rows contain the particular letter in all columns, we can delete these by deleting all rows for which all columns have equal values.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(magrittr)
library(rvest)
url &amp;lt;- &amp;quot;https://www.nationsonline.org/oneworld/country_code_list.htm&amp;quot;
iso_codes &amp;lt;- url %&amp;gt;%
  read_html() %&amp;gt;%
  html_nodes(xpath = &amp;#39;//*[@id=&amp;quot;CountryCode&amp;quot;]&amp;#39;) %&amp;gt;%
  html_table()
iso_codes &amp;lt;- iso_codes[[1]][, -1]
iso_codes &amp;lt;- iso_codes[!apply(iso_codes, 1, function(x){all(x == x[1])}), ]
names(iso_codes) &amp;lt;- c(&amp;quot;Country&amp;quot;, &amp;quot;ISO2&amp;quot;, &amp;quot;ISO3&amp;quot;, &amp;quot;UN&amp;quot;)
head(iso_codes)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##          Country ISO2 ISO3  UN
## 2    Afghanistan   AF  AFG 004
## 3  Aland Islands   AX  ALA 248
## 4        Albania   AL  ALB 008
## 5        Algeria   DZ  DZA 012
## 6 American Samoa   AS  ASM 016
## 7        Andorra   AD  AND 020&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we’ll collect our first data set, which is a data set on childlessness provided by the United Nations. We download the file from the link, save it locally, and then load it into RStudio using the &lt;code&gt;read_excel()&lt;/code&gt; function in the &lt;code&gt;readxl&lt;/code&gt; package.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(readxl)
url &amp;lt;- &amp;quot;https://www.un.org/en/development/desa/population/publications/dataset/fertility/wfr2012/Data/Data_Sources/TABLE%20A.8.%20%20Percentage%20of%20childless%20women%20and%20women%20with%20parity%20three%20or%20higher.xlsx&amp;quot;
destfile &amp;lt;- &amp;quot;dataset_childlessness.xlsx&amp;quot;
download.file(url, destfile)
childlessness_data &amp;lt;- read_excel(destfile)&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;head(childlessness_data)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## # A tibble: 6 x 17
##   `United Nations… ...2  ...3  ...4  ...5  ...6  ...7  ...8  ...9  ...10
##   &amp;lt;chr&amp;gt;            &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt; &amp;lt;chr&amp;gt;
## 1 &amp;quot;TABLE  A.8. PE… &amp;lt;NA&amp;gt;  &amp;lt;NA&amp;gt;  &amp;lt;NA&amp;gt;  &amp;lt;NA&amp;gt;  &amp;lt;NA&amp;gt;  &amp;lt;NA&amp;gt;  &amp;lt;NA&amp;gt;  &amp;lt;NA&amp;gt;  &amp;lt;NA&amp;gt; 
## 2 Country          ISO … Peri… Refe… Perc… &amp;lt;NA&amp;gt;  &amp;lt;NA&amp;gt;  Perc… &amp;lt;NA&amp;gt;  &amp;lt;NA&amp;gt; 
## 3 &amp;lt;NA&amp;gt;             &amp;lt;NA&amp;gt;  &amp;lt;NA&amp;gt;  &amp;lt;NA&amp;gt;  35-39 40-44 45-49 35-39 40-44 45-49
## 4 Afghanistan      4     Earl… ..    ..    ..    ..    ..    ..    ..   
## 5 Afghanistan      4     Midd… ..    ..    ..    ..    ..    ..    ..   
## 6 Afghanistan      4     Late… 2010  2.6   2.6   2.1   93.8  94.5  94   
## # … with 7 more variables: ...11 &amp;lt;chr&amp;gt;, ...12 &amp;lt;chr&amp;gt;, ...13 &amp;lt;chr&amp;gt;,
## #   ...14 &amp;lt;chr&amp;gt;, ...15 &amp;lt;chr&amp;gt;, ...16 &amp;lt;chr&amp;gt;, ...17 &amp;lt;lgl&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can see that the childlessness data are a bit messy, especially when it comes to the first couple of rows and column names. We only want to maintain the columns that have country names, periods, and childlessness estimates for different age groups, as well as the rows that refer to data for specific countries. The resulting data look much better. Note that when we convert the childlessness percentage columns to numeric type later on, the “..” values will automatically change to NA.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;cols &amp;lt;- which(grepl(&amp;quot;childless&amp;quot;, childlessness_data[2, ]))
childlessness_data &amp;lt;- childlessness_data[-c(1:3), c(1, 3, cols:(cols + 2))]
names(childlessness_data) &amp;lt;- c(&amp;quot;Country&amp;quot;, &amp;quot;Period&amp;quot;, &amp;quot;35-39&amp;quot;, &amp;quot;40-44&amp;quot;, &amp;quot;45-49&amp;quot;)
head(childlessness_data)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## # A tibble: 6 x 5
##   Country     Period  `35-39` `40-44` `45-49`
##   &amp;lt;chr&amp;gt;       &amp;lt;chr&amp;gt;   &amp;lt;chr&amp;gt;   &amp;lt;chr&amp;gt;   &amp;lt;chr&amp;gt;  
## 1 Afghanistan Earlier ..      ..      ..     
## 2 Afghanistan Middle  ..      ..      ..     
## 3 Afghanistan Latest  2.6     2.6     2.1    
## 4 Albania     Earlier 7.2     5.5     5.2    
## 5 Albania     Middle  ..      ..      ..     
## 6 Albania     Latest  4.8     4.3     3.3&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our second data set is about measures of gender inequality, provided by the World Bank. We read this .csv file directly into RStudio from the URL link.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;gender_index_data &amp;lt;- read.csv(&amp;quot;https://s3.amazonaws.com/datascope-ast-datasets-nov29/datasets/743/data.csv&amp;quot;)
head(gender_index_data)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##   Country.ISO3 Country.Name Indicator.Id
## 1          AGO       Angola        27959
## 2          AGO       Angola        27960
## 3          AGO       Angola        27961
## 4          AGO       Angola        27962
## 5          AGO       Angola        28158
## 6          AGO       Angola        28159
##                                                           Indicator
## 1                                   Overall Global Gender Gap Index
## 2                  Global Gender Gap Political Empowerment subindex
## 3                  Global Gender Gap Political Empowerment subindex
## 4                                   Overall Global Gender Gap Index
## 5 Global Gender Gap Economic Participation and Opportunity Subindex
## 6 Global Gender Gap Economic Participation and Opportunity Subindex
##   Subindicator.Type   X2006    X2007    X2008    X2009   X2010   X2011
## 1             Index  0.6038   0.6034   0.6032   0.6353  0.6712  0.6624
## 2              Rank 81.0000  92.0000 103.0000  36.0000 24.0000 24.0000
## 3             Index  0.0696   0.0696   0.0711   0.2007  0.2901  0.2898
## 4              Rank 96.0000 110.0000 114.0000 106.0000 81.0000 87.0000
## 5              Rank 69.0000  87.0000  87.0000  96.0000 76.0000 96.0000
## 6             Index  0.5872   0.5851   0.5843   0.5832  0.6296  0.5937
##   X2012   X2013    X2014   X2015   X2016   X2018
## 1    NA  0.6659   0.6311   0.637   0.643   0.633
## 2    NA 34.0000  38.0000  38.000  40.000  58.000
## 3    NA  0.2614   0.2402   0.251   0.251   0.206
## 4    NA 92.0000 121.0000 126.000 117.000 125.000
## 5    NA 92.0000 111.0000 116.000 120.000 113.000
## 6    NA  0.6163   0.5878   0.590   0.565   0.602&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Luckily, these data are better structured than the childlessness data. The data contains gender inequality measures per year, and for convenience we add a new column with the values for the most recent year for which data are available. In this post, we’ll only look at the rank indicators rather than indices and normalized scores. We drop the Subindicator and IndicatorID columns using the &lt;code&gt;select()&lt;/code&gt; function from the &lt;code&gt;dplyr&lt;/code&gt; package, as we won’t need these further.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(dplyr)
gender_index_data[&amp;quot;RecentYear&amp;quot;] &amp;lt;- apply(gender_index_data, 1, function(x){as.numeric(x[max(which(!is.na(x)))])})
gender_index_data &amp;lt;- gender_index_data[gender_index_data$Subindicator.Type == &amp;quot;Rank&amp;quot;, ] %&amp;gt;% 
  select(-Subindicator.Type, -Indicator.Id)
names(gender_index_data) &amp;lt;- c(&amp;quot;ISO3&amp;quot;, &amp;quot;Country&amp;quot;, &amp;quot;Indicator&amp;quot;, as.character(c(2006:2016, 2018)), &amp;quot;RecentYear&amp;quot;)
head(gender_index_data)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##    ISO3 Country
## 2   AGO  Angola
## 4   AGO  Angola
## 5   AGO  Angola
## 7   AGO  Angola
## 9   AGO  Angola
## 11  AGO  Angola
##                                                                                           Indicator
## 2                                                  Global Gender Gap Political Empowerment subindex
## 4                                                                   Overall Global Gender Gap Index
## 5                                 Global Gender Gap Economic Participation and Opportunity Subindex
## 7                                                 Global Gender Gap Educational Attainment Subindex
## 9                                                    Global Gender Gap Health and Survival Subindex
## 11 Wage equality between women and men for similar work (survey data, normalized on a 0-to-1 scale)
##    2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2018 RecentYear
## 2    81   92  103   36   24   24   NA   34   38   38   40   58         58
## 4    96  110  114  106   81   87   NA   92  121  126  117  125        125
## 5    69   87   87   96   76   96   NA   92  111  116  120  113        113
## 7   107  119  122  127  125  126   NA  127  138  141  138  143        143
## 9     1    1    1    1    1    1   NA    1   61    1    1    1          1
## 11   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA  135   94         94&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we load in our world data with geographical coordinates directly from the &lt;code&gt;ggplot2&lt;/code&gt; package. These data contain geographical coordinates of all countries worldwide, which we’ll later need to plot the worldmaps.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(maps)
library(ggplot2)
world_data &amp;lt;- ggplot2::map_data(&amp;#39;world&amp;#39;)
world_data &amp;lt;- fortify(world_data)
head(world_data)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##     long   lat group order region subregion
## 1 -69.90 12.45     1     1  Aruba      &amp;lt;NA&amp;gt;
## 2 -69.90 12.42     1     2  Aruba      &amp;lt;NA&amp;gt;
## 3 -69.94 12.44     1     3  Aruba      &amp;lt;NA&amp;gt;
## 4 -70.00 12.50     1     4  Aruba      &amp;lt;NA&amp;gt;
## 5 -70.07 12.55     1     5  Aruba      &amp;lt;NA&amp;gt;
## 6 -70.05 12.60     1     6  Aruba      &amp;lt;NA&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To map our data, we need to merge the childlessness, gender gap index, and world map data. As I said before, these all have different notations for country names, which is why we’ll use the ISO3 codes. However, even between the ISO code data and the other data sets, there is discrepancy in country names. Unfortunately, to solve this, we need to manually change some country names in our data to match those in the ISO code data set. The code for doing so is long and tedious, so I won’t show that here, but for your reference you can find it &lt;a href=&#34;https://github.com/fverkroost/RStudio-Blogs/blob/master/interactive_worldmap_shiny_app.R&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now that the name changes for countries have been made, we can add the ISO3 codes to our childlessness and world map data. The gender gap index data already contain these codes, so there’s no need for us to add these there.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;childlessness_data[&amp;#39;ISO3&amp;#39;] &amp;lt;- iso_codes$ISO3[match(childlessness_data$Country, iso_codes$Country)]
world_data[&amp;quot;ISO3&amp;quot;] &amp;lt;- iso_codes$ISO3[match(world_data$region, iso_codes$Country)]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we melt the childlessness and gender gap index data into long format such that they will have similar shape and column names for merging. The &lt;code&gt;melt()&lt;/code&gt; function is included in package &lt;code&gt;reshape2&lt;/code&gt;. The goal here is to create variables that have different unique values for the different data, such that I can show you how to adapt the R Shiny app input to the users’ choices. For example, we’ll create a &lt;em&gt;DataType&lt;/em&gt; column that has value &lt;em&gt;Childlessness&lt;/em&gt; for the rows of the childlessness data and value &lt;em&gt;Gender Gap Index&lt;/em&gt; for all rows of the gender gap index data. We’ll also create a column &lt;em&gt;Period&lt;/em&gt; that contains earlier, middle and later periods for the childlessness data, and different years for the gender gap index data. As such, when the user chooses to explore the childlessness data, the input for the period will only contain the choices relevant to the childlessness data (i.e., earlier, middle, and later periods and no years). When the user chooses to explore the gender gap index data, they will only see different years as choices for the input of the period, and not earlier, middle, and later periods. The same goes for the &lt;em&gt;Indicator&lt;/em&gt; column. This may sound slightly vague at this point, but we’ll see this in practice later on when building the R Shiny app.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(reshape2)
childlessness_melt &amp;lt;- melt(childlessness_data, id = c(&amp;quot;Country&amp;quot;, &amp;quot;ISO3&amp;quot;, &amp;quot;Period&amp;quot;), 
                           variable.name = &amp;quot;Indicator&amp;quot;, value.name = &amp;quot;Value&amp;quot;)
childlessness_melt$Value &amp;lt;- as.numeric(childlessness_melt$Value)
gender_index_melt &amp;lt;- melt(gender_index_data, id = c(&amp;quot;ISO3&amp;quot;, &amp;quot;Country&amp;quot;, &amp;quot;Indicator&amp;quot;), 
                          variable.name = &amp;quot;Period&amp;quot;, value.name = &amp;quot;Value&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After melting the data and ensuring they’re in the same format, we merge them together using the &lt;code&gt;rbind()&lt;/code&gt; function, which we can do here because the data have the same column names.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;childlessness_melt[&amp;quot;DataType&amp;quot;] &amp;lt;- rep(&amp;quot;Childlessness&amp;quot;, nrow(childlessness_melt))
gender_index_melt[&amp;quot;DataType&amp;quot;] &amp;lt;- rep(&amp;quot;Gender Gap Index&amp;quot;, nrow(gender_index_melt))
df &amp;lt;- rbind(childlessness_melt, gender_index_melt)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;section-creating-an-interactive-world-map&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Creating an interactive world map&lt;/h2&gt;
&lt;p&gt;Next, it’s time to define the function that we’ll use for building our world maps. The inputs to this function are the merged data frame, the world data containing geographical coordinates, and the data type, period and indicator the user will select in the R Shiny app. We first define our own theme, &lt;code&gt;my_theme()&lt;/code&gt;, for setting the aesthetics of the plot. Next, we select only the data that the user has selected to view, resulting in &lt;em&gt;plotdf&lt;/em&gt;. We keep only the rows for which the ISO3 code has been specified (some countries, e.g., Channel Islands in the childlessness data, are not contained in the ISO code data). We then add the data the user wants to see to the geographical world data. Finally, we plot the world map. The most important part of this plot is that contained in the &lt;code&gt;geom_polygon_interactive()&lt;/code&gt; function from the &lt;code&gt;ggiraph&lt;/code&gt; package. This function draws the world map in white with grey lines, fills it up according to the value of the data selected (either childlessness or gender gap rank) in a red-to-blue color scheme set using the &lt;code&gt;brewer.pal()&lt;/code&gt; function from the &lt;code&gt;RColorBrewer&lt;/code&gt; package, and interactively shows in the tooltip the ISO3 code and value when hovering over the plot.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;worldMaps &amp;lt;- function(df, world_data, data_type, period, indicator){
  
  # Function for setting the aesthetics of the plot
  my_theme &amp;lt;- function () { 
    theme_bw() + theme(axis.text = element_text(size = 14),
                       axis.title = element_text(size = 14),
                       strip.text = element_text(size = 14),
                       panel.grid.major = element_blank(), 
                       panel.grid.minor = element_blank(),
                       panel.background = element_blank(), 
                       legend.position = &amp;quot;bottom&amp;quot;,
                       panel.border = element_blank(), 
                       strip.background = element_rect(fill = &amp;#39;white&amp;#39;, colour = &amp;#39;white&amp;#39;))
  }
  
  # Select only the data that the user has selected to view
  plotdf &amp;lt;- df[df$Indicator == indicator &amp;amp; df$DataType == data_type &amp;amp; df$Period == period,]
  plotdf &amp;lt;- plotdf[!is.na(plotdf$ISO3), ]
  
  # Add the data the user wants to see to the geographical world data
  world_data[&amp;#39;DataType&amp;#39;] &amp;lt;- rep(data_type, nrow(world_data))
  world_data[&amp;#39;Period&amp;#39;] &amp;lt;- rep(period, nrow(world_data))
  world_data[&amp;#39;Indicator&amp;#39;] &amp;lt;- rep(indicator, nrow(world_data))
  world_data[&amp;#39;Value&amp;#39;] &amp;lt;- plotdf$Value[match(world_data$ISO3, plotdf$ISO3)]
  
  # Create caption with the data source to show underneath the map
  capt &amp;lt;- paste0(&amp;quot;Source: &amp;quot;, ifelse(data_type == &amp;quot;Childlessness&amp;quot;, &amp;quot;United Nations&amp;quot; , &amp;quot;World Bank&amp;quot;))
  
  # Specify the plot for the world map
  library(RColorBrewer)
  library(ggiraph)
  g &amp;lt;- ggplot() + 
    geom_polygon_interactive(data = world_data, color = &amp;#39;gray70&amp;#39;, size = 0.1,
                                    aes(x = long, y = lat, fill = Value, group = group, 
                                        tooltip = sprintf(&amp;quot;%s&amp;lt;br/&amp;gt;%s&amp;quot;, ISO3, Value))) + 
    scale_fill_gradientn(colours = brewer.pal(5, &amp;quot;RdBu&amp;quot;), na.value = &amp;#39;white&amp;#39;) + 
    scale_y_continuous(limits = c(-60, 90), breaks = c()) + 
    scale_x_continuous(breaks = c()) + 
    labs(fill = data_type, color = data_type, title = NULL, x = NULL, y = NULL, caption = capt) + 
    my_theme()
  
  return(g)
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;section-building-an-r-shiny-app&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Building an R Shiny app&lt;/h2&gt;
&lt;p&gt;Now that we have our data and world mapping function ready and specified, we can start building our R Shiny app. (If you’re not familiar with R Shiny, I recommend that you to have a look at the &lt;a href=&#34;https://shiny.rstudio.com/tutorial/&#34;&gt;Getting Started guide&lt;/a&gt; first.) We can build our app by specifying the UI and server components. In the UI, we include a fixed user input selection where the user can choose whether they want to see the childlessness or gender gap index data. We further include dynamic inputs for the period and indicators the user wants to see. As mentioned before, these are dynamic because the choices shown will depend on the selections made by the user on previous inputs. We then use the &lt;code&gt;ggiraph&lt;/code&gt; package to output our interactive world map. We use the &lt;code&gt;sidebarLayout()&lt;/code&gt; function to show the input selections on the left side and the world map on the right side, rather than the two stacked vertically.&lt;/p&gt;
&lt;p&gt;Everything that depends on the inputs by the user needs to be specified in the server function, which in this case is not only the world map creation, but also the second and third input choices, since these depend on the previous inputs made by the user. For example, when we run the app later, we’ll see that when the user selects the childlessness data for the first input for data type, the third indicator input will only show age groups, and the text above the selector will also show “age group”, whereas when the user selects the gender gap index data, the third indicator will show different measures and the text above the selector will show “indicator” rather than “age group”.&lt;/p&gt;
&lt;p&gt;Finally, we can run our app by either clicking “Run App” in the top of our RStudio IDE, or by running&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;shinyApp(ui = ui, server = server)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Below is a screen shot of the app. You can check out the live app &lt;a href=&#34;https://fverkroost.shinyapps.io/interactive_worldmap_app/&#34;&gt;here&lt;/a&gt;. In &lt;a href=&#34;https://github.com/fverkroost/RStudio-Blogs/blob/master/interactive_worldmap_shiny_embedded.Rmd&#34;&gt;this&lt;/a&gt; post on my GitHub, you can also see how to embed a Shiny app in your R Markdown files, which is a really cool and innovative way of preparing interactive documents. Finally, the source code used to build the live app can also be found on my GitHub &lt;a href=&#34;https://github.com/fverkroost/RStudio-Blogs/blob/master/interactive_worldmap_shiny_app.R&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now try selecting different inputs and see how the input choices change when doing so. Also, don’t forget to try hovering over the world map to see different data values for different countries interactively!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;interactive_worldmap_app.png&#34; /&gt;&lt;/p&gt;
&lt;/div&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2019/10/09/building-interactive-world-maps-in-shiny/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>Multiple Hypothesis Testing in R</title>
      <link>https://rviews.rstudio.com/2019/10/02/multiple-hypothesis-testing/</link>
      <pubDate>Wed, 02 Oct 2019 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2019/10/02/multiple-hypothesis-testing/</guid>
      <description>
        


&lt;p&gt;In the &lt;a href=&#34;https://ras44.github.io/blog/2019/04/08/validating-type-i-and-ii-errors-in-a-b-tests-in-r.html&#34;&gt;first article of this series&lt;/a&gt;, we looked at understanding type I and type II errors in the context of an A/B test, and highlighted the issue of “peeking”. In the &lt;a href=&#34;https://ras44.github.io/blog/2019/08/04/calculating-always-valid-p-values-in-r.html&#34;&gt;second&lt;/a&gt;, we illustrated a way to calculate always-valid p-values that were immune to peeking. We will now explore multiple hypothesis testing, or what happens when multiple tests are conducted on the same family of data.&lt;/p&gt;
&lt;p&gt;We will set things up as before, with the false positive rate &lt;span class=&#34;math inline&#34;&gt;\(\alpha = 0.05\)&lt;/span&gt; and false negative rate &lt;span class=&#34;math inline&#34;&gt;\(\beta=0.20\)&lt;/span&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(pwr)
library(ggplot2)
set.seed(1)

mde &amp;lt;- 0.1  # minimum detectable effect
cr_a &amp;lt;- 0.25 # the expected conversion rate for group A
alpha &amp;lt;- 0.05 # the false positive rate
power &amp;lt;- 0.80 # 1-false negative rate

ptpt &amp;lt;- pwr.2p.test(h = ES.h(p1 = cr_a, p2 = (1+mde)*cr_a), 
           sig.level = alpha, 
           power = power
           )
n_obs &amp;lt;- ceiling(ptpt$n)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To illustrate the concepts in this article, we are going to use the same &lt;code&gt;monte_carlo&lt;/code&gt; utility function that we used previously:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;#
# monte carlo runs n_simulations and calls the callback function each time with the ... optional args
#
monte_carlo &amp;lt;- function(n_simulations, callback, ...){
  simulations &amp;lt;- 1:n_simulations

  sapply(1:n_simulations, function(x){
    callback(...)
  })
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’ll use the &lt;code&gt;monte_carlo&lt;/code&gt; utility function to run 1000 experiments, measuring whether the p.value is less than alpha &lt;strong&gt;after &lt;code&gt;n_obs&lt;/code&gt; observations&lt;/strong&gt;. If it is, we reject the null hypothesis. We will set the effect size to 0; we know that there is no effect and that the null hypothesis is globally true. In this case, we expect about 50 rejections and about 950 non-rejections, since 50/1000 would represent our expected maximum false positive rate of 5%.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;set.seed(1)

# make our &amp;quot;true&amp;quot; effect zero: the null hypothesis is always true
effect &amp;lt;- 0
cr_b &amp;lt;- (1+effect)*cr_a
observations &amp;lt;- 2*n_obs

reject_at_i &amp;lt;- function(observations, i){
  conversions_a &amp;lt;- rbinom(observations, 1, cr_a)
  conversions_b &amp;lt;- rbinom(observations, 1, cr_b)
  ( prop.test(c(sum(conversions_a[1:i]),sum(conversions_b[1:i])), c(i,i))$p.value ) &amp;lt; alpha
}

# run the simulation
rejected.H0 &amp;lt;- monte_carlo(1000, 
                           callback=reject_at_i,
                           observations=n_obs,
                           i=n_obs
                           )

# output the rejection table
table(rejected.H0)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## rejected.H0
## FALSE  TRUE 
##   939    61&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In practice, we don’t usually test the same thing 1000 times; instead, we test it once and state that there is a maximum 5% chance that we have falsely said there was an effect when there wasn’t one&lt;a href=&#34;#fn1&#34; class=&#34;footnote-ref&#34; id=&#34;fnref1&#34;&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;div id=&#34;the-family-wise-error-rate-fwer&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;The Family-Wise Error Rate (FWER)&lt;/h2&gt;
&lt;p&gt;Now imagine we test two separate statistics using the same source data, with each test constrained by the same &lt;span class=&#34;math inline&#34;&gt;\(\alpha\)&lt;/span&gt; and &lt;span class=&#34;math inline&#34;&gt;\(\beta\)&lt;/span&gt; as before. What is the probability that we will detect &lt;em&gt;at least one&lt;/em&gt; false positive considering the results of both tests? This is known as the family-wise error rate (FWER&lt;a href=&#34;#fn2&#34; class=&#34;footnote-ref&#34; id=&#34;fnref2&#34;&gt;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt;&lt;a href=&#34;#fn3&#34; class=&#34;footnote-ref&#34; id=&#34;fnref3&#34;&gt;&lt;sup&gt;3&lt;/sup&gt;&lt;/a&gt;), and would apply to the case where a researcher claims there is a difference between the populations if any of the tests yields a positive result. It’s clear that this could present issues, as the &lt;a href=&#34;https://en.wikipedia.org/wiki/Family-wise_error_rate&#34;&gt;family-wise error rate&lt;/a&gt; Wikipedia page illustrates:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Suppose the treatment is a new way of teaching writing to students, and the control is the standard way of teaching writing. Students in the two groups can be compared in terms of grammar, spelling, organization, content, and so on. As more attributes are compared, it becomes increasingly likely that the treatment and control groups will appear to differ on at least one attribute due to random sampling error alone.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What is the FWER for the two tests? To calculate the probability that &lt;em&gt;at least one&lt;/em&gt; false positive will arise in our two-test example, consider that the probability that one test will not reject the null is &lt;span class=&#34;math inline&#34;&gt;\(1-\alpha\)&lt;/span&gt;. Thus, the probability that both tests will not reject the null is &lt;span class=&#34;math inline&#34;&gt;\((1-\alpha)^2)\)&lt;/span&gt; and the probability that &lt;em&gt;at least one&lt;/em&gt; test will reject the null is &lt;span class=&#34;math inline&#34;&gt;\(1-(1-\alpha)^2\)&lt;/span&gt;. For &lt;span class=&#34;math inline&#34;&gt;\(m\)&lt;/span&gt; tests, this generalizes to &lt;span class=&#34;math inline&#34;&gt;\(1-(1-\alpha)^m\)&lt;/span&gt;. With &lt;span class=&#34;math inline&#34;&gt;\(\alpha=0.05\)&lt;/span&gt; and &lt;span class=&#34;math inline&#34;&gt;\(m=2\)&lt;/span&gt;, we have:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;m&amp;lt;-2
1-(1-alpha)^m&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] 0.0975&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s see if we can produce the same result with a Monte Carlo simulation. We will run the Monte Carlo for &lt;code&gt;n_trials&lt;/code&gt; and run &lt;code&gt;n_tests_per_trial&lt;/code&gt;. For each trial, if &lt;em&gt;at least one&lt;/em&gt; of the &lt;code&gt;n_tests_per_trial&lt;/code&gt; results in a rejection of the null, we consider that the trial rejects the null. We should see that about 1 in 10 trials reject the null. This is implemented below:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;set.seed(1)
n_tests_per_trial &amp;lt;- 2
n_trials &amp;lt;- 1000
rejects &amp;lt;- 0

for(i in 1:n_trials){
  # run the sim
  rejected.H0 &amp;lt;- monte_carlo(n_tests_per_trial,
                             callback=reject_at_i,
                             observations=n_obs,
                             i=n_obs
                             )
  if(!is.na(table(rejected.H0)[2])) {
    rejects &amp;lt;- rejects + 1
  }
}

# Calculate FWER
rejects/n_trials&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] 0.103&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both results show that evaluating two tests on the same family of data will lead to a ~10% chance that a researcher will claim a “significant” result if they look for either test to reject the null. Any claim there is a maximum 5% false positive rate would be mistaken. As an exercise, verify that doing the same on &lt;span class=&#34;math inline&#34;&gt;\(m=4\)&lt;/span&gt; tests will lead to an ~18% chance!&lt;/p&gt;
&lt;p&gt;A bad testing platform would be one that claims a maximum 5% false positive rate when any one of multiple tests on the same family of data show significance at the 5% level. Clearly, if a researcher is going to claim that the FWER is no more than &lt;span class=&#34;math inline&#34;&gt;\(\alpha\)&lt;/span&gt;, then they must control for the FWER and carefully consider how individual tests reject the null.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;controlling-the-fwer&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Controlling the FWER&lt;/h2&gt;
&lt;p&gt;There are many ways to control for the FWER, and the most conservative is the &lt;a href=&#34;https://en.wikipedia.org/wiki/Bonferroni_correction&#34;&gt;Bonferroni correction&lt;/a&gt;. The “Bonferroni method” will reject null hypotheses if &lt;span class=&#34;math inline&#34;&gt;\(p_i \le \frac{\alpha}{m}\)&lt;/span&gt;. Let’s switch our &lt;code&gt;reject_at_i&lt;/code&gt; function for a &lt;code&gt;p_value_at_i&lt;/code&gt; function, and then add in the Bonferroni correction:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;set.seed(1)

p_value_at_i &amp;lt;- function(observations, i){
  conversions_a &amp;lt;- rbinom(observations, 1, cr_a)
  conversions_b &amp;lt;- rbinom(observations, 1, cr_b)

  prop.test(c(sum(conversions_a[1:i]),sum(conversions_b[1:i])), c(i,i))$p.value
}

n_tests_per_trial &amp;lt;- 2
n_trials &amp;lt;- 1000
rejects &amp;lt;- 0

for(i in 1:n_trials){
  # run n_tests_per_trial
  p_values &amp;lt;- monte_carlo(n_tests_per_trial,
                             callback=p_value_at_i,
                             observations=n_obs,
                             i=n_obs
                             )
  # Bonferroni: adjust the p-values and reject any cases with p-values &amp;lt;= alpha
  rejects &amp;lt;- rejects + sum(any(p_values*n_tests_per_trial &amp;lt;= alpha))

}

# Calculate FWER
rejects/n_trials&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] 0.055&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the Bonferroni correction, we see that the realized false positive rate is back near the 5% level. Note that we use &lt;code&gt;any(...)&lt;/code&gt; to add 1 if any hypothesis is rejected.&lt;/p&gt;
&lt;p&gt;Until now, we have only shown that the Bonferroni correction controls the FWER for the case that all null hypotheses are actually true: the effect is set to zero. This is called controlling in the &lt;em&gt;weak sense&lt;/em&gt;. Next, let’s use R’s &lt;code&gt;p.adjust&lt;/code&gt; function to illustrate the Bonferroni and &lt;a href=&#34;https://en.wikipedia.org/wiki/Holm%E2%80%93Bonferroni_method&#34;&gt;Holm&lt;/a&gt; adjustments to the p-values:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;set.seed(1)

n_tests_per_trial &amp;lt;- 2
n_trials &amp;lt;- 1000

res_bf &amp;lt;- c()
res_holm &amp;lt;- c()

for(i in 1:n_trials){

  # run n_tests_per_trial
  p_values &amp;lt;- monte_carlo(n_tests_per_trial,
                          callback=p_value_at_i,
                          observations=n_obs,
                          i=n_obs
                          )
  # Bonferroni: adjust the p-values and reject/accept
  bf_reject &amp;lt;- p.adjust(p_values, &amp;quot;bonferroni&amp;quot;) &amp;lt;= alpha
  res_bf &amp;lt;- c(res_bf, sum(any(bf_reject)))

  # Holm: adjust the p-values and reject/accept
  holm_reject &amp;lt;- p.adjust(sort(p_values), &amp;quot;holm&amp;quot;) &amp;lt;= alpha
  res_holm &amp;lt;- c(res_holm, sum(any(holm_reject)))

}

# Calculate FWER
sum(res_bf)/length(res_bf)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] 0.055&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;sum(res_holm)/length(res_holm)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] 0.055&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We see that the Holm correction is very similar to the Bonferroni correction in the case that the null hypothesis is always true.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;strongly-controlling-the-fwer&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Strongly controlling the FWER&lt;/h1&gt;
&lt;p&gt;Both the Bonferroni and Holm corrections guarantee that the FWER is controlled in the &lt;em&gt;strong sense&lt;/em&gt;, in which we have any configuration of true and non-true null hypothesis. This is ideal, because in reality, we do not know if there is an effect or not.&lt;/p&gt;
&lt;p&gt;The Holm correction is uniformly more powerful than the Bonferroni correction, meaning that in the case that there &lt;em&gt;is&lt;/em&gt; an effect and the null is false, using the Holm correction will be more likely to detect positives.&lt;/p&gt;
&lt;p&gt;Let’s test this by randomly setting the effect size to the minimum detectable effect in about half the cases. Note the slightly modified &lt;code&gt;p_value_at_i&lt;/code&gt; function as well as the &lt;code&gt;null_true&lt;/code&gt; variable, which will randomly decide if there is a minimum detectable effect size or not for that particular trial.&lt;/p&gt;
&lt;p&gt;Note that in the below example, we will not calculate the FWER using the same &lt;code&gt;any(...)&lt;/code&gt; construct from the previous code segments. If we were to do this, we would see that the both corrections have the same FWER &lt;em&gt;and&lt;/em&gt; the same power (since the outcome of the trial is then decided by whether at least one of the hypotheses was rejected for the trial). Instead, we will tabulate the result for each of the hypotheses. We should see the same false positive rate&lt;a href=&#34;#fn4&#34; class=&#34;footnote-ref&#34; id=&#34;fnref4&#34;&gt;&lt;sup&gt;4&lt;/sup&gt;&lt;/a&gt;, but great power for the Holm method.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;set.seed(1)

p_value_at_i &amp;lt;- function(observations, i, cr_a, cr_b){
  conversions_a &amp;lt;- rbinom(observations, 1, cr_a)
  conversions_b &amp;lt;- rbinom(observations, 1, cr_b)

  prop.test(c(sum(conversions_a[1:i]),sum(conversions_b[1:i])), c(i,i))$p.value
}

n_tests_per_trial &amp;lt;- 2
n_trials &amp;lt;- 1000

res_bf &amp;lt;- c()
res_holm &amp;lt;- c()

for(i in 1:n_trials){
  null_true &amp;lt;- rbinom(1,1,prob = 0.5)
  effect &amp;lt;- mde * null_true
  cr_b &amp;lt;- (1+effect)*cr_a

  # run n_tests_per_trial
  p_values &amp;lt;- monte_carlo(n_tests_per_trial,
                          callback=p_value_at_i,
                          observations=n_obs,
                          i=n_obs,
                          cr_a=cr_a,
                          cr_b=cr_b
                          )
  # Bonferroni: adjust the p-values and reject/accept
  reject_bf &amp;lt;- p.adjust(p_values, &amp;quot;bonferroni&amp;quot;) &amp;lt;= alpha
  for(r in reject_bf){
    res_bf &amp;lt;- rbind(res_bf, c(r, null_true))
  }

  # Holm: adjust the p-values and reject/accept
  reject_holm &amp;lt;- p.adjust(sort(p_values), &amp;quot;holm&amp;quot;) &amp;lt;= alpha
  for(r in reject_holm){
    res_holm &amp;lt;- rbind(res_holm, c(r, null_true))
  }
  
}

# the rows of the table represent the test result
# while the columns represent the null truth
table_bf &amp;lt;- table(Test=res_bf[,1], Null=res_bf[,2])
table_holm &amp;lt;- table(Test=res_holm[,1], Null=res_holm[,2])

# False positive rate
fpr_bf &amp;lt;- table_bf[&amp;#39;1&amp;#39;,&amp;#39;0&amp;#39;]/sum(table_bf[,&amp;#39;0&amp;#39;])
fpr_holm &amp;lt;- table_holm[&amp;#39;1&amp;#39;,&amp;#39;0&amp;#39;]/sum(table_holm[,&amp;#39;0&amp;#39;])

print(paste0(&amp;quot;FPR Bonferroni: &amp;quot;, round(fpr_bf,3), &amp;quot; FPR Holm: &amp;quot;, round(fpr_holm,3)))&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] &amp;quot;FPR Bonferroni: 0.029 FPR Holm: 0.029&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# Power
power_bf &amp;lt;- table_bf[&amp;#39;1&amp;#39;,&amp;#39;1&amp;#39;]/sum(table_bf[,&amp;#39;1&amp;#39;])
power_holm &amp;lt;- table_holm[&amp;#39;1&amp;#39;,&amp;#39;1&amp;#39;]/sum(table_holm[,&amp;#39;1&amp;#39;])

print(paste0(&amp;quot;Power Bonferroni: &amp;quot;, round(power_bf,3), &amp;quot; Power Holm: &amp;quot;, round(power_holm,3)))&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] &amp;quot;Power Bonferroni: 0.713 Power Holm: 0.774&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# Comparing the Power of Holm vs. Bonferroni
print(paste0(&amp;quot;Power Holm/Power Bonferroni: &amp;quot;, round(power_holm/power_bf,3)))&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] &amp;quot;Power Holm/Power Bonferroni: 1.084&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Indeed, we observe that while the realized false positive rates of both the Bonferroni and Holm methods are very similar, the Holm method has greater power. These corrections essentially reduce our threshold for each test so that across the family of tests, we produce false positives with a probability of no more than &lt;span class=&#34;math inline&#34;&gt;\(\alpha\)&lt;/span&gt;. This comes at the expense of a reduction in power from the optimal power (&lt;span class=&#34;math inline&#34;&gt;\(1-\beta\)&lt;/span&gt;).&lt;/p&gt;
&lt;p&gt;We have illustrated two methods for deciding what null hypotheses in a family of tests to reject. The Bonferroni method rejects hypotheses at the &lt;span class=&#34;math inline&#34;&gt;\(\alpha/m\)&lt;/span&gt; level. The Holm method has a more involved algorithm for which hypotheses to reject. The Bonferroni and Holm methods have the property that they &lt;em&gt;do&lt;/em&gt; control the FWER at &lt;span class=&#34;math inline&#34;&gt;\(\alpha\)&lt;/span&gt;, and Holm is uniformly more powerful than Bonferroni.&lt;/p&gt;
&lt;p&gt;This raises an interesting question: What if we are not concerned about controlling the probability of detecting at least one false positive, but something else? We might be more interested in controlling the expected proportion of false discoveries amongst all discoveries, known as the false discovery rate. As a quick preview, let’s calculate the false discovery rate for our two cases:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;table_holm&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##     Null
## Test   0   1
##    0 959 229
##    1  29 783&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;table_bf&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##     Null
## Test   0   1
##    0 959 290
##    1  29 722&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;fdr_holm &amp;lt;- table_holm[&amp;#39;1&amp;#39;,&amp;#39;0&amp;#39;]/sum(table_holm[&amp;#39;1&amp;#39;,])
fdr_holm&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] 0.03571&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;fdr_bf &amp;lt;- table_bf[&amp;#39;1&amp;#39;,&amp;#39;0&amp;#39;]/sum(table_bf[&amp;#39;1&amp;#39;,])
fdr_bf&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] 0.03862&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By choosing to control for a metric other than the FWER, we may be able to produce results with power closer to the optimal power (&lt;span class=&#34;math inline&#34;&gt;\(1-\beta\)&lt;/span&gt;). We will look at the false discovery rate and other measures in a future article.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Roland Stevenson is a data scientist and consultant who may be reached on &lt;a href=&#34;https://www.linkedin.com/in/roland-stevenson/&#34;&gt;LinkedIn&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;footnotes&#34;&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id=&#34;fn1&#34;&gt;&lt;p&gt;And a maximum 20% chance that we said there wasn’t an effect when there was one.&lt;a href=&#34;#fnref1&#34; class=&#34;footnote-back&#34;&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id=&#34;fn2&#34;&gt;&lt;p&gt;Hochberg, Y.; Tamhane, A. C. (1987). Multiple Comparison Procedures. New York: Wiley. p. 5. ISBN 978-0-471-82222-6&lt;a href=&#34;#fnref2&#34; class=&#34;footnote-back&#34;&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id=&#34;fn3&#34;&gt;&lt;p&gt;&lt;a href=&#34;https://www.jstor.org/stable/2336325?seq=1#page_scan_tab_contents&#34;&gt;A sharper Bonferroni procedure for multiple tests of significance&lt;/a&gt;&lt;a href=&#34;#fnref3&#34; class=&#34;footnote-back&#34;&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id=&#34;fn4&#34;&gt;&lt;p&gt;Verify this by inspecting the &lt;code&gt;table_bf&lt;/code&gt; and &lt;code&gt;table_holm&lt;/code&gt; variables.&lt;a href=&#34;#fnref4&#34; class=&#34;footnote-back&#34;&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2019/10/02/multiple-hypothesis-testing/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>R/Medicine 2019 Workshops</title>
      <link>https://rviews.rstudio.com/2019/09/12/r-medicine-2019-workshops/</link>
      <pubDate>Thu, 12 Sep 2019 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2019/09/12/r-medicine-2019-workshops/</guid>
      <description>
        &lt;p&gt;&lt;a href=&#34;https://r-medicine.com/&#34;&gt;R/Medicine 2019&lt;/a&gt; kicked off on Thursday with two outstanding workshops. It was difficult to choose between the two, but fortunately both presenters developed rich sets of materials that are available online.&lt;/p&gt;

&lt;p&gt;Alison Hill delivered &lt;a href=&#34;https://rmd4medicine.netlify.com/&#34;&gt;R Markdown for Medicine&lt;/a&gt; with an elegant HTML exposition masterfully created to cultivate beginners while still engaging experienced R Markdown users.
&lt;img src=&#34;/post/2019-09-12-rmedicine_files/surgery.jpg&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://unsplash.com/photos/FvNp_SY4kF0&#34;&gt;Photo by Samuel Zeller on Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In four sections: (1) &lt;a href=&#34;https://rmd4medicine.netlify.com/materials/01-rmd-anatomy/&#34;&gt;R Markdown Anatomy&lt;/a&gt;, (2) &lt;a href=&#34;https://rmd4medicine.netlify.com/materials/02-output-tables/&#34;&gt;Outputs and Tables&lt;/a&gt;, (3) &lt;a href=&#34;https://rmd4medicine.netlify.com/materials/03/&#34;&gt;Graphics for Communication&lt;/a&gt; and (4)
&lt;a href=&#34;https://rmd4medicine.netlify.com/materials/04-data-workflows/&#34;&gt;Data and Workflows&lt;/a&gt; she developed aspects of R Markdown aimed at statisticians and clinicians writing medical document which should also delight a wide audience of R Markdown users.&lt;/p&gt;

&lt;p&gt;In the parallel session, Elizabeth (Beth) Atkinson distilled years of experience &lt;a href=&#34;https://github.com/bethatkinson/rmed2019_surv&#34;&gt;Wrangling survival data&lt;/a&gt; at the Mayo Clinic while presenting new functionality from version 3.0 of Terry Therneau&amp;rsquo;s &lt;code&gt;Survival&lt;/code&gt; package which contains significant new material on multi-state models. (Version 3.0 is expected to make it to CRAN very soon, but if you can&amp;rsquo;t wait, you can install the new version from GitHub with: &lt;code&gt;install_github(&amp;quot;therneau/survival&amp;quot;, dependencies=TRUE)&lt;/code&gt;. Terry, who will be delivering the opening keynote presentation, also attended the workshop. It was a rare treat to hear Beth and Terry discuss best practices, pitfalls and common errors while fielding questions from the attendees. Beth assembled so much material it will take a &amp;ldquo;month of Sundays&amp;rdquo; to work through it all, but I doubt that there is a better source of material anywhere that makes the special difficulties of wrangling survival data more easily accessible. The following gem shows up early in the presentation.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;/post/2019-09-12-rmedicine_files/kp.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

&lt;p&gt;R/Medicine is off to a great start.&lt;/p&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2019/09/12/r-medicine-2019-workshops/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>Calculating Always-Valid p-values in R</title>
      <link>https://rviews.rstudio.com/2019/08/22/calculating-always-valid-p-values-in-r/</link>
      <pubDate>Thu, 22 Aug 2019 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2019/08/22/calculating-always-valid-p-values-in-r/</guid>
      <description>
        


&lt;p&gt;In this post, we will develop a framework for always-valid inference based on the paper &lt;a href=&#34;https://arxiv.org/pdf/1512.04922.pdf&#34;&gt;Always Valid Inference: Continuous Monitoring of A/B Tests&lt;/a&gt; (2019 Johari, Pekelis, Walsh). Using an always-valid p-value allows us to continuously monitor A/B tests, and potentially stop the test early in a valid way&lt;a href=&#34;#fn1&#34; class=&#34;footnote-ref&#34; id=&#34;fnref1&#34;&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In section 5 of the paper, the authors propose their method for calculating always-valid p-values: the mixture sequential ratio probability test (mSPRT), first introduced by Robbins (1970). To keep this post brief, we will not do the paper’s theoretical foundations justice. Instead, we will focus on the most important equations, which we will use to produce always valid p-values in our R code. For those uninterested in the math, feel free to skip ahead.&lt;/p&gt;
&lt;p&gt;The paper makes some basic assumptions about the data and its functional form: it is real valued and drawn from a single-parameter exponential family where tests are of the parameter &lt;span class=&#34;math inline&#34;&gt;\(\theta\)&lt;/span&gt;. The mSPRT is parameterized by mixing distribution &lt;span class=&#34;math inline&#34;&gt;\(H\)&lt;/span&gt; over &lt;span class=&#34;math inline&#34;&gt;\(\Theta\)&lt;/span&gt;, an open interval that contains all &lt;span class=&#34;math inline&#34;&gt;\(\theta\)&lt;/span&gt;. Given an observed sample average &lt;span class=&#34;math inline&#34;&gt;\(s_n\)&lt;/span&gt; at time &lt;span class=&#34;math inline&#34;&gt;\(n\)&lt;/span&gt;, the mixture likelihood ratio of &lt;span class=&#34;math inline&#34;&gt;\(\theta\)&lt;/span&gt; against &lt;span class=&#34;math inline&#34;&gt;\(\theta_0\)&lt;/span&gt; with respect to H is defined as:&lt;/p&gt;
&lt;p&gt;&lt;span class=&#34;math inline&#34;&gt;\(\Lambda_{n}^{H}(s_{n}) = \displaystyle\int_{\Theta}\bigg(\frac{f_{\theta}(s_{n})}{f_{\theta_{0}}(s_{n})}\bigg)^{n} dH(\theta)\)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We are told to calculate the p-values as:&lt;/p&gt;
&lt;p&gt;&lt;span class=&#34;math inline&#34;&gt;\(p_{0} = 1; p_{n} = min\{p_{n-1}, 1/\Lambda_{n}^{H}\}\)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So, if we have a mixing distribution &lt;span class=&#34;math inline&#34;&gt;\(H(\theta)\)&lt;/span&gt; and iterate over it, we can calculate &lt;span class=&#34;math inline&#34;&gt;\(\Lambda_{n}^{H}(s_{n})\)&lt;/span&gt; for the sample average we have observed at time &lt;span class=&#34;math inline&#34;&gt;\(n\)&lt;/span&gt;. If &lt;span class=&#34;math inline&#34;&gt;\(\Lambda_{n}^{H}\)&lt;/span&gt; is ever greater than &lt;span class=&#34;math inline&#34;&gt;\(\alpha\)&lt;/span&gt;, our tolerance for the false positive rate &lt;span class=&#34;math inline&#34;&gt;\(p_n\)&lt;/span&gt; will exceed &lt;span class=&#34;math inline&#34;&gt;\(alpha\)&lt;/span&gt;, and we can stop the test and reject the null hypothesis.&lt;/p&gt;
&lt;p&gt;We will implement this for a simple comparison of two binomial distributions first, where some convenient results make &lt;span class=&#34;math inline&#34;&gt;\(p_{n}\)&lt;/span&gt; easier to calculate. In section 6.1, the paper describes the deployment for two-stream p-values. In this case, we are looking at two streams of &lt;em&gt;Bernoulli&lt;/em&gt; data: two streams of ones and zeros describing whether we observed a conversion or an abandonment in each stream.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://en.wikipedia.org/wiki/Central_limit_theorem&#34;&gt;central limit theorem&lt;/a&gt; tells us that the distribution of the average number of conversions will be approximately normal after a large number of observations (say, more than 100). What we want to know is: is there a statistically significant difference between the measured averages of the two streams? Put another way: is the difference between the measured averages of the two streams significantly different from zero? It is this second formulation that we will implement.&lt;/p&gt;
&lt;p&gt;We will skip the derivation for normal data and say that if we have two streams of Bernoulli data, &lt;span class=&#34;math inline&#34;&gt;\(A_n\)&lt;/span&gt; and &lt;span class=&#34;math inline&#34;&gt;\(B_n\)&lt;/span&gt;, that each yield approximately normal average conversion rates &lt;span class=&#34;math inline&#34;&gt;\(\mu_{A,n}\)&lt;/span&gt; and &lt;span class=&#34;math inline&#34;&gt;\(\mu_{B,n}\)&lt;/span&gt; after &lt;span class=&#34;math inline&#34;&gt;\(n\)&lt;/span&gt; observations, we can take &lt;span class=&#34;math inline&#34;&gt;\(f_\theta(s_n) \approx N(\theta, \sigma^2)\)&lt;/span&gt; and use a normal mixing distribution &lt;span class=&#34;math inline&#34;&gt;\(H=N(0,\tau^2)\)&lt;/span&gt; in &lt;span class=&#34;math inline&#34;&gt;\(\Lambda_n^H\)&lt;/span&gt; to decide if &lt;span class=&#34;math inline&#34;&gt;\(\theta_n\)&lt;/span&gt; is significantly different from zero. Conveniently, this mixing distribution yields a closed-form formula for &lt;span class=&#34;math inline&#34;&gt;\(\Lambda_n^H\)&lt;/span&gt;:&lt;/p&gt;
&lt;p&gt;&lt;span class=&#34;math inline&#34;&gt;\(\Lambda_n^H = \sqrt{\frac{\sigma_n^2}{\sigma_n^2+n\tau^2}} exp\bigg\{\frac{n^2\tau^2(\theta_n)^2}{2\sigma_n^2(\sigma_n^2+n\tau^2)}\bigg\}\)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Let’s look at the variables in the above equation. We can calculate &lt;span class=&#34;math inline&#34;&gt;\(\theta_n = \mu_{A,n}-\mu_{B,n}\)&lt;/span&gt; from our streams of Bernoulli data by calculating the average conversion rates at &lt;span class=&#34;math inline&#34;&gt;\(n\)&lt;/span&gt; for each stream. We can also use the fact that the variance &lt;span class=&#34;math inline&#34;&gt;\(\nu\)&lt;/span&gt; of a binomial distribution with mean &lt;span class=&#34;math inline&#34;&gt;\(\mu\)&lt;/span&gt; is &lt;span class=&#34;math inline&#34;&gt;\(\nu = \mu(1-\mu)\)&lt;/span&gt;, and that the variance of the sum of two random binomials is the sum of their variances:&lt;/p&gt;
&lt;p&gt;&lt;span class=&#34;math inline&#34;&gt;\(\nu_{AB} = \nu_A + \nu_B = \mu_{A,n}(1-\mu_{A,n}) + \mu_{B,n}(1-\mu_{B,n}) = \sigma_n^2\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;One lingering issue is: how do we define &lt;span class=&#34;math inline&#34;&gt;\(\tau^2\)&lt;/span&gt;? The answer is that we can choose any &lt;span class=&#34;math inline&#34;&gt;\(\tau^2\)&lt;/span&gt; we would like, though experience will show it is best if &lt;span class=&#34;math inline&#34;&gt;\(\tau^2\)&lt;/span&gt; is on the order of &lt;span class=&#34;math inline&#34;&gt;\(\sigma^2\)&lt;/span&gt;. Later we will explore what different &lt;span class=&#34;math inline&#34;&gt;\(\tau^2\)&lt;/span&gt; values yield, and how there is an optimal &lt;span class=&#34;math inline&#34;&gt;\(\tau^2\)&lt;/span&gt; to choose.&lt;/p&gt;
&lt;p&gt;Let’s convert the above into code:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# First we define the function that calculates lambda
# given n, the means at n of A and B, and tau_squared 
lambda &amp;lt;- function(n, mu_a_n, mu_b_n, tau_sq){
  v_n &amp;lt;- (mu_a_n*(1-mu_a_n) + mu_b_n*(1-mu_b_n))
  nts &amp;lt;- n*tau_sq
  if(v_n == 0){
    return(1.0)
  }
  else {
    return(
      sqrt((v_n)/(v_n+nts))*
        exp(
          ((n*nts)*(mu_a_n-mu_b_n)^2)/
            ((2.0*v_n)*(v_n+nts))
        )
    )
  }
}

# Next we will calculate the always valid p-values at each n
calc_avpvs &amp;lt;- function(n_obs, cr_a_obs, cr_b_obs, tau_sq = 0.1){
  p_n &amp;lt;- rep(1.0,n_obs)

  for (i in 2:n_obs) {
    mu_a_n &amp;lt;- mean(cr_a_obs[1:i])
    mu_b_n &amp;lt;- mean(cr_b_obs[1:i])
    p_n[[i]] &amp;lt;- min(p_n[[i-1]],1/lambda(i,mu_a_n, mu_b_n, tau_sq))
  }
  
  p_n
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, let’s create a test example, similar to our previous post, and set an effect size that is twice the minimum detectable effect. In this case, we should expect that after having observed the “correct” number of observations given by the power calculation, there is a very high probability that we’ll see an effect.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(pwr)
library(ggplot2)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Warning: package &amp;#39;ggplot2&amp;#39; was built under R version 3.5.2&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;set.seed(5)

mde &amp;lt;- 0.1  # minimum detectable effect
cr_a &amp;lt;- 0.25 # the expected conversion rate for group A
alpha &amp;lt;- 0.05 # the false positive rate
power &amp;lt;- 0.80 # 1-false negative rate

ptpt &amp;lt;- pwr.2p.test(h = ES.h(p1 = cr_a, p2 = (1+mde)*cr_a), 
                    sig.level = alpha, 
                    power = power
)
n_obs &amp;lt;- ceiling(ptpt$n)

# make our &amp;quot;true&amp;quot; effect 1.5x larger than the mde
# this should yield a conclusive test result
effect &amp;lt;- 1.5*mde
cr_b &amp;lt;- (1+effect)*cr_a
observations &amp;lt;- 2*n_obs

# two streams of {0,1} conversions
conversions_a &amp;lt;- rbinom(observations, 1, cr_a)
conversions_b &amp;lt;- rbinom(observations, 1, cr_b)

# now we&amp;#39;ll calculate the always-valid p-values
avpvs &amp;lt;- calc_avpvs(observations, conversions_a, conversions_b)


# And we&amp;#39;ll calculate &amp;quot;regular&amp;quot; p-values as well
tt &amp;lt;- sapply(10:observations, function(x){
  t.test(conversions_a[1:x],conversions_b[1:x])$p.value
})

tt &amp;lt;- data.frame(p.value = unlist(tt))

# for plots
conf_95 &amp;lt;- data.frame( x = c(-Inf, Inf), y = 0.95 )
obs_limit_line &amp;lt;- data.frame( x = n_obs, y = c(-Inf, Inf) )

# plot the evolution of p-values and always-valid p-values
ggplot(tt, aes(x=seq_along(p.value), y=1-p.value)) + 
  geom_line() + 
  geom_line(aes(x, y, color=&amp;quot;alpha=5%&amp;quot;), linetype=3, conf_95) + 
  geom_line(aes(x, y, color=&amp;quot;end of test&amp;quot;), linetype=4, obs_limit_line) +
  geom_line(data=data.frame(x=seq(1:observations),y=1-avpvs), aes(x=x,y=y, color=&amp;quot;avpv&amp;quot;)) +
  xlab(&amp;quot;Observation (n)&amp;quot;) +
  scale_color_discrete(name = &amp;quot;Legend&amp;quot;) +
  ylim(c(0,1))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;/post/2019-08-20-calculating-always-valid-p-values-in-r/index_files/figure-html/unnamed-chunk-2-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;In this case, we see that by using the always valid p-value, we would be able to terminate early while still controlling the false positive rate at &lt;span class=&#34;math inline&#34;&gt;\(\alpha\)&lt;/span&gt;. Let’s look at what happens when there is no effect:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;effect &amp;lt;- 0
cr_b &amp;lt;- (1+effect)*cr_a
observations &amp;lt;- 2*n_obs

# two streams of {0,1} conversions
conversions_a &amp;lt;- rbinom(observations, 1, cr_a)
conversions_b &amp;lt;- rbinom(observations, 1, cr_b)

# now we&amp;#39;ll calculate the always-valid p-values
avpvs &amp;lt;- calc_avpvs(observations, conversions_a, conversions_b)

# And we&amp;#39;ll calculate &amp;quot;regular&amp;quot; p-values as well
tt &amp;lt;- sapply(10:observations, function(x){
  t.test(conversions_a[1:x],conversions_b[1:x])$p.value
})
tt &amp;lt;- data.frame(p.value = unlist(tt))

# for plots
conf_95 &amp;lt;- data.frame( x = c(-Inf, Inf), y = 0.95 )
obs_limit_line &amp;lt;- data.frame( x = n_obs, y = c(-Inf, Inf) )

# plot the evolution of p-values and always-valid p-values
ggplot(tt, aes(x=seq_along(p.value), y=1-p.value)) + 
  geom_line() + 
  geom_line(aes(x, y, color=&amp;quot;alpha=5%&amp;quot;), linetype=3, conf_95) + 
  geom_line(aes(x, y, color=&amp;quot;end of test&amp;quot;), linetype=4, obs_limit_line) +
  geom_line(data=data.frame(x=seq(1:observations),y=1-avpvs), aes(x=x,y=y, color=&amp;quot;avpv&amp;quot;)) +
  xlab(&amp;quot;Observation (n)&amp;quot;) +
  scale_color_discrete(name = &amp;quot;Legend&amp;quot;) +
  ylim(c(0,1))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;/post/2019-08-20-calculating-always-valid-p-values-in-r/index_files/figure-html/unnamed-chunk-3-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;In this case, we see a test in which the p-values oscillate near alpha. A “peeker” looking at this might have incorrectly called it a winner early on, however our always-valid p-values maintain the correct outcome.&lt;/p&gt;
&lt;p&gt;Now set things up to have an effect 1.5 times the size of the minimum detectable effect and see what effect varying &lt;span class=&#34;math inline&#34;&gt;\(\tau^2\)&lt;/span&gt; has. We’ll choose five different values at different orders of magnitude between 0.0001 and 1:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(pwr)
library(ggplot2)
set.seed(5)

mde &amp;lt;- 0.1  # minimum detectable effect
cr_a &amp;lt;- 0.25 # the expected conversion rate for group A
alpha &amp;lt;- 0.05 # the false positive rate
power &amp;lt;- 0.80 # 1-false negative rate

ptpt &amp;lt;- pwr.2p.test(h = ES.h(p1 = cr_a, p2 = (1+mde)*cr_a), 
                    sig.level = alpha, 
                    power = power
)
n_obs &amp;lt;- ceiling(ptpt$n)
n_obs&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] 4860&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# make our &amp;quot;true&amp;quot; effect 1.5x larger than the mde
# this should yield a conclusive test result
effect &amp;lt;- 1.5*mde
cr_b &amp;lt;- (1+effect)*cr_a
observations &amp;lt;- 2*n_obs

# two streams of {0,1} conversions
conversions_a &amp;lt;- rbinom(observations, 1, cr_a)
conversions_b &amp;lt;- rbinom(observations, 1, cr_b)

# now we&amp;#39;ll calculate the always-valid p-values
avpvs_tsa &amp;lt;- calc_avpvs(observations, conversions_a, conversions_b, tau_sq = 0.0001)
avpvs_tsb &amp;lt;- calc_avpvs(observations, conversions_a, conversions_b, tau_sq = 0.001)
avpvs_tsc &amp;lt;- calc_avpvs(observations, conversions_a, conversions_b, tau_sq = 0.01)
avpvs_tsd &amp;lt;- calc_avpvs(observations, conversions_a, conversions_b, tau_sq = 0.1)
avpvs_tse &amp;lt;- calc_avpvs(observations, conversions_a, conversions_b, tau_sq = 1)

# And we&amp;#39;ll calculate &amp;quot;regular&amp;quot; p-values as well
tt &amp;lt;- sapply(10:observations, function(x){
  t.test(conversions_a[1:x],conversions_b[1:x])$p.value
})
tt &amp;lt;- data.frame(p.value = unlist(tt))

# for plots
conf_95 &amp;lt;- data.frame( x = c(-Inf, Inf), y = 0.95 )
obs_limit_line &amp;lt;- data.frame( x = n_obs, y = c(-Inf, Inf) )

# plot the evolution of p-values and always-valid p-values
ggplot(tt, aes(x=seq_along(p.value), y=1-p.value)) + 
  geom_line() + 
  geom_line(aes(x, y, color=&amp;quot;alpha=5%&amp;quot;), linetype=3, conf_95) + 
  geom_line(aes(x, y, color=&amp;quot;end of test&amp;quot;), linetype=4, obs_limit_line) +
  geom_line(data=data.frame(x=seq(1:observations),y=1-avpvs_tsa), aes(x=x,y=y, color=&amp;quot;avpv_0.0001&amp;quot;)) +
  geom_line(data=data.frame(x=seq(1:observations),y=1-avpvs_tsb), aes(x=x,y=y, color=&amp;quot;avpv_0.001&amp;quot;)) +
  geom_line(data=data.frame(x=seq(1:observations),y=1-avpvs_tsc), aes(x=x,y=y, color=&amp;quot;avpv_0.01&amp;quot;)) +
  geom_line(data=data.frame(x=seq(1:observations),y=1-avpvs_tsd), aes(x=x,y=y, color=&amp;quot;avpv_0.1&amp;quot;)) +
  geom_line(data=data.frame(x=seq(1:observations),y=1-avpvs_tse), aes(x=x,y=y, color=&amp;quot;avpv_1&amp;quot;)) +
  xlab(&amp;quot;Observation (n)&amp;quot;) +
  scale_color_discrete(name = &amp;quot;Legend&amp;quot;) +
  ylim(c(0,1))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;/post/2019-08-20-calculating-always-valid-p-values-in-r/index_files/figure-html/unnamed-chunk-4-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;It looks like we might be able to stop early if we use anything but the lowest and highest values for &lt;span class=&#34;math inline&#34;&gt;\(\tau^2\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Using always-valid p-values, particularly for small- or zero-sized effects, can be helpful in avoiding false positives. I’ve glossed over some important details that should be considered when working with non-normal data, but I hope this provides an introduction to an alternative way of evaluating binomial A/B tests. See &lt;a href=&#34;https://arxiv.org/pdf/1512.04922.pdf&#34;&gt;Always Valid Inference: Continuous Monitoring of A/B Tests&lt;/a&gt; for more details.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Roland Stevenson is a data scientist and consultant who may be reached on &lt;a href=&#34;https://www.linkedin.com/in/roland-stevenson/&#34;&gt;LinkedIn&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;div class=&#34;footnotes&#34;&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id=&#34;fn1&#34;&gt;&lt;p&gt;For an even simpler approach, see what &lt;a href=&#34;https://codeascraft.com/2018/10/03/how-etsy-handles-peeking-in-a-b-testing/&#34;&gt;Etsy does&lt;/a&gt;.&lt;a href=&#34;#fnref1&#34; class=&#34;footnote-back&#34;&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2019/08/22/calculating-always-valid-p-values-in-r/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>Plumber Logging</title>
      <link>https://rviews.rstudio.com/2019/08/13/plumber-logging/</link>
      <pubDate>Tue, 13 Aug 2019 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2019/08/13/plumber-logging/</guid>
      <description>
        


&lt;p&gt;The &lt;a href=&#34;https://www.rplumber.io/docs/&#34;&gt;plumber R package&lt;/a&gt; is used to expose R functions as API endpoints. Due to plumber’s incredible flexibility, most major API design decisions are left up to the developer. One important consideration to be made when developing APIs is how to log information about API requests and responses. This information can be used to determine how plumber APIs are performing and how they are being utilized.&lt;/p&gt;
&lt;p&gt;An example of logging API requests in plumber is included in the &lt;a href=&#34;https://www.rplumber.io/docs/routing-and-input.html#filters&#34;&gt;package documentation&lt;/a&gt;. That example uses a filter to log information about incoming requests before a response has been generated. This is certainly a valid approach, but it means that the log cannot contain details about the response since the response hasn’t been created yet. In this post we will look at an alternative approach to logging plumber APIs that uses &lt;a href=&#34;https://www.rplumber.io/docs/programmatic-usage.html#router-hooks&#34;&gt;preroute and postroute hooks&lt;/a&gt; to log information about each API request and its associated response.&lt;/p&gt;
&lt;div id=&#34;logging&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Logging&lt;/h2&gt;
&lt;p&gt;In this example, we will use the &lt;a href=&#34;https://daroczig.github.io/logger/&#34;&gt;logger package&lt;/a&gt; to generate the actual log entries. Using this package isn’t required, but it does provide some convenient functionality that we will take advantage of.&lt;/p&gt;
&lt;p&gt;Since we will be registering hooks for our API, we will need both a &lt;code&gt;plumber.R&lt;/code&gt; file and an &lt;code&gt;entrypoint.R&lt;/code&gt; file. The &lt;code&gt;plumber.R&lt;/code&gt; file contains the following:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# plumber.R
# A simple API to illustrate logging with Plumber

library(plumber)

#* @apiTitle Logging Example

#* @apiDescription Simple example API for implementing logging with Plumber

#* Echo back the input
#* @param msg The message to echo
#* @get /echo
function(msg = &amp;quot;&amp;quot;) {
  list(msg = paste0(&amp;quot;The message is: &amp;#39;&amp;quot;, msg, &amp;quot;&amp;#39;&amp;quot;))
}

#* Plot a histogram
#* @png
#* @get /plot
function() {
  rand &amp;lt;- rnorm(100)
  hist(rand)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we’ve defined two endpoints (&lt;code&gt;/echo&lt;/code&gt; and &lt;code&gt;/plot&lt;/code&gt;), we can use &lt;code&gt;entrypoint.R&lt;/code&gt; to setup logging using preroute and postroute hooks. First, we need to configure the logger package:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# entrypoint.R
library(plumber)

# logging
library(logger)

# Specify how logs are written
log_dir &amp;lt;- &amp;quot;logs&amp;quot;
if (!fs::dir_exists(log_dir)) fs::dir_create(log_dir)
log_appender(appender_tee(tempfile(&amp;quot;plumber_&amp;quot;, log_dir, &amp;quot;.log&amp;quot;)))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;log_appender()&lt;/code&gt; function is used to specify which appender method is used for logging. Here we use &lt;code&gt;appender_tee()&lt;/code&gt; so that logs will be written to &lt;code&gt;stdout&lt;/code&gt; and to a specific file path. We create a directory called &lt;code&gt;logs/&lt;/code&gt; in the current working directory to store the resulting logs. Every log file is assigned a unique name using &lt;code&gt;tempfile()&lt;/code&gt;. This prevents errors that can occur if concurrent processes try to write to the same file.&lt;/p&gt;
&lt;p&gt;Now, we need to create a helper function that we will use when creating log entries:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;convert_empty &amp;lt;- function(string) {
  if (string == &amp;quot;&amp;quot;) {
    &amp;quot;-&amp;quot;
  } else {
    string
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function takes an empty string and converts it into a dash (&lt;code&gt;&amp;quot;-&amp;quot;&lt;/code&gt;). We will use this to ensure that empty log values still get recorded so that it is easy to read the log files. We’re now ready to create our plumber router and register the hooks necessary for logging:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;pr &amp;lt;- plumb(&amp;quot;plumber.R&amp;quot;)

pr$registerHooks(
  list(
    preroute = function() {
      # Start timer for log info
      tictoc::tic()
    },
    postroute = function(req, res) {
      end &amp;lt;- tictoc::toc(quiet = TRUE)
      # Log details about the request and the response
      log_info(&amp;#39;{convert_empty(req$REMOTE_ADDR)} &amp;quot;{convert_empty(req$HTTP_USER_AGENT)}&amp;quot; {convert_empty(req$HTTP_HOST)} {convert_empty(req$REQUEST_METHOD)} {convert_empty(req$PATH_INFO)} {convert_empty(res$status)} {round(end$toc - end$tic, digits = getOption(&amp;quot;digits&amp;quot;, 5))}&amp;#39;)
    }
  )
)

pr&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We use the &lt;code&gt;$registerHooks()&lt;/code&gt; method to register both preroute and postroute hooks. The preroute hook uses the &lt;a href=&#34;http://collectivemedia.github.io/tictoc/&#34;&gt;tictoc package&lt;/a&gt; to start a timer. The postroute hook stops the timer and then writes a log entry using the &lt;code&gt;log_info()&lt;/code&gt; function from the logger package. Each log entry contains the following information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Log level: This is a distinction made by the logger package, and in this
example the value is always INFO&lt;/li&gt;
&lt;li&gt;Timestamp: The timestamp for when the response was generated and sent back to
the client&lt;/li&gt;
&lt;li&gt;Remote Address: The address of the client making the request&lt;/li&gt;
&lt;li&gt;User Agent: The user agent making the request&lt;/li&gt;
&lt;li&gt;Http Host: The host of the API&lt;/li&gt;
&lt;li&gt;Method: The HTTP method attached to the request&lt;/li&gt;
&lt;li&gt;Path: The specific API endpoint requested&lt;/li&gt;
&lt;li&gt;Status: The HTTP status of the response&lt;/li&gt;
&lt;li&gt;Execution Time: The amount of time from when the request received until the response was generated&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This log format is loosely inspired by the &lt;a href=&#34;https://en.wikipedia.org/wiki/Common_Log_Format&#34;&gt;NCSA Common log format&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;testing&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Testing&lt;/h2&gt;
&lt;p&gt;Now that our API is all setup, it’s time to test to make sure logging works as expected. First, we need to start the API. The easiest way to do this is to click the Run API button that appears at the top of the &lt;code&gt;plumber.R&lt;/code&gt; file in the RStudio IDE. Once the API is running, you’ll see a message in the console similar to the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Running plumber API at http://127.0.0.1:5762&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we know the API is running, we need to make a request. One of the easiest ways to make a request in this case is to open a web browser (like Google Chrome) and type the API address in the address bar followed by &lt;code&gt;/plot&lt;/code&gt;. In this example, I would type &lt;code&gt;http://127.0.0.1:5762/plot&lt;/code&gt; into the address bar of my browser. If all goes well, you should see a plot rendered in the browser. The RStudio console will display the log output:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;INFO [2019-08-09 12:30:23] 127.0.0.1 &amp;quot;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36&amp;quot; localhost:5762 GET /plot 200 0.158&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A new &lt;code&gt;logs/&lt;/code&gt; directory will have been created in the current working directory and it will contain a file with the log entry. You can generate more log entries by refreshing your browser window.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;analyzing&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Analyzing&lt;/h2&gt;
&lt;p&gt;Let’s say that we refreshed the browser window 1,000 times. The log file generated will contain an entry for each request. We can analyze this log file to find helpful information about the API. For example, we could plot a histogram of execution time:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(ggplot2)

plumber_log &amp;lt;- readr::read_log(&amp;quot;logs/plumber_fe3daed895d.log&amp;quot;,
                               col_names = c(&amp;quot;log_level&amp;quot;,
                                             &amp;quot;timestamp&amp;quot;,
                                             &amp;quot;remote_address&amp;quot;,
                                             &amp;quot;user_agent&amp;quot;,
                                             &amp;quot;http_host&amp;quot;,
                                             &amp;quot;method&amp;quot;,
                                             &amp;quot;path&amp;quot;,
                                             &amp;quot;status&amp;quot;,
                                             &amp;quot;execution_time&amp;quot;))

ggplot(plumber_log, aes(x = execution_time)) +
  geom_histogram() +
  theme_bw() +
  labs(title = &amp;quot;Execution Times&amp;quot;,
       x = &amp;quot;Execution Time&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;/post/2019-08-10-plumber-logging/index_files/figure-html/unnamed-chunk-1-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;We could even build a &lt;a href=&#34;http://shiny.rstudio.com&#34;&gt;Shiny application&lt;/a&gt; to monitor the &lt;code&gt;logs/&lt;/code&gt; directory and provide real-time visibility into API metrics!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;log-monitoring.gif&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The details of this Shiny application go beyond the scope of this post, but the source code is available &lt;a href=&#34;https://github.com/sol-eng/plumber-logging/blob/master/R/shiny/app.R&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Plumber APIs published to &lt;a href=&#34;https://www.rstudio.com/products/connect/&#34;&gt;RStudio Connect&lt;/a&gt; can use this pattern to log and monitor API requests. Details on this use case can be found in &lt;a href=&#34;https://github.com/sol-eng/plumber-logging#deployment&#34;&gt;this repository&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;conclusion&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Plumber is an incredibly flexible package for exposing R functions as API endpoints. Logging information about API requests and responses provides visibility into API usage and performance. These log files can be manually inspected or used in connection with other tools (like Shiny) to provide real-time metrics around API use. The code used in this example along with additional information is available in &lt;a href=&#34;https://github.com/sol-eng/plumber-logging&#34;&gt;this GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you are interested in learning more about using plumber, logging, Shiny, and RStudio Connect, please visit &lt;a href=&#34;https://community.rstudio.com/&#34;&gt;community.rstudio.com&lt;/a&gt; and let us know!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;James Blair is a solutions engineer at RStudio who focuses on tools,
technologies, and best practices for using R in the enterprise.&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2019/08/13/plumber-logging/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>Validating Type I and II Errors in A/B Tests in R</title>
      <link>https://rviews.rstudio.com/2019/07/31/validating-type-i-and-ii-errors-in-a-b-tests-in-r/</link>
      <pubDate>Wed, 31 Jul 2019 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2019/07/31/validating-type-i-and-ii-errors-in-a-b-tests-in-r/</guid>
      <description>
        


&lt;p&gt;In this post, we seek to develop an intuitive sense of what type I (false-positive) and type II (false-negative) errors represent when comparing metrics in A/B tests, in order to gain an appreciation for “peeking”, one of the major problems plaguing the analysis of A/B test today.&lt;/p&gt;
&lt;p&gt;To better understand what “peeking” is, it helps to first understand how to properly run a test. We will focus on the case of testing whether there is a difference between the conversion rates &lt;code&gt;cr_a&lt;/code&gt; and &lt;code&gt;cr_b&lt;/code&gt; for groups A and B. We define conversion rate as the total number of conversions in a group divided by the total number of subjects. The basic idea is that we create two experiences, A and B, and give half of the randomly-selected subjects experience A and half B. Then, after some number of users have gone through our test, we measure how many conversions happened in each group. The important question is: how many users do we need to have in groups A and B in order to measure a difference in conversion rates of a particular size?&lt;/p&gt;
&lt;p&gt;To correctly run a test, one should first calculate the required sample size by doing a power calculation. This is easily done in R using the &lt;code&gt;pwr&lt;/code&gt; library, which requires a few parameters: the desired significance level (the false positive rate), the desired statistical power (1-false negative rate), the minimum detectable effect, and the baseline conversion rate &lt;code&gt;cr_a&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(pwr)
mde &amp;lt;- 0.1  # minimum detectable effect
cr_a &amp;lt;- 0.25 # the expected conversion rate for group A
alpha &amp;lt;- 0.05 # the false positive rate
power &amp;lt;- 0.80 # 1-false negative rate

ptpt &amp;lt;- pwr.2p.test(h = ES.h(p1 = cr_a, p2 = (1+mde)*cr_a), 
           sig.level = alpha, 
           power = power
           )
n_obs &amp;lt;- ceiling(ptpt$n)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This result tells us that we need to observe 4860 subjects in each of the A and B test groups if we want to detect a difference of 10% in their conversion rates. Once we have observed that quantity, we can calculate whether there is a statistically significant difference between the two sets of observations via a t-test.&lt;/p&gt;
&lt;p&gt;Given the parameters we included in our power calculation, there are two things to be aware of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;there is a 5% chance that our t-test will predict that there is a statistically significant difference when, in fact, there isn’t (a false positive). That is a result of our alpha parameter, which sets a false-positive rate of 5%.&lt;/li&gt;
&lt;li&gt;there is a 20% chance that the t-test will predict no difference when there actually was a difference (a false negative). This is the false-negative rate (or 1-power), and is commonly referred to as beta.&lt;/li&gt;
&lt;/ul&gt;
&lt;div id=&#34;illustrating-alpha-and-beta-parameters&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Illustrating alpha and beta parameters&lt;/h2&gt;
&lt;p&gt;Let’s try to illustrate this through a simulation. We’ll simulate two sequences of conversions with conversion rates such that &lt;code&gt;cr_b = (1+mde)*cr_a&lt;/code&gt;. We’ll then run a t-test comparing all of the available observations of the two groups &lt;strong&gt;each time we have a new pair of observations&lt;/strong&gt;. If the p-value of the t-test is below 5%, we will reject the null-hypothesis that there is no difference between the distributions; hence, &lt;code&gt;p.value &amp;lt; 0.05&lt;/code&gt; implies there is a statistically significant difference between the conversion rates. Finally we’ll plot &lt;code&gt;1-p.value&lt;/code&gt; to represent “confidence” relative to where we are in the sequence. This essentially simulates what we would see &lt;strong&gt;if we were to continually monitor p-values&lt;/strong&gt; as subjects convert or abandon. We add the 95% confidence line horizontally, as well as a vertical line at &lt;code&gt;n_obs&lt;/code&gt;, the number of observations our power calculation says to use to conduct a t-test.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(ggplot2)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Warning: package &amp;#39;ggplot2&amp;#39; was built under R version 3.5.2&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;set.seed(2)

# make our &amp;quot;true&amp;quot; effect larger than the mde
effect &amp;lt;- mde
cr_b &amp;lt;- (1+effect)*cr_a
observations &amp;lt;- 2*n_obs

# a sequence of {0,1} conversions
conversions_a &amp;lt;- rbinom(observations, 1, cr_a)
conversions_b &amp;lt;- rbinom(observations, 1, cr_b)

# Calculate p-values at each simultaneous observation of the a and b groups
tt &amp;lt;- sapply(10:observations, function(x){
  t.test(conversions_a[1:x],conversions_b[1:x])$p.value
})

tt &amp;lt;- data.frame(p.value = unlist(tt))

# for plots
conf_95 &amp;lt;- data.frame( x = c(-Inf, Inf), y = 0.95 )
obs_limit_line &amp;lt;- data.frame( x = n_obs, y = c(-Inf, Inf) )

# plot the evolution of p-value over time, if &amp;quot;peeking&amp;quot;
ggplot(tt, aes(x=seq_along(p.value), y=1-p.value)) + 
  geom_line() + 
  geom_line(aes(x, y, color=&amp;quot;alpha=5%&amp;quot;), linetype=3, conf_95) + 
  geom_line(aes(x, y, color=&amp;quot;end of test&amp;quot;), linetype=4, obs_limit_line) +
  xlab(&amp;quot;Observation (day)&amp;quot;) +
  scale_color_discrete(name = &amp;quot;Legend&amp;quot;) +
  ylim(c(0,1))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;/post/2019-07-23-validating-type-i-and-ii-errors-in-a-b-tests-in-r_files/figure-html/unnamed-chunk-2-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;We observe that in the above example, we would correctly have measured a difference in conversion rates &lt;em&gt;for this particular simulation&lt;/em&gt;. However, we should expect that if we run this experiment 100 times, about 20 of those times will result in an incorrect negative prediction due to our false negative rate being 20% (power=80).&lt;/p&gt;
&lt;p&gt;To test this, and other concepts in this article, we are going to create a utility function that will run a simulation repeatedly:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;#
# monte carlo runs n_simulations and calls the callback function each time with the ... optional args
#
monte_carlo &amp;lt;- function(n_simulations, callback, ...){
  simulations &amp;lt;- 1:n_simulations

  sapply(1:n_simulations, function(x){
    callback(...)
  })
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we’ll use the &lt;code&gt;monte_carlo&lt;/code&gt; utility function to run 1000 experiments, measuring whether the p.value is less than alpha &lt;strong&gt;after &lt;code&gt;n_obs&lt;/code&gt; observations&lt;/strong&gt;. If it is, we reject the null hypothesis. We expect about 800 rejections and about 200 non-rejections, since 200/1000 would represent our expected 20% false negative rate.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;reject_at_i &amp;lt;- function(observations, i){
  conversions_a &amp;lt;- rbinom(observations, 1, cr_a)
  conversions_b &amp;lt;- rbinom(observations, 1, cr_b)
  ( t.test(conversions_a[1:i],
           conversions_b[1:i])$p.value ) &amp;lt; alpha
}

# run the sim
rejected.H0 &amp;lt;- monte_carlo(1000, 
                           callback=reject_at_i,
                           observations=n_obs,
                           i=n_obs
                           )

# output the rejection table
table(rejected.H0)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## rejected.H0
## FALSE  TRUE 
##   190   810&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will use the same functions to test the false positive rate. In this case, we want to set the two conversion rates to the same value, &lt;code&gt;cr_a&lt;/code&gt;, and confirm that out of 1000 experiments, about 50 show up as having rejected the null hypothesis (and predict a difference in the conversion rates).&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;reject_at_i &amp;lt;- function(observations, i){
  conversions_a &amp;lt;- rbinom(observations, 1, cr_a)
  conversions_b &amp;lt;- rbinom(observations, 1, cr_a) # this is now the same conversion rate
  ( t.test(conversions_a[1:i],
           conversions_b[1:i])$p.value ) &amp;lt; alpha
}

# run the sim
rejected.H0 &amp;lt;- monte_carlo(1000, 
                           callback=reject_at_i,
                           observations=n_obs,
                           i=n_obs
                           )

# output the rejection table
table(rejected.H0)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## rejected.H0
## FALSE  TRUE 
##   962    38&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Indeed, the results are as expected. We have shown that if we measure the results of our experiment when our power calculation tells us to, we can expect the false positive and false negative rates to reflect the values we have set in &lt;code&gt;alpha&lt;/code&gt; and &lt;code&gt;power&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;peeking&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Peeking&lt;/h2&gt;
&lt;p&gt;What happens if we don’t do a power calculation and instead monitor the conversions as they come in? This is what is termed “peeking”. In other words, to run a test correctly, you should only observe the results at one moment: when the power calculation has told you the test is complete. If you look at any moment prior to this, you are peeking at the results.&lt;/p&gt;
&lt;p&gt;Peeking is widespread when it comes to analyzing experiments. In fact, some popular testing services peek continuously and automatically notify as soon as a p-value is below alpha. What you may notice, however, is that p-values can fluctuate around alpha multiple times before “choosing” a side, particularly where the size of an effect is small. Continuously monitoring p-values will inflate false positive rates. Let’s see what effect continuous peeking has on our false positive rates:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;peeking_method &amp;lt;- function (observations, by=1){
  # create the conversions
  conversions_a &amp;lt;- rbinom(observations, 1, cr_a)
  conversions_b &amp;lt;- rbinom(observations, 1, cr_a)  # no effect

  reject &amp;lt;- FALSE;

  # for each simulation, calculate the running conversion rates and days to complete.
  # Break the first time we have run more than the required days to complete
  for (i in seq(from=by,to=observations,by=by)) {
    tryCatch(
      {
        reject &amp;lt;- ( t.test(conversions_a[1:i],conversions_b[1:i])$p.value ) &amp;lt; alpha
        if(reject){
          break;
        }
      }, error=function(e){
        print(e)
      }
    )
  }
  
  reject

}
# run the sim
rejected.H0 &amp;lt;- monte_carlo(1000,
                           callback=peeking_method,
                           observations=n_obs,
                           by=100
                           )


# output the rejection table
table(rejected.H0)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## rejected.H0
## FALSE  TRUE 
##   653   347&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Indeed, even peeking every 100 sets of observations leads to an inflated false positive rate of about 30%! Peeking at test results to make decisions, and especially automating peeking to make decisions, is a big no-no.&lt;/p&gt;
&lt;p&gt;In a subsequent article, we’ll explore an alternative way of measuring p-values that is not affected by peeking, based on sequential probability ratio tests.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Roland Stevenson is a data scientist and consultant who may be reached on &lt;a href=&#34;https://www.linkedin.com/in/roland-stevenson/&#34;&gt;LinkedIn&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2019/07/31/validating-type-i-and-ii-errors-in-a-b-tests-in-r/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>An R Users Guide to JSM 2019</title>
      <link>https://rviews.rstudio.com/2019/07/19/an-r-users-guide-to-jsm-2019/</link>
      <pubDate>Fri, 19 Jul 2019 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2019/07/19/an-r-users-guide-to-jsm-2019/</guid>
      <description>
        


&lt;p&gt;If you are like me, and rather last minute about making a plan to get the most out of a large conference, you are just starting to think about &lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/&#34;&gt;JSM 2019&lt;/a&gt; which will begin in just a few days. My plans always begin with an attempt to sleuth out the R-related sessions. While in the past it took quite a bit of work to identify talks that were likely backed by R-based calculations, this is clearly no longer the case. In fact, because Stanford Professor &lt;a href=&#34;http://web.stanford.edu/~hastie/bio.htm&#34;&gt;Trevor Hastie&lt;/a&gt; will be delivering the prestigious &lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=300247&#34;&gt;Wald Lectures&lt;/a&gt; this year, R-backed work will be front and center.&lt;/p&gt;
&lt;p&gt;Professor Hastie has made numerous, important contributions to statistical learning, machine learning, data science and statistical computing. Among the latter, is the &lt;code&gt;glmnet&lt;/code&gt; package he co-authored with Jerome Friedman, Rob Tibshirani, Noah Simon, Balasubramanian Narasimhan and Junyang Qian which has become a fundamental resource.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/post/2019-07-18-an-r-users-guide-to-jsm-2019_files/Hastie.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;
&lt;p&gt;The Wald Lectures will be delivered over three days in room CC Four Seasons 1 according to the following schedule:&lt;br /&gt;
* Lecture 1: Mon, 7/29/2019, 10:30 AM - 12:20 PM&lt;br /&gt;
* Lecture 2: Tue, 7/30/2019, 2:00 PM - 3:50 PM&lt;br /&gt;
* Lecture 3: Wed, 7/31/2019, 10:30 AM - 12:20 PM&lt;/p&gt;
&lt;p&gt;If you want to do some preparation for the lectures, you might have a look at the book &lt;a href=&#34;https://web.stanford.edu/~hastie/StatLearnSparsity_files/SLS.pdf&#34;&gt;&lt;em&gt;Statistical Learnig with Sparsity; The Lasso and Generalizations&lt;/em&gt;&lt;/a&gt; by Hastie, Tibshirani and Wainwright.&lt;/p&gt;
&lt;p&gt;The rest of this post lists some R-related talks that can help you fill your days at JSM! I am sure my list is not complete. Please feel free to add anything I may have missed to the comments section following this post.&lt;/p&gt;
&lt;div id=&#34;sunday-july-28-2019&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Sunday, July 28, 2019&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=307242&#34;&gt;Findings from Analysis and Visualization of the New York City Housing and Vacancy Survey Data&lt;/a&gt; - CC 501 - 3:20 PM - Nels Grevstad, Metropolitan State University of Denver; Rachel Rosebrook, Metropolitan State University of Denver; Lance Barto, Metropolitan State University of Denver; Gil Leibovich, Metropolitan State University of Denver; Elizabeth Foster, Metropolitan State University of Denver; ThienNgo Le, Metropolitan State University of Denver; Kelsey Smith, Metropolitan State University of Denver; Nathanael Whitney, Metropolitan State University of Denver; Zoe Girkin, Metropolitan State University of Denver; Ahern Nelson, Metropolitan State University of Denver; Karan Bhargava, Metropolitan State University of Denver; Alex Whalen-Wagner, Metropolitan State University of Denver; Gemma Hoeppner, Metropolitan State University of Denver; Larry Breeden, Metropolitan State University of Denver; Ayako Zrust, Metropolitan State University of Denver; Travis Rebhan, Metropolitan State University of Denver; Anayeli Ochoa, Metropolitan State University of Denver&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=306740&#34;&gt;Bayesian Uncertainty Estimation Under Complex Sampling&lt;/a&gt; - Speed: CC 502 - 3:00 PM -
Matthew Williams, National Science Foundation; Terrance Savitsky, Bureau of Labor Statistics&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=307461&#34;&gt;Measuring Gentrification Over Time with the NYCHVS&lt;/a&gt; - Poster: CC Hall C - 4:00 PM - 4:45 PM
Robert Montgomery, NORC; Quentin Brummet, NORC; Nola du Toit, NORC at the University of Chicago; Peter Herman, NORC at the University of Chicago; Edward Mulrow, NORC at the University of Chicago&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=306967&#34;&gt;A SHINY Markov Machine for Decision-Making in Major League Baseball&lt;/a&gt; - Part 1: CC105 - 2:45 PM and Part 2: CC Hall C - 4:00 PM to 4:45 PM
Jason Osborne, North Carolina State University&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=306895&#34;&gt;Measuring Gentrification Over Time with the NYCHVS&lt;/a&gt; - CC 501 - 2:55 PM -
Robert Montgomery, NORC; Quentin Brummet, NORC; Nola du Toit, NORC at the University of Chicago; Peter Herman, NORC at the University of Chicago; Edward Mulrow, NORC at the University of Chicago&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=304652&#34;&gt;A New Tidy Data Structure to Support Exploration and Modeling of Temporal Data&lt;/a&gt; - CC 301 - 3:25 PM -
Earo Wang, Monash University; Dianne Cook, Monash University; Rob J Hyndman, Monash Univeristy&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=305348&#34;&gt;TensorFlow Versus H20, Predicting the SandP500&lt;/a&gt; - CC 504 - 4:50 PM - Kenneth Davis&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=303068&#34;&gt;Model-Based Clustering Using Adjacent-Categories Logit Models via Finite Mixture Model&lt;/a&gt; - CC 504 - 5:05 PM -
Lingyu Li, Victoria University of Wellington; Ivy Liu, Victoria University of Wellington; Richard Arnold, Victoria University of Wellington&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=307410&#34;&gt;The Estimable Luke Tierney – and Estimability in R&lt;/a&gt; - CC 501 - 5:20 PM -
Russell V. Lenth, University of Iowa&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;monday-july-29-2019&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Monday, July 29, 2019&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=304581&#34;&gt;Training Students Concurrently in Data Science and Team Science: Results and Lessons Learned from Multi-Institutional Interdisciplinary Student-Led Research Teams 2012-2018&lt;/a&gt; -Poster: CC Hall C- 2:00 PM to 3:50 PM -
Brent Ladd, Purdue University; Mark Ward, Purdue University&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=305372&#34;&gt;A Natural Language Processing Algorithm for Medication Extraction from Electronic Health Records Using the R Programming Language: MedExtractR&lt;/a&gt; - Pister: CC Hall C - 2:00 PM to 3:50 PM - Hannah L Weeks, Vanderbilt University; Cole Beck, Vanderbilt University Medical Center; Elizabeth McNeer, Vanderbilt University; Joshua C Denny, Vanderbilt University; Cosmin A Bejan, Vanderbilt University; Leena Choi, Vanderbilt University Medical Center&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=307055&#34;&gt;Conditional Probability and SQL for Data Science&lt;/a&gt; - Poster: CC Hall C - 10:30 AM to 12:20 PM - Eric Suess, CSU East Bay&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=304301&#34;&gt;R Markdown: a Software Ecosystem for Reproducible Publications&lt;/a&gt; - CC 107- 11:55 PM - Yihui Xie, RStudio, Inc.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=305095&#34;&gt;Infusing Bayesian Strategies for Pharmaceutical Manufacturing and Development&lt;/a&gt; - CC 109- 12:05 PM -
Bill Pikounis, Johnson &amp;amp; Johnson; Dwaine Banton, Janssen R&amp;amp;D; John Oleynick, Johnson &amp;amp; Johnson; Jyh-Ming Shoung, Janssen R&amp;amp;D&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;tuesday-july-30-2019&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Tuesday, July 30, 2019&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=307352&#34;&gt;Controlling the False Discovery Proportion: a Simulation Study&lt;/a&gt; - Poster: CC Hall C - 10:30 AM to 12:20 PM
HARLAN MCCAFFERY, University of Michigan; Chi Chang, Michigan State University&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=300322&#34;&gt;Give Your Statistician Colleague Iris Bulbs for Their House Warming!&lt;/a&gt; - CC 605 - 11:05 AM - Dianne Cook, Monash University&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=307787&#34;&gt;From Prediction Models to Shiny App: Creating a Tool for Contaminated Food Source Prediction in Salmonella and STEC Outbreaks&lt;/a&gt; - CC Hall C - 11:35 AM to 12:20 PM - Caroline Ledbetter, University of Colorado; Alice White, Colorado School of Public Health; Elaine Scallan Walter, Colorado School of Public Health; David Weitzenkamp, Colorado School of Public Health&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=305065&#34;&gt;Stats for Data Science&lt;/a&gt; - H-Centennial Ballroom G-H - Round Table: 12:30 PM to 1:50 PM - Daniel Kaplan, Macalester College&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=307830&#34;&gt;Experiences with Incorporating R into a Second-Level Biostatistics Course for MPH Students&lt;/a&gt; - CC Hall C - 2:00 PM to 2:45 PM - Christine Mauro, Columbia University; Nicholas Williams, Columbia University; Anjile An, Columbia University&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=304924&#34;&gt;From Prediction Models to Shiny App: Creating a Tool for Contaminated Food Source Prediction in Salmonella and STEC Outbreaks&lt;/a&gt; - CC 501 - 8:40 AM
Caroline Ledbetter, University of Colorado; Alice White, Colorado School of Public Health; Elaine Scallan Walter, Colorado School of Public Health; David Weitzenkamp, Colorado School of Public Health&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=304734&#34;&gt;Tools for Evaluating Quality of State and Local Administrative Data&lt;/a&gt; - CC708 - 9:15AM -
Zachary H Seeskin, NORC at the University of Chicago; Gabriel Ugarte, NORC at the University of Chicago; Rupa Datta, NORC at the University of Chicago&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;wednesday-july-31-2019&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Wednesday, July 31, 2019&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=306644&#34;&gt;Ggvoronoi: Voronoi Tessellations in R&lt;/a&gt; - CC 105 - 11:20 AM -Thomas J Fisher, Miami University; Robert C Garrett, Miami University; Karsten Maurer, Miami University&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=304690&#34;&gt;Using R to Conduct Retrospective Analyzes of EHR and Imaging Data: a Case Study in MS&lt;/a&gt; - Poster: CC Hall C - 10:30 AM - 12:20 PM - Melissa Martin, University of Pennsylvania; Russell Shinohara, University of Pennsylvania&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=307941&#34;&gt;Generalized Causal Mediation and Path Analysis and Its R Package &lt;code&gt;gmediation&lt;/code&gt;&lt;/a&gt; Talk: - CC 501 - 8:45 AM - and Poster: CC Hall C - 11:35 AM - 12:20 PM -
Jang Ik Cho, Eli Lilly and Company; Jeffrey M Albert, Case Western Reserve University&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=307952&#34;&gt;Tidi_MIBI: a Tidy Pipeline for Microbiome Analysis and Visualization in R&lt;/a&gt; - Speed Talk: CC 501 - 10:15 AM and Poster: CC Hall C - 11:35 AM - 12:20 PM -
Charlie Carpenter, University of Colorado-Biostatistics&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=307953&#34;&gt;Incorporating Spatial Statistics into Routine Analysis of Agricultural Field Trials&lt;/a&gt; - CC Hall C - 11:35 AM - 12:20 PM -
Julia Piaskowski, University of Idaho; Chad Jackson, University of Idaho; Juliet Marshall, University of Idaho; William J Price, University of Idaho&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=307180&#34;&gt;Incorporating Spatial Statistics into Routine Analysis of Agricultural Field Trials&lt;/a&gt; - CC 501 - 10:05 AM -
Julia Piaskowski, University of Idaho; Chad Jackson, University of Idaho; Juliet Marshall, University of Idaho; William J Price, University of Idaho&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=307339&#34;&gt;DemoR: Tools for Teaching and Presenting R Code&lt;/a&gt; - CC 302 - 10:35 AM -
Kelly Bodwin, California Polytechnic State University; Hunter Glanz, California Polytechnic State University&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=306367&#34;&gt;Ghclass: An R Package for Managing Classes with GitHub&lt;/a&gt; - CC 302 - 10:50 AM -
Colin Rundel, Duke University&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=300318&#34;&gt;Using and Building Shiny Apps for Teaching Introductory Biostatistics&lt;/a&gt; CC 504 - 11:05 AM -
Adam Ciarleglio, The George Washington University&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=304247&#34;&gt;Using GitHub and RStudio to Facilitate Authentic Learning Experiences in a Regression Analysis Course&lt;/a&gt; - CC 302 - 11:05 AM -
Maria Tackett, Duke University&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=307332&#34;&gt;A Generalized Additive Cox Model with L1-Penalty for Heart Failure Time-To-Event Outcomes and Comparison to Other Machine Learning Approaches&lt;/a&gt; - CC 712 - 3:20 PM - Matthias Kormaksson&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;thursday-august-1-2019&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Thursday, August 1, 2019&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=303011&#34;&gt;A Journey Teaching Applied Statistics for Health Sciences in an Asynchronous Team Based Learning Format Using Data Science Ideas&lt;/a&gt; - CC 110 - 8:50 AM - Ben Barnard&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ww2.amstat.org/meetings/jsm/2019/onlineprogram/AbstractDetails.cfm?abstractid=306515&#34;&gt;Noncentral Algorithm Assessments&lt;/a&gt; - CC 104 - 9:20 AM
Jerry Lewis, Biogen Idec&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;supplementary-code&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Supplementary Code&lt;/h3&gt;
&lt;p&gt;In case you are wondering how I produced the plot above, here is the code which uses the &lt;code&gt;cranly&lt;/code&gt; and &lt;code&gt;dlstats&lt;/code&gt; packages to investigate CRAN.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(tidyverse)
library(cranly)
library(dlstats)
# Get clean copy of CRAN
p_db &amp;lt;- tools::CRAN_package_db()
package_db &amp;lt;- clean_CRAN_db(p_db)
# Build package network
package_network &amp;lt;- build_network(package_db)

# Find Hastie packages
pkgs &amp;lt;- package_by(package_network, &amp;quot;Trevor Hastie&amp;quot;)
# Find most downloaded Hastie packages
dstats &amp;lt;- cran_stats(pkgs)
topdown &amp;lt;- group_by(dstats,package) %&amp;gt;% 
           summarize(n=sum(downloads)) %&amp;gt;% 
           arrange(desc(n)) %&amp;gt;% filter(n &amp;gt; 100000)

# Plot the monthly downloads for Hastie&amp;#39;s top 5 packages
shortlist &amp;lt;- select(topdown,package) %&amp;gt;% slice(1:5) 
toppkgs &amp;lt;- cran_stats(as.vector(shortlist$package))

ggplot(toppkgs, aes(end, downloads, group=package, color=package)) +
  geom_line() + geom_point(aes(shape=package)) + xlab(&amp;quot;Monthly Downloads&amp;quot;) + ggtitle(&amp;quot;Trevor Hastie Packages&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2019/07/19/an-r-users-guide-to-jsm-2019/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>Imagine your Data Before You Collect It</title>
      <link>https://rviews.rstudio.com/2019/07/01/imagine-your-data-before-you-collect-it/</link>
      <pubDate>Mon, 01 Jul 2019 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2019/07/01/imagine-your-data-before-you-collect-it/</guid>
      <description>
        


&lt;p&gt;As data scientists, we are often presented with a dataset and are asked to use it to produce insights. We use R to wrangle, visualize, model, and produce tables and plots for sharing or publication. When we focus on the data in hand in this way, we don’t get to consider where the data came from. The sample size and the set of variables and their scales are fixed. Yet the procedures used to gather or generate them are hugely consequential for how we should analyze the data and also the quality of the insights we can ultimately deliver. Sampling procedures have implications for how the resulting data should be analyzed. For studies that seek to measure causal effects, it matters how some units come to be treated and others left untreated.&lt;/p&gt;
&lt;p&gt;Because these processes are so important, we wanted to make a tool that would help data scientists and other researchers &lt;strong&gt;imagine&lt;/strong&gt; their data &lt;strong&gt;before&lt;/strong&gt; they collect it so that any changes to process can be made before it’s too late.&lt;/p&gt;
&lt;p&gt;When the data is already collected, the tool allows you to imagine your data before you &lt;strong&gt;analyze&lt;/strong&gt; it. When we make data wrangling and modeling decisions based on the results we find under each procedure, or using model fit statistics, we are vulnerable to the unconscious biases labeled the &lt;a href=&#34;http://www.stat.columbia.edu/~gelman/research/unpublished/p_hacking.pdf&#34;&gt;garden of forking paths or p-hacking&lt;/a&gt; that may lead us to select the analysis procedure that produces the best answer. We use the actual data because we don’t have a good substitute: data with the same structure and variables that we have collected.&lt;/p&gt;
&lt;p&gt;This post introduces the &lt;code&gt;fabricatr&lt;/code&gt; package, whose role in the &lt;code&gt;DeclareDesign&lt;/code&gt; suite of packages is to simulate data structure and variables. See this &lt;a href=&#34;https://rviews.rstudio.com/2019/06/04/introducing-declaredesign/&#34;&gt;RViews post introducing &lt;code&gt;DeclareDesign&lt;/code&gt;&lt;/a&gt; and the philosophy behind it. &lt;code&gt;fabricatr&lt;/code&gt; helps you to think about your data before you start analysis or even collection. What are the units? How are they structured? What measurements will you take? What are their ranges and how are they correlated? &lt;code&gt;fabricatr&lt;/code&gt; can help you simulate mock data before you collect the real data, and test out different estimation strategies without worrying about biasing your inferences.&lt;/p&gt;
&lt;div id=&#34;imagining-your-data-structure&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Imagining your data structure&lt;/h1&gt;
&lt;p&gt;Most simply, &lt;code&gt;fabricatr&lt;/code&gt; will create a single-level data structure given a number of units.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(fabricatr)
fabricate(N = 100, temp_fahrenheit = rnorm(N, mean = 80, sd = 20))&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Warning: `is_lang()` is deprecated as of rlang 0.2.0.
## Please use `is_call()` instead.
## This warning is displayed once per session.&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Warning: `lang_name()` is deprecated as of rlang 0.2.0.
## Please use `call_name()` instead.
## This warning is displayed once per session.&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr class=&#34;header&#34;&gt;
&lt;th align=&#34;left&#34;&gt;ID&lt;/th&gt;
&lt;th align=&#34;right&#34;&gt;temp_fahrenheit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;001&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;56.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;002&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;46.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;003&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;90.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;004&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;75.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;005&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;85.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;006&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;102.8&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Social science data is often &lt;strong&gt;hierarchical&lt;/strong&gt;. For example, schools have classrooms that have students. &lt;code&gt;fabricatr&lt;/code&gt; shines here with the &lt;code&gt;add_level&lt;/code&gt; command. By default, new levels are nested within the levels above them.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(fabricatr)
fabricate(
  # five schools
  school  = add_level(N = 5,
  n_classrooms = sample(10:15, N, replace = TRUE)),
  # 10 to 15 classrooms per school
  classroom  = add_level(N = n_classrooms),
  # 15 students per classroom
  student = add_level(N = 15)
  )&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Warning: `lang_modify()` is deprecated as of rlang 0.2.0.
## Please use `call_modify()` instead.
## This warning is displayed once per session.&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr class=&#34;header&#34;&gt;
&lt;th align=&#34;left&#34;&gt;school&lt;/th&gt;
&lt;th align=&#34;right&#34;&gt;n_classrooms&lt;/th&gt;
&lt;th align=&#34;left&#34;&gt;classroom&lt;/th&gt;
&lt;th align=&#34;left&#34;&gt;student&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;1&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;12&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;01&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;001&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;1&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;12&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;01&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;002&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;1&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;12&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;01&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;003&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;1&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;12&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;01&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;004&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;1&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;12&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;01&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;005&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;1&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;12&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;01&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;006&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The real world often produces messy, overlapping hierarchies. For example, student data may be collected from middle school and also high school, in which case students are nested in two different schools, but those schools are not nested within each other. Here’s how to make such “cross-classified” data. The &lt;code&gt;rho&lt;/code&gt; parameter governs how correlated &lt;code&gt;primary_rank&lt;/code&gt; and &lt;code&gt;secondary_rank&lt;/code&gt; should be.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;dat &amp;lt;- 
fabricate(
  primary_schools = add_level(N = 5, primary_rank = 1:N),
  secondary_schools = add_level(N = 6, secondary_rank = 1:N, nest = FALSE),
  students = link_levels(N = 15, by = join(primary_rank, secondary_rank, rho = 0.9))
)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## `link_levels()` calls are faster if the `mvnfast` package is installed.&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;ggplot(dat, aes(primary_rank, secondary_rank)) + geom_point(position = position_jitter(width = 0.1, height = 0.1), alpha = 0.5) + theme_bw()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;/post/2019-06-21-imagine-your-data-before-you-collect-it_files/figure-html/unnamed-chunk-6-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Similarly, you can create longitudinal data via &lt;code&gt;cross_levels&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;fabricate(
 students = add_level(N = 2),
 years = add_level(N = 20, year = 1981:2000, nest = FALSE),
 student_year = cross_levels(by = join(students, years))
)&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr class=&#34;header&#34;&gt;
&lt;th align=&#34;left&#34;&gt;students&lt;/th&gt;
&lt;th align=&#34;left&#34;&gt;years&lt;/th&gt;
&lt;th align=&#34;right&#34;&gt;year&lt;/th&gt;
&lt;th align=&#34;left&#34;&gt;student_year&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;1&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;01&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;1981&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;2&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;01&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;1981&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;02&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;1&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;02&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;1982&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;03&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;2&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;02&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;1982&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;04&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td align=&#34;left&#34;&gt;1&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;03&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;1983&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;05&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td align=&#34;left&#34;&gt;2&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;03&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;1983&lt;/td&gt;
&lt;td align=&#34;left&#34;&gt;06&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div id=&#34;imagining-your-variables&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Imagining your variables&lt;/h1&gt;
&lt;p&gt;R has lots of great tools for simulating variables. In some cases, though, common kinds of outcome variables are surprisingly tough to simulate. &lt;code&gt;fabricatr&lt;/code&gt; collects a small number of functions to create variable types commonly used by social scientists, with simple syntax. We describe two examples here, but see our
&lt;a href=&#34;https://declaredesign.org/r/fabricatr/articles/common_social.html&#34;&gt;variable creation vignette&lt;/a&gt; for the rest.&lt;/p&gt;
&lt;div id=&#34;variables-with-intra-class-correlation&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Variables with intra-class correlation&lt;/h2&gt;
&lt;p&gt;With the data structure tools described above, you can construct data that has
within-unit and between-unit variation, for example, variation within classrooms
and variation across classrooms in test scores. However, many times you want to
set the level of intra-class correlation (ICC) more precisely. We help with
&lt;code&gt;draw_normal_icc&lt;/code&gt; and &lt;code&gt;draw_binary_icc&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;dat &amp;lt;- 
  fabricate(
    N = 1000,
    clusters = sample(LETTERS, N, replace = TRUE),
    Y1 = draw_normal_icc(clusters = clusters, ICC = .2),
    Y2 = draw_binary_icc(clusters = clusters, ICC = .2)
  )
ICC::ICCbare(clusters, Y1, dat)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] 0.09726701&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;ICC::ICCbare(clusters, Y2, dat)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] 0.176036&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;ordered-outcomes&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Ordered outcomes&lt;/h2&gt;
&lt;p&gt;We provide a set of tools for discrete random variables (including ordered outcomes). We take a latent variable (i.e., &lt;code&gt;test_ability&lt;/code&gt;) and transform it into an ordered variable (&lt;code&gt;test_score&lt;/code&gt;).&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;dat &amp;lt;- 
fabricate(
  N = 100,
  test_ability = rnorm(N),
  test_score = draw_ordered(test_ability, breaks = c(-.5, 0, .5))
)
ggplot(dat, aes(test_ability, test_score)) + geom_point() + theme_bw()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;/post/2019-06-21-imagine-your-data-before-you-collect-it_files/figure-html/unnamed-chunk-10-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;fabricatr&lt;/code&gt; is compatible with almost any R variable creation function. We highlight other terrific R packages that help simulate social science-relevant variables &lt;a href=&#34;https://declaredesign.org/r/fabricatr/articles/other_packages.html&#34;&gt;in a vignette here&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&#34;where-to-go-next&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Where to go next&lt;/h1&gt;
&lt;p&gt;This post is a high-level teaser for &lt;code&gt;fabricatr&lt;/code&gt;’s functionality, but for a deeper introduction, check out the &lt;a href=&#34;https://declaredesign.org/r/fabricatr/articles/getting_started.html&#34;&gt;fabricatr getting started vignette&lt;/a&gt;. You can also download and print this &lt;a href=&#34;https://github.com/rstudio/cheatsheets/raw/master/fabricatr.pdf&#34;&gt;cheatsheet&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can install &lt;code&gt;fabricatr&lt;/code&gt; from CRAN:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;install.packages(&amp;quot;fabricatr&amp;quot;)
library(fabricatr)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&#34;https://graemeblair.com/&#34;&gt;Graeme Blair&lt;/a&gt; is an Assistant Professor of Political Science at UCLA.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&#34;http://jasper-cooper.com/&#34;&gt;Jasper Cooper&lt;/a&gt; is a Postdoctoral Research Associate at the Kahneman-Treisman Center for Behavioral Science and Public Policy at Princeton University.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&#34;https://alexandercoppock.com/&#34;&gt;Alexander Coppock&lt;/a&gt; is an Assistant Professor of Political Science at Yale University.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&#34;http://www.macartan.nyc/&#34;&gt;Macartan Humphreys&lt;/a&gt; is a Professor of Political Science at Columbia University and a Director of the research group “Institutions and Political Inequality” at the WZB Berlin Social Science Center.&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2019/07/01/imagine-your-data-before-you-collect-it/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>Equal Size kmeans</title>
      <link>https://rviews.rstudio.com/2019/06/13/equal-size-kmeans/</link>
      <pubDate>Thu, 13 Jun 2019 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2019/06/13/equal-size-kmeans/</guid>
      <description>
        


&lt;p&gt;We were recently presented with a problem where the decision maker wanted to understand how their data would naturally group together. The classic technique of &lt;em&gt;k-means clustering&lt;/em&gt; was a natural choice; it’s well known, computationally efficient, and implemented in base R via the &lt;code&gt;kmeans()&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;Our problem has a slight wrinkle: the decision maker wished to see the data grouped with (nearly) equal sizes. Now, a ‘true’ statistician would tell the client that the right thing to do from a theoretical perspective was to use native k-means results because some centers can simply have more nearby points than other centers. However, we are practitioners, and if the visualization provides additional information useful to the way people make decisions, we are not going to tell them they are wrong!&lt;/p&gt;
&lt;div id=&#34;approach&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Approach&lt;/h2&gt;
&lt;p&gt;This is very similar to a mathematical optimization problem commonly faced by organizations like fire and police departments; specifically, ‘where trucks/patrol cars should be stationed to minimize response time’.&lt;/p&gt;
&lt;p&gt;The general strategy is to decompose the hard problem into two easier sub-problems, to wit:&lt;/p&gt;
&lt;ol style=&#34;list-style-type: decimal&#34;&gt;
&lt;li&gt;If we knew where the centroids were, determining group membership would be easy.&lt;/li&gt;
&lt;li&gt;If we knew group membership, determining centroids would be trivial.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The key insight (and it really is all downhill from here) is to simply pretend that we have the solution to issue 1, and iterate between these two tasks until convergence is reached, that is - make a guess at where the centroids are, pick group members, then adjust the centroid based on group membership. This has the same ‘feel’ as &lt;em&gt;mathematical induction&lt;/em&gt;, and we’ll name the steps accordingly.&lt;/p&gt;
&lt;p&gt;Our example is based on &lt;code&gt;mtcars&lt;/code&gt; a built-in R dataset, with three clusters of equal size.&lt;/p&gt;
&lt;p&gt;First, some libraries:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(magrittr); library(dplyr); library(ggplot2)&lt;/code&gt;&lt;/pre&gt;
&lt;div id=&#34;basis-step&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Basis Step&lt;/h3&gt;
&lt;p&gt;We have to start somewhere, and in this example, we will use an initial solution coming from the basic &lt;code&gt;kmeans&lt;/code&gt; algorithm. Another approach would be to pick initial centroids at the ‘corners’ of the space, or to simply pick a few random data points as centroids:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;data(mtcars)
k = 3
kdat = mtcars %&amp;gt;% select(c(mpg, wt))
kdat %&amp;gt;% kmeans(k) -&amp;gt; kclust&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So far, so good. Now we’ll compute the distance matrix between each point and each centroid; this begins the&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;assignment-step&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Assignment step&lt;/h3&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;kdist = function(x1, y1, x2, y2){
  sqrt((x1-x2)^2 + (y1-y2)^2)
}
centers = kclust$centers

kdat %&amp;lt;&amp;gt;% 
  mutate(D1 = kdist(mpg, wt, centers[1,1], centers[1,2]))
kdat %&amp;lt;&amp;gt;% 
  mutate(D2 = kdist(mpg, wt, centers[2,1], centers[2,2]))
kdat %&amp;lt;&amp;gt;% 
  mutate(D3 = kdist(mpg, wt, centers[3,1], centers[3,2]))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From here, we assign clusters, which we do greedily, using a technique we like to call ‘little kids soccer’ - because this is the way kids generally pick teams - by going in order and picking the ‘best’ option available to them at the time. The algorithm interrogates each cluster in turn and picks the ‘closest’ unassigned member until each cluster is filled. There’s one minor wrinkle that needed to be worked out: the final round consists of the ones that are the ‘worst fits’ across all k clusters; in this case, the points choose the clusters.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;kdat$assigned = 0
kdat$index = 1:nrow(kdat)
working = kdat
FirstRound = nrow(kdat) - (nrow(kdat) %% k)

for(i in 1:FirstRound){ 
  #cluster counts can be off by 1 due to uneven multiples of k. 
  j = if(i %% k == 0) k else (i %% k)
  itemloc = 
    working$index[which(working[,(paste0(&amp;quot;D&amp;quot;, j))] ==
    min(working[,(paste0(&amp;quot;D&amp;quot;,j))]))[1]]
  kdat$assigned[kdat$index == itemloc] = j
  working %&amp;lt;&amp;gt;% filter(!index == itemloc)
##The sorting hat says... GRYFFINDOR!!! 
}
for(i in 1:nrow(working)){
  #these leftover points get assigned to whoever&amp;#39;s closest, without regard to k
  kdat$assigned[kdat$index ==
                  working$index[i]] = 
    which(working[i,3:5] == min(working[i, 3:5])) 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we recalculate the centroids. It’s kind of smooth to simply use &lt;code&gt;k-means&lt;/code&gt; with &lt;code&gt;k = 1&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;NewCenters &amp;lt;- kdat %&amp;gt;% filter(assigned == 1) %&amp;gt;% 
                        select(mpg, wt) %&amp;gt;%
                        kmeans(1) %$% centers

NewCenters %&amp;lt;&amp;gt;% rbind(kdat %&amp;gt;% 
                        filter(assigned == 2) %&amp;gt;%
                        select(mpg, wt) %&amp;gt;%
                        kmeans(1) %$% centers)

NewCenters %&amp;lt;&amp;gt;% rbind(kdat %&amp;gt;%
                        filter(assigned == 3) %&amp;gt;%
                        select(mpg, wt) %&amp;gt;%
                        kmeans(1) %$% centers)

NewCenters %&amp;lt;&amp;gt;% as.data.frame()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result a single round is presented here:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;kdat$assigned %&amp;lt;&amp;gt;% as.factor()
kdat %&amp;gt;% ggplot(aes(x = mpg, y = wt, color = assigned)) +
  theme_minimal() + geom_point() + 
  geom_point(data = NewCenters, aes(x = mpg, y = wt),
             color = &amp;quot;black&amp;quot;, size = 4) + 
  geom_point(data = as.data.frame(centers), 
             aes(x = mpg, y = wt), color = &amp;quot;grey&amp;quot;, size = 4)&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&#34;figure&#34;&gt;&lt;span id=&#34;fig:netplot&#34;&gt;&lt;/span&gt;
&lt;img src=&#34;/post/2019-06-11-equal-size-kmeans_files/figure-html/netplot-1.png&#34; alt=&#34;Iterated k-means with one step&#34; width=&#34;672&#34; /&gt;
&lt;p class=&#34;caption&#34;&gt;
Figure 1: Iterated k-means with one step
&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;You will notice there is a single point assigned to Group 1 that is on the ‘frontier’ between Groups 2 and 3. This point appears to be misclassified, and the way to resolve this is to iterate the algorithm (see below).&lt;/p&gt;
&lt;p&gt;You can see how coercing the size made the cluster centroids migrate - significantly in the case of the higher mpg cluster. Grey dots are the original centroid, black are the updated (equal size) centroid.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;functionalized-and-iterated&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Functionalized and iterated&lt;/h3&gt;
&lt;p&gt;It is straightforward to ‘wrap’ the above code into a function (truncated here for brevity), which we call &lt;code&gt;kMeanAdj&lt;/code&gt;. It and takes the incumbent centers, data, number of iterations, and &lt;span class=&#34;math inline&#34;&gt;\(k\)&lt;/span&gt; as arguments. We may plot the result as follows:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;x = kMeanAdj(NewCenters, kdat, iter = 3, k) 

x$Data$assigned %&amp;lt;&amp;gt;% as.factor()

x$Data %&amp;gt;% ggplot(aes(x = mpg, y = wt, color = assigned)) +
  theme_minimal() +  geom_point() +
  geom_point(data = x$centers, aes(x = mpg, y=wt),
             color = &amp;quot;black&amp;quot;, size = 4)&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&#34;figure&#34;&gt;&lt;span id=&#34;fig:plot&#34;&gt;&lt;/span&gt;
&lt;img src=&#34;/post/2019-06-11-equal-size-kmeans_files/figure-html/plot-1.png&#34; alt=&#34;Equal Size Clusters with 3 iterations&#34; width=&#34;672&#34; /&gt;
&lt;p class=&#34;caption&#34;&gt;
Figure 2: Equal Size Clusters with 3 iterations
&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Iterating the algorithm over several steps ‘stabilizes’ both the groups and centers, yielding the desired characteristics.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;summary&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;Programming projects like this can sometimes feel like traveling by hot air balloon, in the sense that you don’t know which way you will be headed until you begin to travel. In this case, we did not initially anticipate the poor performance of our initial method in the case where &lt;code&gt;k&lt;/code&gt; does not divide &lt;code&gt;n&lt;/code&gt;. The only way to discover issues like this, of course, is to frequently prototype and test code. Overcoming this challenge added both to the fun and the reward of this exercise. Additionally, it showcases how the (robust) existing routines in the R language and popular packages may be rapidly combined with new ideas. This flexibility is what makes R a natural choice for both practitioners and theorists in Statistics and Operations Research.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Harrison Schramm, CAP, PStat, is a Senior Fellow at the &lt;a href=&#34;https://csbaonline.org/&#34;&gt;Center for Strategic and Budgetary Assessments&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Carol DeZwarte, CAP, PMP, whose passion for advanced analytics predates it becoming a buzzword, is in Supply Chain Analytics at &lt;a href=&#34;https://www.wayfair.com/&#34;&gt;Wayfair&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2019/06/13/equal-size-kmeans/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>reticulate, virtualenv, and Python in Linux</title>
      <link>https://rviews.rstudio.com/2019/06/10/reticulate-virtualenv-and-python-in-linux/</link>
      <pubDate>Mon, 10 Jun 2019 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2019/06/10/reticulate-virtualenv-and-python-in-linux/</guid>
      <description>
        


&lt;p&gt;Roland Stevenson is a data scientist and consultant who may be reached on &lt;a href=&#34;https://www.linkedin.com/in/roland-stevenson/&#34;&gt;Linkedin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://rstudio.github.io/reticulate/&#34;&gt;&lt;code&gt;reticulate&lt;/code&gt;&lt;/a&gt; is an R package that allows us to use Python modules from within RStudio. I recently found this functionality useful while trying to compare the results of different uplift models. Though I did have R’s &lt;code&gt;uplift&lt;/code&gt; package producing &lt;a href=&#34;https://rdrr.io/cran/uplift/man/qini.html&#34;&gt;Qini&lt;/a&gt; charts and metrics, I also wanted to see how things looked with Wayfair’s promising &lt;a href=&#34;https://github.com/wayfair/pylift&#34;&gt;&lt;code&gt;pylift&lt;/code&gt; package&lt;/a&gt;. Since &lt;code&gt;pylift&lt;/code&gt; is only available in python, &lt;code&gt;reticulate&lt;/code&gt; made it easy for me to quickly use &lt;code&gt;pylift&lt;/code&gt; from within RStudio.&lt;/p&gt;
&lt;p&gt;In the article below, I’ll show how I worked through the following circumstances:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Since &lt;code&gt;pylift&lt;/code&gt; has only been tested on Python &amp;gt;= 3.6, and my system version of Python was 2.7, I needed to build and install Python 3.6 for myself, preferably within a self-contained virtual environment.&lt;/li&gt;
&lt;li&gt;I wanted to install &lt;code&gt;pylift&lt;/code&gt; in the virtual environment and set up &lt;code&gt;reticulate&lt;/code&gt; in my R Project to work within that environment.&lt;/li&gt;
&lt;li&gt;Finally, I needed to access &lt;code&gt;pylift&lt;/code&gt; from an R Markdown document via the &lt;code&gt;reticulate&lt;/code&gt; interface.&lt;/li&gt;
&lt;/ul&gt;
&lt;div id=&#34;setting-up-python-virtualenv-and-rstudio&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Setting up Python, virtualenv, and RStudio&lt;/h1&gt;
&lt;p&gt;Note: for consistency, I always use an instance created via &lt;a href=&#34;https://github.com/ras44/rstudio-instance&#34;&gt;r-studio-instance&lt;/a&gt; and a base project from &lt;a href=&#34;https://github.com/ras44/rstudio-instance&#34;&gt;r-studio-project&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Python 2.7 is the default on the systems I use (CentOS 6/7). Since I did not want to modify the system-level Python version, I installed Python 3.6.x at the user level in &lt;code&gt;$HOME/opt&lt;/code&gt; and created a virtual environment using Python 3. I then activated the Python 3 environment and installed &lt;code&gt;pylift&lt;/code&gt;. Finally, I ensured RStudio-Server 1.2 was installed, as it has advanced &lt;code&gt;reticulate&lt;/code&gt; support like plotting graphs in line in R Markdown documents.&lt;/p&gt;
&lt;p&gt;Below is a brief script that accomplishes the tasks in bash on CentOS 7:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd ~
mkdir tmp
cd tmp
wget https://www.python.org/ftp/python/3.6.2/Python-3.6.2.tgz
tar -xzvf Python-3.6.2.tgz
cd Python-3.6.2
./configure --prefix=$HOME/opt/python-3.6.2 --enable-shared
make
make install
cd ~
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$HOME/opt/python-3.6.2/lib
virtualenv -p $HOME/opt/python-3.6.2/bin/python3 pylift
source pylift/bin/activate
cd pylift
git clone https://github.com/wayfair/pylift
cd pylift
pip install .
pip install -r requirements.txt
cd
wget https://s3.amazonaws.com/rstudio-ide-build/server/centos6/x86_64/rstudio-server-rhel-1.2.1335-x86_64.rpm
sudo yum install -y --nogpgcheck rstudio-server-rhel-1.2.1335-x86_64.rpm
sudo rstudio-server start&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Some notes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;--enable-shared&lt;/code&gt; option is &lt;a href=&#34;https://github.com/rstudio/reticulate/issues/138&#34;&gt;required&lt;/a&gt; when building Python in order for &lt;code&gt;reticulate&lt;/code&gt; to work&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt; library also needs to be set prior to creating the virtual environment&lt;/li&gt;
&lt;li&gt;we use virtualenv to create a virtual environment called “pylift” and then ensure that all Python packages are installed to that environment only (so as not to pollute any other environments we are working with)&lt;/li&gt;
&lt;li&gt;we then clone the &lt;code&gt;pylift&lt;/code&gt; source and install &lt;code&gt;pylift&lt;/code&gt; along with all of its requirements via &lt;code&gt;pip install -r requirements.txt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;finally, we install the RStudio Server 1.2 Preview version in order to leverage its advanced &lt;code&gt;reticulate&lt;/code&gt; features&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div id=&#34;using-python-from-within-rstudio-via-reticulate&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Using Python from within RStudio via reticulate&lt;/h1&gt;
&lt;p&gt;Switching from bash to RStudio, we load &lt;code&gt;reticulate&lt;/code&gt; and set it up to use the virtual environment we just created. Finally, and specific to &lt;code&gt;pylift&lt;/code&gt;, we set matplotlib parameters so that we can plot directly in R.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(reticulate)

Sys.setenv(LD_LIBRARY_PATH = paste0(Sys.getenv(&amp;quot;HOME&amp;quot;),&amp;quot;/opt/python-3.6.2/lib&amp;quot;))
Sys.getenv(&amp;quot;LD_LIBRARY_PATH&amp;quot;)
use_virtualenv(&amp;quot;/home/rstevenson/pylift&amp;quot;, required=TRUE)
py_config()

# Currently this must be run in order for R-markdown plotting to work
matplotlib &amp;lt;- import(&amp;quot;matplotlib&amp;quot;)
matplotlib$use(&amp;quot;Agg&amp;quot;, force = TRUE)&lt;/code&gt;&lt;/pre&gt;
&lt;div id=&#34;test-that-it-works&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Test that it works&lt;/h2&gt;
&lt;p&gt;The following replicates the first part of &lt;a href=&#34;https://github.com/wayfair/pylift/blob/master/examples/simulated_data/sample.ipynb&#34;&gt;pylift tutorial: simulated data&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&#34;python&#34;&gt;&lt;code&gt;import matplotlib.pyplot as plt
import numpy as np
t = np.arange(0.0, 2.0, 0.01)
s = 1 + np.sin(2*np.pi*t)
plt.plot(t,s)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;/post/2019-06-03-roland_files/reticulate1.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;
&lt;p&gt;When run, the above code chunk should display a sinusoidal graph below it.&lt;/p&gt;
&lt;pre class=&#34;python&#34;&gt;&lt;code&gt;import numpy as np, matplotlib as mpl, matplotlib.pyplot as plt, pandas as pd
from pylift import TransformedOutcome
from pylift.generate_data import dgp
# Generate some data.
df = dgp(N=10000, discrete_outcome=True)

# Specify your dataframe, treatment column, and outcome column.
up = TransformedOutcome(df, col_treatment=&amp;#39;Treatment&amp;#39;, col_outcome=&amp;#39;Outcome&amp;#39;, stratify=df[&amp;#39;Treatment&amp;#39;])

# This function randomly shuffles your training data set and calculates net information value.
up.NIV()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;/post/2019-06-03-roland_files/reticulate2.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;
&lt;p&gt;The above Python chunk uses &lt;code&gt;reticulate&lt;/code&gt; from within RStudio to interact with &lt;code&gt;pylift&lt;/code&gt; in the context of a custom virtual environment, using a custom version of Python. This degree of customization and functionality should be useful to users who:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;want to use a different Python version than they typically use while not affecting their typical setup by way of a virtual environment&lt;/li&gt;
&lt;li&gt;want to install a Python module like &lt;code&gt;pylift&lt;/code&gt; within a virtual environment so as not to affect any of their user- or system-level Python module installations&lt;/li&gt;
&lt;li&gt;want to use &lt;code&gt;reticulate&lt;/code&gt; from RStudio to access a custom virtual environment, Python version, and Python modules&lt;/li&gt;
&lt;li&gt;wants to be able to delete the virtual environment and R-Project and have everything go back to the way it was&lt;/li&gt;
&lt;li&gt;wants to be able to reproduce or share the environment exactly so that the workflow can be shared with others&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2019/06/10/reticulate-virtualenv-and-python-in-linux/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>Introducing DeclareDesign, a Platform for Research Design</title>
      <link>https://rviews.rstudio.com/2019/06/04/introducing-declaredesign/</link>
      <pubDate>Tue, 04 Jun 2019 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2019/06/04/introducing-declaredesign/</guid>
      <description>
           



&lt;p&gt;&lt;a href=&#34;https://graemeblair.com/&#34;&gt;Graeme Blair&lt;/a&gt; is an Assistant Professor of Political Science at UCLA.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://jasper-cooper.com/&#34;&gt;Jasper Cooper&lt;/a&gt; is a Postdoctoral Research Associate at the Kahneman-Treisman Center for Behavioral Science and Public Policy at Princeton University.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://alexandercoppock.com/&#34;&gt;Alexander Coppock&lt;/a&gt; is an Assistant Professor of Political Science at Yale University.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://www.macartan.nyc/&#34;&gt;Macartan Humphreys&lt;/a&gt; is a Professor of Political Science at Columbia University and a Director of the research group “Institutions and Political Inequality” at the WZB Berlin Social Science Center.&lt;/p&gt;
&lt;p&gt;Research design consists of a set of choices about what research &lt;em&gt;procedures&lt;/em&gt; to use. For example, how many subjects to interview, which questions to ask them, and what to do in the analysis phase with the data that results from these choices. We do not have good tools for assessing whether the chosen procedures are good ones. &lt;a href=&#34;https://declaredesign.org/r/declaredesign&#34;&gt;&lt;code&gt;DeclareDesign&lt;/code&gt;&lt;/a&gt; is an R package for learning about, implementing, and communicating research procedures, from data collection to data analysis.&lt;/p&gt;
&lt;p&gt;Today, most data scientists take raw data as the starting point for analysis, and consider how best to import, tidy, transform, visualize, model, and communicate their results. Yet often during this process, we learn that our data can only provide limited answers to our questions. Perhaps the sample size is too small, we do not have enough observations from men and women to estimate gender differences, or we did not collect data on units’ geographic location, so we cannot merge to administrative data. We rely on rules of thumb or expert opinion to navigate the myriad research design choices we face. We select analysis strategies tailored to the data that we have before us rather than the data that could have arisen. In doing so, we risk introducing biases. Our practices can often lead to bad inferences and bad science.&lt;/p&gt;
&lt;p&gt;To learn whether a research procedure is a good one, we need to know what the results &lt;em&gt;would have been&lt;/em&gt; had the data turned out differently. With that knowledge, we can assess our data analysis procedures in terms of whether they will yield answers that are unbiased and precise. &lt;code&gt;DeclareDesign&lt;/code&gt; helps us simulate different data sets that could result from our data collection procedures. With these simulated data, we can ask the question: is our set of procedures likely to yield unbiased, low-variance answers to our research questions? We can assess not only whether we should select difference-in-means or regression as our estimator, but also whether it would have been better to assign half of subjects to treatment in our A/B test, or more than half.&lt;/p&gt;
&lt;p&gt;We’ve built a set of linked tools that help with each step of the research design process. In this post, we’ll talk about using &lt;code&gt;DeclareDesign&lt;/code&gt; to declare and diagnose the properties of a full set of procedures. We’ll also introduce &lt;a href=&#34;https://declaredesign.org/library/&#34;&gt;&lt;code&gt;DesignLibrary&lt;/code&gt;&lt;/a&gt;, which helps you quickly get started learning about your research design for a set of common designs. In the coming weeks, we’ll talk about tools for simulating data (&lt;a href=&#34;https://declaredesign.org/r/fabricatr&#34;&gt;&lt;code&gt;fabricatr&lt;/code&gt;&lt;/a&gt;), sampling cases and assigning A/B tests (&lt;a href=&#34;https://declaredesign.org/r/randomizr&#34;&gt;&lt;code&gt;randomizr&lt;/code&gt;&lt;/a&gt;), and estimating effects (&lt;a href=&#34;https://declaredesign.org/r/estimatr/&#34;&gt;&lt;code&gt;estimatr&lt;/code&gt;&lt;/a&gt;).&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(DeclareDesign)
library(tidyverse)&lt;/code&gt;&lt;/pre&gt;
&lt;div id=&#34;a-grammar-of-research-designs&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;A grammar of research designs&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;DeclareDesign&lt;/code&gt; implements a set of ideas laid out in &lt;a href=&#34;https://declaredesign.org/declare.pdf&#34;&gt;our paper&lt;/a&gt;, forthcoming at the &lt;em&gt;American Political Science Review&lt;/em&gt;. We think of any research design as having four parts:&lt;/p&gt;
&lt;ol style=&#34;list-style-type: decimal&#34;&gt;
&lt;li&gt;A &lt;strong&gt;M&lt;/strong&gt;odel of the world.&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;I&lt;/strong&gt;nquiry about that model.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;D&lt;/strong&gt;ata strategy according to which data will be collected or brought into existence.&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;A&lt;/strong&gt;nswer strategy according to which the data will be summarized to generate a guess about the inquiry.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Writing designs down is hard, and &lt;code&gt;DeclareDesign&lt;/code&gt; can make that process simpler, more explicit, and reproducible.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;declaration-steps&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Declaration Steps&lt;/h1&gt;
&lt;p&gt;In &lt;code&gt;DeclareDesign&lt;/code&gt;, you declare each component of a research design using one of the &lt;code&gt;declare_*&lt;/code&gt; functions.&lt;/p&gt;
&lt;table&gt;
&lt;colgroup&gt;
&lt;col width=&#34;33%&#34; /&gt;
&lt;col width=&#34;4%&#34; /&gt;
&lt;col width=&#34;62%&#34; /&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr class=&#34;header&#34;&gt;
&lt;th&gt;&lt;code&gt;declare_*&lt;/code&gt; function&lt;/th&gt;
&lt;th&gt;MIDA&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td&gt;&lt;code&gt;declare_population()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;M&lt;/td&gt;
&lt;td&gt;Pre-treatment covariates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td&gt;&lt;code&gt;declare_potential_outcomes()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;M&lt;/td&gt;
&lt;td&gt;Relationships between treatments, covariates, and outcomes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td&gt;&lt;code&gt;declare_estimand()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;I&lt;/td&gt;
&lt;td&gt;Quantity of interest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td&gt;&lt;code&gt;declare_sampling()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;D&lt;/td&gt;
&lt;td&gt;(Possibly random) sampling procedure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td&gt;&lt;code&gt;declare_assignment()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;D&lt;/td&gt;
&lt;td&gt;(Possibly random) assignment procedure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;even&#34;&gt;
&lt;td&gt;&lt;code&gt;declare_reveal()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;D&lt;/td&gt;
&lt;td&gt;Function that maps potential outcomes to realized outcomes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;odd&#34;&gt;
&lt;td&gt;&lt;code&gt;declare_estimator()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;Estimation (and hypothesis testing, if applicable) procedure&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Each &lt;code&gt;declare_*&lt;/code&gt; function is a “function factory”, which means that each of these functions themselves returns a function. Each function takes data as an argument, and returns either data or statistics. Many R users are not used to having functions return functions, but this makes simulation very easy, and is not so odd once you get used to it. In the example below, we define the &lt;code&gt;pop&lt;/code&gt; function using &lt;code&gt;declare_population()&lt;/code&gt;. Each time we call the resulting &lt;code&gt;pop&lt;/code&gt; function, we get a slightly different data set.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;pop &amp;lt;- declare_population(N = 3, X = rnorm(N))
pop()&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;  ID         X
1  1 -2.120337
2  2 -1.156013
3  3  1.703992&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;pop()&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;  ID          X
1  1 -0.6172369
2  2  0.3183334
3  3 -1.3762445&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;chaining-steps-together&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Chaining steps together&lt;/h1&gt;
&lt;p&gt;A research design can be thought of as a series of steps. In &lt;code&gt;DeclareDesign&lt;/code&gt;, we chain steps together using the &lt;code&gt;+&lt;/code&gt; operator, which may be familiar to &lt;code&gt;ggplot2&lt;/code&gt; users. Each step takes the data generated in the step before it, and either returns statistics or passes that data on to the next step, having added new variables or changed the shape of the data. In the next example, we declare a workhorse research strategy used in many scientific fields, the two-arm randomized trial:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;design &amp;lt;-
  
  # M -- Model
  declare_population(N = 100, X = rpois(N, 4), noise = rnorm(N)) +
  declare_potential_outcomes(Y ~ 0.2 * Z + 0.5 * X + noise) +
  
  # I -- Inquiry
  declare_estimand(ATE = mean(Y_Z_1 - Y_Z_0)) +
  
  # D -- Data Strategy
  declare_sampling(n = 50) +
  declare_assignment(m = 25) +
  declare_reveal(Y, Z) +
  
  # A -- Answer Strategy
  declare_estimator(Y ~ Z, model = difference_in_means, estimand = &amp;quot;ATE&amp;quot;, label = &amp;quot;DIM&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Some points to note about this declaration:&lt;/p&gt;
&lt;ol style=&#34;list-style-type: decimal&#34;&gt;
&lt;li&gt;&lt;code&gt;design&lt;/code&gt; is a “design object,” which can then be used to learn all kinds of things about the design. We’ll turn to some common post-declaration functions in a moment.&lt;/li&gt;
&lt;li&gt;“Potential outcomes” is a concept from the causal inference literature. Potential outcomes are the outcomes that each unit would express depending on the counterfactual level of some treatment. The revealed outcome is the &lt;strong&gt;observed&lt;/strong&gt; outcome: treated subjects reveal their treated outcome and untreated subjects reveal their untreated outcome. The potential outcomes model of causality is deep. For users who are new to thinking in terms of potential outcomes, we promise it will get easier with time. &lt;a href=&#34;https://en.wikipedia.org/wiki/Rubin_causal_model&#34;&gt;The Wikipedia article&lt;/a&gt; is OK, but we would suggest &lt;a href=&#34;https://www.amazon.com/Field-Experiments-Design-Analysis-Interpretation/dp/0393979954&#34;&gt;Chapter 2 of this book&lt;/a&gt; if it’s available to you.&lt;/li&gt;
&lt;li&gt;In the code above, the estimand and estimator steps are explicitly linked together – we define the average treatment effect (ATE) in terms of potential outcomes, and then in the estimation step, we tell the estimator that it is shooting at the ATE. This means that a design “knows” what question each estimator is trying to answer.&lt;/li&gt;
&lt;li&gt;These declaration functions depend on the other packages in the &lt;code&gt;DeclareDesign&lt;/code&gt; set of packages, which includes &lt;code&gt;fabricatr&lt;/code&gt;, &lt;code&gt;randomizr&lt;/code&gt;, and &lt;code&gt;estimatr&lt;/code&gt;. We’ll introduce each of these packages in upcoming posts. The population and potential outcomes steps use &lt;code&gt;fabricatr&lt;/code&gt;, the sampling and assignment use &lt;code&gt;randomizr&lt;/code&gt;, and the estimator steps use &lt;code&gt;estimatr&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div id=&#34;what-can-you-do-with-a-design-object&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;What can you do with a design object?&lt;/h1&gt;
&lt;p&gt;The first thing you might want to do with a design object is &lt;code&gt;draw_data&lt;/code&gt;. Take a look:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;dat &amp;lt;- draw_data(design)
head(dat)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;   ID X       noise    Y_Z_0    Y_Z_1 S_inclusion_prob Z Z_cond_prob
1 001 3 -0.09946134 1.400539 1.600539              0.5 1         0.5
2 002 3  0.42156668 1.921567 2.121567              0.5 1         0.5
3 003 3  0.24064949 1.740649 1.940649              0.5 0         0.5
4 005 5 -1.29990991 1.200090 1.400090              0.5 1         0.5
5 007 4  1.38503254 3.385033 3.585033              0.5 1         0.5
6 008 3  0.16181031 1.661810 1.861810              0.5 1         0.5
         Y
1 1.600539
2 2.121567
3 1.740649
4 1.400090
5 3.585033
6 1.861810&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can quickly generate simulated data that (if you did your declaration right!) is the same size and shape as the data your study &lt;strong&gt;will&lt;/strong&gt; generate, once it’s run. Imagining your data ex ante gives you the chance to think about logistical, practical, theoretical, or analytic concerns that might otherwise be put off until far later in the design process.&lt;/p&gt;
&lt;p&gt;Next up, you can take a look a the estimands and estimates produced by one run of your design:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;draw_estimands(design)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;  estimand_label estimand
1            ATE      0.2&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;draw_estimates(design)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;  estimator_label term  estimate std.error statistic   p.value   conf.low
1             DIM    Z 0.1302799 0.3997006 0.3259437 0.7459163 -0.6738506
  conf.high      df outcome estimand_label
1 0.9344104 46.9193       Y            ATE&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These functions give you a chance to ensure that your estimands and estimators make sense and are returning what you expect them to return. If a &lt;code&gt;0.2&lt;/code&gt; average treatment effect is implausible in your research setting, change it!&lt;/p&gt;
&lt;p&gt;You can also add steps to a design. Here, we’re adding another estimator that &lt;strong&gt;also&lt;/strong&gt; shoots at the ATE, but this time adjusts for a pre-treatment covariate, &lt;code&gt;X&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;design &amp;lt;- design + declare_estimator(Y ~ Z + X, model = lm_robust, estimand = &amp;quot;ATE&amp;quot;, label = &amp;quot;OLS&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;simulating-designs&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Simulating designs&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;DeclareDesign&lt;/code&gt; has two functions for conducting simulations: &lt;code&gt;simulate_design&lt;/code&gt; and &lt;code&gt;diagnose_design&lt;/code&gt;. If your answer strategy returns estimates, &lt;span class=&#34;math inline&#34;&gt;\(p\)&lt;/span&gt;-values, and standard errors, and it is linked to an estimand, &lt;code&gt;diagnose_design&lt;/code&gt; can tell you a lot about your design: its power, its coverage probability, its “true” standard error (the standard deviation of the estimates across repeat samples), the average standard error it returns, its bias, its root mean square error, the probability you get the sign of the estimand wrong, its false positive rate, and more. If you just want to simulate the design, &lt;code&gt;simulate_design&lt;/code&gt; will return a data set in which each row is an estimator-estimand pair. Since we have two estimators, each run of the design produces two rows.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;simulations &amp;lt;- simulate_design(design, sims = 500)
head(simulations)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;  design_label sim_ID estimand_label estimand estimator_label term
1       design      1            ATE      0.2             DIM    Z
2       design      1            ATE      0.2             OLS    Z
3       design      2            ATE      0.2             DIM    Z
4       design      2            ATE      0.2             OLS    Z
5       design      3            ATE      0.2             DIM    Z
6       design      3            ATE      0.2             OLS    Z
     estimate std.error  statistic    p.value     conf.low conf.high
1 -0.73149295 0.4594131 -1.5922335 0.11789917 -1.655212062 0.1922262
2 -0.04367092 0.3435135 -0.1271301 0.89937977 -0.734730962 0.6473891
3  0.23917794 0.4454914  0.5368856 0.59384801 -0.656742583 1.1350985
4  0.62821514 0.3105686  2.0227904 0.04880466  0.003431732 1.2529986
5  0.27936681 0.3567281  0.7831365 0.43758754 -0.438821047 0.9975547
6  0.04149547 0.2894608  0.1433544 0.88662317 -0.540824504 0.6238154
        df outcome
1 47.98565       Y
2 47.00000       Y
3 47.58893       Y
4 47.00000       Y
5 45.69013       Y
6 47.00000       Y&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The simulations data can then be used to summarize the design using &lt;code&gt;ggplot2&lt;/code&gt; and &lt;code&gt;dplyr&lt;/code&gt;. For example, simulation is useful to understand the &lt;strong&gt;distribution&lt;/strong&gt; of effect estimates; are they centered on the estimand? Is the distribution wide or narrow? Are there outliers that might indicate estimation issues?&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;summary_df &amp;lt;-
  simulations %&amp;gt;%
  group_by(estimator_label) %&amp;gt;%
  summarize(`Mean Estimate` = mean(estimate),
            `Mean Estimand` = mean(estimand)) %&amp;gt;%
  gather(key, value, `Mean Estimand`, `Mean Estimate`)

ggplot(simulations, aes(estimate)) +
  geom_histogram(bins = 30) +
  geom_vline(data = summary_df, aes(xintercept = value, color = key)) +
  facet_wrap( ~ estimator_label) + 
  theme_bw() +
  theme(strip.background = element_blank(),
        legend.position = &amp;quot;bottom&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;/post/2019-05-19-introducing-declaredesign_files/figure-html/unnamed-chunk-8-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;getting-started-quickly-with-designlibrary&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Getting started quickly with DesignLibrary&lt;/h1&gt;
&lt;p&gt;You can get started by declaring a design in a single line of code with &lt;code&gt;DesignLibrary&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(DesignLibrary)
two_arm_design &amp;lt;- two_arm_designer(N = 1000)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;DesignLibrary&lt;/code&gt; contains 15 common research designs that can be declared in this simple
way. In each case, a set of function arguments are set up to define the core inputs to the design, such as sample size. For example, we can declare a pretest-posttest design in which data is collected before (pre) and after (post) a treatment is assigned. We define the sample size (&lt;code&gt;N&lt;/code&gt;), the average treatment effect (&lt;code&gt;ate&lt;/code&gt;), and the correlation in outcomes between the pre and post periods (&lt;code&gt;rho&lt;/code&gt;):&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;prepost_design &amp;lt;- pretest_posttest_designer(N = 1000, ate = 5, rho = 0.25)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;using-declaredesign-in-your-data-science-workflow&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Using DeclareDesign in your data science workflow&lt;/h1&gt;
&lt;p&gt;Since the four helper packages can be used to simulate data, sample units, randomize treatments, and analyze data, we see at least seven ways you can get value out of using &lt;code&gt;DeclareDesign&lt;/code&gt; in your everyday data-science workflow.&lt;/p&gt;
&lt;ol style=&#34;list-style-type: decimal&#34;&gt;
&lt;li&gt;Simulate mock data (&lt;code&gt;draw_data()&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Assess the properties of your design before you run it (&lt;code&gt;diagnose_design()&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Front-load design decisions before data is collected (&lt;code&gt;+&lt;/code&gt; to declare it).&lt;/li&gt;
&lt;li&gt;Compare the properties of several possible designs (&lt;code&gt;compare_designs()&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Write a preanalysis plan or registered report (&lt;code&gt;summary(design)&lt;/code&gt; to get started).&lt;/li&gt;
&lt;li&gt;Describe your design in code in a paper or report (&lt;code&gt;print_code(design)&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Learn about properties of someone else’s design (&lt;code&gt;diagnose_design()&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Finally, you may find the &lt;a href=&#34;https://github.com/rstudio/cheatsheets/blob/master/declaredesign.pdf&#34;&gt;DeclareDesign cheatsheet&lt;/a&gt; hosted by RStudio helpful.&lt;/p&gt;
&lt;/div&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2019/06/04/introducing-declaredesign/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>Virtual Morel Foraging with R</title>
      <link>https://rviews.rstudio.com/2019/05/13/virtual-morel-foraging-with-r/</link>
      <pubDate>Mon, 13 May 2019 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2019/05/13/virtual-morel-foraging-with-r/</guid>
      <description>
        
&lt;script src=&#34;/rmarkdown-libs/htmlwidgets/htmlwidgets.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/jquery/jquery.min.js&#34;&gt;&lt;/script&gt;
&lt;link href=&#34;/rmarkdown-libs/leaflet/leaflet.css&#34; rel=&#34;stylesheet&#34; /&gt;
&lt;script src=&#34;/rmarkdown-libs/leaflet/leaflet.js&#34;&gt;&lt;/script&gt;
&lt;link href=&#34;/rmarkdown-libs/leafletfix/leafletfix.css&#34; rel=&#34;stylesheet&#34; /&gt;
&lt;script src=&#34;/rmarkdown-libs/Proj4Leaflet/proj4-compressed.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/Proj4Leaflet/proj4leaflet.js&#34;&gt;&lt;/script&gt;
&lt;link href=&#34;/rmarkdown-libs/rstudio_leaflet/rstudio_leaflet.css&#34; rel=&#34;stylesheet&#34; /&gt;
&lt;script src=&#34;/rmarkdown-libs/leaflet-binding/leaflet.js&#34;&gt;&lt;/script&gt;


&lt;p&gt;&lt;em&gt;&lt;a href=&#34;http://illposed.net/&#34;&gt;Bryan Lewis&lt;/a&gt; is a mathematician, R developer and mushroom forager.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/post/2019-05-07-Lewis-Foraging_files/morchella-americana.jpg&#34; width=&#34;600&#34; height=&#34;600&#34;/&gt;&lt;/p&gt;
&lt;p&gt;                              Morchella Americana by Bryan W. Lewis, see &lt;a href=&#34;https://ohiomushroomsociety.wordpress.com/&#34; class=&#34;uri&#34;&gt;https://ohiomushroomsociety.wordpress.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;
It’s that time of year again, when people in the Midwestern US go nuts for morel
mushrooms. Although fairly common in Western Pennsylvania, Ohio, Indiana,
Illinois, Wisconsin, and, especially, Michigan&lt;a href=&#34;#fn1&#34; class=&#34;footnote-ref&#34; id=&#34;fnref1&#34;&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt;, they can still be
tricky to find due to the vagaries of weather and mysteries of morel
reproduction.&lt;/p&gt;
&lt;p&gt;Morels are indeed delicious mushrooms, but I really think a big part of their
appeal is their elusive nature. It’s so exciting when you finally find some–or
even one!–after hours and hours of hiking in the woods.&lt;/p&gt;
&lt;p&gt;For all of you not fortunate to be in the Midwest in the spring, here is a
not-so-serious note on virtual morel foraging. But really, this note explores
ways you can mine image data from the internet using a cornucopia of data
science software tools orchestrated by R.&lt;/p&gt;
&lt;p&gt;Typical forays begin with a slow, deliberate hunt for mushrooms in the forest.
Morels, like many mushrooms, may form complex symbiotic relationships with
plants and trees, so seek out tree species that they like (elms, tulip tress,
apple trees, and some others). Upon finding a mushroom, look around and closely
observe its habitat, maybe photograph it, and perhaps remove the fruiting body
for closer inspection and analysis, and maybe for eating. The mushroom you
pick is kind of like a fruit - it is the spore-distributing body of a much
larger organism that lives below the ground. When picking, get in the habit of
carefully examining a portion of the mushroom below the ground because
sometimes that includes important identifying characteristics. Later, use
field guide keys and expert advice to identify the mushroom, maybe even
examining spores under a microscope. Sometimes we might even send a portion of
clean tissue in for DNA analysis (see, for instance,
&lt;a href=&#34;https://mycomap.com/projects&#34; class=&#34;uri&#34;&gt;https://mycomap.com/projects&lt;/a&gt;). Then, finally, for choice edible mushrooms like
morels, once you are sure of your bounty, cook and eat them!&lt;/p&gt;
&lt;p&gt;Edible morels are actually pretty easy to identify in the field. There &lt;em&gt;are&lt;/em&gt; a
few poisinous mushrooms that kind of look like morels, but on closer inspection
not really. Chief among them are the false morels or &lt;a href=&#34;https://en.wikipedia.org/wiki/Gyromitra&#34;&gt;Gyromitra&lt;/a&gt;, some of which we will find below in
our virtual foray!&lt;/p&gt;
&lt;p&gt;Our virtual foray proceeds similarly as follows:&lt;/p&gt;
&lt;ol style=&#34;list-style-type: decimal&#34;&gt;
&lt;li&gt;Virtually hunt for images of morel mushrooms on the internet.&lt;/li&gt;
&lt;li&gt;Inspect each image for GPS location data.&lt;/li&gt;
&lt;li&gt;Map the results!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now, I know what you’re saying: most mushroom
hunters - especially morel hunters - are secretive
about their locations, and will strip GPS information from their pictures.
And we will see that is exactly the case: only about 1% of the pictures
we find include GPS data.
But there are &lt;em&gt;lots&lt;/em&gt; of pictures on the internet,
so eventually even that 1% can be interesting to look at…&lt;/p&gt;
&lt;div id=&#34;the-hunt&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;The Hunt&lt;/h2&gt;
&lt;p&gt;Our virtual mushroom foray begins as any real-world foray does, looking around
for mushrooms! But instead of a forest, we’ll use the internet. In particular,
let’s ask popular search engines to search for images of morels, and then
inspect those images for GPS coordinates.&lt;/p&gt;
&lt;p&gt;But how can we ask internet search engines to return image information directly
to R? Unfortunately, the main image search engines like Google and Bing today
rely on interactive JavaScript operation, precluding simple use of, say, R’s
excellent &lt;code&gt;curl&lt;/code&gt; package. Fortunately, there exists a tool for
&lt;em&gt;web browser automation&lt;/em&gt; called &lt;a href=&#34;https://docs.seleniumhq.org/&#34;&gt;Selenium&lt;/a&gt; and, of course, a corresponding R interface package called &lt;a href=&#34;https://cran.r-project.org/package=RSelenium&#34;&gt;&lt;code&gt;RSelenium&lt;/code&gt;&lt;/a&gt;.
&lt;code&gt;RSelenium&lt;/code&gt; essentially allows R to use a web browser like a human, including
clicking on buttons, etc. Using web browser automation is not ideal because
we rely on fragile front-end web page/JavaScript interfaces that can change
at any time instead of something well-organized like HTML, but we
seem to be forced into this approach by the modern internet.&lt;/p&gt;
&lt;p&gt;Our hunt requires that the Google Chrome browser is installed on your
system&lt;a href=&#34;#fn2&#34; class=&#34;footnote-ref&#34; id=&#34;fnref2&#34;&gt;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt;, and of course you’ll need R! You’ll need at least the
following R packages installed. If you don’t have them, install them from CRAN:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(wdman)
library(RSelenium)
library(jsonlite)
library(leaflet)
library(parallel)
library(htmltools)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s define two functions, one to search Microsoft Bing images, and another
to search Google images. Each function takes an &lt;code&gt;RSelenium&lt;/code&gt; browser and
a search term as input, and returns a list of search result URLs.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;bing = function(wb, search_term)
{
  url = sprintf(&amp;quot;https://www.bing.com/images/search?q=%s&amp;amp;FORM=HDRSC2&amp;quot;, search_term)
  wb$navigate(url)
  invisible(replicate(200, wb$executeScript(&amp;quot;window.scrollBy(0, 10000)&amp;quot;))) # infinite scroll down to load more results...
  x = wb$findElements(using=&amp;quot;class name&amp;quot;, value=&amp;quot;btn_seemore&amp;quot;) # more results...
  if(length(x) &amp;gt; 0) x[[1]]$click()
  invisible(replicate(200, wb$executeScript(&amp;quot;window.scrollBy(0, 10000)&amp;quot;)))
  Map(function(x)
  {
    y = x$getElementAttribute(&amp;quot;innerHTML&amp;quot;)
    y = gsub(&amp;quot;.* m=\\\&amp;quot;&amp;quot;, &amp;quot;&amp;quot;, y)
    y = gsub(&amp;quot;\\\&amp;quot;.*&amp;quot;, &amp;quot;&amp;quot;, y)
    y = gsub(&amp;quot;&amp;amp;quot;&amp;quot;, &amp;quot;\\\&amp;quot;&amp;quot;, y)
    y = gsub(&amp;quot;&amp;amp;amp;&amp;quot;, &amp;quot;&amp;amp;&amp;quot;, y)
    fromJSON(y)[c(&amp;quot;purl&amp;quot;, &amp;quot;murl&amp;quot;)]
  }, wb$findElements(using = &amp;quot;class name&amp;quot;, value = &amp;quot;imgpt&amp;quot;))
}
google = function(wb, search_term)
{
  url = sprintf(&amp;quot;https://www.google.com/search?q=%s&amp;amp;source=lnms&amp;amp;tbm=isch&amp;quot;, search_term)
  wb$navigate(url)
  invisible(replicate(400, wb$executeScript(&amp;quot;window.scrollBy(0, 10000)&amp;quot;)))
  Map(function(x)
  {
    ans = fromJSON(x$getElementAttribute(&amp;quot;innerHTML&amp;quot;)[[1]])[c(&amp;quot;isu&amp;quot;, &amp;quot;ou&amp;quot;)]
    names(ans) = c(&amp;quot;purl&amp;quot;, &amp;quot;murl&amp;quot;) # comply with Bing (cf.)
    ans
  }, wb$findElements(using = &amp;quot;xpath&amp;quot;, value = &amp;#39;//div[contains(@class,&amp;quot;rg_meta&amp;quot;)]&amp;#39;))
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These functions emulate what a human would do by scrolling down to get more
image results (both web sites us an ‘infinite scroll’ paradigm), and, in the
Bing case, clicking a button. This is what I meant above when I said that this
approach is fragile and not optimal - it’s quite possible that some small change
in either search engine in the future will cause the above functions to not
work.&lt;/p&gt;
&lt;p&gt;Let’s finally run our virtual mushroom hunt! We set up a Google Chrome-based
&lt;code&gt;RSelenium&lt;/code&gt; web browser interface, and run some searches:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;eCaps = list(chromeOptions = list( args = c(&amp;#39;--headless&amp;#39;, &amp;#39;--disable-gpu&amp;#39;, &amp;#39;--window-size=1280,800&amp;#39;)))
cr = chrome(port = 4444L)
wb = remoteDriver(browserName = &amp;quot;chrome&amp;quot;, port = 4444L, extraCapabilities = eCaps)
wb$open()
foray = c(google(wb, &amp;quot;morels&amp;quot;),
          google(wb, &amp;quot;indiana morel&amp;quot;),
          google(wb, &amp;quot;michigan morel&amp;quot;),
          google(wb, &amp;quot;oregon morel&amp;quot;),
          bing(wb, &amp;quot;morels&amp;quot;),
          bing(wb, &amp;quot;morel mushrooms&amp;quot;),
          bing(wb, &amp;quot;michigan morels&amp;quot;))
wb$close()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Feel free to try out different search terms. The result is a big list of possible
image URLs that just might contain pictures of morels with their coordinates.
This particular foray result above, run in late April, 2019, returned about
2000 results.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;identification&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Identification&lt;/h2&gt;
&lt;p&gt;Next, we scan every result for GPS coordinates using the nifty external
command-line tool called &lt;a href=&#34;https://www.sno.phy.queensu.ca/~phil/exiftool/&#34;&gt;&lt;code&gt;exiftool&lt;/code&gt;&lt;/a&gt;
and the venerable &lt;a href=&#34;https://curl.haxx.se/download.html&#34;&gt;&lt;code&gt;curl&lt;/code&gt; program&lt;/a&gt;.
If you don’t have those tools, you’ll need to install them on your computer.
They are available for most major operating systems. On Debian flavors of GNU/Linux
like Ubuntu it’s really easy, just run:&lt;/p&gt;
&lt;pre class=&#34;bash&#34;&gt;&lt;code&gt;sudo apt-get install exiftool curl&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once the &lt;code&gt;curl&lt;/code&gt; and &lt;code&gt;exiftool&lt;/code&gt; programs are installed, we can invoke them for each
image URL result from R to efficiently scan through part of the image for GPS
coordinates using these functions:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;#&amp;#39; Extract exif image data
#&amp;#39; @param url HTTP image URL
#&amp;#39; @return vector of exif character data or NA
exif = function(url)
{
  tryCatch({
    cmd = sprintf(&amp;quot;curl --max-time 5 --connect-timeout 2 -s \&amp;quot;%s\&amp;quot; | exiftool -fast2 -&amp;quot;, url)
    system(cmd, intern=TRUE)
  }, error = function(e) NA)
}
#&amp;#39; Convert an exif GPS character string into decimal latitude and longitude coordinates
#&amp;#39; @param x an exif GPS string
#&amp;#39; @return a named numeric vector of lat/lon coordinates or NA
decimal_degrees = function(x)
{
  s = strsplit(strsplit(x, &amp;quot;:&amp;quot;)[[1]][2], &amp;quot;,&amp;quot;)[[1]]
  ans = Map(function(y)
            ifelse(y[4] == &amp;quot;S&amp;quot; || y[4] == &amp;quot;W&amp;quot;, -1, 1) *
              (as.integer(y[1]) + as.numeric(y[2])/60 + as.numeric(y[3])/3600),
          strsplit(gsub(&amp;quot; +&amp;quot;, &amp;quot; &amp;quot;, gsub(&amp;quot;^ +&amp;quot;, &amp;quot;&amp;quot;, gsub(&amp;quot;deg|&amp;#39;|\&amp;quot;&amp;quot;, &amp;quot; &amp;quot;, s))), &amp;quot; &amp;quot;))
  names(ans) = c(&amp;quot;lat&amp;quot;, &amp;quot;lon&amp;quot;)
  ans
}
#&amp;#39; Evaluate a picture and return GPS info if available
#&amp;#39; @param url image URL
#&amp;#39; @return a list with pic, date, month, label, lat, lon entries or NULL
forage = function(url)
{
  ex = exif(url)
  i = grep(&amp;quot;GPS Position&amp;quot;, ex)
  if(length(i) == 0) return(NULL)
  pos = decimal_degrees(ex[i])
  date = tryCatch(strsplit(ex[grep(&amp;quot;Create Date&amp;quot;, ex)], &amp;quot;: &amp;quot;)[[1]][2], error=function(e) NA)
  month = ifelse(is.na(date), NA, as.numeric(strftime(strptime(date, &amp;quot;%Y:%m:%d %H:%M:%S&amp;quot;), format=&amp;quot;%m&amp;quot;)))
  label = paste(date, &amp;quot;  source: &amp;quot;, url)
  list(pic=paste0(&amp;quot;&amp;lt;img width=200 height=200 src=&amp;#39;&amp;quot;, url, &amp;quot;&amp;#39;/&amp;gt;&amp;quot;),
       date=date, month=month, label=label,
       lat=pos$lat, lon=pos$lon)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, there might be many search results to evaluate. Each evaluation is not
very compute intensive. And the results are independent of each other. So why
not run this evaluation step in parallel? R makes this easy to do,
although with some differences between operating systems. The following works
well on Linux or Mac systems. It will also run fine on Windows systems, but
sequentially.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;options(mc.cores = detectCores() + 2) # overload cpu a bit
print(system.time({
bounty = do.call(function(...) rbind.data.frame(..., stringsAsFactors=FALSE),
  mcMap(function(x)
  {
    forage(x$murl)
  }, foray)
)
}))
# Omit zero-ed out lat/lon coordinates
bounty = bounty[round(bounty$lat) != 0 &amp;amp; round(bounty$lon != 0), ]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above R code runs through every image result, returning those containing
GPS coordinates as observations in a data frame with image URL, date, month,
label, and decimal latitude and longitude variables.&lt;/p&gt;
&lt;p&gt;Starting with over 2,000 image results, I ended up with about 20 pictures
with GPS coordinates. Morels are as elusive in the virtual world as the
real one!&lt;/p&gt;
&lt;p&gt;Finally, let’s plot each result colored by the month of the image on
a map using the superb R &lt;code&gt;leaflet&lt;/code&gt; package. You can click on each point
to see its picture.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;colors = c(January=&amp;quot;#555555&amp;quot;, February=&amp;quot;#ffff00&amp;quot;, March=&amp;quot;#000000&amp;quot;,
           April=&amp;quot;#0000ff&amp;quot;, May=&amp;quot;#00aa00&amp;quot;, June=&amp;quot;#ff9900&amp;quot;, July=&amp;quot;#00ffff&amp;quot;,
           August=&amp;quot;#ff00ff&amp;quot;, September=&amp;quot;#55aa11&amp;quot;, October=&amp;quot;#aa9944&amp;quot;,
           November=&amp;quot;#77ffaa&amp;quot;, December=&amp;quot;#ccaa99&amp;quot;)
clr = as.vector(colors[bounty$month])
map = addTiles(leaflet(width=&amp;quot;100%&amp;quot;))
map = addCircleMarkers(map, data=bounty, lng=~lon, lat=~lat, fillOpacity=0.6,
         stroke=FALSE, fillColor=clr, label=~label, popup=~pic)
i = sort(unique(bounty$month))
map = addLegend(map, position=&amp;quot;bottomright&amp;quot;, colors=colors[i],
        labels=names(colors)[i], title=&amp;quot;Month&amp;quot;, opacity=1)
map&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;div id=&#34;htmlwidget-1&#34; style=&#34;width:100%;height:480px;&#34; class=&#34;leaflet html-widget&#34;&gt;&lt;/div&gt;
&lt;script type=&#34;application/json&#34; data-for=&#34;htmlwidget-1&#34;&gt;{&#34;x&#34;:{&#34;options&#34;:{&#34;crs&#34;:{&#34;crsClass&#34;:&#34;L.CRS.EPSG3857&#34;,&#34;code&#34;:null,&#34;proj4def&#34;:null,&#34;projectedBounds&#34;:null,&#34;options&#34;:{}}},&#34;calls&#34;:[{&#34;method&#34;:&#34;addTiles&#34;,&#34;args&#34;:[&#34;//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png&#34;,null,null,{&#34;minZoom&#34;:0,&#34;maxZoom&#34;:18,&#34;tileSize&#34;:256,&#34;subdomains&#34;:&#34;abc&#34;,&#34;errorTileUrl&#34;:&#34;&#34;,&#34;tms&#34;:false,&#34;noWrap&#34;:false,&#34;zoomOffset&#34;:0,&#34;zoomReverse&#34;:false,&#34;opacity&#34;:1,&#34;zIndex&#34;:1,&#34;detectRetina&#34;:false,&#34;attribution&#34;:&#34;&amp;copy; &lt;a href=\&#34;http://openstreetmap.org\&#34;&gt;OpenStreetMap&lt;\/a&gt; contributors, &lt;a href=\&#34;http://creativecommons.org/licenses/by-sa/2.0/\&#34;&gt;CC-BY-SA&lt;\/a&gt;&#34;}]},{&#34;method&#34;:&#34;addCircleMarkers&#34;,&#34;args&#34;:[[44.3256388888889,41.8755055555556,44.9031666666667,44.2735861111111,39.1164888888889,40.47425,36.1430555555556,39.5449638888889,39.9478861111111,44.0998555555556,45.1679888888889,45.1122444444444,45.1679833333333,44.0736666666667,42.2995,43.9366666666667,44.9594027777778,44.959975,39.8735555555556,39.1174972222222,47.3865222222222,45.7521666666667,45.8490722222222,45.8591416666667],[-72.167825,-93.8518361111111,-93.285,-72.2118694444444,-86.5915138888889,-79.01725,-86.9559833333333,-82.6802055555556,-84.9164305555556,-92.5018472222222,-84.9178916666667,-84.4094305555556,-84.9178305555556,-121.429,-122.878666666667,-121.411833333333,-116.191002777778,-116.191047222222,-86.3160472222222,-85.7993611111111,-120.571686111111,-121.394263888889,-121.507736111111,-122.779372222222],10,null,null,{&#34;interactive&#34;:true,&#34;className&#34;:&#34;&#34;,&#34;stroke&#34;:false,&#34;color&#34;:&#34;#03F&#34;,&#34;weight&#34;:5,&#34;opacity&#34;:0.5,&#34;fill&#34;:true,&#34;fillColor&#34;:[&#34;#00aa00&#34;,&#34;#00aa00&#34;,&#34;#0000ff&#34;,&#34;#00aa00&#34;,&#34;#0000ff&#34;,&#34;#00aa00&#34;,&#34;#0000ff&#34;,&#34;#0000ff&#34;,&#34;#0000ff&#34;,&#34;#00aa00&#34;,&#34;#00aa00&#34;,&#34;#00aa00&#34;,&#34;#00aa00&#34;,&#34;#0000ff&#34;,&#34;#ff9900&#34;,&#34;#00aa00&#34;,&#34;#00aa00&#34;,&#34;#00aa00&#34;,&#34;#0000ff&#34;,&#34;#0000ff&#34;,&#34;#ff9900&#34;,&#34;#00aa00&#34;,&#34;#0000ff&#34;,&#34;#0000ff&#34;],&#34;fillOpacity&#34;:0.6},null,null,[&#34;&lt;img width=200 height=200 src=&#39;https://www.fairbanksmuseum.org/blog/wp-content/uploads/2016/05/Common-Morels.jpg&#39;/&gt;&#34;,&#34;&lt;img width=200 height=200 src=&#39;https://www.thegreatmorel.com/wp-content/uploads/2018/08/post_image_1921x621_clump.jpg&#39;/&gt;&#34;,&#34;&lt;img width=200 height=200 src=&#39;https://i2.wp.com/foragerchef.com/wp-content/uploads/2013/05/Dreamy-Minnesota-Morels.jpg?fit=1200%2C1115&amp;ssl=1&amp;resize=350%2C200&#39;/&gt;&#34;,&#34;&lt;img width=200 height=200 src=&#39;http://www.fairbanksmuseum.org/blog/wp-content/uploads/2016/05/False-Morels.jpg&#39;/&gt;&#34;,&#34;&lt;img width=200 height=200 src=&#39;http://indianamushrooms.com/images/late_April_109.JPG&#39;/&gt;&#34;,&#34;&lt;img width=200 height=200 src=&#39;https://i0.wp.com/www.thegreatmorel.com/wp-content/uploads/2018/05/PA_Diltown_FB79184-Large.jpeg?resize=331%2C426&amp;ssl=1&#39;/&gt;&#34;,&#34;&lt;img width=200 height=200 src=&#39;https://i1.wp.com/morelmushroomhunting.com/wp-content/uploads/2018/09/DSC05044.jpg?fit=3840%2C2160&amp;ssl=1&#39;/&gt;&#34;,&#34;&lt;img width=200 height=200 src=&#39;https://blog-assets.thedyrt.com/uploads/2018/05/IMG_20180426_160603.jpg&#39;/&gt;&#34;,&#34;&lt;img width=200 height=200 src=&#39;https://farm2.static.flickr.com/1594/26565966582_c5832e2c0e_b.jpg&#39;/&gt;&#34;,&#34;&lt;img width=200 height=200 src=&#39;https://mwoutdoors.com/wp-content/uploads/2017/05/20170511_090240-1-495x400.jpg&#39;/&gt;&#34;,&#34;&lt;img width=200 height=200 src=&#39;https://i0.wp.com/morelmushroomhunting.com/wp-content/uploads/2018/09/2016-MI2-group-pic.jpg?resize=604%2C390&amp;ssl=1&#39;/&gt;&#34;,&#34;&lt;img width=200 height=200 src=&#39;https://i0.wp.com/morelmushroomhunting.com/wp-content/uploads/2018/09/2016-MI3-group-pic.jpg?resize=604%2C449&amp;ssl=1&#39;/&gt;&#34;,&#34;&lt;img width=200 height=200 src=&#39;https://i0.wp.com/morelmushroomhunting.com/wp-content/uploads/2018/09/2016-MI1-group-pic.jpg?resize=604%2C379&amp;ssl=1&#39;/&gt;&#34;,&#34;&lt;img width=200 height=200 src=&#39;http://www.mushroomsinbend.org/wp-content/uploads/2016/04/Kevins16morelsinApril.jpg&#39;/&gt;&#34;,&#34;&lt;img width=200 height=200 src=&#39;http://greatoregonoutdoors.com/wp-content/uploads/2016/03/Photo-Jun-30-5-28-50-PM.jpg&#39;/&gt;&#34;,&#34;&lt;img width=200 height=200 src=&#39;http://www.mushroomsinbend.org/wp-content/uploads/2016/05/Deermorels-Buddy.jpg&#39;/&gt;&#34;,&#34;&lt;img width=200 height=200 src=&#39;https://ronspomeroutdoors.com/wp-content/uploads/2017/05/IMG_6566-827x900.jpg&#39;/&gt;&#34;,&#34;&lt;img width=200 height=200 src=&#39;https://ronspomeroutdoors.com/wp-content/uploads/2017/05/IMG_6615-900x675.jpg&#39;/&gt;&#34;,&#34;&lt;img width=200 height=200 src=&#39;http://peopleplacespies.com/wp-content/uploads/2016/05/IMG_16001.jpg&#39;/&gt;&#34;,&#34;&lt;img width=200 height=200 src=&#39;https://static1.squarespace.com/static/559d5353e4b0a6ed5cd890d9/t/56133cbae4b014e6e912683a/1469711728639/Lissa%27s+Morels&#39;/&gt;&#34;,&#34;&lt;img width=200 height=200 src=&#39;https://upload.wikimedia.org/wikipedia/commons/7/71/Morchella_elata_4846.JPG&#39;/&gt;&#34;,&#34;&lt;img width=200 height=200 src=&#39;http://www.yellowelanor.com/wp-content/uploads/2015/04/IMG_4091.jpg&#39;/&gt;&#34;,&#34;&lt;img width=200 height=200 src=&#39;http://www.yellowelanor.com/wp-content/uploads/2015/04/IMG_7849.jpg&#39;/&gt;&#34;,&#34;&lt;img width=200 height=200 src=&#39;http://www.yellowelanor.com/wp-content/uploads/2015/04/IMG_2266.jpg&#39;/&gt;&#34;],null,[&#34;2016:05:19 18:01:50   source:  https://www.fairbanksmuseum.org/blog/wp-content/uploads/2016/05/Common-Morels.jpg&#34;,&#34;2018:05:13 17:00:32   source:  https://www.thegreatmorel.com/wp-content/uploads/2018/08/post_image_1921x621_clump.jpg&#34;,&#34;2012:04:12 15:07:27   source:  https://i2.wp.com/foragerchef.com/wp-content/uploads/2013/05/Dreamy-Minnesota-Morels.jpg?fit=1200%2C1115&amp;amp;ssl=1&amp;amp;resize=350%2C200&#34;,&#34;2016:05:19 16:58:15   source:  http://www.fairbanksmuseum.org/blog/wp-content/uploads/2016/05/False-Morels.jpg&#34;,&#34;2019:04:22 15:09:10   source:  http://indianamushrooms.com/images/late_April_109.JPG&#34;,&#34;2018:05:11 17:40:42   source:  https://i0.wp.com/www.thegreatmorel.com/wp-content/uploads/2018/05/PA_Diltown_FB79184-Large.jpeg?resize=331%2C426&amp;amp;ssl=1&#34;,&#34;2016:04:16 18:36:01   source:  https://i1.wp.com/morelmushroomhunting.com/wp-content/uploads/2018/09/DSC05044.jpg?fit=3840%2C2160&amp;amp;ssl=1&#34;,&#34;2018:04:26 16:06:03   source:  https://blog-assets.thedyrt.com/uploads/2018/05/IMG_20180426_160603.jpg&#34;,&#34;2016:04:26 10:50:46   source:  https://farm2.static.flickr.com/1594/26565966582_c5832e2c0e_b.jpg&#34;,&#34;2017:05:11 09:02:40   source:  https://mwoutdoors.com/wp-content/uploads/2017/05/20170511_090240-1-495x400.jpg&#34;,&#34;2016:05:22 18:57:29   source:  https://i0.wp.com/morelmushroomhunting.com/wp-content/uploads/2018/09/2016-MI2-group-pic.jpg?resize=604%2C390&amp;amp;ssl=1&#34;,&#34;2016:05:23 13:38:41   source:  https://i0.wp.com/morelmushroomhunting.com/wp-content/uploads/2018/09/2016-MI3-group-pic.jpg?resize=604%2C449&amp;amp;ssl=1&#34;,&#34;2016:05:21 19:04:08   source:  https://i0.wp.com/morelmushroomhunting.com/wp-content/uploads/2018/09/2016-MI1-group-pic.jpg?resize=604%2C379&amp;amp;ssl=1&#34;,&#34;2016:04:17 13:39:01   source:  http://www.mushroomsinbend.org/wp-content/uploads/2016/04/Kevins16morelsinApril.jpg&#34;,&#34;2012:06:30 17:28:50   source:  http://greatoregonoutdoors.com/wp-content/uploads/2016/03/Photo-Jun-30-5-28-50-PM.jpg&#34;,&#34;2016:05:04 11:14:41   source:  http://www.mushroomsinbend.org/wp-content/uploads/2016/05/Deermorels-Buddy.jpg&#34;,&#34;2017:05:20 10:24:18   source:  https://ronspomeroutdoors.com/wp-content/uploads/2017/05/IMG_6566-827x900.jpg&#34;,&#34;2017:05:20 12:05:11   source:  https://ronspomeroutdoors.com/wp-content/uploads/2017/05/IMG_6615-900x675.jpg&#34;,&#34;2016:04:22 17:20:26   source:  http://peopleplacespies.com/wp-content/uploads/2016/05/IMG_16001.jpg&#34;,&#34;2015:04:25 08:11:06   source:  https://static1.squarespace.com/static/559d5353e4b0a6ed5cd890d9/t/56133cbae4b014e6e912683a/1469711728639/Lissa%27s+Morels&#34;,&#34;2012:06:14 22:46:35   source:  https://upload.wikimedia.org/wikipedia/commons/7/71/Morchella_elata_4846.JPG&#34;,&#34;2014:05:17 20:30:47   source:  http://www.yellowelanor.com/wp-content/uploads/2015/04/IMG_4091.jpg&#34;,&#34;2015:04:03 16:43:20   source:  http://www.yellowelanor.com/wp-content/uploads/2015/04/IMG_7849.jpg&#34;,&#34;2014:04:14 17:34:00   source:  http://www.yellowelanor.com/wp-content/uploads/2015/04/IMG_2266.jpg&#34;],{&#34;interactive&#34;:false,&#34;permanent&#34;:false,&#34;direction&#34;:&#34;auto&#34;,&#34;opacity&#34;:1,&#34;offset&#34;:[0,0],&#34;textsize&#34;:&#34;10px&#34;,&#34;textOnly&#34;:false,&#34;className&#34;:&#34;&#34;,&#34;sticky&#34;:true},null]},{&#34;method&#34;:&#34;addLegend&#34;,&#34;args&#34;:[{&#34;colors&#34;:[&#34;#0000ff&#34;,&#34;#00aa00&#34;,&#34;#ff9900&#34;],&#34;labels&#34;:[&#34;April&#34;,&#34;May&#34;,&#34;June&#34;],&#34;na_color&#34;:null,&#34;na_label&#34;:&#34;NA&#34;,&#34;opacity&#34;:1,&#34;position&#34;:&#34;bottomright&#34;,&#34;type&#34;:&#34;unknown&#34;,&#34;title&#34;:&#34;Month&#34;,&#34;extra&#34;:null,&#34;layerId&#34;:null,&#34;className&#34;:&#34;info legend&#34;,&#34;group&#34;:null}]}],&#34;limits&#34;:{&#34;lat&#34;:[36.1430555555556,47.3865222222222],&#34;lng&#34;:[-122.878666666667,-72.167825]}},&#34;evals&#34;:[],&#34;jsHooks&#34;:[]}&lt;/script&gt;
Click on the points to see their associated pictures…&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;closing-notes&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Closing Notes&lt;/h2&gt;
&lt;p&gt;You may have noticed that not all of the pictures are of morels. Indeed,
there are several foray group photos, a picture of a deer, and even a few
pictures of (poisonous) &lt;em&gt;false morel&lt;/em&gt; mushrooms.&lt;/p&gt;
&lt;p&gt;What could be done about that? Well, if you are truly geeky and somewhat
bored - OK very bored - you could train a deep neural network to identify morels,
and then feed the above image results into that. Me, I prefer wasting my time
wandering actual woods looking for interesting mushrooms… Even if there are
no morels to find, wandering in the woods is almost always fun. It’s also worth
pointing out that the false morel and morel habitats are often quite similar, so
those false morel sightings spotted in the map above might actually be
interesting places to forage.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;footnotes&#34;&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id=&#34;fn1&#34;&gt;&lt;p&gt;To be sure, morels are found in many other places across the
US and the world. But I mostly forage in the Midwest and know it best.&lt;a href=&#34;#fnref1&#34; class=&#34;footnote-back&#34;&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id=&#34;fn2&#34;&gt;&lt;p&gt;I tried first to use the &lt;code&gt;phantomjs&lt;/code&gt; driver from R’s &lt;a href=&#34;https://cran.r-project.org/package=wdman&#34;&gt;&lt;code&gt;wdman&lt;/code&gt;&lt;/a&gt; package that doesn’t require
an external web browser. But I could only get that to work for searching
Microsoft Bing image results, not Google image search. Help or
advice on getting &lt;code&gt;phantomjs&lt;/code&gt; to work would be appreciated!&lt;a href=&#34;#fnref2&#34; class=&#34;footnote-back&#34;&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2019/05/13/virtual-morel-foraging-with-r/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>Reproducible Environments</title>
      <link>https://rviews.rstudio.com/2019/04/22/reproducible-environments/</link>
      <pubDate>Mon, 22 Apr 2019 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2019/04/22/reproducible-environments/</guid>
      <description>
        
&lt;script src=&#34;/rmarkdown-libs/htmlwidgets/htmlwidgets.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/plotly-binding/plotly.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/typedarray/typedarray.min.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;/rmarkdown-libs/jquery/jquery.min.js&#34;&gt;&lt;/script&gt;
&lt;link href=&#34;/rmarkdown-libs/crosstalk/css/crosstalk.css&#34; rel=&#34;stylesheet&#34; /&gt;
&lt;script src=&#34;/rmarkdown-libs/crosstalk/js/crosstalk.min.js&#34;&gt;&lt;/script&gt;
&lt;link href=&#34;/rmarkdown-libs/plotly-htmlwidgets-css/plotly-htmlwidgets.css&#34; rel=&#34;stylesheet&#34; /&gt;
&lt;script src=&#34;/rmarkdown-libs/plotly-main/plotly-latest.min.js&#34;&gt;&lt;/script&gt;


&lt;p&gt;Great data science work should be reproducible. The ability to repeat
experiments is part of the foundation for all science, and reproducible work is
also critical for business applications. Team collaboration, project validation,
and sustainable products presuppose the ability to reproduce work over time.&lt;/p&gt;
&lt;p&gt;In my opinion, mastering just a handful of important tools will make
reproducible work in R much easier for data scientists. R users should be
familiar with version control, RStudio projects, and literate programming
through R Markdown. Once these tools are mastered, the major remaining challenge
is creating a reproducible environment.&lt;/p&gt;
&lt;p&gt;An environment consists of all the dependencies required to enable your code to
run correctly. This includes R itself, R packages, and system dependencies. As
with many programming languages, it can be challenging to manage reproducible R
environments. Common issues include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Code that used to run no longer runs, even though the code has not changed.&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Being afraid to upgrade or install a new package, because it might break your code or someone else’s.&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Typing &lt;code&gt;install.packages&lt;/code&gt; in your environment doesn’t do anything, or doesn’t do the &lt;em&gt;right&lt;/em&gt; thing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These challenges can be addressed through a careful combination of tools and
strategies. This post describes two use cases for reproducible environments:&lt;/p&gt;
&lt;ol style=&#34;list-style-type: decimal&#34;&gt;
&lt;li&gt;Safely upgrading packages&lt;/li&gt;
&lt;li&gt;Collaborating on a team&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The sections below each cover a strategy to address the use case, and the necessary
tools to implement each strategy. Additional use cases, strategies, and tools are
presented at &lt;a href=&#34;https://environments.rstudio.com&#34; class=&#34;uri&#34;&gt;https://environments.rstudio.com&lt;/a&gt;. This website is a work in
progress, but we look forward to your feedback.&lt;/p&gt;
&lt;div id=&#34;safely-upgrading-packages&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Safely Upgrading Packages&lt;/h2&gt;
&lt;p&gt;Upgrading packages can be a risky affair. It is not difficult to find serious R
users who have been in a situation where upgrading a package had unintended
consequences. For example, the upgrade may have broken parts of their current code, or upgrading a
package for one project accidentally broke the code in another project. A
strategy for safely upgrading packages consists of three steps:&lt;/p&gt;
&lt;ol style=&#34;list-style-type: decimal&#34;&gt;
&lt;li&gt;Isolate a project&lt;/li&gt;
&lt;li&gt;Record the current dependencies&lt;/li&gt;
&lt;li&gt;Upgrade packages&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The first step in this strategy ensures one project’s packages and upgrades
won’t interfere with any other projects. Isolating projects is accomplished by
creating per-project libraries. A tool that makes this easy is the new &lt;a href=&#34;https://github.com/rstudio/renv&#34;&gt;&lt;code&gt;renv&lt;/code&gt;
package&lt;/a&gt;. Inside of your R project, simply use:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# inside the project directory
renv::init()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second step is to record the current dependencies. This step is critical
because it creates a safety net. If the package upgrade goes poorly, you’ll be
able to revert the changes and return to the record of the working state. Again,
the &lt;code&gt;renv&lt;/code&gt; package makes this process easy.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# record the current dependencies in a file called renv.lock
renv::snapshot()

# commit the lockfile alongside your code in version control
# and use this function to view the history of your lockfile
renv::history()

# if an upgrade goes astray, revert the lockfile
renv::revert(commit = &amp;quot;abc123&amp;quot;)

# and restore the previous environment
renv::restore()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With an isolated project and a safety net in place, you can now proceed to
upgrade or add new packages, while remaining certain the current functional
environment is still reproducible. The &lt;a href=&#34;https://github.com/r-lib/pak&#34;&gt;&lt;code&gt;pak&lt;/code&gt;
package&lt;/a&gt; can be used to install and upgrade
packages in an interactive environment:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# upgrade packages quickly and safely
pak::pkg_install(&amp;quot;ggplot2&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The safety net provided by the &lt;code&gt;renv&lt;/code&gt; package relies on access to older versions
of R packages. For public packages, CRAN provides these older versions in the
&lt;a href=&#34;https://cran.rstudio.com/src/contrib/Archive&#34;&gt;CRAN archive&lt;/a&gt;. Organizations can
use tools like &lt;a href=&#34;https://rstudio.com/products/package-manager&#34;&gt;RStudio Package
Manager&lt;/a&gt; to make multiple versions
of private packages available. The &lt;a href=&#34;https://environments.rstudio.com/snapshot&#34;&gt;“snapshot and
restore”&lt;/a&gt; approach can also be used
to &lt;a href=&#34;https://environments.rstudio.com/deploy&#34;&gt;promote content to production&lt;/a&gt;. In
fact, this approach is exactly how &lt;a href=&#34;https://rstudio.com/products/connect&#34;&gt;RStudio
Connect&lt;/a&gt; and
&lt;a href=&#34;https://shinyapps.io&#34;&gt;shinyapps.io&lt;/a&gt; deploy thousands of R applications to
production each day!&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;team-collaboration&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Team Collaboration&lt;/h2&gt;
&lt;p&gt;A common challenge on teams is sharing and running code. One strategy that
administrators and R users can adopt to facilitate collaboration is
shared baselines. The basics of the strategy are simple:&lt;/p&gt;
&lt;ol style=&#34;list-style-type: decimal&#34;&gt;
&lt;li&gt;Administrators setup a common environment for R users by installing RStudio Server.&lt;/li&gt;
&lt;li&gt;On the server, administrators &lt;a href=&#34;https://support.rstudio.com/hc/en-us/articles/215488098&#34;&gt;install multiple versions of R&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Each version of R is tied to a frozen repository using a Rprofile.site file.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By using a frozen repository, either administrators or users can install
packages while still being sure that everyone will get the same set of packages.
A frozen repository also ensures that adding new packages won’t upgrade other
shared packages as a side-effect. New packages and upgrades are offered to users
over time through the addition of new versions of R.&lt;/p&gt;
&lt;p&gt;Frozen repositories can be created by manually cloning CRAN, accessing a service
like MRAN, or utilizing a supported product like &lt;a href=&#34;https://rstudio.com/products/package-manager&#34;&gt;RStudio Package
Manager&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/post/2019-04-15-repro-envs_files/figure-html/unnamed-chunk-4-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;adaptable-strategies&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Adaptable Strategies&lt;/h2&gt;
&lt;p&gt;The prior sections presented specific strategies for creating reproducible
environments in two common cases. The same strategy may not be appropriate for
every organization, R user, or situation. If you’re a student reporting an
error to your professor, capturing your &lt;code&gt;sessionInfo()&lt;/code&gt; may be all you need. In
contrast, a statistician working on a clinical trial will need a robust
framework for recreating their environment. &lt;strong&gt;Reproducibility is not binary!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/post/2019-04-15-repro-envs_files/figure-html/unnamed-chunk-5-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;To help pick between strategies, we’ve developed a &lt;a href=&#34;https://environments.rstudio.com/reproduce&#34;&gt;strategy
map&lt;/a&gt;. By answering two questions,
you can quickly identify where your team falls on this map and identify the
nearest successful strategy. The two questions are represented on the x and
y-axis of the map:&lt;/p&gt;
&lt;ol style=&#34;list-style-type: decimal&#34;&gt;
&lt;li&gt;Do I have any restrictions on what packages can be used?&lt;/li&gt;
&lt;li&gt;Who is responsible for managing installed packages?&lt;/li&gt;
&lt;/ol&gt;
&lt;div id=&#34;htmlwidget-1&#34; style=&#34;width:672px;height:480px;&#34; class=&#34;plotly html-widget&#34;&gt;&lt;/div&gt;
&lt;script type=&#34;application/json&#34; data-for=&#34;htmlwidget-1&#34;&gt;{&#34;x&#34;:{&#34;data&#34;:[{&#34;x&#34;:[-0.05,1.05],&#34;y&#34;:[0.15,1.25],&#34;text&#34;:&#34;&#34;,&#34;type&#34;:&#34;scatter&#34;,&#34;mode&#34;:&#34;lines&#34;,&#34;line&#34;:{&#34;width&#34;:1.88976377952756,&#34;color&#34;:&#34;rgba(0,0,0,0.2)&#34;,&#34;dash&#34;:&#34;solid&#34;},&#34;hoveron&#34;:&#34;points&#34;,&#34;showlegend&#34;:false,&#34;xaxis&#34;:&#34;x&#34;,&#34;yaxis&#34;:&#34;y&#34;,&#34;hoverinfo&#34;:&#34;skip&#34;,&#34;frame&#34;:null},{&#34;x&#34;:[0,0,0.8,0],&#34;y&#34;:[0.2,1,1,0.2],&#34;text&#34;:&#34;NA&#34;,&#34;type&#34;:&#34;scatter&#34;,&#34;mode&#34;:&#34;lines&#34;,&#34;line&#34;:{&#34;width&#34;:1.88976377952756,&#34;color&#34;:&#34;transparent&#34;,&#34;dash&#34;:&#34;solid&#34;},&#34;fill&#34;:&#34;toself&#34;,&#34;fillcolor&#34;:&#34;rgba(255,0,0,0.1)&#34;,&#34;hoveron&#34;:&#34;fills&#34;,&#34;showlegend&#34;:false,&#34;xaxis&#34;:&#34;x&#34;,&#34;yaxis&#34;:&#34;y&#34;,&#34;hoverinfo&#34;:&#34;skip&#34;,&#34;frame&#34;:null},{&#34;x&#34;:[0,1,0.8,0,0],&#34;y&#34;:[null,0.8,1,0.2,null],&#34;text&#34;:&#34;NA&#34;,&#34;type&#34;:&#34;scatter&#34;,&#34;mode&#34;:&#34;lines&#34;,&#34;line&#34;:{&#34;width&#34;:1.88976377952756,&#34;color&#34;:&#34;transparent&#34;,&#34;dash&#34;:&#34;solid&#34;},&#34;fill&#34;:&#34;toself&#34;,&#34;fillcolor&#34;:&#34;rgba(0,255,0,0.1)&#34;,&#34;hoveron&#34;:&#34;fills&#34;,&#34;showlegend&#34;:false,&#34;xaxis&#34;:&#34;x&#34;,&#34;yaxis&#34;:&#34;y&#34;,&#34;hoverinfo&#34;:&#34;skip&#34;,&#34;frame&#34;:null},{&#34;x&#34;:[0,0,1,0.2,0],&#34;y&#34;:[0,0.2,0.8,0,0],&#34;text&#34;:&#34;&#34;,&#34;type&#34;:&#34;scatter&#34;,&#34;mode&#34;:&#34;lines&#34;,&#34;line&#34;:{&#34;width&#34;:1.88976377952756,&#34;color&#34;:&#34;transparent&#34;,&#34;dash&#34;:&#34;solid&#34;},&#34;fill&#34;:&#34;toself&#34;,&#34;fillcolor&#34;:&#34;rgba(0,255,0,0.1)&#34;,&#34;hoveron&#34;:&#34;fills&#34;,&#34;showlegend&#34;:false,&#34;xaxis&#34;:&#34;x&#34;,&#34;yaxis&#34;:&#34;y&#34;,&#34;hoverinfo&#34;:&#34;skip&#34;,&#34;frame&#34;:null},{&#34;x&#34;:[0.2,1,1,0.2],&#34;y&#34;:[0,0,0.8,0],&#34;text&#34;:&#34;NA&#34;,&#34;type&#34;:&#34;scatter&#34;,&#34;mode&#34;:&#34;lines&#34;,&#34;line&#34;:{&#34;width&#34;:1.88976377952756,&#34;color&#34;:&#34;transparent&#34;,&#34;dash&#34;:&#34;solid&#34;},&#34;fill&#34;:&#34;toself&#34;,&#34;fillcolor&#34;:&#34;rgba(255,0,0,0.1)&#34;,&#34;hoveron&#34;:&#34;fills&#34;,&#34;showlegend&#34;:false,&#34;xaxis&#34;:&#34;x&#34;,&#34;yaxis&#34;:&#34;y&#34;,&#34;hoverinfo&#34;:&#34;skip&#34;,&#34;frame&#34;:null},{&#34;x&#34;:[-0.05,1.05],&#34;y&#34;:[-0.25,0.85],&#34;text&#34;:&#34;&#34;,&#34;type&#34;:&#34;scatter&#34;,&#34;mode&#34;:&#34;lines&#34;,&#34;line&#34;:{&#34;width&#34;:1.88976377952756,&#34;color&#34;:&#34;rgba(0,0,0,0.2)&#34;,&#34;dash&#34;:&#34;solid&#34;},&#34;hoveron&#34;:&#34;points&#34;,&#34;showlegend&#34;:false,&#34;xaxis&#34;:&#34;x&#34;,&#34;yaxis&#34;:&#34;y&#34;,&#34;hoverinfo&#34;:&#34;text&#34;,&#34;frame&#34;:null},{&#34;x&#34;:[0.5,0.75,0.2],&#34;y&#34;:[0.75,0.2,0.8],&#34;text&#34;:[&#34;Open access, &lt;br /&gt; not reproducible, &lt;br /&gt; how we learn&#34;,&#34;Backdoor package access, &lt;br /&gt; offline systems without a strategy&#34;,&#34;Admins involved, &lt;br /&gt; no testing, &lt;br /&gt; slow updates, &lt;br /&gt; high risk of breakage&#34;],&#34;type&#34;:&#34;scatter&#34;,&#34;mode&#34;:&#34;markers&#34;,&#34;marker&#34;:{&#34;autocolorscale&#34;:false,&#34;color&#34;:&#34;rgba(255,0,0,1)&#34;,&#34;opacity&#34;:1,&#34;size&#34;:5.66929133858268,&#34;symbol&#34;:&#34;circle&#34;,&#34;line&#34;:{&#34;width&#34;:1.88976377952756,&#34;color&#34;:&#34;rgba(255,0,0,1)&#34;}},&#34;hoveron&#34;:&#34;points&#34;,&#34;name&#34;:&#34;FALSE&#34;,&#34;legendgroup&#34;:&#34;FALSE&#34;,&#34;showlegend&#34;:true,&#34;xaxis&#34;:&#34;x&#34;,&#34;yaxis&#34;:&#34;y&#34;,&#34;hoverinfo&#34;:&#34;text&#34;,&#34;frame&#34;:null},{&#34;x&#34;:[0.1,0.5,0.8],&#34;y&#34;:[0.1,0.5,0.8],&#34;text&#34;:[&#34;Admins test and approve &lt;br /&gt; a subset of CRAN&#34;,&#34;All or most of CRAN, &lt;br /&gt; updated with R versions, &lt;br /&gt; tied to a system library&#34;,&#34;Open access, user or system &lt;br /&gt; records per-project dependencies&#34;],&#34;type&#34;:&#34;scatter&#34;,&#34;mode&#34;:&#34;markers&#34;,&#34;marker&#34;:{&#34;autocolorscale&#34;:false,&#34;color&#34;:&#34;rgba(163,197,134,1)&#34;,&#34;opacity&#34;:1,&#34;size&#34;:5.66929133858268,&#34;symbol&#34;:&#34;circle&#34;,&#34;line&#34;:{&#34;width&#34;:1.88976377952756,&#34;color&#34;:&#34;rgba(163,197,134,1)&#34;}},&#34;hoveron&#34;:&#34;points&#34;,&#34;name&#34;:&#34; TRUE&#34;,&#34;legendgroup&#34;:&#34; TRUE&#34;,&#34;showlegend&#34;:true,&#34;xaxis&#34;:&#34;x&#34;,&#34;yaxis&#34;:&#34;y&#34;,&#34;hoverinfo&#34;:&#34;text&#34;,&#34;frame&#34;:null},{&#34;x&#34;:[0.125,0.525,0.525,0.825,0.775,0.225],&#34;y&#34;:[0.125,0.525,0.775,0.825,0.225,0.825],&#34;text&#34;:[&#34;Validated&#34;,&#34;Shared Baseline&#34;,&#34;Wild West&#34;,&#34;Snapshot&#34;,&#34;Blocked&#34;,&#34;Ticket System&#34;],&#34;hovertext&#34;:[&#34;&#34;,&#34;&#34;,&#34;&#34;,&#34;&#34;,&#34;&#34;,&#34;&#34;],&#34;textfont&#34;:{&#34;size&#34;:14.6645669291339,&#34;color&#34;:&#34;rgba(0,0,0,1)&#34;},&#34;type&#34;:&#34;scatter&#34;,&#34;mode&#34;:&#34;text&#34;,&#34;hoveron&#34;:&#34;points&#34;,&#34;showlegend&#34;:false,&#34;xaxis&#34;:&#34;x&#34;,&#34;yaxis&#34;:&#34;y&#34;,&#34;hoverinfo&#34;:&#34;text&#34;,&#34;frame&#34;:null}],&#34;layout&#34;:{&#34;margin&#34;:{&#34;t&#34;:43.7625570776256,&#34;r&#34;:7.30593607305936,&#34;b&#34;:40.1826484018265,&#34;l&#34;:89.8630136986302},&#34;font&#34;:{&#34;color&#34;:&#34;rgba(0,0,0,1)&#34;,&#34;family&#34;:&#34;&#34;,&#34;size&#34;:14.6118721461187},&#34;title&#34;:&#34;Reproducing Environments: Strategies and Danger Zones&#34;,&#34;titlefont&#34;:{&#34;color&#34;:&#34;rgba(0,0,0,1)&#34;,&#34;family&#34;:&#34;&#34;,&#34;size&#34;:17.5342465753425},&#34;xaxis&#34;:{&#34;domain&#34;:[0,1],&#34;automargin&#34;:true,&#34;type&#34;:&#34;linear&#34;,&#34;autorange&#34;:false,&#34;range&#34;:[-0.05,1.05],&#34;tickmode&#34;:&#34;array&#34;,&#34;ticktext&#34;:[&#34;Admins&#34;,&#34;&#34;,&#34;&#34;,&#34;&#34;,&#34;Users&#34;],&#34;tickvals&#34;:[0,0.25,0.5,0.75,1],&#34;categoryorder&#34;:&#34;array&#34;,&#34;categoryarray&#34;:[&#34;Admins&#34;,&#34;&#34;,&#34;&#34;,&#34;&#34;,&#34;Users&#34;],&#34;nticks&#34;:null,&#34;ticks&#34;:&#34;&#34;,&#34;tickcolor&#34;:null,&#34;ticklen&#34;:3.65296803652968,&#34;tickwidth&#34;:0,&#34;showticklabels&#34;:true,&#34;tickfont&#34;:{&#34;color&#34;:&#34;rgba(77,77,77,1)&#34;,&#34;family&#34;:&#34;&#34;,&#34;size&#34;:11.689497716895},&#34;tickangle&#34;:-0,&#34;showline&#34;:false,&#34;linecolor&#34;:null,&#34;linewidth&#34;:0,&#34;showgrid&#34;:true,&#34;gridcolor&#34;:&#34;rgba(235,235,235,1)&#34;,&#34;gridwidth&#34;:0.66417600664176,&#34;zeroline&#34;:false,&#34;anchor&#34;:&#34;y&#34;,&#34;title&#34;:&#34;Who is Responsible for Reproducing the Environment?&#34;,&#34;titlefont&#34;:{&#34;color&#34;:&#34;rgba(0,0,0,1)&#34;,&#34;family&#34;:&#34;&#34;,&#34;size&#34;:14.6118721461187},&#34;hoverformat&#34;:&#34;.2f&#34;},&#34;yaxis&#34;:{&#34;domain&#34;:[0,1],&#34;automargin&#34;:true,&#34;type&#34;:&#34;linear&#34;,&#34;autorange&#34;:false,&#34;range&#34;:[-0.05,1.05],&#34;tickmode&#34;:&#34;array&#34;,&#34;ticktext&#34;:[&#34;Locked Down&#34;,&#34;&#34;,&#34;&#34;,&#34;&#34;,&#34;Open&#34;],&#34;tickvals&#34;:[0,0.25,0.5,0.75,1],&#34;categoryorder&#34;:&#34;array&#34;,&#34;categoryarray&#34;:[&#34;Locked Down&#34;,&#34;&#34;,&#34;&#34;,&#34;&#34;,&#34;Open&#34;],&#34;nticks&#34;:null,&#34;ticks&#34;:&#34;&#34;,&#34;tickcolor&#34;:null,&#34;ticklen&#34;:3.65296803652968,&#34;tickwidth&#34;:0,&#34;showticklabels&#34;:true,&#34;tickfont&#34;:{&#34;color&#34;:&#34;rgba(77,77,77,1)&#34;,&#34;family&#34;:&#34;&#34;,&#34;size&#34;:11.689497716895},&#34;tickangle&#34;:-0,&#34;showline&#34;:false,&#34;linecolor&#34;:null,&#34;linewidth&#34;:0,&#34;showgrid&#34;:true,&#34;gridcolor&#34;:&#34;rgba(235,235,235,1)&#34;,&#34;gridwidth&#34;:0.66417600664176,&#34;zeroline&#34;:false,&#34;anchor&#34;:&#34;x&#34;,&#34;title&#34;:&#34;Package Access&#34;,&#34;titlefont&#34;:{&#34;color&#34;:&#34;rgba(0,0,0,1)&#34;,&#34;family&#34;:&#34;&#34;,&#34;size&#34;:14.6118721461187},&#34;hoverformat&#34;:&#34;.2f&#34;},&#34;shapes&#34;:[{&#34;type&#34;:&#34;rect&#34;,&#34;fillcolor&#34;:null,&#34;line&#34;:{&#34;color&#34;:null,&#34;width&#34;:0,&#34;linetype&#34;:[]},&#34;yref&#34;:&#34;paper&#34;,&#34;xref&#34;:&#34;paper&#34;,&#34;x0&#34;:0,&#34;x1&#34;:1,&#34;y0&#34;:0,&#34;y1&#34;:1}],&#34;showlegend&#34;:false,&#34;legend&#34;:{&#34;bgcolor&#34;:null,&#34;bordercolor&#34;:null,&#34;borderwidth&#34;:0,&#34;font&#34;:{&#34;color&#34;:&#34;rgba(0,0,0,1)&#34;,&#34;family&#34;:&#34;&#34;,&#34;size&#34;:11.689497716895},&#34;y&#34;:1},&#34;hovermode&#34;:&#34;closest&#34;,&#34;barmode&#34;:&#34;relative&#34;},&#34;config&#34;:{&#34;doubleClick&#34;:&#34;reset&#34;,&#34;modeBarButtonsToAdd&#34;:[{&#34;name&#34;:&#34;Collaborate&#34;,&#34;icon&#34;:{&#34;width&#34;:1000,&#34;ascent&#34;:500,&#34;descent&#34;:-50,&#34;path&#34;:&#34;M487 375c7-10 9-23 5-36l-79-259c-3-12-11-23-22-31-11-8-22-12-35-12l-263 0c-15 0-29 5-43 15-13 10-23 23-28 37-5 13-5 25-1 37 0 0 0 3 1 7 1 5 1 8 1 11 0 2 0 4-1 6 0 3-1 5-1 6 1 2 2 4 3 6 1 2 2 4 4 6 2 3 4 5 5 7 5 7 9 16 13 26 4 10 7 19 9 26 0 2 0 5 0 9-1 4-1 6 0 8 0 2 2 5 4 8 3 3 5 5 5 7 4 6 8 15 12 26 4 11 7 19 7 26 1 1 0 4 0 9-1 4-1 7 0 8 1 2 3 5 6 8 4 4 6 6 6 7 4 5 8 13 13 24 4 11 7 20 7 28 1 1 0 4 0 7-1 3-1 6-1 7 0 2 1 4 3 6 1 1 3 4 5 6 2 3 3 5 5 6 1 2 3 5 4 9 2 3 3 7 5 10 1 3 2 6 4 10 2 4 4 7 6 9 2 3 4 5 7 7 3 2 7 3 11 3 3 0 8 0 13-1l0-1c7 2 12 2 14 2l218 0c14 0 25-5 32-16 8-10 10-23 6-37l-79-259c-7-22-13-37-20-43-7-7-19-10-37-10l-248 0c-5 0-9-2-11-5-2-3-2-7 0-12 4-13 18-20 41-20l264 0c5 0 10 2 16 5 5 3 8 6 10 11l85 282c2 5 2 10 2 17 7-3 13-7 17-13z m-304 0c-1-3-1-5 0-7 1-1 3-2 6-2l174 0c2 0 4 1 7 2 2 2 4 4 5 7l6 18c0 3 0 5-1 7-1 1-3 2-6 2l-173 0c-3 0-5-1-8-2-2-2-4-4-4-7z m-24-73c-1-3-1-5 0-7 2-2 3-2 6-2l174 0c2 0 5 0 7 2 3 2 4 4 5 7l6 18c1 2 0 5-1 6-1 2-3 3-5 3l-174 0c-3 0-5-1-7-3-3-1-4-4-5-6z&#34;},&#34;click&#34;:&#34;function(gd) { \n        // is this being viewed in RStudio?\n        if (location.search == &#39;?viewer_pane=1&#39;) {\n          alert(&#39;To learn about plotly for collaboration, visit:\\n https://cpsievert.github.io/plotly_book/plot-ly-for-collaboration.html&#39;);\n        } else {\n          window.open(&#39;https://cpsievert.github.io/plotly_book/plot-ly-for-collaboration.html&#39;, &#39;_blank&#39;);\n        }\n      }&#34;}],&#34;cloud&#34;:false,&#34;displayModeBar&#34;:false},&#34;source&#34;:&#34;A&#34;,&#34;attrs&#34;:{&#34;f87a7b9b28cc&#34;:{&#34;intercept&#34;:{},&#34;slope&#34;:{},&#34;type&#34;:&#34;scatter&#34;},&#34;f87a793a87a&#34;:{&#34;x&#34;:{},&#34;y&#34;:{},&#34;text&#34;:{},&#34;x.1&#34;:{},&#34;y.1&#34;:{}},&#34;f87a6f19e578&#34;:{&#34;x&#34;:{},&#34;y&#34;:{},&#34;text&#34;:{},&#34;x.1&#34;:{},&#34;y.1&#34;:{}},&#34;f87ad286244&#34;:{&#34;x&#34;:{},&#34;y&#34;:{},&#34;text&#34;:{},&#34;x.1&#34;:{},&#34;y.1&#34;:{}},&#34;f87a564b651b&#34;:{&#34;x&#34;:{},&#34;y&#34;:{},&#34;text&#34;:{},&#34;x.1&#34;:{},&#34;y.1&#34;:{}},&#34;f87a6fdafbdf&#34;:{&#34;intercept&#34;:{},&#34;slope&#34;:{}},&#34;f87a11ce26d8&#34;:{&#34;x&#34;:{},&#34;y&#34;:{},&#34;colour&#34;:{},&#34;text&#34;:{},&#34;x.1&#34;:{},&#34;y.1&#34;:{}},&#34;f87a75583809&#34;:{&#34;x&#34;:{},&#34;y&#34;:{},&#34;label&#34;:{},&#34;x.1&#34;:{},&#34;y.1&#34;:{}}},&#34;cur_data&#34;:&#34;f87a7b9b28cc&#34;,&#34;visdat&#34;:{&#34;f87a7b9b28cc&#34;:[&#34;function (y) &#34;,&#34;x&#34;],&#34;f87a793a87a&#34;:[&#34;function (y) &#34;,&#34;x&#34;],&#34;f87a6f19e578&#34;:[&#34;function (y) &#34;,&#34;x&#34;],&#34;f87ad286244&#34;:[&#34;function (y) &#34;,&#34;x&#34;],&#34;f87a564b651b&#34;:[&#34;function (y) &#34;,&#34;x&#34;],&#34;f87a6fdafbdf&#34;:[&#34;function (y) &#34;,&#34;x&#34;],&#34;f87a11ce26d8&#34;:[&#34;function (y) &#34;,&#34;x&#34;],&#34;f87a75583809&#34;:[&#34;function (y) &#34;,&#34;x&#34;]},&#34;highlight&#34;:{&#34;on&#34;:&#34;plotly_click&#34;,&#34;persistent&#34;:false,&#34;dynamic&#34;:false,&#34;selectize&#34;:false,&#34;opacityDim&#34;:0.2,&#34;selected&#34;:{&#34;opacity&#34;:1},&#34;debounce&#34;:0},&#34;base_url&#34;:&#34;https://plot.ly&#34;,&#34;.hideLegend&#34;:true},&#34;evals&#34;:[&#34;config.modeBarButtonsToAdd.0.click&#34;],&#34;jsHooks&#34;:[]}&lt;/script&gt;
&lt;p&gt;For more information on picking and using these strategies, please visit
&lt;a href=&#34;https://environments.rstudio.com&#34; class=&#34;uri&#34;&gt;https://environments.rstudio.com&lt;/a&gt;. By adopting a strategy for reproducible
environments, R users, administrators, and teams can solve a number of important
challenges. Ultimately, reproducible work adds credibility, creating a solid
foundation for research, business applications, and production systems. We are
excited to be working on tools to make reproducible work in R easy and fun. We
look forward to your feedback, community discussions, and future posts.&lt;/p&gt;
&lt;/div&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2019/04/22/reproducible-environments/&#39;;&lt;/script&gt;
      </description>
    </item>
    
  </channel>
</rss>
