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=