Staged bilateral hip scopes

Author

Kingery MT

Published

April 30, 2023

Explore data

Code
# Explore vars
df %>% 
  group_by(staged_over_1year) %>% 
  skimr::skim(revision_scope_sum,
              tha_sum,
              any_reop_sum) 
Data summary
Name Piped data
Number of rows 152
Number of columns 54
_______________________
Column type frequency:
logical 3
________________________
Group variables staged_over_1year

Variable type: logical

skim_variable staged_over_1year n_missing complete_rate mean count
revision_scope_sum FALSE 0 1 0.07 FAL: 80, TRU: 6
revision_scope_sum TRUE 0 1 0.12 FAL: 58, TRU: 8
tha_sum FALSE 0 1 0.05 FAL: 82, TRU: 4
tha_sum TRUE 0 1 0.03 FAL: 64, TRU: 2
any_reop_sum FALSE 0 1 0.12 FAL: 76, TRU: 10
any_reop_sum TRUE 0 1 0.15 FAL: 56, TRU: 10
Code
# Explore vars
df %>% 
  skimr::skim(age_surgery_1,
              age_surgery_2,
              sex,
              bmi,
              alpha_AP,
              alpha_frog,
              alpha_90,
              lcea,
              posterior_wall,
              crossover,
              ischial)
Data summary
Name Piped data
Number of rows 152
Number of columns 54
_______________________
Column type frequency:
factor 4
numeric 7
________________________
Group variables None

Variable type: factor

skim_variable n_missing complete_rate ordered n_unique top_counts
sex 0 1.00 FALSE 2 fem: 90, mal: 62
posterior_wall 35 0.77 FALSE 2 0: 93, 1: 24
crossover 36 0.76 FALSE 2 0: 67, 1: 49
ischial 36 0.76 FALSE 2 0: 60, 1: 56

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
age_surgery_1 0 1.00 36.06 13.02 15.49 26.74 34.34 45.07 70.02 ▅▇▃▂▁
age_surgery_2 0 1.00 37.65 13.25 15.62 28.36 36.29 45.57 71.23 ▅▇▆▃▂
bmi 0 1.00 25.66 4.63 18.00 22.58 24.50 28.22 39.15 ▃▇▃▁▁
alpha_AP 35 0.77 65.24 15.62 37.80 52.20 68.20 76.60 116.90 ▆▅▇▁▁
alpha_frog 36 0.76 56.59 10.21 37.10 48.98 55.36 64.28 81.80 ▅▇▆▅▂
alpha_90 40 0.74 47.79 12.77 30.50 39.65 43.95 54.32 118.90 ▇▃▁▁▁
lcea 35 0.77 34.73 6.33 19.80 30.30 34.06 38.30 53.80 ▂▇▇▃▁
Code
# Visualize data
df %>% ggplot(aes(x = time_between_cases,
                  y = MHHS_2yr, 
                  group = case_id,
                  col = case_id)) + 
  geom_point(size = 3) + 
  geom_smooth(method = lm, 
              se = TRUE,
              linewidth = 1.2) +
  scale_color_viridis_d(option = 'plasma',
                        end = .7)

Results

Demographics

Code
n_patients <- df %>% distinct(id) %>% nrow() %>% as.numeric()
n_hip <- df %>% nrow() %>% as.numeric()

# Demographics
demos <- df %>%
  filter(case_id == '1') %>% 
  summarise(
    across(
      c(age_surgery_1,
        age_surgery_2,
        bmi,
        time_between_cases),
      list(mean = mean,
           sd = sd,
           min = min,
           max = max),
      .names = '{.col}.{.fn}'
    )
  ) %>% 
  pivot_longer(everything()) %>% 
  mutate(
    value = round(value, digits = 1)
  ) %>% 
  separate_wider_delim(
    cols = name,
    delim = '.',
    names = c('var', 'stat')
  ) %>% 
  pivot_wider(
    names_from = stat,
    values_from = value
  ) %>% 
  as.data.frame() %>% 
  column_to_rownames(var = 'var')

# Followup
demos <- df %>% 
  mutate(fu_fix = case_when(
    followup < 1.7 & !is.na(MHHS_2yr) ~ 2,
    TRUE ~ followup
  )) %>% 
  filter(case_id == '1') %>%
  filter(followup >= 1.7) %>% 
  summarise(
    across(c(fu_fix),
           list(mean = mean,
                sd = sd),
           .names = '{.col}.{.fn}')
  ) %>% 
  pivot_longer(everything()) %>% 
  mutate(
    value = round(value, digits = 1)
  ) %>% 
  separate_wider_delim(
    cols = name,
    delim = '.',
    names = c('var', 'stat')
  ) %>% 
  pivot_wider(
    names_from = stat,
    values_from = value
  ) %>% 
  as.data.frame() %>% 
  column_to_rownames(var = 'var') %>% 
  bind_rows(demos)

A total of 76 patients (152 hips) underwent staged bilateral primary hip arthroscopies with a minimum of two years postoperative followup and were included in this study. The mean age at the time of the first surgery was 36.1 +/- 13.1 years and the mean age at the time of the second surgery was 37.7 +/- 13.3 years. There was a mean duration of 1.6 +/- 1.8 years between the staged procedures (range 0.1 to 7.9 years). The mean followup for the cohort was 4.4 +/- 2.3 years.

Code
t1 <- df %>%
  filter(case_id == '1') %>% 
  select(
    age_surgery_1,
    age_surgery_2,
    sex,
    bmi,
    time_between_cases,
    staged_over_1year
  ) %>%
  mutate(
    sex = recode_factor(sex,
                        'male' = 'Male',
                        'female' = 'Female')
  ) %>% 
  mutate(
    staged_over_1year = recode_factor(as.factor(staged_over_1year),
                                       'FALSE' = '< 1 year',
                                       'TRUE' = '> 1 year')
  ) %>% 
  tbl_summary(
    by = staged_over_1year,
    missing = 'no',
    statistic = list(
      all_continuous() ~ '{mean} +/- {sd}'
      ),
    digits = list(
      all_categorical() ~ c(0,1),
      all_continuous() ~ c(1,1)),
    label = list(
      age_surgery_1 ~ 'Age at first stage (years)',
      age_surgery_2 ~ 'Age at second stage (years)',
      sex ~ 'Sex',
      bmi ~ 'BMI',
      time_between_cases ~ 'Duration between stages (years)'
      )
    ) %>%
  add_p(test = list(
    all_continuous() ~ 't.test'
    ),
    pvalue_fun = function(x) style_pvalue(x, digits = 3),
    test.args = all_tests('fisher.test') ~ list(simulate.p.value = TRUE)) %>%
  bold_p(t = 0.05) %>%
  add_overall() %>% 
  modify_spanning_header(all_stat_cols(stat_0 = F) ~ "**Duration between stages**") %>%
  modify_caption("<div style='text-align: left; font-weight: bold'>
                 Patient Demographics") %>%
  bold_labels() %>%
  as_gt() %>%
  gt::tab_options(
    table.font.size = '12px',
    data_row.padding = gt::px(3)
    ) 

t1
Table 1: Demographics for patients who underwent staged bilateral hip arthroscopies.
Characteristic Overall, N = 761 Duration between stages p-value2
< 1 year, N = 431 > 1 year, N = 331
Age at first stage (years) 36.1 +/- 13.1 33.8 +/- 12.8 39.0 +/- 13.0 0.092
Age at second stage (years) 37.7 +/- 13.3 34.3 +/- 12.9 42.1 +/- 12.7 0.011
Sex 0.828
    Male 31 (40.8%) 18 (41.9%) 13 (39.4%)
    Female 45 (59.2%) 25 (58.1%) 20 (60.6%)
BMI 25.5 +/- 4.6 25.6 +/- 4.8 25.4 +/- 4.5 0.867
Duration between stages (years) 1.6 +/- 1.8 0.4 +/- 0.3 3.1 +/- 1.9 <0.001
1 Mean +/- SD; n (%)
2 Welch Two Sample t-test; Pearson’s Chi-squared test

Radiographic parameters

Code
t2 <- df %>%
  select(
    alpha_AP,
    alpha_frog,
    alpha_90,
    lcea,
    posterior_wall,
    crossover,
    ischial
  ) %>%
  mutate(
    posterior_wall = recode_factor(posterior_wall,
                                   '0' = 'FALSE',
                                   '1' = 'TRUE'),
    crossover = recode_factor(crossover,
                                   '0' = 'FALSE',
                                   '1' = 'TRUE'),
    ischial = recode_factor(ischial,
                                   '0' = 'FALSE',
                                   '1' = 'TRUE')
  ) %>% 
  mutate(
    across(
      where(is.factor),
      as.logical
    )
  ) %>% 
  tbl_summary(
    missing = 'no',
    statistic = list(
      all_continuous() ~ '{mean} +/- {sd}'
      ),
    digits = list(
      all_categorical() ~ c(0,1),
      all_continuous() ~ c(1,1)),
    label = list(
      alpha_AP ~ 'Alpha angle (AP view)',
      alpha_frog ~ 'Alpha angle (frog lateral view)',
      alpha_90 ~ 'Alpha angle (Dunn view)',
      lcea ~ 'Lateral center edge angle',
      posterior_wall ~ 'Posterior wall sign',
      crossover ~ 'Crossover sign',
      ischial ~ 'Ischial spine sign'
      )
    ) %>% 
  modify_caption("<div style='text-align: left; font-weight: bold'>
                 Radiographic parameters") %>%
  bold_labels() %>%
  as_gt() %>%
  gt::tab_options(
    table.font.size = '12px',
    data_row.padding = gt::px(3)
    ) 

t2
Table 2: Radiographic parameters
Characteristic N = 1521
Alpha angle (AP view) 65.2 +/- 15.6
Alpha angle (frog lateral view) 56.6 +/- 10.2
Alpha angle (Dunn view) 47.8 +/- 12.8
Lateral center edge angle 34.7 +/- 6.3
Posterior wall sign 24 (20.5%)
Crossover sign 49 (42.2%)
Ischial spine sign 56 (48.3%)
1 Mean +/- SD; n (%)

Outcomes

There was no difference in the rate of revision arthroscopy, conversion to THA, or overall failure of primary arthroscopy between patients who underwent staged bilateral hip arthroscopies less than 1 year apart and those who underwent staged surgeries greater than 1 year apart.

Code
var_names <- df %>% 
  select(all_of(starts_with('mhhs_', ignore.case = FALSE)),
         all_of(starts_with('nahs_', ignore.case = FALSE))) %>% 
  colnames()

t3 <- df %>%
  filter(case_id == '1') %>% 
  select(
    revision_scope_sum,
    tha_sum,
    any_reop_sum,
    mhhs_baseline_side1,
    mhhs_2yr_side1,
    nahs_baseline_side1,
    nahs_2yr_side1,
    mhhs_baseline_side2,
    mhhs_2yr_side2,
    nahs_baseline_side2,
    nahs_2yr_side2,
    staged_over_1year
  ) %>%
  mutate(
    staged_over_1year = recode_factor(as.factor(staged_over_1year),
                                       'FALSE' = '< 1 year',
                                       'TRUE' = '> 1 year')
  ) %>% 
  tbl_summary(
    by = staged_over_1year,
    missing = 'no',
    statistic = list(
      all_continuous() ~ '{mean} +/- {sd}'
      ),
    digits = list(
      all_categorical() ~ c(0,1),
      all_continuous() ~ c(1,1)),
    label = list(
      revision_scope_sum ~ 'Revision arthroscopy',
      tha_sum ~ 'Conversion to THA',
      any_reop_sum ~ 'Failure (any reoperation)',
      mhhs_baseline_side1 ~ 'mHHS - Baseline',
      mhhs_2yr_side1 ~ 'mHHS - 2 years',
      nahs_baseline_side1 ~ 'NAHS - Baseline',
      nahs_2yr_side1 ~ 'NAHS - 2 years',
      mhhs_baseline_side2 ~ 'mHHS - Baseline',
      mhhs_2yr_side2 ~ 'mHHS - 2 years',
      nahs_baseline_side2 ~ 'NAHS - Baseline',
      nahs_2yr_side2 ~ 'NAHS - 2 years'
      )
    ) %>%
  add_p(test = list(
    all_continuous() ~ 't.test'
    ),
    pvalue_fun = function(x) style_pvalue(x, digits = 3),
    test.args = all_tests('fisher.test') ~ list(simulate.p.value = TRUE)) %>%
  bold_p(t = 0.05) %>%
  add_overall() %>% 
  modify_spanning_header(all_stat_cols(stat_0 = F) ~ "**Duration between stages**") %>%
  modify_caption("<div style='text-align: left; font-weight: bold'>
                 Outcomes") %>%
  bold_labels() %>%
  as_gt() %>%
  tab_options(
    table.font.size = '12px',
    data_row.padding = gt::px(3)
    ) %>% 
  tab_row_group(label = 'Second hip',
                    rows = 8:11) %>% 
  tab_style(
    style = cell_text(weight = 'normal'),
    locations = cells_body(
      columns = label,
      rows = 8:11
    )
  ) %>% 
  tab_style(
    style = cell_text(indent = px(10)),
    locations = cells_body(
      columns = label,
      rows = 8:11
    )
  ) %>% 
  tab_row_group(label = 'First hip',
                    rows = 4:7) %>% 
  tab_style(
    style = cell_text(weight = 'normal'),
    locations = cells_body(
      columns = label,
      rows = 4:7
    )
  ) %>% 
  tab_style(
    style = cell_text(indent = px(10)),
    locations = cells_body(
      columns = label,
      rows = 4:7
    )
  ) %>% 
  tab_row_group(label = 'Failure of primary arthroscopy',
                    rows = 1:3) %>% 
  tab_style(
    style = cell_text(weight = 'normal'),
    locations = cells_body(
      columns = label,
      rows = 1:3
    )
  ) %>% 
  tab_style(
    style = cell_text(indent = px(10)),
    locations = cells_body(
      columns = label,
      rows = 1:3
    )
  )
  
t3
Table 3: Outcomes
Characteristic Overall, N = 761 Duration between stages p-value2
< 1 year, N = 431 > 1 year, N = 331
Failure of primary arthroscopy
Revision arthroscopy 7 (9.2%) 3 (7.0%) 4 (12.1%) 0.460
Conversion to THA 3 (3.9%) 2 (4.7%) 1 (3.0%) >0.999
Failure (any reoperation) 10 (13.2%) 5 (11.6%) 5 (15.2%) 0.739
First hip
mHHS - Baseline 53.6 +/- 16.0 56.5 +/- 14.7 50.1 +/- 17.1 0.107
mHHS - 2 years 83.8 +/- 15.6 85.2 +/- 17.9 82.5 +/- 13.3 0.588
NAHS - Baseline 52.3 +/- 15.7 55.8 +/- 14.3 48.2 +/- 16.4 0.048
NAHS - 2 years 84.3 +/- 16.0 85.3 +/- 19.1 83.2 +/- 12.4 0.660
Second hip
mHHS - Baseline 53.7 +/- 14.8 57.7 +/- 13.5 48.5 +/- 15.1 0.010
mHHS - 2 years 82.9 +/- 18.1 84.7 +/- 19.3 79.3 +/- 16.0 0.514
NAHS - Baseline 52.8 +/- 16.7 56.1 +/- 15.9 48.7 +/- 17.1 0.064
NAHS - 2 years 85.9 +/- 19.0 86.5 +/- 22.8 84.9 +/- 12.2 0.823
1 n (%); Mean +/- SD
2 Fisher’s exact test; Welch Two Sample t-test

Based on logistic regression, the duration between hip arthroscopy stages does not have an effect on the risk of failure when controlling for age, sex, BMI, preoperative alpha angle (measured on Dunn view), and LCEA.

Code
library(lme4)

m <- glm(as.factor(any_reop) ~ time_between_cases + age + sex + bmi + alpha_90 + lcea,
           data = df,
           family = binomial)
# summary(m)
# exp(coef(m))

rt1 <- m %>% 
  tbl_regression(exponentiate = TRUE,
                 label = list(
                   time_between_cases ~ 'Time between stages (years)',
                   age ~ 'Age',
                   sex ~ 'Sex',
                   bmi ~ 'BMI',
                   alpha_90 ~ 'Alpha angle (Dunn view)',
                   lcea ~ 'LCEA'
                   ),
                 pvalue_fun = function(x) style_pvalue(x, digits = 3)) %>% 
  bold_p(t = 0.05) %>%
  modify_caption("<div style='text-align: left; font-weight: bold'>
                 Odds of failure of primary hip arthroscopy") %>%
  modify_table_body(
    ~.x %>% 
      mutate(label = case_when(
        label == 'male' ~ 'Male',
        label == 'female' ~ 'Female',
        TRUE ~ label
      ))
  ) %>% 
  as_gt() %>%
  gt::tab_options(
    table.font.size = 'small',
    data_row.padding = gt::px(3)
  )

rt1
Table 4: Logistic regression model evaluating the odds of failure of primary hip arthroscopy based on the duration between staged bilateral surgeries.
Characteristic OR1 95% CI1 p-value
Time between stages (years) 1.29 0.83, 1.94 0.227
Age 1.10 1.03, 1.20 0.010
Sex
    Female
    Male 1.04 0.17, 5.72 0.964
BMI 1.02 0.82, 1.26 0.838
Alpha angle (Dunn view) 1.06 0.99, 1.14 0.072
LCEA 1.06 0.91, 1.22 0.458
1 OR = Odds Ratio, CI = Confidence Interval