What type of test would you use for each of the following scenarios?
They say that there is a 50/50 chance of getting heads (or tails) when you flip a coin. You want to put it to the test by flipping a coin 100 times to see if it matches those chances. What test would you use?
The meditation app, Headspace, has released a new version of their app and they want to see if there has been an increase in user satisfaction. They survey the same users before and after the update asking them if they’re satisfied with the app (yes or no). How would you test this difference?
A small veterinary clinic wants to test the association between dog breed (pug vs french bulldog) and breathing problems (yes or no). The problem is, they have a small sample size. Which test can they use?
Load packages
p_needed <- c("tidyr",
"tidyverse",
"dplyr",
"haven",
"ggplot2",
"readxl",
"exact2x2",
"gmodels",
"WebPower")
packages <- rownames(installed.packages())
p_to_install <- p_needed[!(p_needed %in% packages)]
if(length(p_to_install) > 0){
install.packages(p_to_install)
}
lapply(p_needed, library, character.only = TRUE)# read excel files with the 'readxl' package
df <- read_xlsx("/Users/kareenadelrosario/Desktop/local r code/regression_code/data6.xlsx")
# what variables do we have
colnames(df)## [1] "...1" "PRE_male"
## [3] "PRE_female" "POST_male"
## [5] "POST_female" "POST_male_salary"
## [7] "POST_female_salary" "POST_male_friendly"
## [9] "POST_female_friendly" "POST_male_intelligent"
## [11] "POST_female_intelligent" "Hire_m"
## [13] "Hire_f" "Choice_m"
## [15] "Choice_f" "itemnum"
## [17] "partnum" "Gender"
## [19] "Ide" "Political"
## [21] "Edu"
Calculate the mean and standard deviation of the PRE_female variable, which indicates percent of women chosen
We want to plot the mean and sd of PRE_female. ggplot’s bar plots does not like it if you don’t specify both an x and y-axis. For that reason, we have to get creative
bar_plot <- df %>%
dplyr::select(PRE_female) %>% # we only need this variable
pivot_longer(PRE_female) %>% # pivot_longer separates out the variable name "PRE_female" and the value, giving us two new cols: name, value
ggplot(aes(x = name, y = value, fill = name)) + # have to add "fill" in aes() if you want to change the color manually later
stat_summary(fun=mean, geom="bar", width = .3) + # plot mean as barplot
stat_summary(fun.data = mean_se, geom = "errorbar", width = .1) # mean_se is an argument that calculates SD (can calculate SE with additional arguments)
print(bar_plot)Let’s make prettier to match python
If you’re interested in more color options, check out RColorBrewer: https://r-graph-gallery.com/38-rcolorbrewers-palettes.html
# library(RColorBrewer) # more color options
bar_plot +
theme_classic() +
scale_fill_manual(values = "deepskyblue4") +
theme(axis.title.x=element_blank(), # get rid of x-axis title
axis.title.y=element_blank()) # get rid of y-axis titledensity_plot <- df %>%
ggplot (aes(PRE_female)) +
geom_density() +
scale_x_continuous(limits=c(-0.5,1.5)) + # specify range of x-axis so it doesn't get cut off
geom_vline(aes(xintercept=mean(PRE_female)), # add a line that represents the mean
color="deepskyblue4", linetype="dashed", size=1) +
theme_classic()
print(density_plot)We can layer the density plot on top of a histogram, if that’s more informative
density_plot + geom_histogram(aes(y=..density..), fill = "blue", alpha = 0.4, bins = 9) # bins is the width of the histogram bars## [1] 0 1
## [1] "numeric"
## Rows: 260
## Columns: 21
## $ ...1 <dbl> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, …
## $ PRE_male <dbl> 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1…
## $ PRE_female <dbl> 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0…
## $ POST_male <dbl> 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1…
## $ POST_female <dbl> 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1…
## $ POST_male_salary <dbl> 57, 63, 16, 27, 53, 49, 70, 67, 34, 47, 45, 37…
## $ POST_female_salary <dbl> 88, 44, 21, 18, 44, 72, 50, 66, 50, 85, 64, 68…
## $ POST_male_friendly <dbl> 95, 66, 34, 26, 76, 98, 89, 96, 87, 88, 65, 28…
## $ POST_female_friendly <dbl> 73, 79, 32, 30, 90, 99, 90, 93, 88, 94, 78, 64…
## $ POST_male_intelligent <dbl> 90, 74, 17, 34, 73, 90, 89, 92, 88, 88, 37, 45…
## $ POST_female_intelligent <dbl> 92, 96, 34, 36, 85, 92, 91, 91, 87, 88, 39, 63…
## $ Hire_m <dbl> 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1…
## $ Hire_f <dbl> 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1…
## $ Choice_m <dbl> 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0…
## $ Choice_f <dbl> 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1…
## $ itemnum <dbl> 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1…
## $ partnum <dbl> 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7…
## $ Gender <chr> "Female", "Female", "Female", "Female", "Femal…
## $ Ide <dbl> 100, 100, 42, 42, 51, 51, 92, 92, 2, 2, 13, 13…
## $ Political <chr> "Democrat", "Democrat", "Other", "Other", "Ind…
## $ Edu <chr> "College Degree", "College Degree", "High Scho…
hint: we can use dplyr’s group_by and count functions
##
## Chi-squared test for given probabilities
##
## data: table(df$PRE_female)
## X-squared = 12.938, df = 1, p-value = 0.0003219
\[w = \sqrt\frac{\chi^2} {N} \]
# Calculate the observed frequencies of 0,1
observed_freq <- table(df$PRE_female)
# Perform the Chi-square test
chi_square_result <- chisq.test(observed_freq)
# Total sample size
N <- sum(observed_freq)
# Calculate effect size using the equation above
effect_size <- sqrt(chi_square_result$statistic / N)
# Print the effect size
print(effect_size)## X-squared
## 0.2230769
bar_plot_twogender <- df %>%
dplyr::select(PRE_female, PRE_male) %>% # we only need these variable
pivot_longer(cols = c(PRE_female, PRE_male), names_to = "category", values_to = "value") %>% # nesting male and female under one category
ggplot(aes(x = category, y = value, fill = category)) + # have to add "fill" in aes() if you want to change the color manually later
stat_summary(fun=mean, geom="bar", position = position_dodge(), width = .3) + # plot mean as barplot
stat_summary(fun.data = mean_se, geom = "errorbar", position = position_dodge(0.9), width = .1) + # mean_se is an argument that calculates SD (can calculate SE with additional arguments)
scale_fill_manual(values = c("deepskyblue4", "deepskyblue2")) + # how to add two colors. they're in alphabetical order of the variables (e.g., PRE_female comes before PRE_male)
labs(x = "Gender", y = "Mean") + # add title to x and y-axis (labs = "labels")
theme_minimal() + # preset theme
ggtitle("Means of Female and Males in this Dataframe")
bar_plot_twogender##
## Pearson's Chi-squared test with Yates' continuity correction
##
## data: df$PRE_female and df$PRE_male
## X-squared = 0.13286, df = 1, p-value = 0.7155
# Option 2: The kitchen sink
CrossTable(df$PRE_female, df$PRE_male,
fisher = TRUE,
chisq = TRUE,
expected = TRUE,
prop.c = FALSE, # This stops the column proportions from being displayed
prop.t = FALSE, # This stops the total proportions from being displayed
prop.chisq = FALSE, # This stops the chisquared proportions from being displayed
sresid = TRUE, # Produces standardized residuals
format = "SPSS") # To see residuals, must use SPSS format##
## Cell Contents
## |-------------------------|
## | Count |
## | Expected Values |
## | Row Percent |
## | Std Residual |
## |-------------------------|
##
## Total Observations in Table: 260
##
## | df$PRE_male
## df$PRE_female | 0 | 1 | Row Total |
## --------------|-----------|-----------|-----------|
## 0 | 104 | 55 | 159 |
## | 102.127 | 56.873 | |
## | 65.409% | 34.591% | 61.154% |
## | 0.185 | -0.248 | |
## --------------|-----------|-----------|-----------|
## 1 | 63 | 38 | 101 |
## | 64.873 | 36.127 | |
## | 62.376% | 37.624% | 38.846% |
## | -0.233 | 0.312 | |
## --------------|-----------|-----------|-----------|
## Column Total | 167 | 93 | 260 |
## --------------|-----------|-----------|-----------|
##
##
## Statistics for All Table Factors
##
##
## Pearson's Chi-squared test
## ------------------------------------------------------------
## Chi^2 = 0.2472369 d.f. = 1 p = 0.6190274
##
## Pearson's Chi-squared test with Yates' continuity correction
## ------------------------------------------------------------
## Chi^2 = 0.1328593 d.f. = 1 p = 0.7154857
##
##
## Fisher's Exact Test for Count Data
## ------------------------------------------------------------
## Sample estimate odds ratio: 1.139965
##
## Alternative hypothesis: true odds ratio is not equal to 1
## p = 0.6907062
## 95% confidence interval: 0.6553751 1.97583
##
## Alternative hypothesis: true odds ratio is less than 1
## p = 0.7361355
## 95% confidence interval: 0 1.818849
##
## Alternative hypothesis: true odds ratio is greater than 1
## p = 0.3568877
## 95% confidence interval: 0.7128049 Inf
##
##
##
## Minimum expected frequency: 36.12692
That’s a lot of information. What do we actually care about?
Pearson’s chi-square has been criticized for being susceptible to Type I error. This test also gives you the Yates continuity correction which is a more conservative version of Pearson’s chi-square.
Copying what we did in Python, let’s say we had a small sample. Which test statistic would you use from this output?
##
## Fisher's Exact Test for Count Data
##
## data: df$PRE_female and df$PRE_male
## p-value = 0.6907
## alternative hypothesis: true odds ratio is not equal to 1
## 95 percent confidence interval:
## 0.6553751 1.9758303
## sample estimates:
## odds ratio
## 1.139965
df %>%
summarize(mean_female = mean(PRE_female, na.rm = TRUE),
mean_male = mean(PRE_male, na.rm = TRUE),
sd_female = sd(PRE_female, na.rm = TRUE),
sd_male = sd(PRE_male, na.rm = TRUE))## # A tibble: 1 × 4
## mean_female mean_male sd_female sd_male
## <dbl> <dbl> <dbl> <dbl>
## 1 0.388 0.358 0.488 0.480
Let’s copy the code we used to visually compare PRE_female and PRE_male. How would you change it to show PRE_female and POST_female?
bar_plot_prepost <- df %>%
dplyr::select(PRE_female, PRE_male) %>% # we only need these variable
pivot_longer(cols = c(PRE_female, PRE_male), names_to = "category", values_to = "value") %>%
mutate(category = factor(category, levels = c("PRE_female", "PRE_male"))) %>% # Set the order of factors
ggplot(aes(x = category, y = value, fill = category)) + # have to add "fill" in aes() if you want to change the color manually later
stat_summary(fun=mean, geom="bar", position = position_dodge(), width = .3) + # plot mean as barplot
stat_summary(fun.data = mean_se, geom = "errorbar", position = position_dodge(0.9), width = .1) + # mean_se is an argument that calculates SD (can calculate SE with additional arguments)
scale_fill_manual(values = c("cornflowerblue", "deepskyblue4")) + # how to add two colors. they're in the order of the df
labs(x = "Time", y = "Mean") + # add title to x and y-axis (labs = "labels")
theme_minimal() + # preset theme
ggtitle("Means of Pre- and Post-Measures for Women")
print(bar_plot_prepost)##
## 0 1
## 0 85 74
## 1 24 77
## POST_female
## PRE_female 0 1
## 0 85 74
## 1 24 77
How many women are only in the PRE?
__
How many women are only in the POST?
__
How many women do we have in both?
__
##
## McNemar's Chi-squared test
##
## data: df$PRE_female and df$POST_female
## McNemar's chi-squared = 25.51, df = 1, p-value = 4.4e-07
# the continuity correction (default) is a conservative test, best for super small samples and uneven distributionsdf %>%
summarize(mean_pre = mean(PRE_female, na.rm = TRUE),
mean_post = mean(POST_female, na.rm = TRUE),
sd_pre = sd(PRE_female, na.rm = TRUE),
sd_post = sd(POST_female, na.rm = TRUE))## # A tibble: 1 × 4
## mean_pre mean_post sd_pre sd_post
## <dbl> <dbl> <dbl> <dbl>
## 1 0.388 0.581 0.488 0.494
We don’t typically use Cohen’s W for these tests, which captures the strength of an association. Instead, we use an odds ratio. Odds ratios work for any categorical test and they’re pretty easy to interpret. Know that it can only compare two variables.
Odds ratio : odds of numerator vs denominator
##
## Exact McNemar test (with central confidence intervals)
##
## data: df$PRE_female and df$POST_female
## b = 74, c = 24, p-value = 4.216e-07
## alternative hypothesis: true odds ratio is not equal to 1
## 95 percent confidence interval:
## 1.922863 5.110849
## sample estimates:
## odds ratio
## 3.083333
##
## Fisher's Exact Test for Count Data
##
## data: df$PRE_female and df$POST_female
## p-value = 2.815e-06
## alternative hypothesis: true odds ratio is not equal to 1
## 95 percent confidence interval:
## 2.052735 6.713070
## sample estimates:
## odds ratio
## 3.666142
Finally, we’re going to do a power analysis using the WebPower r package. To do this for a chi-square, we’re going to run a power analysis for “tests of proportions.” This is also what it’s called in G*Power.
# note that alpha = 0.5 is the default argument.
# if you're running this post-hoc, include n1 (and n2)
wp.prop(h = .2, # cohen's h: effect size
type = "1p", # 1p = 1 sample, 2p = 2 sample, 2p2n = 2 sample, unequal sample size
alpha = .05,
power = .95)## Power for one-sample proportion test
##
## h n alpha power
## 0.2 324.8677 0.05 0.95
##
## URL: http://psychstat.org/prop