--- title: "The sign test" editor: markdown: wrap: 72 --- ## Packages ```{r} library(tidyverse) library(smmr) ``` `smmr` is new. See later how to install it. ## Duality between confidence intervals and hypothesis tests - Tests and CIs really do the same thing, if you look at them the right way. They are both telling you something about a parameter, and they use same things about data. - To illustrate, some data (two groups): ```{r inference-3-R-1} my_url <- "http://ritsokiguess.site/datafiles/duality.txt" twogroups <- read_delim(my_url," ") ``` ## The data ```{r inference-3-R-2} twogroups ``` ## 95% CI (default) for difference in means, group 1 minus group 2: ```{r inference-3-R-3} t.test(y ~ group, data = twogroups) ``` ## 90% CI ```{r inference-3-R-4} t.test(y ~ group, data = twogroups, conf.level = 0.90) ``` ## Hypothesis test Null is that difference in means is zero: ```{r inference-3-R-5} t.test(y ~ group, mu=0, data = twogroups) ``` ## Comparing results Recall null here is $H_0 : \mu_1 - \mu_2 = 0$. P-value 0.0668. - 95% CI from $-5.6$ to $0.2$, contains $0$. - 90% CI from $-5.0$ to $-0.3$, does not contain $0$. - At $\alpha = 0.05$, would not reject $H_0$ since P-value $> 0.05$. - At $\alpha = 0.10$, *would* reject $H_0$ since P-value $< 0.10$. ## Test and CI Not just coincidence. Let $C = 100(1 - \alpha)$, so C% gives corresponding CI to level-$\alpha$ test. Then following always true. (Symbol $\iff$ means "if and only if".) | Test decision | | Confidence interval | |:--------------------------|:---------------:|:--------------------------| | Reject $H_0$ at level $\alpha$ | $\iff$ | $C\%$ CI does not contain $H_0$ value | | Do not reject $H_0$ at level $\alpha$ | $\iff$ | $C\%$ CI contains $H_0$ value | Idea: "Plausible" parameter value inside CI, not rejected; "Implausible" parameter value outside CI, rejected. ## The value of this - If you have a test procedure but no corresponding CI: - you make a CI by including all the parameter values that would not be rejected by your test. - Use: - $\alpha = 0.01$ for a 99% CI, - $\alpha = 0.05$ for a 95% CI, - $\alpha = 0.10$ for a 90% CI, and so on. ## Testing for non-normal data - The IRS ("Internal Revenue Service") is the US authority that deals with taxes (like Revenue Canada). - One of their forms is supposed to take no more than 160 minutes to complete. A citizen's organization claims that it takes people longer than that on average. - Sample of 30 people; time to complete form recorded. - Read in data, and do $t$-test of $H_0 : \mu = 160$ vs. $H_a : \mu > 160$. - For reading in, there is only one column, so can pretend it is delimited by anything. ## Read in data ```{r inference-3-R-6, message=FALSE} my_url <- "http://ritsokiguess.site/datafiles/irs.txt" irs <- read_csv(my_url) irs ``` ## Test whether mean is 160 or greater ```{r inference-3-R-7} with(irs, t.test(Time, mu = 160, alternative = "greater")) ``` Reject null; mean (for all people to complete form) greater than 160. ## But, look at a graph ```{r inference-3-R-8, fig.height=3.5} ggplot(irs, aes(x = Time)) + geom_histogram(bins = 6) ``` ## Comments - Skewed to right. - Should look at *median*, not mean. ## The sign test - But how to test whether the median is greater than 160? - Idea: if the median really is 160 ($H_0$ true), the sampled values from the population are equally likely to be above or below 160. - If the population median is greater than 160, there will be a lot of sample values greater than 160, not so many less. Idea: test statistic is number of sample values greater than hypothesized median. ## Getting a P-value for sign test 1/3 - How to decide whether "unusually many" sample values are greater than 160? Need a sampling distribution. - If $H_0$ true, pop. median is 160, then each sample value independently equally likely to be above or below 160. - So number of observed values above 160 has binomial distribution with $n = 30$ (number of data values) and $p = 0.5$ (160 is hypothesized to be *median*). ## Getting P-value for sign test 2/3 - Count values above/below 160: ```{r inference-3-R-9} irs %>% count(Time > 160) ``` - 17 above, 13 below. How unusual is that? Need a *binomial table*. ## Getting P-value for sign test 3/3 - R function `dbinom` gives the probability of eg. exactly 17 successes in a binomial with $n = 30$ and $p = 0.5$: ```{r inference-3-R-10} dbinom(17, 30, 0.5) ``` - but we want probability of 17 *or more*, so get all of those, find probability of each, and add them up: ```{r inference-3-R-11} tibble(x=17:30) %>% mutate(prob=dbinom(x, 30, 0.5)) %>% summarize(total=sum(prob)) ``` or ```{r} pbinom(17, 30, 0.5) # prob of <= 17 ``` and hence (note first input): ```{r} pbinom(16, 30, 0.5, lower.tail = FALSE) ``` This last is $P(X \ge 17) = P(X > 16)$. ## Using my package `smmr` - I wrote a package `smmr` to do the sign test (and some other things). Installation is a bit fiddly: - Install devtools (once) with ```{r} #| eval = FALSE install.packages("devtools") ``` - then install `smmr` using `devtools` (once): ```{r inference-3-R-12} #| eval = FALSE library(devtools) install_github("nxskok/smmr") ``` - Then load it: ```{r inference-3-R-13, eval=FALSE} library(smmr) ``` ## `smmr` for sign test - `smmr`'s function `sign_test` needs three inputs: a data frame, a column and a null median: ```{r inference-3-R-14} sign_test(irs, Time, 160) ``` ## Comments (1/3) - Testing whether population median *greater than* 160, so want *upper-tail* P-value 0.2923. Same as before. - Also get table of values above and below; this too as we got. ## Comments (2/3) - P-values are: | Test | P-value | |:-----|--------:| | $t$ | 0.0392 | | Sign | 0.2923 | - These are very different: we reject a mean of 160 (in favour of the mean being bigger), but clearly *fail* to reject a median of 160 in favour of a bigger one. - Why is that? Obtain mean and median: ```{r inference-3-R-15} irs %>% summarize(mean_time = mean(Time), median_time = median(Time)) ``` ## Comments (3/3) {.smaller} - The mean is pulled a long way up by the right skew, and is a fair bit bigger than 160. - The median is quite close to 160. - We ought to be trusting the sign test and not the t-test here (median and not mean), and therefore there is no evidence that the "typical" time to complete the form is longer than 160 minutes. - Having said that, there are clearly some people who take a lot longer than 160 minutes to complete the form, and the IRS could focus on simplifying its form for these people. - In this example, looking at any kind of average is not really helpful; a better question might be "do an unacceptably large fraction of people take longer than (say) 300 minutes to complete the form?": that is, thinking about worst-case rather than average-case. ## Confidence interval for the median - The sign test does not naturally come with a confidence interval for the median. - So we use the "duality" between test and confidence interval to say: the (95%) confidence interval for the median contains exactly those values of the null median that would not be rejected by the two-sided sign test (at $\alpha = 0.05$). ## For our data - The procedure is to try some values for the null median and see which ones are inside and which outside our CI. - smmr has pval_sign that gets just the 2-sided P-value: ```{r inference-3-R-16} pval_sign(160, irs, Time) ``` - Try a couple of null medians: ```{r inference-3-R-17} pval_sign(200, irs, Time) pval_sign(300, irs, Time) ``` - So 200 inside the 95% CI and 300 outside. ## Doing a whole bunch - Choose our null medians first: ```{r inference-3-R-18} (d <- tibble(null_median=seq(100,300,20))) ``` ## ... and then "for each null median, run the function `pval_sign` for that null median and get the P-value": ```{r inference-3-R-19} d %>% rowwise() %>% mutate(p_value = pval_sign(null_median, irs, Time)) ``` ## Make it easier for ourselves ```{r inference-3-R-20} d %>% rowwise() %>% mutate(p_value = pval_sign(null_median, irs, Time)) %>% mutate(in_out = ifelse(p_value > 0.05, "inside", "outside")) ``` ## confidence interval for median? - 95% CI to this accuracy from 120 to 200. - Can get it more accurately by looking more closely in intervals from 100 to 120, and from 200 to 220. ## A more efficient way: bisection - Know that top end of CI between 200 and 220: ```{r inference-3-R-21} lo <- 200 hi <- 220 ``` - Try the value halfway between: is it inside or outside? ```{r inference-3-R-22} try <- (lo + hi) / 2 try pval_sign(try,irs,Time) ``` - Inside, so upper end is between 210 and 220. Repeat (over): ## ... bisection continued ```{r inference-3-R-23} lo <- try try <- (lo + hi) / 2 try pval_sign(try, irs, Time) ``` - 215 is inside too, so upper end between 215 and 220. - Continue until have as accurate a result as you want. ## Bisection automatically - A loop, but not a `for` since we don't know how many times we're going around. Keep going `while` a condition is true: ```{r inference-3-R-24, eval=F} lo = 200 hi = 220 while (hi - lo > 1) { try = (hi + lo) / 2 ptry = pval_sign(try, irs, Time) print(c(try, ptry)) if (ptry <= 0.05) hi = try else lo = try } ``` ## The output from this loop ```{r inference-3-R-25, echo=F} lo = 200 hi = 220 while (hi - lo > 1) { try = (hi + lo) / 2 ptry = pval_sign(try, irs, Time) print(c(try, ptry)) if (ptry <= 0.05) hi = try else lo = try } ``` - 215 inside, 215.625 outside. Upper end of interval to this accuracy is 215. ## Using smmr - `smmr` has function `ci_median` that does this (by default 95% CI): ```{r inference-3-R-26} ci_median(irs, Time) ``` - Uses a more accurate bisection than we did. - Or get, say, 90% CI for median: ```{r inference-3-R-27} ci_median(irs, Time, conf.level=0.90) ``` - 90% CI is shorter, as it should be. ## Bootstrap - but, was the sample size (30) big enough to overcome the skewness? - Bootstrap, again: ```{r inference-3-R-28, echo=FALSE} set.seed(457299) ``` ```{r inference-3-R-29} tibble(sim = 1:1000) %>% rowwise() %>% mutate(my_sample = list(sample(irs$Time, replace = TRUE))) %>% mutate(my_mean = mean(my_sample)) %>% ggplot(aes(x=my_mean)) + geom_histogram(bins=10) -> g ``` ## With normal quantile plot (see after that section) ```{r inference-3-R-29xxx} tibble(sim = 1:10000) %>% rowwise() %>% mutate(my_sample = list(sample(irs$Time, replace = TRUE))) %>% mutate(my_mean = mean(my_sample)) %>% ggplot(aes(sample = my_mean)) + stat_qq() + stat_qq_line() ``` ## The sampling distribution ```{r inference-3-R-30} g ``` ## Comments - A little skewed to right, but not nearly as much as I was expecting. - The $t$-test for the mean might actually be OK for these data, *if the mean is what you want*. - In actual data, mean and median very different; we chose to make inference about the median. - Thus for us it was right to use the sign test.