Knowledge, Attitudes, and Perceived Barriers Toward Pediatric Preventive Dentistry

Demetrio Data

Published

March 7, 2026

1 Setup

1.1 Load packages

Code
# Load all required packages via pacman
if (!requireNamespace("pacman", quietly = TRUE)) install.packages("pacman")
pacman::p_load(tidyverse, here, janitor, viridis, scales, MASS, gtsummary)

1.2 Read and clean data

Code
# Read the raw CSV, normalize column names with janitor, then rename
# to short workable aliases. This is the only persistent object we keep
# thorughout the analysis
df <- read_csv(here("data", "data.csv"),
         show_col_types = FALSE) |> 
  clean_names() |> 
  # Rename the long janitor names to short aliases
  rename(
    specialty      = dental_specialty,
    q1_sugar       = x1_what_is_the_maximum_recommended_sugar_consumption_during_the_day,
    q2_first_visit = x2_at_what_age_is_the_first_visit_recommended,
    q3_fluoride    = x3_which_of_the_following_statements_about_fluoride_is_correct,
    q4_sealants    = x4_which_of_the_following_questions_about_sealants_is_correct,
    q5_ecc         = x5_which_of_the_following_statements_about_early_childhood_caries_ecc_is_correct,
    q6_brushing    = x6_which_of_the_following_statements_about_brushing_is_correct,
    q7_risk        = x7_which_of_the_following_statements_about_oral_health_risk_factors_is_wrong,
    q8_smoking     = x8_dentists_should_carry_out_preventive_activities_for_smoking_cessation,
    q9_paed_prev   = x9_dentists_should_carry_out_preventive_activities_in_paediatric_dentistry,
    q10_equipped   = x10_dentists_are_well_equipped_in_carrying_out_preventive_care_in_paediatric_dentistry,
    q11_sealants   = x11_when_needed_do_you_apply_dental_sealants,
    q12_varnish    = x12_when_needed_do_you_apply_fluoride_varnish,
    q13_ohi        = x13_when_needed_do_you_give_oral_hygiene_instructions,
    q14_diet       = x14_when_needed_do_you_provide_any_dietary_advice,
    q15_time       = x15_lack_of_time,
    q16_remuneration = x16_lack_of_remuneration,
    q17_motivation = x17_lack_of_motivation_in_implementing_preventive_care,
    q18_compliance = x18_poor_patient_compliance,
    q19_knowledge_b = x19_lack_of_knowledge,
    q20_training   = x20_lack_of_training
  ) |> 
  # Recode categorical variables
  mutate(
    age_group = factor(age,
                       levels = 1:6,
                       labels = c("25-34", "35-44", "45-54",
                                  "55-64", "65-74", "75+")),
    gender_label = factor(gender,
                          levels = c(0, 1),
                          labels = c("Male", "Female")),
    specialty_label = factor(specialty,
                             levels = 1:7,
                             labels = c("General Dentistry", "Pediatric Dentistry",
                                        "Orthodontics", "Oral Surgery",
                                        "Restorative/Endo", "Prosthodontics",
                                        "Periodontics")),
    country = factor(country)
  ) -> df

2 Demographics

2.1 Demographics table (Table 1)

Code
# Table 1: sample characteristics stratified by country using gtsummary
df |> 
  select(country, age_group, gender_label, specialty_label) |> 
  tbl_summary(
    by = country,
    label = list(
      age_group       ~ "Age group",
      gender_label    ~ "Gender",
      specialty_label ~ "Dental specialty"
    ),
    missing = "ifany",
    missing_text = "Missing"
  ) |> 
  add_overall() |> 
  add_p(test = list(all_categorical() ~ "chisq.test")) |> 
  modify_header(label ~ "**Characteristic**") |> 
  bold_labels()
Characteristic Overall, N = 3,5771 Argentina, N = 3821 Chile, N = 7481 India, N = 3601 Italy, N = 3221 Latvia, N = 1001 Serbia, N = 8451 Spain, N = 7081 Switzerland, N = 1121 p-value2
Age group <0.001
    25-34 1,098 (31%) 86 (23%) 280 (37%) 318 (88%) 51 (16%) 27 (27%) 183 (22%) 130 (18%) 23 (21%)
    35-44 981 (27%) 82 (21%) 279 (37%) 28 (7.8%) 47 (15%) 33 (33%) 268 (32%) 208 (29%) 36 (32%)
    45-54 749 (21%) 122 (32%) 106 (14%) 13 (3.6%) 56 (17%) 23 (23%) 232 (27%) 178 (25%) 19 (17%)
    55-64 526 (15%) 73 (19%) 58 (7.8%) 1 (0.3%) 105 (33%) 16 (16%) 128 (15%) 118 (17%) 27 (24%)
    65-74 201 (5.6%) 15 (3.9%) 24 (3.2%) 0 (0%) 58 (18%) 1 (1.0%) 31 (3.7%) 67 (9.5%) 5 (4.5%)
    75+ 22 (0.6%) 4 (1.0%) 1 (0.1%) 0 (0%) 5 (1.6%) 0 (0%) 3 (0.4%) 7 (1.0%) 2 (1.8%)
Gender <0.001
    Male 1,118 (31%) 77 (20%) 237 (32%) 108 (30%) 212 (66%) 5 (5.0%) 196 (23%) 236 (33%) 47 (43%)
    Female 2,457 (69%) 305 (80%) 511 (68%) 252 (70%) 110 (34%) 95 (95%) 649 (77%) 472 (67%) 63 (57%)
    Missing 2 0 0 0 0 0 0 0 2
Dental specialty <0.001
    General Dentistry 1,825 (51%) 172 (45%) 438 (59%) 147 (41%) 128 (40%) 82 (82%) 480 (57%) 350 (49%) 28 (25%)
    Pediatric Dentistry 518 (14%) 98 (26%) 140 (19%) 19 (5.3%) 13 (4.0%) 11 (11%) 152 (18%) 64 (9.0%) 21 (19%)
    Orthodontics 240 (6.7%) 32 (8.4%) 29 (3.9%) 9 (2.5%) 36 (11%) 1 (1.0%) 44 (5.2%) 75 (11%) 14 (12%)
    Oral Surgery 262 (7.3%) 21 (5.5%) 25 (3.3%) 22 (6.1%) 39 (12%) 0 (0%) 55 (6.5%) 96 (14%) 4 (3.6%)
    Restorative/Endo 414 (12%) 42 (11%) 34 (4.5%) 146 (41%) 45 (14%) 2 (2.0%) 40 (4.7%) 79 (11%) 26 (23%)
    Prosthodontics 246 (6.9%) 12 (3.1%) 78 (10%) 9 (2.5%) 41 (13%) 2 (2.0%) 54 (6.4%) 38 (5.4%) 12 (11%)
    Periodontics 72 (2.0%) 5 (1.3%) 4 (0.5%) 8 (2.2%) 20 (6.2%) 2 (2.0%) 20 (2.4%) 6 (0.8%) 7 (6.2%)
1 n (%)
2 Pearson's Chi-squared test

3 Knowledge

3.1 Descriptives by country

Code
# Knowledge score summary stratified by country
df |> 
  select(country, score_knowledge) |> 
  tbl_summary(
    by = country,
    label = list(score_knowledge ~ "Knowledge score (0-7)"),
    statistic = list(all_continuous() ~ "{mean} ({sd}) | {median} [{p25}, {p75}]"),
    missing = "no"
  ) |> 
  add_overall() |> 
  add_p(test = list(all_continuous() ~ "kruskal.test")) |> 
  bold_labels()
Characteristic Overall, N = 3,5771 Argentina, N = 3821 Chile, N = 7481 India, N = 3601 Italy, N = 3221 Latvia, N = 1001 Serbia, N = 8451 Spain, N = 7081 Switzerland, N = 1121 p-value
Knowledge score (0-7)
    0 7 (0.2%) 0 (0%) 0 (0%) 3 (0.8%) 0 (0%) 0 (0%) 0 (0%) 3 (0.4%) 1 (0.9%)
    1 29 (0.8%) 1 (0.3%) 0 (0%) 19 (5.3%) 1 (0.3%) 1 (1.0%) 3 (0.4%) 1 (0.1%) 3 (2.7%)
    2 83 (2.3%) 16 (4.2%) 1 (0.1%) 16 (4.4%) 23 (7.1%) 4 (4.0%) 8 (0.9%) 14 (2.0%) 1 (0.9%)
    3 301 (8.4%) 30 (7.9%) 4 (0.5%) 52 (14%) 74 (23%) 6 (6.0%) 64 (7.6%) 55 (7.8%) 16 (14%)
    4 622 (17%) 82 (21%) 16 (2.1%) 49 (14%) 102 (32%) 19 (19%) 197 (23%) 134 (19%) 23 (21%)
    5 910 (25%) 123 (32%) 91 (12%) 93 (26%) 85 (26%) 43 (43%) 228 (27%) 220 (31%) 27 (24%)
    6 958 (27%) 98 (26%) 252 (34%) 89 (25%) 37 (11%) 27 (27%) 240 (28%) 191 (27%) 24 (21%)
    7 667 (19%) 32 (8.4%) 384 (51%) 39 (11%) 0 (0%) 0 (0%) 105 (12%) 90 (13%) 17 (15%)
1 n (%)

3.2 Item-level correct response rate

Code
# Proportion correct for each knowledge item, stratified by country
df |> 
  select(country, q1_sugar:q7_risk) |> 
  # Convert to factors so gtsummary shows proportions
  mutate(across(q1_sugar:q7_risk, ~ factor(.x, levels = c(1, 0),
                                            labels = c("Correct", "Incorrect")))) |> 
  tbl_summary(
    by = country,
    label = list(
      q1_sugar       ~ "Sugar intake",
      q2_first_visit ~ "First visit age",
      q3_fluoride    ~ "Fluoride",
      q4_sealants    ~ "Sealants",
      q5_ecc         ~ "ECC",
      q6_brushing    ~ "Brushing",
      q7_risk        ~ "Risk factors"
    ),
    value = list(q1_sugar ~ "Correct", q2_first_visit ~ "Correct",
                 q3_fluoride ~ "Correct", q4_sealants ~ "Correct",
                 q5_ecc ~ "Correct", q6_brushing ~ "Correct",
                 q7_risk ~ "Correct"),
    missing = "no"
  ) |> 
  add_overall() |> 
  add_p(test = list(all_categorical() ~ "chisq.test")) |> 
  bold_labels()
Characteristic Overall, N = 3,5771 Argentina, N = 3821 Chile, N = 7481 India, N = 3601 Italy, N = 3221 Latvia, N = 1001 Serbia, N = 8451 Spain, N = 7081 Switzerland, N = 1121 p-value2
Sugar intake 2,427 (68%) 218 (58%) 593 (79%) 212 (59%) 174 (54%) 77 (77%) 551 (65%) 516 (74%) 86 (77%) <0.001
First visit age 2,587 (72%) 344 (91%) 731 (98%) 262 (73%) 0 (0%) 95 (95%) 637 (75%) 466 (66%) 52 (47%) <0.001
Fluoride 2,694 (76%) 199 (53%) 642 (86%) 218 (62%) 209 (65%) 77 (77%) 773 (91%) 504 (73%) 72 (64%) <0.001
Sealants 1,952 (55%) 204 (54%) 688 (92%) 262 (73%) 139 (44%) 45 (45%) 301 (36%) 257 (37%) 56 (50%) <0.001
ECC 3,271 (92%) 337 (90%) 699 (93%) 302 (85%) 308 (97%) 89 (89%) 786 (93%) 656 (94%) 94 (85%) <0.001
Brushing 3,341 (94%) 344 (91%) 729 (97%) 263 (74%) 303 (96%) 95 (95%) 818 (97%) 679 (97%) 110 (99%) <0.001
Risk factors 2,281 (64%) 232 (61%) 651 (87%) 156 (44%) 191 (60%) 2 (2.0%) 448 (53%) 528 (75%) 73 (65%) <0.001
1 n (%)
2 Pearson's Chi-squared test

3.3 Knowledge by specialty

Code
# Knowledge score stratified by specialty
df |> 
  select(country, age_group, gender_label, specialty_label) |> 
  tbl_summary(
    by = country,
    label = list(
      age_group       ~ "Age group",
      gender_label    ~ "Gender",
      specialty_label ~ "Dental specialty"
    ),
    missing = "ifany",
    missing_text = "Missing"
  ) |> 
  add_overall() |> 
  add_p(test = list(all_categorical() ~ "chisq.test.no.correct"),
        test.args = all_tests("chisq.test.no.correct") ~ 
          list(simulate.p.value = TRUE, B = 2000)) |> 
  modify_header(label ~ "**Characteristic**") |> 
  bold_labels()
Characteristic Overall, N = 3,5771 Argentina, N = 3821 Chile, N = 7481 India, N = 3601 Italy, N = 3221 Latvia, N = 1001 Serbia, N = 8451 Spain, N = 7081 Switzerland, N = 1121 p-value2
Age group <0.001
    25-34 1,098 (31%) 86 (23%) 280 (37%) 318 (88%) 51 (16%) 27 (27%) 183 (22%) 130 (18%) 23 (21%)
    35-44 981 (27%) 82 (21%) 279 (37%) 28 (7.8%) 47 (15%) 33 (33%) 268 (32%) 208 (29%) 36 (32%)
    45-54 749 (21%) 122 (32%) 106 (14%) 13 (3.6%) 56 (17%) 23 (23%) 232 (27%) 178 (25%) 19 (17%)
    55-64 526 (15%) 73 (19%) 58 (7.8%) 1 (0.3%) 105 (33%) 16 (16%) 128 (15%) 118 (17%) 27 (24%)
    65-74 201 (5.6%) 15 (3.9%) 24 (3.2%) 0 (0%) 58 (18%) 1 (1.0%) 31 (3.7%) 67 (9.5%) 5 (4.5%)
    75+ 22 (0.6%) 4 (1.0%) 1 (0.1%) 0 (0%) 5 (1.6%) 0 (0%) 3 (0.4%) 7 (1.0%) 2 (1.8%)
Gender <0.001
    Male 1,118 (31%) 77 (20%) 237 (32%) 108 (30%) 212 (66%) 5 (5.0%) 196 (23%) 236 (33%) 47 (43%)
    Female 2,457 (69%) 305 (80%) 511 (68%) 252 (70%) 110 (34%) 95 (95%) 649 (77%) 472 (67%) 63 (57%)
    Missing 2 0 0 0 0 0 0 0 2
Dental specialty <0.001
    General Dentistry 1,825 (51%) 172 (45%) 438 (59%) 147 (41%) 128 (40%) 82 (82%) 480 (57%) 350 (49%) 28 (25%)
    Pediatric Dentistry 518 (14%) 98 (26%) 140 (19%) 19 (5.3%) 13 (4.0%) 11 (11%) 152 (18%) 64 (9.0%) 21 (19%)
    Orthodontics 240 (6.7%) 32 (8.4%) 29 (3.9%) 9 (2.5%) 36 (11%) 1 (1.0%) 44 (5.2%) 75 (11%) 14 (12%)
    Oral Surgery 262 (7.3%) 21 (5.5%) 25 (3.3%) 22 (6.1%) 39 (12%) 0 (0%) 55 (6.5%) 96 (14%) 4 (3.6%)
    Restorative/Endo 414 (12%) 42 (11%) 34 (4.5%) 146 (41%) 45 (14%) 2 (2.0%) 40 (4.7%) 79 (11%) 26 (23%)
    Prosthodontics 246 (6.9%) 12 (3.1%) 78 (10%) 9 (2.5%) 41 (13%) 2 (2.0%) 54 (6.4%) 38 (5.4%) 12 (11%)
    Periodontics 72 (2.0%) 5 (1.3%) 4 (0.5%) 8 (2.2%) 20 (6.2%) 2 (2.0%) 20 (2.4%) 6 (0.8%) 7 (6.2%)
1 n (%)
2 Pearson's Chi-squared test

3.4 Kruskal-Wallis: knowledge by country

Code
# Non-parametric comparison given ordinal nature of the score
kruskal.test(score_knowledge ~ country, data = df)

    Kruskal-Wallis rank sum test

data:  score_knowledge by country
Kruskal-Wallis chi-squared = 884.47, df = 7, p-value < 2.2e-16

3.5 Kruskal-Wallis: knowledge by specialty

Code
# Compare knowledge scores accross specialties
kruskal.test(score_knowledge ~ specialty_label, data = df)

    Kruskal-Wallis rank sum test

data:  score_knowledge by specialty_label
Kruskal-Wallis chi-squared = 176.89, df = 6, p-value < 2.2e-16

3.6 Knowledge by gender

Code
# Knowledge score comparison by gender
df |> 
  filter(!is.na(gender_label)) |> 
  select(gender_label, score_knowledge) |> 
  tbl_summary(
    by = gender_label,
    label = list(score_knowledge ~ "Knowledge score (0-7)"),
    statistic = list(all_continuous() ~ "{mean} ({sd}) | {median} [{p25}, {p75}]"),
    missing = "no"
  ) |> 
  bold_labels()
Characteristic Male, N = 1,1181 Female, N = 2,4571
Knowledge score (0-7)
    0 5 (0.4%) 2 (<0.1%)
    1 12 (1.1%) 17 (0.7%)
    2 36 (3.2%) 47 (1.9%)
    3 115 (10%) 186 (7.6%)
    4 213 (19%) 409 (17%)
    5 286 (26%) 624 (25%)
    6 263 (24%) 695 (28%)
    7 188 (17%) 477 (19%)
1 n (%)

4 Attitudes

Likert scale: 1 = Strongly agree, 2 = Agree, 3 = Disagree, 4 = Strongly disagree.

4.1 Attitude items by country

Code
# Attitude items as ordered factors, stratified by country
df |> 
  select(country, q8_smoking, q9_paed_prev, q10_equipped) |> 
  mutate(across(q8_smoking:q10_equipped,
                ~ factor(.x, levels = 1:4,
                         labels = c("Strongly agree", "Agree",
                                    "Disagree", "Strongly disagree")))) |> 
  tbl_summary(
    by = country,
    label = list(
      q8_smoking   ~ "Preventive activities: smoking cessation",
      q9_paed_prev ~ "Preventive activities: paediatric dentistry",
      q10_equipped ~ "Well equipped for preventive care"
    ),
    missing = "no"
  ) |> 
  add_overall() |> 
  add_p(test = list(all_categorical() ~ "chisq.test")) |> 
  bold_labels()
Characteristic Overall, N = 3,5771 Argentina, N = 3821 Chile, N = 7481 India, N = 3601 Italy, N = 3221 Latvia, N = 1001 Serbia, N = 8451 Spain, N = 7081 Switzerland, N = 1121 p-value2
Preventive activities: smoking cessation <0.001
    Strongly agree 1,956 (57%) 320 (84%) 579 (77%) 240 (67%) 170 (53%) 15 (15%) 24 (2.8%) 608 (86%) 0 (NA%)
    Agree 496 (14%) 0 (0%) 134 (18%) 88 (25%) 116 (36%) 29 (29%) 129 (15%) 0 (0%) 0 (NA%)
    Disagree 512 (15%) 54 (14%) 31 (4.1%) 28 (7.9%) 33 (10%) 30 (30%) 248 (29%) 88 (12%) 0 (NA%)
    Strongly disagree 495 (14%) 8 (2.1%) 4 (0.5%) 0 (0%) 3 (0.9%) 26 (26%) 444 (53%) 10 (1.4%) 0 (NA%)
Preventive activities: paediatric dentistry <0.001
    Strongly agree 2,342 (66%) 374 (98%) 704 (94%) 230 (65%) 250 (78%) 13 (13%) 3 (0.4%) 695 (99%) 73 (65%)
    Agree 264 (7.4%) 0 (0%) 38 (5.1%) 101 (29%) 61 (19%) 2 (2.0%) 32 (3.8%) 0 (0%) 30 (27%)
    Disagree 192 (5.4%) 5 (1.3%) 3 (0.4%) 23 (6.5%) 9 (2.8%) 17 (17%) 118 (14%) 9 (1.3%) 8 (7.1%)
    Strongly disagree 768 (22%) 3 (0.8%) 3 (0.4%) 0 (0%) 0 (0%) 68 (68%) 692 (82%) 1 (0.1%) 1 (0.9%)
Well equipped for preventive care <0.001
    Strongly agree 1,478 (41%) 260 (68%) 311 (42%) 181 (51%) 71 (22%) 10 (10%) 108 (13%) 505 (72%) 32 (29%)
    Agree 785 (22%) 0 (0%) 260 (35%) 96 (27%) 90 (28%) 11 (11%) 272 (32%) 0 (0%) 56 (50%)
    Disagree 935 (26%) 104 (27%) 163 (22%) 78 (22%) 131 (41%) 29 (29%) 234 (28%) 173 (25%) 23 (21%)
    Strongly disagree 368 (10%) 18 (4.7%) 14 (1.9%) 0 (0%) 27 (8.5%) 50 (50%) 231 (27%) 27 (3.8%) 1 (0.9%)
1 n (%)
2 Pearson's Chi-squared test

5 Practice

Likert scale: 1 = Always, 2 = Often, 3 = Sometimes, 4 = Never.

5.1 Practice items by country

Code
# Practice items as ordered factors, stratified by country
df |> 
  select(country, q11_sealants:q14_diet) |> 
  mutate(across(q11_sealants:q14_diet,
                ~ factor(.x, levels = 1:4,
                         labels = c("Always", "Often",
                                    "Sometimes", "Never")))) |> 
  tbl_summary(
    by = country,
    label = list(
      q11_sealants ~ "Apply dental sealants",
      q12_varnish  ~ "Apply fluoride varnish",
      q13_ohi      ~ "Give oral hygiene instructions",
      q14_diet     ~ "Provide dietary advice"
    ),
    missing = "no"
  ) |> 
  add_overall() |> 
  add_p(test = list(all_categorical() ~ "chisq.test")) |> 
  bold_labels()
Characteristic Overall, N = 3,5771 Argentina, N = 3821 Chile, N = 7481 India, N = 3601 Italy, N = 3221 Latvia, N = 1001 Serbia, N = 8451 Spain, N = 7081 Switzerland, N = 1121 p-value2
Apply dental sealants <0.001
    Always 1,109 (31%) 133 (35%) 343 (46%) 148 (42%) 154 (48%) 23 (23%) 21 (2.5%) 257 (37%) 30 (27%)
    Often 1,090 (31%) 176 (46%) 281 (38%) 154 (44%) 121 (38%) 44 (44%) 68 (8.0%) 210 (30%) 36 (33%)
    Sometimes 667 (19%) 56 (15%) 81 (11%) 45 (13%) 32 (10%) 20 (20%) 242 (29%) 166 (24%) 25 (23%)
    Never 692 (19%) 17 (4.5%) 42 (5.6%) 7 (2.0%) 13 (4.1%) 13 (13%) 514 (61%) 67 (9.6%) 19 (17%)
Apply fluoride varnish <0.001
    Always 1,397 (39%) 156 (41%) 587 (79%) 124 (35%) 71 (22%) 25 (25%) 174 (21%) 219 (31%) 41 (37%)
    Often 1,140 (32%) 140 (37%) 139 (19%) 148 (42%) 108 (34%) 45 (45%) 270 (32%) 254 (36%) 36 (32%)
    Sometimes 699 (20%) 63 (16%) 14 (1.9%) 74 (21%) 86 (27%) 27 (27%) 240 (28%) 178 (25%) 17 (15%)
    Never 322 (9.1%) 23 (6.0%) 7 (0.9%) 8 (2.3%) 55 (17%) 3 (3.0%) 161 (19%) 48 (6.9%) 17 (15%)
Give oral hygiene instructions <0.001
    Always 2,265 (64%) 353 (92%) 608 (82%) 261 (73%) 269 (85%) 75 (75%) 1 (0.1%) 625 (89%) 73 (65%)
    Often 409 (11%) 28 (7.3%) 124 (17%) 69 (19%) 44 (14%) 25 (25%) 4 (0.5%) 78 (11%) 37 (33%)
    Sometimes 187 (5.2%) 1 (0.3%) 14 (1.9%) 21 (5.9%) 4 (1.3%) 0 (0%) 145 (17%) 1 (0.1%) 1 (0.9%)
    Never 702 (20%) 0 (0%) 0 (0%) 5 (1.4%) 1 (0.3%) 0 (0%) 695 (82%) 0 (0%) 1 (0.9%)
Provide dietary advice <0.001
    Always 1,399 (39%) 253 (67%) 254 (34%) 215 (61%) 156 (49%) 32 (32%) 21 (2.5%) 409 (58%) 59 (54%)
    Often 1,126 (32%) 95 (25%) 303 (41%) 109 (31%) 124 (39%) 54 (54%) 175 (21%) 223 (32%) 43 (39%)
    Sometimes 676 (19%) 28 (7.4%) 162 (22%) 25 (7.1%) 34 (11%) 14 (14%) 338 (40%) 68 (9.6%) 7 (6.4%)
    Never 358 (10%) 4 (1.1%) 27 (3.6%) 3 (0.9%) 6 (1.9%) 0 (0%) 311 (37%) 6 (0.8%) 1 (0.9%)
1 n (%)
2 Pearson's Chi-squared test

5.2 Practice by age group

Code
# Practice items stratified by age group
df |> 
  select(age_group, q11_sealants:q14_diet) |> 
  mutate(across(q11_sealants:q14_diet,
                ~ factor(.x, levels = 1:4,
                         labels = c("Always", "Often",
                                    "Sometimes", "Never")))) |> 
  tbl_summary(
    by = age_group,
    label = list(
      q11_sealants ~ "Apply dental sealants",
      q12_varnish  ~ "Apply fluoride varnish",
      q13_ohi      ~ "Give oral hygiene instructions",
      q14_diet     ~ "Provide dietary advice"
    ),
    missing = "no"
  ) |> 
  add_p(test = list(all_categorical() ~ "chisq.test")) |> 
  bold_labels()
Characteristic 25-34, N = 1,0981 35-44, N = 9811 45-54, N = 7491 55-64, N = 5261 65-74, N = 2011 75+, N = 221 p-value2
Apply dental sealants <0.001
    Always 370 (34%) 274 (28%) 207 (28%) 174 (33%) 77 (38%) 7 (33%)
    Often 395 (36%) 281 (29%) 196 (26%) 150 (29%) 61 (30%) 7 (33%)
    Sometimes 194 (18%) 205 (21%) 144 (19%) 87 (17%) 34 (17%) 3 (14%)
    Never 132 (12%) 220 (22%) 196 (26%) 111 (21%) 29 (14%) 4 (19%)
Apply fluoride varnish <0.001
    Always 502 (46%) 403 (41%) 263 (35%) 166 (32%) 57 (28%) 6 (27%)
    Often 342 (31%) 310 (32%) 230 (31%) 176 (34%) 72 (36%) 10 (45%)
    Sometimes 192 (18%) 179 (18%) 157 (21%) 117 (22%) 49 (24%) 5 (23%)
    Never 54 (5.0%) 88 (9.0%) 94 (13%) 63 (12%) 22 (11%) 1 (4.5%)
Give oral hygiene instructions <0.001
    Always 717 (66%) 577 (59%) 441 (59%) 356 (68%) 157 (79%) 17 (77%)
    Often 164 (15%) 128 (13%) 68 (9.1%) 37 (7.0%) 11 (5.5%) 1 (4.5%)
    Sometimes 70 (6.4%) 59 (6.0%) 36 (4.8%) 14 (2.7%) 7 (3.5%) 1 (4.5%)
    Never 140 (13%) 216 (22%) 201 (27%) 118 (22%) 24 (12%) 3 (14%)
Provide dietary advice <0.001
    Always 391 (36%) 353 (36%) 311 (42%) 232 (44%) 104 (52%) 8 (38%)
    Often 405 (37%) 315 (32%) 207 (28%) 131 (25%) 59 (30%) 9 (43%)
    Sometimes 225 (21%) 210 (21%) 116 (16%) 96 (18%) 25 (12%) 4 (19%)
    Never 67 (6.2%) 101 (10%) 111 (15%) 67 (13%) 12 (6.0%) 0 (0%)
1 n (%)
2 Pearson's Chi-squared test

6 Barriers

Likert scale: 1 = Strongly disagree, 2 = Disagree, 3 = Agree, 4 = Strongly agree.

6.1 Barrier items by country

Code
# Barrier items as ordered factors, stratified by country
# Includes overall column and chi-squared p-values
df |> 
  select(country, q15_time:q20_training) |> 
  mutate(across(q15_time:q20_training,
                ~ factor(.x, levels = 1:4,
                         labels = c("Strongly disagree", "Disagree",
                                    "Agree", "Strongly agree")))) |> 
  tbl_summary(
    by = country,
    label = list(
      q15_time         ~ "Lack of time",
      q16_remuneration ~ "Lack of remuneration",
      q17_motivation   ~ "Lack of motivation",
      q18_compliance   ~ "Poor patient compliance",
      q19_knowledge_b  ~ "Lack of knowledge",
      q20_training     ~ "Lack of training"
    ),
    missing = "no"
  ) |> 
  add_overall() |> 
  add_p(test = list(all_categorical() ~ "chisq.test")) |> 
  bold_labels()
Characteristic Overall, N = 3,5771 Argentina, N = 3821 Chile, N = 7481 India, N = 3601 Italy, N = 3221 Latvia, N = 1001 Serbia, N = 8451 Spain, N = 7081 Switzerland, N = 1121 p-value2
Lack of time <0.001
    Strongly disagree 1,443 (40%) 164 (43%) 402 (54%) 134 (38%) 29 (9.1%) 23 (23%) 267 (32%) 409 (58%) 15 (13%)
    Disagree 775 (22%) 0 (0%) 239 (32%) 104 (29%) 75 (24%) 17 (17%) 283 (33%) 0 (0%) 57 (51%)
    Agree 834 (23%) 102 (27%) 92 (12%) 118 (33%) 109 (34%) 37 (37%) 201 (24%) 151 (21%) 24 (21%)
    Strongly agree 515 (14%) 116 (30%) 14 (1.9%) 0 (0%) 106 (33%) 23 (23%) 94 (11%) 146 (21%) 16 (14%)
Lack of remuneration <0.001
    Strongly disagree 1,260 (35%) 207 (54%) 114 (15%) 112 (32%) 40 (12%) 16 (16%) 353 (42%) 407 (58%) 11 (9.8%)
    Disagree 657 (18%) 0 (0%) 192 (26%) 104 (30%) 75 (23%) 26 (26%) 233 (28%) 0 (0%) 27 (24%)
    Agree 1,028 (29%) 82 (22%) 310 (42%) 130 (38%) 109 (34%) 35 (35%) 160 (19%) 152 (22%) 50 (45%)
    Strongly agree 610 (17%) 92 (24%) 130 (17%) 0 (0%) 96 (30%) 23 (23%) 99 (12%) 146 (21%) 24 (21%)
Lack of motivation <0.001
    Strongly disagree 1,473 (41%) 197 (52%) 444 (60%) 116 (33%) 20 (6.3%) 24 (24%) 305 (36%) 364 (52%) 3 (2.7%)
    Disagree 672 (19%) 0 (0%) 209 (28%) 122 (34%) 48 (15%) 26 (26%) 232 (27%) 0 (0%) 35 (32%)
    Agree 870 (24%) 100 (26%) 78 (10%) 117 (33%) 100 (31%) 30 (30%) 209 (25%) 191 (27%) 45 (41%)
    Strongly agree 544 (15%) 84 (22%) 15 (2.0%) 0 (0%) 151 (47%) 20 (20%) 99 (12%) 148 (21%) 27 (25%)
Poor patient compliance <0.001
    Strongly disagree 1,087 (30%) 195 (51%) 214 (29%) 117 (33%) 39 (12%) 8 (8.0%) 63 (7.5%) 438 (62%) 13 (12%)
    Disagree 693 (19%) 0 (0%) 178 (24%) 137 (39%) 106 (33%) 12 (12%) 211 (25%) 0 (0%) 49 (44%)
    Agree 1,102 (31%) 118 (31%) 149 (20%) 101 (28%) 125 (39%) 32 (32%) 348 (41%) 194 (27%) 35 (31%)
    Strongly agree 685 (19%) 68 (18%) 207 (28%) 0 (0%) 50 (16%) 48 (48%) 223 (26%) 74 (10%) 15 (13%)
Lack of knowledge <0.001
    Strongly disagree 1,069 (30%) 197 (52%) 170 (23%) 115 (32%) 35 (11%) 21 (21%) 273 (32%) 252 (36%) 6 (5.4%)
    Disagree 647 (18%) 0 (0%) 134 (18%) 102 (29%) 80 (25%) 29 (29%) 261 (31%) 0 (0%) 41 (37%)
    Agree 1,039 (29%) 97 (26%) 172 (23%) 138 (39%) 117 (37%) 30 (30%) 211 (25%) 229 (32%) 45 (40%)
    Strongly agree 810 (23%) 85 (22%) 272 (36%) 0 (0%) 88 (28%) 20 (20%) 100 (12%) 225 (32%) 20 (18%)
Lack of training <0.001
    Strongly disagree 1,165 (33%) 215 (56%) 188 (25%) 114 (32%) 24 (7.5%) 23 (23%) 307 (36%) 291 (41%) 3 (2.7%)
    Disagree 719 (20%) 0 (0%) 252 (34%) 96 (27%) 55 (17%) 35 (35%) 253 (30%) 0 (0%) 28 (25%)
    Agree 957 (27%) 79 (21%) 155 (21%) 145 (41%) 110 (34%) 23 (23%) 192 (23%) 204 (29%) 49 (44%)
    Strongly agree 723 (20%) 87 (23%) 153 (20%) 0 (0%) 131 (41%) 19 (19%) 93 (11%) 209 (30%) 31 (28%)
1 n (%)
2 Pearson's Chi-squared test

6.2 Barrier items by specialty

Code
# Barriers stratified by speciaty
df |> 
  select(specialty_label, q15_time:q20_training) |> 
  mutate(across(q15_time:q20_training,
                ~ factor(.x, levels = 1:4,
                         labels = c("Strongly disagree", "Disagree",
                                    "Agree", "Strongly agree")))) |> 
  tbl_summary(
    by = specialty_label,
    label = list(
      q15_time         ~ "Lack of time",
      q16_remuneration ~ "Lack of remuneration",
      q17_motivation   ~ "Lack of motivation",
      q18_compliance   ~ "Poor patient compliance",
      q19_knowledge_b  ~ "Lack of knowledge",
      q20_training     ~ "Lack of training"
    ),
    missing = "no"
  ) |> 
  add_p(test = list(all_categorical() ~ "chisq.test")) |> 
  bold_labels()
Characteristic General Dentistry, N = 1,8251 Pediatric Dentistry, N = 5181 Orthodontics, N = 2401 Oral Surgery, N = 2621 Restorative/Endo, N = 4141 Prosthodontics, N = 2461 Periodontics, N = 721 p-value2
Lack of time <0.001
    Strongly disagree 750 (41%) 218 (42%) 100 (42%) 117 (45%) 149 (36%) 90 (37%) 19 (27%)
    Disagree 407 (22%) 93 (18%) 43 (18%) 36 (14%) 104 (25%) 67 (27%) 25 (35%)
    Agree 422 (23%) 117 (23%) 53 (22%) 67 (26%) 108 (26%) 51 (21%) 16 (23%)
    Strongly agree 242 (13%) 89 (17%) 44 (18%) 41 (16%) 52 (13%) 36 (15%) 11 (15%)
Lack of remuneration 0.2
    Strongly disagree 640 (35%) 194 (38%) 82 (34%) 109 (42%) 151 (37%) 67 (27%) 17 (24%)
    Disagree 339 (19%) 95 (18%) 37 (15%) 40 (15%) 82 (20%) 47 (19%) 17 (24%)
    Agree 528 (29%) 142 (28%) 73 (30%) 69 (26%) 113 (28%) 79 (32%) 24 (34%)
    Strongly agree 310 (17%) 83 (16%) 48 (20%) 43 (16%) 62 (15%) 51 (21%) 13 (18%)
Lack of motivation <0.001
    Strongly disagree 791 (44%) 246 (48%) 83 (35%) 98 (38%) 149 (36%) 83 (34%) 23 (32%)
    Disagree 363 (20%) 80 (16%) 33 (14%) 37 (14%) 80 (19%) 67 (28%) 12 (17%)
    Agree 419 (23%) 115 (22%) 67 (28%) 65 (25%) 128 (31%) 55 (23%) 21 (30%)
    Strongly agree 244 (13%) 73 (14%) 56 (23%) 61 (23%) 57 (14%) 38 (16%) 15 (21%)
Poor patient compliance <0.001
    Strongly disagree 554 (30%) 133 (26%) 81 (34%) 107 (41%) 138 (33%) 61 (25%) 13 (19%)
    Disagree 323 (18%) 104 (20%) 42 (18%) 43 (16%) 99 (24%) 55 (23%) 27 (39%)
    Agree 590 (32%) 155 (30%) 70 (29%) 74 (28%) 124 (30%) 73 (30%) 16 (23%)
    Strongly agree 354 (19%) 125 (24%) 47 (20%) 37 (14%) 53 (13%) 55 (23%) 14 (20%)
Lack of knowledge <0.001
    Strongly disagree 520 (29%) 179 (35%) 76 (32%) 75 (29%) 118 (29%) 84 (34%) 17 (24%)
    Disagree 349 (19%) 74 (14%) 25 (11%) 42 (16%) 90 (22%) 52 (21%) 15 (21%)
    Agree 517 (28%) 137 (26%) 79 (33%) 80 (31%) 140 (34%) 63 (26%) 23 (32%)
    Strongly agree 434 (24%) 127 (25%) 58 (24%) 64 (25%) 66 (16%) 45 (18%) 16 (23%)
Lack of training <0.001
    Strongly disagree 591 (32%) 199 (38%) 69 (29%) 89 (34%) 132 (32%) 70 (29%) 15 (21%)
    Disagree 385 (21%) 102 (20%) 32 (13%) 42 (16%) 76 (18%) 67 (27%) 15 (21%)
    Agree 475 (26%) 119 (23%) 76 (32%) 64 (25%) 132 (32%) 69 (28%) 22 (31%)
    Strongly agree 371 (20%) 97 (19%) 62 (26%) 64 (25%) 72 (17%) 38 (16%) 19 (27%)
1 n (%)
2 Pearson's Chi-squared test

7 Visualizations

7.1 Knowledge score by country

Code
# Boxplot ordered by median knowledge score
df |> 
  ggplot(aes(x = fct_reorder(country, score_knowledge, .fun = median),
             y = score_knowledge, fill = country)) +
  geom_boxplot(alpha = 0.8, show.legend = FALSE) +
  geom_jitter(alpha = 0.05, width = 0.2) +
  scale_fill_viridis_d() +
  labs(x = "Country", y = "Knowledge Score (0-7)") +
  coord_flip() +
  theme_minimal(base_size = 13) +
  theme(legend.position = "none")

ggsave(here("output", "fig1_knowledge_by_country.png"),
       width = 8, height = 5, dpi = 300)
Figure 1: Knowledge score distribution by country

7.2 Knowledge item heatmap

Code
# Heatmap of item-level performace across countries
df |> 
  select(country, q1_sugar:q7_risk) |> 
  pivot_longer(cols = q1_sugar:q7_risk,
               names_to = "item",
               values_to = "correct") |> 
  mutate(
    item = case_when(
      item == "q1_sugar"       ~ "Sugar intake",
      item == "q2_first_visit" ~ "First visit age",
      item == "q3_fluoride"    ~ "Fluoride",
      item == "q4_sealants"    ~ "Sealants",
      item == "q5_ecc"         ~ "ECC",
      item == "q6_brushing"    ~ "Brushing",
      item == "q7_risk"        ~ "Risk factors"
    )
  ) |> 
  group_by(country, item) |> 
  summarise(pct_correct = mean(correct, na.rm = TRUE) * 100,
            .groups = "drop") |> 
  ggplot(aes(x = item, y = country, fill = pct_correct)) +
  geom_tile(color = "white", linewidth = 0.5) +
  geom_text(aes(label = sprintf("%.0f%%", pct_correct)), size = 3) +
  scale_fill_viridis_c(name = "% Correct", limits = c(0, 100)) +
  labs(x = NULL, y = NULL) +
  theme_minimal(base_size = 12) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

ggsave(here("output", "fig2_knowledge_heatmap.png"),
       width = 9, height = 5, dpi = 300)
Figure 2: Correct response rate by knowledge item and country

7.3 Knowledge by specialty

Code
# Violin + boxplot combination for specialty comparisons
df |> 
  ggplot(aes(x = fct_reorder(specialty_label, score_knowledge, .fun = median),
             y = score_knowledge, fill = specialty_label)) +
  geom_violin(alpha = 0.5, show.legend = FALSE) +
  geom_boxplot(width = 0.15, alpha = 0.8, show.legend = FALSE) +
  scale_fill_viridis_d() +
  labs(x = "Specialty", y = "Knowledge Score (0-7)") +
  coord_flip() +
  theme_minimal(base_size = 13) + 
  ggtitle("Knowledge score distribution by dental specialty")

ggsave(here("output", "fig6_knowledge_by_specialty.png"),
       width = 9, height = 5, dpi = 300)
Figure 3: Knowledge score distribution by dental specialty
Code
df |> 
  ggplot(aes(x = fct_reorder(specialty_label, score_knowledge, .fun = median),
             y = score_knowledge, fill = specialty_label)) +
  geom_violin(alpha = 0.5, show.legend = FALSE) +
  geom_boxplot(width = 0.15, alpha = 0.8, show.legend = FALSE) +
  scale_fill_viridis_d() +
  labs(x = "Specialty", y = "Knowledge Score (0-7)") +
  coord_flip() +
  theme_minimal(base_size = 13) + 
  facet_grid( . ~ country) + 
  ggtitle("Knowledge score distribution by dental specialty and country")

Code
ggsave(here("output", "fig6_1_knowledge_by_specialty_by_country.png"),
       width = 9, height = 5, dpi = 300)

7.4 Barriers: stacked bar chart

Code
# Stacked bar chart for barrier Likert responses
df |> 
  select(q15_time:q20_training) |> 
  pivot_longer(cols = everything(),
               names_to = "barrier",
               values_to = "response") |> 
  filter(!is.na(response)) |> 
  mutate(
    response_label = factor(response,
                            levels = 1:4,
                            labels = c("Strongly disagree", "Disagree",
                                       "Agree", "Strongly agree")),
    barrier = case_when(
      barrier == "q15_time"         ~ "Lack of time",
      barrier == "q16_remuneration" ~ "Lack of remuneration",
      barrier == "q17_motivation"   ~ "Lack of motivation",
      barrier == "q18_compliance"   ~ "Poor compliance",
      barrier == "q19_knowledge_b"  ~ "Lack of knowledge",
      barrier == "q20_training"     ~ "Lack of training"
    )
  ) |> 
  count(barrier, response_label) |> 
  group_by(barrier) |> 
  mutate(pct = n / sum(n) * 100) |> 
  ungroup() |> 
  ggplot(aes(x = fct_rev(barrier), y = pct, fill = response_label)) +
  geom_col(position = "stack", width = 0.7) +
  scale_fill_viridis_d(name = "Response", option = "D") +
  labs(x = NULL, y = "Percentage (%)") +
  coord_flip() +
  theme_minimal(base_size = 13) + 
  theme(legend.position = "top") + 
  ggtitle("Perceived barriers to preventive dentistry (overall)")

ggsave(here("output", "fig3_barriers_stacked.png"),
       width = 9, height = 5, dpi = 300)
Figure 4: Perceived barriers to preventive dentistry (overall)

7.5 Barriers heatmap by country

Code
# Heatmap showing barrier agreement rates across countries
df |> 
  select(country, q15_time:q20_training) |> 
  pivot_longer(cols = q15_time:q20_training,
               names_to = "barrier",
               values_to = "response") |> 
  filter(!is.na(response)) |> 
  mutate(
    agree = if_else(response >= 3, 1, 0),
    barrier = case_when(
      barrier == "q15_time"         ~ "Time",
      barrier == "q16_remuneration" ~ "Remuneration",
      barrier == "q17_motivation"   ~ "Motivation",
      barrier == "q18_compliance"   ~ "Patient compliance",
      barrier == "q19_knowledge_b"  ~ "Knowledge",
      barrier == "q20_training"     ~ "Training"
    )
  ) |> 
  group_by(country, barrier) |> 
  summarise(pct_agree = mean(agree) * 100, .groups = "drop") |> 
  ggplot(aes(x = barrier, y = country, fill = pct_agree)) +
  geom_tile(color = "white", linewidth = 0.5) +
  geom_text(aes(label = sprintf("%.0f%%", pct_agree)), size = 3) +
  scale_fill_viridis_c(name = "% Agree", option = "D") +
  labs(x = NULL, y = NULL) +
  theme_minimal(base_size = 12) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) + 
  ggtitle("Perceived barriers by country (% Agree or Strongly Agree)")

ggsave(here("output", "fig5_barriers_heatmap.png"),
       width = 9, height = 5, dpi = 300)
Figure 5: Perceived barriers by country (% Agree or Strongly Agree)

7.6 Practice frequency by country

Code
# Grouped bar chart for practice items across countries
df |> 
  select(country, q11_sealants:q14_diet) |> 
  pivot_longer(cols = q11_sealants:q14_diet,
               names_to = "practice",
               values_to = "response") |> 
  filter(!is.na(response)) |> 
  mutate(
    frequent = if_else(response <= 2, 1, 0),
    practice = case_when(
      practice == "q11_sealants" ~ "Sealants",
      practice == "q12_varnish"  ~ "Fluoride varnish",
      practice == "q13_ohi"      ~ "OHI",
      practice == "q14_diet"     ~ "Dietary advice"
    )
  ) |> 
  group_by(country, practice) |> 
  summarise(pct = mean(frequent) * 100, .groups = "drop") |> 
  ggplot(aes(x = country, y = pct, fill = practice)) +
  geom_col(position = position_dodge(width = 0.8), width = 0.7) +
  scale_fill_viridis_d(name = "Practice", option = "D") +
  labs(x = NULL, y = "% Always or Often") +
  theme_minimal(base_size = 12) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  coord_flip() + 
  ggtitle("Preventive practice frequency by country (% Always or Often)")

ggsave(here("output", "fig4_practice_by_country.png"),
       width = 10, height = 5, dpi = 300)
Figure 6: Preventive practice frequency by country (% Always or Often)

7.7 Overall ranking of preventive practices

Code
# Proportion reporting Always/Often for each practice item, overall
df |> 
  select(q11_sealants:q14_diet) |> 
  pivot_longer(cols = everything(),
               names_to = "practice",
               values_to = "response") |> 
  filter(!is.na(response)) |> 
  mutate(
    frequent = if_else(response <= 2, 1, 0),
    practice = case_when(
      practice == "q11_sealants" ~ "Sealants",
      practice == "q12_varnish"  ~ "Fluoride varnish",
      practice == "q13_ohi"      ~ "Oral hygiene instructions",
      practice == "q14_diet"     ~ "Dietary advice"
    )
  ) |> 
  group_by(practice) |> 
  summarise(pct = mean(frequent) * 100, .groups = "drop") |> 
  ggplot(aes(x = fct_reorder(practice, pct), y = pct, fill = pct)) +
  geom_col(width = 0.6) +
  geom_text(aes(label = sprintf("%.1f%%", pct)), hjust = -0.1, size = 4) +
  scale_fill_viridis_c(option = "D", guide = "none") +
  labs(x = NULL, y = "% Always or Often") +
  coord_flip(ylim = c(0, 100)) +
  theme_minimal(base_size = 13) + 
  ggtitle("Overall ranking of preventive practices (% Always or Often)")

ggsave(here("output", "fig7_practice_ranking_overall.png"),
       width = 8, height = 4, dpi = 300)
Figure 7: Overall ranking of preventive practices (% Always or Often)

7.8 Overall ranking of preventive practices by country

Code
# Practice ranking faceted by country
df |> 
  select(country, q11_sealants:q14_diet) |> 
  pivot_longer(cols = q11_sealants:q14_diet,
               names_to = "practice",
               values_to = "response") |> 
  filter(!is.na(response)) |> 
  mutate(
    frequent = if_else(response <= 2, 1, 0),
    practice = case_when(
      practice == "q11_sealants" ~ "Sealants",
      practice == "q12_varnish"  ~ "Fluoride varnish",
      practice == "q13_ohi"      ~ "OHI",
      practice == "q14_diet"     ~ "Dietary advice"
    )
  ) |> 
  group_by(country, practice) |> 
  summarise(pct = mean(frequent) * 100, .groups = "drop") |> 
  ggplot(aes(x = fct_reorder(practice, pct), y = pct, fill = practice)) +
  geom_col(width = 0.7) +
  geom_text(aes(label = sprintf("%.0f%%", pct)), hjust = -0.1, size = 2.5) +
  scale_fill_viridis_d(option = "C", guide = "none") +
  labs(x = NULL, y = "% Always or Often") +
  coord_flip(ylim = c(0, 105)) +
  facet_wrap(~ country, ncol = 4) +
  theme_minimal(base_size = 11) +
  theme(strip.text = element_text(face = "bold")) + 
  ggtitle("Ranking of preventive practices by country (% Always or Often)")

ggsave(here("output", "fig8_practice_ranking_by_country.png"),
       width = 12, height = 6, dpi = 300)
Figure 8: Ranking of preventive practices by country (% Always or Often)

7.9 Distribution of preventive practice score

Code
# Composite score: sum of all 4 practice items (range 4-16)
# Lower score = check"more frequent preventive practice?
df |> 
  mutate(
    practice_score = q11_sealants + q12_varnish + q13_ohi + q14_diet
  ) |> 
  filter(!is.na(practice_score)) |> 
  ggplot(aes(x = practice_score)) +
  geom_histogram(binwidth = 1, color = "white", linewidth = 0.3) +
  geom_vline(aes(xintercept = mean(practice_score, na.rm = TRUE)),
             linetype = "dashed", color = "red", linewidth = 0.8) +
  # scale_fill_viridis_c(option = "D", guide = "none") +
  scale_x_continuous(breaks = 0:16) +
  labs(
    title = "Distribution of composite preventive practice score", 
    x = "Preventive Practice Score (0 = best, 16 = worst)",
    y = "Frequency"
  ) +
  annotate("text", x = 14, y = Inf, vjust = 2, hjust = 1, size = 3.5,
           label = "Dashed line = mean") +
  theme_minimal(base_size = 13)

ggsave(here("output", "fig9_practice_score_histogram.png"),
       width = 8, height = 5, dpi = 300)
Figure 9: Distribution of composite preventive practice score

8 Summary table

Code
# Recode practice and barrier items into binary for a sake of clarity
df |> 
  transmute(
    country,
    score_knowledge,
    # Practice: binary always/often
    sealants_frequent = if_else(q11_sealants <= 2, 1L, 0L),
    varnish_frequent  = if_else(q12_varnish <= 2, 1L, 0L),
    ohi_frequent      = if_else(q13_ohi <= 2, 1L, 0L),
    diet_frequent     = if_else(q14_diet <= 2, 1L, 0L),
    # Barriers: binary agree/strongly agree
    barrier_time         = if_else(q15_time >= 3, 1L, 0L),
    barrier_remuneration = if_else(q16_remuneration >= 3, 1L, 0L),
    barrier_motivation   = if_else(q17_motivation >= 3, 1L, 0L),
    barrier_compliance   = if_else(q18_compliance >= 3, 1L, 0L),
    barrier_knowledge    = if_else(q19_knowledge_b >= 3, 1L, 0L),
    barrier_training     = if_else(q20_training >= 3, 1L, 0L)
  ) |> 
  tbl_summary(
    by = country,
    label = list(
      score_knowledge      ~ "Knowledge score (0-7)",
      sealants_frequent    ~ "Sealants (Always/Often)",
      varnish_frequent     ~ "Fluoride varnish (Always/Often)",
      ohi_frequent         ~ "OHI (Always/Often)",
      diet_frequent        ~ "Dietary advice (Always/Often)",
      barrier_time         ~ "Barrier: lack of time",
      barrier_remuneration ~ "Barrier: lack of remuneration",
      barrier_motivation   ~ "Barrier: lack of motivation",
      barrier_compliance   ~ "Barrier: poor compliance",
      barrier_knowledge    ~ "Barrier: lack of knowledge",
      barrier_training     ~ "Barrier: lack of training"
    ),
    statistic = list(
      all_continuous()  ~ "{mean} ({sd})",
      all_categorical() ~ "{p}%"
    ),
    missing = "no"
  ) |> 
  add_overall() |> 
  bold_labels()
Characteristic Overall, N = 3,5771 Argentina, N = 3821 Chile, N = 7481 India, N = 3601 Italy, N = 3221 Latvia, N = 1001 Serbia, N = 8451 Spain, N = 7081 Switzerland, N = 1121
Knowledge score (0-7)
    0 0.2% 0% 0% 0.8% 0% 0% 0% 0.4% 0.9%
    1 0.8% 0.3% 0% 5.3% 0.3% 1.0% 0.4% 0.1% 2.7%
    2 2.3% 4.2% 0.1% 4.4% 7.1% 4.0% 0.9% 2.0% 0.9%
    3 8.4% 7.9% 0.5% 14% 23% 6.0% 7.6% 7.8% 14%
    4 17% 21% 2.1% 14% 32% 19% 23% 19% 21%
    5 25% 32% 12% 26% 26% 43% 27% 31% 24%
    6 27% 26% 34% 25% 11% 27% 28% 27% 21%
    7 19% 8.4% 51% 11% 0% 0% 12% 13% 15%
Sealants (Always/Often) 62% 81% 84% 85% 86% 67% 11% 67% 60%
Fluoride varnish (Always/Often) 71% 77% 97% 77% 56% 70% 53% 68% 69%
OHI (Always/Often) 75% 100% 98% 93% 98% 100% 0.6% 100% 98%
Dietary advice (Always/Often) 71% 92% 75% 92% 88% 86% 23% 90% 93%
Barrier: lack of time 38% 57% 14% 33% 67% 60% 35% 42% 36%
Barrier: lack of remuneration 46% 46% 59% 38% 64% 58% 31% 42% 66%
Barrier: lack of motivation 40% 48% 12% 33% 79% 50% 36% 48% 65%
Barrier: poor compliance 50% 49% 48% 28% 55% 80% 68% 38% 45%
Barrier: lack of knowledge 52% 48% 59% 39% 64% 50% 37% 64% 58%
Barrier: lack of training 47% 44% 41% 41% 75% 42% 34% 59% 72%
1 %