Packages
library(tidyverse)
library(janitor)
library(irr)
readxl::excel_sheets("reliability_data.xlsx")
[1] "Sheet1" "Sheet3" "Sheet2"
Original messy dataset
head(df)
Clean names and empty cols
df <- df %>%
janitor::clean_names() %>%
janitor::remove_empty_cols()
Less messy
head(df)
Create two datasets, for min and max
min <- df %>%
select(min:d_21_l) %>%
mutate(type = "comparison") %>%
gather(d_13_d:d_21_l, key = "tooth", value = "difference")
colnames(min)[colnames(min) == "min"] <- "comparison"
max <- df %>%
select(max:d_21_l_1) %>%
mutate(type = "comparison") %>%
gather(d_13_d_1:d_21_l_1, key = "tooth", value = "difference")
colnames(max)[colnames(max) == "max"] <- "comparison"
max$tooth = substr(max$tooth,1,nchar(max$tooth) -2)
Join datasets
df2 <- bind_rows(min, max) #create a semi tidy dataset
df2 <- df2 %>%
select(-type)
# rm(df, min, max) #delete unused datasets
Dataset ready for work
head(df2)
Descriptive stats
df2 %>%
select(comparison:difference) %>%
group_by(comparison) %>%
summarise_each (funs(min,
q25 = quantile(., 0.25),
median = median,
q75 = quantile(., 0.75),
max = max,
mean = mean,
sd = sd),
difference)
`summarise_each()` is deprecated.
Use `summarise_all()`, `summarise_at()` or `summarise_if()` instead.
To map `funs` over a selection of variables, use `summarise_at()`
abs difference
df2 <- df2 %>%
mutate("abs_diff" = abs(difference)) #absolute difference
df2 <- df2 %>%
mutate("expect_diff" = 0) #expected difference = 0
Distribution
df2 %>%
ggplot(aes(x = abs_diff)) +
geom_histogram(bins = 5) +
scale_x_log10()

There is an outlier
which.max(df2$abs_diff)
[1] 830
Delete the outlier
df2 <- df2[-830, ]
df2 %>% ggplot(aes(difference)) +
geom_histogram(bins = 10) +
labs(title = "Distribution of the differences between measurements",
x = "Difference",
y = "Counts") +
theme_minimal()

df3 <- df2 %>%
group_by(tooth, comparison) %>%
summarise("Average" = mean(difference)) %>%
spread(comparison, value = Average)
Agreement
df3 %>%
ungroup() %>%
select(1--10:9--10) %>%
icc(., model = "twoway", type = "agreement")
Single Score Intraclass Correlation
Model: twoway
Type : agreement
Subjects = 12
Raters = 20
ICC(A,1) = 0.149
F-Test, H0: r0 = 0 ; H1: r0 > 0
F(11,226) = 4.47 , p = 4.22e-06
95%-Confidence Interval for ICC Population Values:
0.056 < ICC < 0.377
There are significant differences between the measurements across raters
Absolute values matrix
df3_abs <- df3 %>%
ungroup() %>%
select(1--10:9--10) %>%
abs(.)
df2 %>%
ggplot(aes(x = tooth, y = abs_diff)) +
geom_boxplot() +
geom_jitter(alpha = 0.1) +
coord_flip() +
labs( title = "Measurements per sample",
y = "Absolute difference",
x = "Sample and surface") +
theme_minimal()

Seems that the machine has some thresholds for detection, since there are no 0.01
set.seed(3000)
df2 <- df2 %>%
mutate(expected_values = rnorm(1079, mean = 0, sd = 0.01))
df2 %>%
select(difference, expected_values) %>%
gather("type", "value", 1:2) %>%
ggplot(aes(value, fill = type)) +
geom_density(alpha = 0.4) +
labs(title = "Expected random variance vs real differences",
subtitle = "3 samples, 4 measurements each.",
x = "Difference",
y = "Count",
fill = "Key") +
theme_minimal()

df2 %>%
ggplot(aes(x = difference, y = expected_values)) +
geom_point() +
stat_smooth() +
labs(title = "Scatterplot expected vs real differences",
y = "Expected by random",
x = "Real differences") +
theme_minimal()

library(BlandAltmanLeh)
ba_plot <- bland.altman.plot(df2$difference, df2$expected_values, graph.sys="ggplot2", conf.int=.95)
ba_plot + theme_minimal() +
labs(title = "Altman-Bland plot",
subtitle = "Expected vs real differences",
y = "Difference expected - real",
x = "Mean of measurements")

Read Understanding Bland Altman analysis
Is any statistical difference between expected and real differences?
Against expected distribution
t.test(df2$difference, df2$expected_values)
Welch Two Sample t-test
data: df2$difference and df2$expected_values
t = -0.26614, df = 1344.2, p-value = 0.7902
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
-0.002066110 0.001572482
sample estimates:
mean of x mean of y
0.0004355885 0.0006824026
df2 %>%
select(difference, expected_values) %>%
gather() %>%
ggplot(aes(x = key, y = value)) +
geom_boxplot() +
theme_minimal()

As one sample
t.test(df2$difference, mu = 0)
One Sample t-test
data: df2$difference
t = 0.49828, df = 1078, p-value = 0.6184
alternative hypothesis: true mean is not equal to 0
95 percent confidence interval:
-0.001279716 0.002150893
sample estimates:
mean of x
0.0004355885
Are normally distributed the differences?
shapiro.test(df2$difference)
Shapiro-Wilk normality test
data: df2$difference
W = 0.94936, p-value < 2.2e-16
library("ggpubr")
ggqqplot(df2$difference,
ylab = "Measured differences",
ggtheme = theme_minimal())

Compare with expected values
ggqqplot(df2$expected_values,
ylab = "Expected values",
ggtheme = theme_minimal())

shapiro.test(df2$expected_values)
Shapiro-Wilk normality test
data: df2$expected_values
W = 0.99895, p-value = 0.8007
LS0tDQp0aXRsZTogIlJlbGlhYmlsaXR5IEludHJhb3JhbCBTY2FubmVyIg0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHRydWUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmBgYA0KDQojIFBhY2thZ2VzDQoNCmBgYHtyIHBhY2thZ2VzfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGphbml0b3IpDQpsaWJyYXJ5KGlycikNCmBgYA0KDQpgYGB7ciBjcmVhdGUgZGZ9DQpkZiA8LSByZWFkeGw6OnJlYWRfeGxzeCgicmVsaWFiaWxpdHlfZGF0YS54bHN4Iiwgc2hlZXQgPSAiU2hlZXQyIiApDQoNCmBgYA0KDQpPcmlnaW5hbCBtZXNzeSBkYXRhc2V0DQpgYGB7cn0NCmhlYWQoZGYpDQpgYGANCg0KDQpDbGVhbiBuYW1lcyBhbmQgZW1wdHkgY29scw0KYGBge3IgY2xlYW4gbmFtZXN9DQpkZiA8LSBkZiAgJT4lIA0KICAgICAgICBqYW5pdG9yOjpjbGVhbl9uYW1lcygpICU+JSANCiAgICAgICAgamFuaXRvcjo6cmVtb3ZlX2VtcHR5X2NvbHMoKQ0KDQoNCg0KYGBgDQoNCkxlc3MgbWVzc3kNCmBgYHtyfQ0KaGVhZChkZikNCmBgYA0KDQpDcmVhdGUgdHdvIGRhdGFzZXRzLCBmb3IgbWluIGFuZCBtYXgNCg0KYGBge3IgdHdvIGRhdGFzZXRzfQ0KDQptaW4gPC0gZGYgJT4lIA0KICAgICAgICBzZWxlY3QobWluOmRfMjFfbCkgJT4lIA0KICAgICAgICBtdXRhdGUodHlwZSA9ICJjb21wYXJpc29uIikgJT4lIA0KICAgICAgICBnYXRoZXIoZF8xM19kOmRfMjFfbCwga2V5ID0gInRvb3RoIiwgdmFsdWUgPSAiZGlmZmVyZW5jZSIpDQpjb2xuYW1lcyhtaW4pW2NvbG5hbWVzKG1pbikgPT0gIm1pbiJdIDwtICJjb21wYXJpc29uIg0KDQoNCm1heCA8LSBkZiAlPiUgDQogICAgICAgIHNlbGVjdChtYXg6ZF8yMV9sXzEpICU+JSANCiAgICAgICAgbXV0YXRlKHR5cGUgPSAiY29tcGFyaXNvbiIpICU+JSANCiAgICAgICAgZ2F0aGVyKGRfMTNfZF8xOmRfMjFfbF8xLCBrZXkgPSAidG9vdGgiLCB2YWx1ZSA9ICJkaWZmZXJlbmNlIikNCmNvbG5hbWVzKG1heClbY29sbmFtZXMobWF4KSA9PSAibWF4Il0gPC0gImNvbXBhcmlzb24iDQoNCm1heCR0b290aCA9IHN1YnN0cihtYXgkdG9vdGgsMSxuY2hhcihtYXgkdG9vdGgpIC0yKQ0KDQpgYGANCg0KSm9pbiBkYXRhc2V0cw0KDQpgYGB7ciBiaW5kfQ0KZGYyIDwtIGJpbmRfcm93cyhtaW4sIG1heCkgI2NyZWF0ZSBhIHNlbWkgdGlkeSBkYXRhc2V0DQpkZjIgPC0gZGYyICU+JSANCiAgICAgICAgc2VsZWN0KC10eXBlKQ0KYGBgDQoNCmBgYHtyIGRlbGV0ZSBvbGQgZGF0YXNldHN9DQojIHJtKGRmLCBtaW4sIG1heCkgI2RlbGV0ZSB1bnVzZWQgZGF0YXNldHMNCmBgYA0KDQpEYXRhc2V0IHJlYWR5IGZvciB3b3JrDQpgYGB7cn0NCmhlYWQoZGYyKQ0KYGBgDQoNCg0KI0Rlc2NyaXB0aXZlIHN0YXRzDQoNCmBgYHtyfQ0KZGYyICU+JSANCiAgICAgICAgc2VsZWN0KGNvbXBhcmlzb246ZGlmZmVyZW5jZSkgJT4lIA0KICAgICAgICBncm91cF9ieShjb21wYXJpc29uKSAlPiUgDQogICAgICAgIHN1bW1hcmlzZV9lYWNoIChmdW5zKG1pbiwgDQogICAgICAgICAgICAgICAgICAgICAgcTI1ID0gcXVhbnRpbGUoLiwgMC4yNSksIA0KICAgICAgICAgICAgICAgICAgICAgIG1lZGlhbiA9IG1lZGlhbiwgDQogICAgICAgICAgICAgICAgICAgICAgcTc1ID0gcXVhbnRpbGUoLiwgMC43NSksIA0KICAgICAgICAgICAgICAgICAgICAgIG1heCA9IG1heCwNCiAgICAgICAgICAgICAgICAgICAgICBtZWFuID0gbWVhbiwgDQogICAgICAgICAgICAgICAgICAgICAgc2QgPSBzZCksIA0KICAgICAgICAgICAgICAgICAgICAgIGRpZmZlcmVuY2UpDQoNCmBgYA0KDQoNCg0KYWJzIGRpZmZlcmVuY2UNCmBgYHtyIG11dGF0ZSBhYnMgZGlmZiB5IGV4cGVjdCBkaWZmfQ0KZGYyIDwtIGRmMiAlPiUgDQogICAgICAgIG11dGF0ZSgiYWJzX2RpZmYiID0gYWJzKGRpZmZlcmVuY2UpKSAjYWJzb2x1dGUgZGlmZmVyZW5jZQ0KDQpkZjIgPC0gZGYyICU+JSANCiAgICAgICAgbXV0YXRlKCJleHBlY3RfZGlmZiIgPSAwKSAjZXhwZWN0ZWQgZGlmZmVyZW5jZSA9IDANCg0KYGBgDQoNCiMgRGlzdHJpYnV0aW9uDQpgYGB7ciBoaXN0b2dyYW19DQpkZjIgJT4lIA0KICAgICAgICBnZ3Bsb3QoYWVzKHggPSBhYnNfZGlmZikpICsNCiAgICAgICAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDUpICsgDQogICAgICAgIHNjYWxlX3hfbG9nMTAoKQ0KYGBgDQpUaGVyZSBpcyBhbiBvdXRsaWVyDQoNCmBgYHtyIGxvY2F0ZSBvdXRsaWVyc30NCndoaWNoLm1heChkZjIkYWJzX2RpZmYpDQpgYGANCkRlbGV0ZSB0aGUgb3V0bGllcg0KDQpgYGB7ciBkZWxldGUgb3V0bGllcn0NCmRmMiA8LSBkZjJbLTgzMCwgXQ0KYGBgDQoNCmBgYHtyfQ0KZGYyICU+JSBnZ3Bsb3QoYWVzKGRpZmZlcmVuY2UpKSArIA0KICAgICAgICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTApICsgDQogICAgICAgIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIG1lYXN1cmVtZW50cyIsIA0KICAgICAgICAgICAgIHggPSAiRGlmZmVyZW5jZSIsIA0KICAgICAgICAgICAgIHkgPSAiQ291bnRzIikgKyANCiAgICAgICAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KDQpgYGB7ciBkZjMgbWF0cml4fQ0KZGYzIDwtIGRmMiAlPiUgDQogICAgICAgIGdyb3VwX2J5KHRvb3RoLCBjb21wYXJpc29uKSAlPiUgDQogICAgICAgIHN1bW1hcmlzZSgiQXZlcmFnZSIgPSBtZWFuKGRpZmZlcmVuY2UpKSAlPiUgDQogICAgICAgIHNwcmVhZChjb21wYXJpc29uLCB2YWx1ZSA9IEF2ZXJhZ2UpDQoNCg0KDQpgYGANCg0KDQoNCg0KDQojIEFncmVlbWVudA0KYGBge3IgaWNjfQ0KZGYzICU+JSANCiAgICAgICAgdW5ncm91cCgpICU+JSANCiAgICAgICAgc2VsZWN0KDEtLTEwOjktLTEwKSAlPiUgDQogICAgICAgIGljYyguLCBtb2RlbCA9ICJ0d293YXkiLCB0eXBlID0gImFncmVlbWVudCIpDQoNCmBgYA0KDQpUaGVyZSBhcmUgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGUgbWVhc3VyZW1lbnRzIGFjcm9zcyByYXRlcnMNCg0KQWJzb2x1dGUgdmFsdWVzIG1hdHJpeA0KYGBge3J9DQpkZjNfYWJzIDwtIGRmMyAlPiUgDQogICAgICAgIHVuZ3JvdXAoKSAlPiUgDQogICAgICAgIHNlbGVjdCgxLS0xMDo5LS0xMCkgJT4lIA0KICAgICAgICBhYnMoLikgDQoNCmBgYA0KDQpgYGB7cn0NCmRmMiAlPiUgDQogICAgICAgIGdncGxvdChhZXMoeCA9IHRvb3RoLCB5ID0gYWJzX2RpZmYpKSArDQogICAgICAgIA0KICAgICAgICBnZW9tX2JveHBsb3QoKSArIA0KICAgICAgICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuMSkgKyANCiAgICAgICAgY29vcmRfZmxpcCgpICsNCiAgICAgICAgbGFicyggdGl0bGUgPSAiTWVhc3VyZW1lbnRzIHBlciBzYW1wbGUiLCANCiAgICAgICAgICAgICAgeSA9ICJBYnNvbHV0ZSBkaWZmZXJlbmNlIiwgDQogICAgICAgICAgICAgIHggPSAiU2FtcGxlIGFuZCBzdXJmYWNlIikgKyANCiAgICAgICAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KU2VlbXMgdGhhdCB0aGUgbWFjaGluZSBoYXMgc29tZSB0aHJlc2hvbGRzIGZvciBkZXRlY3Rpb24sIHNpbmNlIHRoZXJlIGFyZSBubyAwLjAxDQoNCg0KYGBge3J9DQpzZXQuc2VlZCgzMDAwKQ0KZGYyIDwtIGRmMiAlPiUgDQogICAgICAgIG11dGF0ZShleHBlY3RlZF92YWx1ZXMgPSBybm9ybSgxMDc5LCBtZWFuID0gMCwgc2QgPSAwLjAxKSkNCmBgYA0KDQpgYGB7cn0NCmRmMiAlPiUgDQogICAgICAgIHNlbGVjdChkaWZmZXJlbmNlLCBleHBlY3RlZF92YWx1ZXMpICU+JSANCiAgICAgICAgZ2F0aGVyKCJ0eXBlIiwgInZhbHVlIiwgMToyKSAlPiUgDQogICAgICAgIGdncGxvdChhZXModmFsdWUsIGZpbGwgPSB0eXBlKSkgKw0KICAgICAgICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjQpICsgDQogICAgICAgIGxhYnModGl0bGUgPSAiRXhwZWN0ZWQgcmFuZG9tIHZhcmlhbmNlIHZzIHJlYWwgZGlmZmVyZW5jZXMiLCANCiAgICAgICAgICAgICBzdWJ0aXRsZSA9ICIzIHNhbXBsZXMsIDQgbWVhc3VyZW1lbnRzIGVhY2guIiwgDQogICAgICAgICAgICAgeCA9ICJEaWZmZXJlbmNlIiwgDQogICAgICAgICAgICAgeSA9ICJDb3VudCIsIA0KICAgICAgICAgICAgIGZpbGwgPSAiS2V5IikgKyANCiAgICAgICAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KDQpgYGB7cn0NCmRmMiAlPiUgDQogICAgICAgIGdncGxvdChhZXMoeCA9IGRpZmZlcmVuY2UsIHkgPSBleHBlY3RlZF92YWx1ZXMpKSArDQogICAgICAgIGdlb21fcG9pbnQoKSArDQogICAgICAgIHN0YXRfc21vb3RoKCkgKyANCiAgICAgICAgbGFicyh0aXRsZSA9ICJTY2F0dGVycGxvdCBleHBlY3RlZCB2cyByZWFsIGRpZmZlcmVuY2VzIiwgDQogICAgICAgICAgICAgeSA9ICJFeHBlY3RlZCBieSByYW5kb20iLCANCiAgICAgICAgICAgICB4ID0gIlJlYWwgZGlmZmVyZW5jZXMiKSArDQogICAgICAgIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCg0KYGBge3J9DQpsaWJyYXJ5KEJsYW5kQWx0bWFuTGVoKQ0KDQpiYV9wbG90IDwtIGJsYW5kLmFsdG1hbi5wbG90KGRmMiRkaWZmZXJlbmNlLCBkZjIkZXhwZWN0ZWRfdmFsdWVzLCBncmFwaC5zeXM9ImdncGxvdDIiLCBjb25mLmludD0uOTUpDQpiYV9wbG90ICsgdGhlbWVfbWluaW1hbCgpICsgDQogICAgICAgIGxhYnModGl0bGUgPSAiQWx0bWFuLUJsYW5kIHBsb3QiLCANCiAgICAgICAgICAgICBzdWJ0aXRsZSA9ICJFeHBlY3RlZCB2cyByZWFsIGRpZmZlcmVuY2VzIiwgDQogICAgICAgICAgICAgeSA9ICJEaWZmZXJlbmNlIGV4cGVjdGVkIC0gcmVhbCIsIA0KICAgICAgICAgICAgIHggPSAiTWVhbiBvZiBtZWFzdXJlbWVudHMiKQ0KYGBgDQpSZWFkIFtVbmRlcnN0YW5kaW5nIEJsYW5kIEFsdG1hbiBhbmFseXNpc10oaHR0cDovL3d3dy5iaW9jaGVtaWEtbWVkaWNhLmNvbS8yMDE1LzI1LzE0MSkNCg0KIyBJcyBhbnkgc3RhdGlzdGljYWwgZGlmZmVyZW5jZSBiZXR3ZWVuIGV4cGVjdGVkIGFuZCByZWFsIGRpZmZlcmVuY2VzPw0KIyMgQWdhaW5zdCBleHBlY3RlZCBkaXN0cmlidXRpb24NCmBgYHtyfQ0KdC50ZXN0KGRmMiRkaWZmZXJlbmNlLCBkZjIkZXhwZWN0ZWRfdmFsdWVzKQ0KYGBgDQoNCmBgYHtyfQ0KZGYyICU+JSANCiAgICAgICAgc2VsZWN0KGRpZmZlcmVuY2UsIGV4cGVjdGVkX3ZhbHVlcykgJT4lIA0KICAgICAgICBnYXRoZXIoKSAlPiUgDQogICAgICAgIGdncGxvdChhZXMoeCA9IGtleSwgeSA9IHZhbHVlKSkgKyANCiAgICAgICAgZ2VvbV9ib3hwbG90KCkgKyANCiAgICAgICAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KDQojIyBBcyBvbmUgc2FtcGxlDQpgYGB7cn0NCnQudGVzdChkZjIkZGlmZmVyZW5jZSwgbXUgPSAwKQ0KYGBgDQoNCkFyZSBub3JtYWxseSBkaXN0cmlidXRlZCB0aGUgZGlmZmVyZW5jZXM/DQoNCmBgYHtyfQ0Kc2hhcGlyby50ZXN0KGRmMiRkaWZmZXJlbmNlKQ0KYGBgDQoNCmBgYHtyfQ0KDQpsaWJyYXJ5KCJnZ3B1YnIiKQ0KZ2dxcXBsb3QoZGYyJGRpZmZlcmVuY2UsIA0KICAgICAgICAgeWxhYiA9ICJNZWFzdXJlZCBkaWZmZXJlbmNlcyIsDQogICAgICAgICBnZ3RoZW1lID0gdGhlbWVfbWluaW1hbCgpKQ0KYGBgDQpDb21wYXJlIHdpdGggZXhwZWN0ZWQgdmFsdWVzDQpgYGB7cn0NCmdncXFwbG90KGRmMiRleHBlY3RlZF92YWx1ZXMsIA0KICAgICAgICAgeWxhYiA9ICJFeHBlY3RlZCB2YWx1ZXMiLA0KICAgICAgICAgZ2d0aGVtZSA9IHRoZW1lX21pbmltYWwoKSkNCmBgYA0KDQpgYGB7cn0NCnNoYXBpcm8udGVzdChkZjIkZXhwZWN0ZWRfdmFsdWVzKQ0KYGBgDQo=