1. Converting table format

a. From wide to long

## # A tibble: 6 × 7
##   `Record id` Lab_a_t0 Lab_a_t1 Lab_a_t2 Lab_b_t0 Lab_b_t1 Lab_b_t2
##         <dbl>    <dbl>    <dbl>    <dbl>    <dbl>    <dbl>    <dbl>
## 1           1        2        5        4      100      400      500
## 2           2        6        5        4      200      400      100
## 3           3        8       29       NA      150      900       NA
## 4           4        1        4        4       NA      500      700
## 5           5        3        7        3      160      550      150
## 6           6       10       48       67      420     1236     2474
dat.format.wide %>%
  pivot_longer(
    cols = Lab_a_t0:Lab_b_t2,
    names_to = c("lab_n","time"), #names of new columns
    names_pattern = "Lab_(.*)_(.*)", #using regular expression pattern in () to match the 2 new columns
    values_to = "Value", #new column name for value
    values_transform  = list(Value = as.numeric) #transform the values to numeric
) 
## # A tibble: 120 × 4
##    `Record id` lab_n time  Value
##          <dbl> <chr> <chr> <dbl>
##  1           1 a     t0        2
##  2           1 a     t1        5
##  3           1 a     t2        4
##  4           1 b     t0      100
##  5           1 b     t1      400
##  6           1 b     t2      500
##  7           2 a     t0        6
##  8           2 a     t1        5
##  9           2 a     t2        4
## 10           2 b     t0      200
## # ℹ 110 more rows

.* from regular expression matches any number of character (except newline).

Other arguments like values_drop_na = , names_prefix() could be added to pivot_longer to further clean the value and column names.


b. From long to wide

## # A tibble: 10 × 2
##    case_number specialty        
##          <dbl> <chr>            
##  1      102001 Cardiology       
##  2      102001 Urology          
##  3      102002 Urology          
##  4      102002 General Surgery  
##  5      102003 Urology          
##  6      102003 <NA>             
##  7      102003 Cardiology       
##  8      102004 Vascular Medicine
##  9      102004 Ob/Gyn           
## 10      102004 <NA>
dat.format.long %>%
  group_by(case_number) %>%
  mutate(spec_index = paste0("specialty_", row_number())) %>% #create a index number
  pivot_wider(
    names_from = spec_index,
    values_from = c(specialty)
  ) 
## # A tibble: 27 × 4
## # Groups:   case_number [27]
##    case_number specialty_1              specialty_2       specialty_3           
##          <dbl> <chr>                    <chr>             <chr>                 
##  1      102001 Cardiology               Urology           <NA>                  
##  2      102002 Urology                  General Surgery   <NA>                  
##  3      102003 Urology                  <NA>              Cardiology            
##  4      102004 Vascular Medicine        Ob/Gyn            <NA>                  
##  5      102005 Urology                  Vascular Medicine Colon and Rectal Surg…
##  6      102006 Urology                  <NA>              <NA>                  
##  7      102007 Vascular Medicine        General Surgery   <NA>                  
##  8      102008 Ob/Gyn                   <NA>              <NA>                  
##  9      102009 General Surgery          Internal Medicine Urology               
## 10      102010 Colon and Rectal Surgery <NA>              <NA>                  
## # ℹ 17 more rows


2. Clean column names with regular expression

## # A tibble: 6 × 11
##   `RedCap ID` `preop_dx_regurgitation (1=1, 0=0)` preop_dx_obstruction (1=1, 0…¹
##         <dbl>                               <dbl>                          <dbl>
## 1           1                                   0                              0
## 2           2                                   1                              0
## 3           3                                   0                              0
## 4           4                                   1                              0
## 5           5                                   0                              1
## 6           6                                   0                              0
## # ℹ abbreviated name: ¹​`preop_dx_obstruction (1=1, 0=0)`
## # ℹ 8 more variables: `preop_dx_respiratory_symp (1=1, 0=0)` <dbl>,
## #   `preop_dx_chest_pain (1=1, 0=0)` <dbl>,
## #   `preop_dx_early_satiety (1=Yes, 0=No)` <dbl>,
## #   `preop_dx_anemia (1=1, 0=0)` <dbl>,
## #   `preop_dx_asymptomatic (1=Yes, 0=No)` <dbl>,
## #   `preop_upper_endoscopy (1=1, 0=0)` <dbl>, …

a. use gsub() from base

gsub (pattern, replacement, x, ignore.case = FALSE, perl = FALSE, fixed = FALSE, useBytes = FALSE)

#use string pattern to rename column names
gsub("\\s*\\(.*\\)", "", names(dat.string))
##  [1] "RedCap ID"                 "preop_dx_regurgitation"   
##  [3] "preop_dx_obstruction"      "preop_dx_respiratory_symp"
##  [5] "preop_dx_chest_pain"       "preop_dx_early_satiety"   
##  [7] "preop_dx_anemia"           "preop_dx_asymptomatic"    
##  [9] "preop_upper_endoscopy"     "preop_endo_hiatal_hernia" 
## [11] "preop_endo_barretts"

\\s represents a white space character.

\\s* matches zero or more white space characters.

\\(: Matches an open parenthesis (.

.*: Matches zero or more of any character (except newlines).

\\): Matches a close parenthesis ).

b. use str_remove() or str_replace() from stringr

colnames(dat.string) <- names(dat.string)
#str_replace(string, pattern, replacement)
#str_remove(string, pattern)

str_replace(names(dat.string), "\\s*\\(.*\\)", "")
##  [1] "RedCap ID"                 "preop_dx_regurgitation"   
##  [3] "preop_dx_obstruction"      "preop_dx_respiratory_symp"
##  [5] "preop_dx_chest_pain"       "preop_dx_early_satiety"   
##  [7] "preop_dx_anemia"           "preop_dx_asymptomatic"    
##  [9] "preop_upper_endoscopy"     "preop_endo_hiatal_hernia" 
## [11] "preop_endo_barretts"

c. clean column names directly when reading the data

3. Date format variables

## # A tibble: 10 × 6
##    patient_n date1 date2     date3       date4     date5                  
##        <dbl> <dbl> <chr>     <chr>       <chr>     <chr>                  
##  1         1 45555 <NA>      <NA>        <NA>      <NA>                   
##  2         2 45432 <NA>      <NA>        <NA>      <NA>                   
##  3        88    NA 12/1/2020 <NA>        <NA>      <NA>                   
##  4       112    NA 2/1/2011  <NA>        <NA>      <NA>                   
##  5       113    NA <NA>      10-Jan-1994 <NA>      <NA>                   
##  6       114    NA <NA>      10-Nov-2024 <NA>      <NA>                   
##  7       115    NA <NA>      <NA>        2025-2-30 <NA>                   
##  8       116    NA <NA>      <NA>        <NA>      10 Feburary 2007       
##  9       117    NA <NA>      <NA>        <NA>      2025-01-10 15:30:00 UTC
## 10       134    NA <NA>      <NA>        <NA>      2010-03-20 13:45:00 UTC

1) using as.Date() ymd()mdy() from base and lubridate package:

dat.date %>% 
  mutate(date1.tidy = as.Date(date1, origin = "1899-12-30"),
         date2.tidy = mdy(date2),
         date3.tidy = as.Date(date3, format = "%d-%b-%Y"),
         date4.tidy = as.Date(date5, format = "%d %m %Y")) %>%  #In "%d-%b-%Y", d=date of month, b=abbr. of month, Y=year
  select(patient_n, date1, date1.tidy, date2, date2.tidy, date3, date3.tidy)
## # A tibble: 10 × 7
##    patient_n date1 date1.tidy date2     date2.tidy date3       date3.tidy
##        <dbl> <dbl> <date>     <chr>     <date>     <chr>       <date>    
##  1         1 45555 2024-09-20 <NA>      NA         <NA>        NA        
##  2         2 45432 2024-05-20 <NA>      NA         <NA>        NA        
##  3        88    NA NA         12/1/2020 2020-12-01 <NA>        NA        
##  4       112    NA NA         2/1/2011  2011-02-01 <NA>        NA        
##  5       113    NA NA         <NA>      NA         10-Jan-1994 1994-01-10
##  6       114    NA NA         <NA>      NA         10-Nov-2024 2024-11-10
##  7       115    NA NA         <NA>      NA         <NA>        NA        
##  8       116    NA NA         <NA>      NA         <NA>        NA        
##  9       117    NA NA         <NA>      NA         <NA>        NA        
## 10       134    NA NA         <NA>      NA         <NA>        NA

as.Date() is used to convert between numeric to date. Starting date should be specify using origin =. The format = should match.

  • %d-%b-%Y matches 10-Jan-1994 and d=date of month, b=abbr. of month, Y=year

  • %d %m %Y matches 10 Feb 2007


mdy(), ymd(), dmy() from lubridate package transforms dates stored in character and numeric vectors to Date format.

  • ymd() needs to be used to correctly convert 2020/01/03 from character to date class.


2) using parse_date() from parsedate package

## # A tibble: 7 × 2
##   patient_n month_year_of_transplantation
##       <dbl> <chr>                        
## 1        88 12/1/2020                    
## 2       112 2/1/2011                     
## 3       113 10-Jan-1994                  
## 4       114 10-Nov-2024                  
## 5       116 10 Feb 2007                  
## 6       117 2025-01-10 15:30:00 UTC      
## 7       134 2010-03-20 13:45:00 UTC

parse_date(dates, approx = TRUE, default_tz = "UTC")

dates is a character vector but can have a wide range of formats

dat.date2 %>% 
  mutate(date.tidy = as.Date(parse_date(month_year_of_transplantation)))
## # A tibble: 7 × 3
##   patient_n month_year_of_transplantation date.tidy
##       <dbl> <chr>                         <date>   
## 1        88 12/1/2020                     NA       
## 2       112 2/1/2011                      NA       
## 3       113 10-Jan-1994                   NA       
## 4       114 10-Nov-2024                   NA       
## 5       116 10 Feb 2007                   NA       
## 6       117 2025-01-10 15:30:00 UTC       NA       
## 7       134 2010-03-20 13:45:00 UTC       NA

4. Collapse multiple columns/rows to a single column/row

a. columns:

coalesce() finds the first non-missing value at each position, given a set of vectors.

## # A tibble: 6 × 8
##   cl_pat_mrn_id drug1 drug2 drug3 drug4 drug5 drug6 drug7
##   <chr>         <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 10014905          1     1     1     1     1     0     1
## 2 10030773          0     1     0     1     0     1     1
## 3 10075530          0     0    NA    NA    NA     0    NA
## 4 10120942          1     0     0    NA     0     0    NA
## 5 10185955          0     0    NA     0     0     0     0
## 6 10189373         NA     0     1     0    NA     1    NA
dat.coalesce %>% 
  mutate(across(everything(), ~ ifelse(.==0, NA, .))) %>% 
  mutate(drug123 = coalesce(drug1, drug2, drug3, drug4, drug5, drug6, drug7, 0)) %>% 
  slice(1:5)
## # A tibble: 5 × 9
##   cl_pat_mrn_id drug1 drug2 drug3 drug4 drug5 drug6 drug7 drug123
##   <chr>         <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>   <dbl>
## 1 10014905          1     1     1     1     1    NA     1       1
## 2 10030773         NA     1    NA     1    NA     1     1       1
## 3 10075530         NA    NA    NA    NA    NA    NA    NA       0
## 4 10120942          1    NA    NA    NA    NA    NA    NA       1
## 5 10185955         NA    NA    NA    NA    NA    NA    NA       0
#Default to 0 if all are NA


b. rows:

summarize() to reduce rows within a group.

##   person_id drug1 drug2 drug3 drug4
## 1      id_1    NA     1     1     0
## 2      id_1     1     1     1    NA
## 3      id_2     1     0     0     0
## 4      id_2     0    NA    NA     0
## 5      id_3     0     1     0     0
## 6      id_3     0     1    NA     1
dat.row %>% 
  group_by(person_id) %>% 
  summarize(across(drug1:drug4, max, na.rm=TRUE))
## Warning: There was 1 warning in `summarize()`.
## ℹ In argument: `across(drug1:drug4, max, na.rm = TRUE)`.
## ℹ In group 1: `person_id = "id_1"`.
## Caused by warning:
## ! The `...` argument of `across()` is deprecated as of dplyr 1.1.0.
## Supply arguments directly to `.fns` through an anonymous function instead.
## 
##   # Previously
##   across(a:b, mean, na.rm = TRUE)
## 
##   # Now
##   across(a:b, \(x) mean(x, na.rm = TRUE))
## # A tibble: 3 × 5
##   person_id drug1 drug2 drug3 drug4
##   <chr>     <dbl> <dbl> <dbl> <dbl>
## 1 id_1          1     1     1     0
## 2 id_2          1     0     0     0
## 3 id_3          0     1     0     1

Also works for character:

##   person_id insurance         edu
## 1      id_1   Private     College
## 2      id_1   Private        <NA>
## 3      id_2    Public High School
## 4      id_2    Public High School
## 5      id_3      <NA>        <NA>
## 6      id_3    Public     College
dat.row.2 %>%
  group_by(person_id) %>%
  summarize(
    insurance = paste(unique(na.omit(insurance)), collapse = ", "),
    edu = paste(unique(na.omit(edu)), collapse = ", "),
    .groups = "drop"
  )
## # A tibble: 3 × 3
##   person_id insurance edu        
##   <chr>     <chr>     <chr>      
## 1 id_1      Private   College    
## 2 id_2      Public    High School
## 3 id_3      Public    College



5. map functions from purrr package

The map functions transform their input by applying a function to each element of a list or atomic vector and returning an object of the same length as the input.

Example 1: copy variable labels
## [1] "PAT_MRN_ID"            "Symptom Group"         "LASTNAME"             
## [4] "FIRSTNAME"             "Pre-Remission Eckardt" "Pre-Remission SDI"
## # A tibble: 6 × 43
##   pat_mrn_id symptom_group       lastname firstname pre_remission_eckardt
##        <int> <chr>               <chr>    <chr>     <chr>                
## 1          1 Persistent Symptoms Auito    Alex      6                    
## 2          2 Persistent Symptoms Bates    Ronald    <NA>                 
## 3          3 Persistent Symptoms Beery    Pamela    <NA>                 
## 4          4 Persistent Symptoms Blevins  Amber     <NA>                 
## 5          5 Persistent Symptoms Coburn   David     5                    
## 6          6 Persistent Symptoms Collette Anthony   1                    
## # ℹ 38 more variables: pre_remission_sdi <chr>,
## #   pre_remission_promis_t_score <chr>, pre_remission_promis_se <chr>,
## #   pre_remission_gerd_hrql_heartburn_total <chr>,
## #   pre_remission_gerd_hrql_regurgitation_total <chr>,
## #   pre_remission_gerd_hrql_total <chr>, pre_remission_gerd_total <chr>,
## #   pre_remission_promis_global_total_t_score_physical <chr>,
## #   pre_remission_promis_global_total_t_score_mental <chr>, …

Apply labels using map2()

#apply labels
map2(names(dat), dat.label,
     function(col_name, col_label) {
  labels(dat[[col_name]]) <<- col_label
})
  • map2 iterates over two vectors or lists (names(dat) and dat.label) in parallel.
  • It applies a function to each pair of elements (one from each input vector).


Example 2
2a) Using map to read multiple files into r

Given there’s multiple spreadsheets in the project folder, we need to read into R and combine as one.

Try use map function to read all csv. data files and combine them into 1 data frame with year as an identifier.

library(purrr)
library(readr)

ParentDir <- "/home/tuc/projects/GeneralSurgery/Sofya_Asfaw/pTQIP_racial/TQIP_PUF"
filepath <- list.files(path = ParentDir,
                       pattern = "PUF_ECODEDES.csv",
                       full.names = TRUE,
                       recursive = TRUE) #This allows to search both specified dir and all of its subdirectories
filepath
## [1] "/home/tuc/projects/GeneralSurgery/Sofya_Asfaw/pTQIP_racial/TQIP_PUF/PUF AY 2008 4/CSV/PUF_ECODEDES.csv"
## [2] "/home/tuc/projects/GeneralSurgery/Sofya_Asfaw/pTQIP_racial/TQIP_PUF/PUF AY 2009/CSV/PUF_ECODEDES.csv"  
## [3] "/home/tuc/projects/GeneralSurgery/Sofya_Asfaw/pTQIP_racial/TQIP_PUF/PUF AY 2010/CSV/PUF_ECODEDES.csv"  
## [4] "/home/tuc/projects/GeneralSurgery/Sofya_Asfaw/pTQIP_racial/TQIP_PUF/PUF AY 2011/CSV/PUF_ECODEDES.csv"  
## [5] "/home/tuc/projects/GeneralSurgery/Sofya_Asfaw/pTQIP_racial/TQIP_PUF/PUF AY 2012/CSV/PUF_ECODEDES.csv"  
## [6] "/home/tuc/projects/GeneralSurgery/Sofya_Asfaw/pTQIP_racial/TQIP_PUF/PUF AY 2013/CSV/PUF_ECODEDES.csv"  
## [7] "/home/tuc/projects/GeneralSurgery/Sofya_Asfaw/pTQIP_racial/TQIP_PUF/PUF AY 2014/CSV/PUF_ECODEDES.csv"  
## [8] "/home/tuc/projects/GeneralSurgery/Sofya_Asfaw/pTQIP_racial/TQIP_PUF/PUF AY 2015/CSV/PUF_ECODEDES.csv"  
## [9] "/home/tuc/projects/GeneralSurgery/Sofya_Asfaw/pTQIP_racial/TQIP_PUF/PUF AY 2016/CSV/PUF_ECODEDES.csv"
data.list <- map(filepath, ~ read_csv(.x, col_types = cols(.default = "c")))
data.list <- map2(data.list, c(2008:2016), ~ mutate(.x, year = .y))

combined.data <- bind_rows(data.list)

combined.data
## # A tibble: 8,019 × 6
##    ECODE ECODEDES                                 INJTYPE INTENT MECHANISM  year
##    <chr> <chr>                                    <chr>   <chr>  <chr>     <int>
##  1 <NA>  No Matching E-Code Found                 <NA>    <NA>   <NA>       2008
##  2 -1    Not Applicable BIU 1                     <NA>    <NA>   <NA>       2008
##  3 -2    Not Known/Not Recorded BIU 2             <NA>    <NA>   <NA>       2008
##  4 800.0 Railway Collision w/ Rolling Stock - Ra… Blunt   Unint… Transpor…  2008
##  5 800.1 Railway Collision w/ Rolling Stock - Ra… Blunt   Unint… Transpor…  2008
##  6 800.2 Railway Collision w/ Rolling Stock - Pe… Blunt   Unint… Pedestri…  2008
##  7 800.3 Railway Collision w/ Rolling Stock - Pe… Blunt   Unint… Pedal cy…  2008
##  8 800.8 Railway Collision w/ Rolling Stock - Ot… Blunt   Unint… Transpor…  2008
##  9 800.9 Railway Collision w/ Rolling Stock - Un… Blunt   Unint… Transpor…  2008
## 10 801.0 Railway Collision w/ Oth Object - Railw… Blunt   Unint… Transpor…  2008
## # ℹ 8,009 more rows


2b) Use data sheet map_func containing patients treated with surgical and non-surgical procedures. The investigator further divided the patients into A, B and C groups based on their post-surgical outcomes. After a period of follow up, they have either censored or died (status). “When” is the column of follow up years.

The investigator would like to know the median follow up time, with quartile range (Q1 and Q3), and the total number with percentage, by Treatment, Outcome, and Status. Please try to use split() function and map_dfr() function to calculate the statistics.

Final statistical table looks like:

## # A tibble: 90,954 × 5
##    PatientID   Treatment Outcome Status  When
##    <chr>       <chr>     <chr>    <dbl> <dbl>
##  1 140Z2314239 Surgical  A            0  2.52
##  2 1406872917  Surgical  A            0  6.90
##  3 1407140585  Surgical  A            0  4.64
##  4 1403943291  Surgical  A            0  2.57
##  5 1403900350  Surgical  A            0  9.98
##  6 1405380447  Surgical  A            0  1.08
##  7 1403329155  Surgical  A            0  1.75
##  8 1403940407  Surgical  A            0 13.3 
##  9 1403961799  Surgical  A            0  8.05
## 10 1405842113  Surgical  A            0  1.44
## # ℹ 90,944 more rows
# Split the data into subsets based on unique combinations of 'Treatment', 'Outcome', and 'Status'
dat.map.split <- split(dat.map, list(dat.map$Treatment, dat.map$Outcome, dat.map$Status), drop=TRUE) #This creates a list where each element corresponds to a unique combination of these three columns.

# Define the function to compute summary statistics for each subset
stats <-  function(df) {
  tibble(
    Treatment = unique(df$Treatment),
    Outcome = unique(df$Outcome),
    Status = unique(df$Status),
    
    Median = median(df$When, na.rm = TRUE),
    Q1 = quantile(df$When, 0.25, na.rm = TRUE),
    Q3 = quantile(df$When, 0.75, na.rm = TRUE),
    N = nrow(df),
    Proportion = nrow(df) / nrow(dat.map) * 100
  )
}

# Apply the 'stats' function to each subset in the data frame and combine the results into a single tibble
stats.tbl <- map_dfr(dat.map.split, stats)

print(stats.tbl)
## # A tibble: 12 × 8
##    Treatment   Outcome Status Median    Q1    Q3     N Proportion
##    <chr>       <chr>    <dbl>  <dbl> <dbl> <dbl> <int>      <dbl>
##  1 Nonsurgical A            0   6.21  3.92  9.00 24485    26.9   
##  2 Surgical    A            0   5.90  3.38  8.93  4957     5.45  
##  3 Nonsurgical B            0   6.17  3.90  8.97 23934    26.3   
##  4 Surgical    B            0   5.82  3.35  8.85  4853     5.34  
##  5 Nonsurgical C            0   6.27  3.96  9.10 25060    27.6   
##  6 Surgical    C            0   5.94  3.40  8.97  5032     5.53  
##  7 Nonsurgical A            1   4.70  2.66  7.39   780     0.858 
##  8 Surgical    A            1   4.30  2.16  5.70    96     0.106 
##  9 Nonsurgical B            1   4.64  2.64  7.28  1331     1.46  
## 10 Surgical    B            1   4.77  2.43  6.97   200     0.220 
## 11 Nonsurgical C            1   5.66  3.68  8.36   205     0.225 
## 12 Surgical    C            1   5.77  4.13  7.07    21     0.0231


2c) It’s common to access the univariate relationship between multiple predictors and one outcome. In R, there is a data set “iris”. Try use map function to build univariate linear regression models with outcome “Sepal.Length”. There should be 4 individual linear regression models. Could you also present the results by combing each model? Something like this:

data(iris)

# Identify all column names except 'Sepal.Length', as these will be used as predictors.

predictors <- setdiff(names(iris), "Sepal.Length")
predictors
## [1] "Sepal.Width"  "Petal.Length" "Petal.Width"  "Species"
# Apply linear regression for each predictor separately and store the results
iris.result <- map_dfr(predictors, 
  ~ { # Fit the model
    model <- lm(Sepal.Length ~ ., data = iris[, c("Sepal.Length", .x), drop = FALSE])
    #Subsets the iris dataset to include only the Sepal.Length column (outcome) and the current predictor (.x).
    
    # broom (Converts the model summary into a tidy data frame format)
    broom::tidy(model) %>%
      mutate(predictors = .x)
  }
) %>% 
  select(predictors, term, estimate, std.error, statistic, p.value)

iris.result
## # A tibble: 9 × 6
##   predictors   term              estimate std.error statistic   p.value
##   <chr>        <chr>                <dbl>     <dbl>     <dbl>     <dbl>
## 1 Sepal.Width  (Intercept)          6.53     0.479      13.6  6.47e- 28
## 2 Sepal.Width  Sepal.Width         -0.223    0.155      -1.44 1.52e-  1
## 3 Petal.Length (Intercept)          4.31     0.0784     54.9  2.43e-100
## 4 Petal.Length Petal.Length         0.409    0.0189     21.6  1.04e- 47
## 5 Petal.Width  (Intercept)          4.78     0.0729     65.5  3.34e-111
## 6 Petal.Width  Petal.Width          0.889    0.0514     17.3  2.33e- 37
## 7 Species      (Intercept)          5.01     0.0728     68.8  1.13e-113
## 8 Species      Speciesversicolor    0.93     0.103       9.03 8.77e- 16
## 9 Species      Speciesvirginica     1.58     0.103      15.4  2.21e- 32


2d) In some cases we have multiple outcomes and only one group variable, could you perform the univariate comparisons on different outcomes?

In the same “iris” data, use “Sepal.Length” as group variable, and the rest are the outcomes. Be aware that some outcomes are continuous and some are categorical.

# Split the outcomes into continuous and categorical
continuous_vars <- c("Sepal.Width", "Petal.Length", "Petal.Width")
categorical_vars <- c("Species")

# Continuous Outcomes: Linear Regression
continuous_results <- map_dfr(continuous_vars, ~ {
  lm(as.formula(paste(.x, "~ Sepal.Length")), data = iris) %>%
    broom::tidy() %>%
    mutate(predictor = .x)
})

# Categorical Outcomes: Logistic Regression (One-vs-All Approach)
categorical_results <- map_dfr(categorical_vars, ~ {
  glm(as.formula(paste(.x, "~ Sepal.Length")), data = iris, family = binomial) %>%
    broom::tidy() %>%
    mutate(predictor = .x)
})

all_results <- bind_rows(continuous_results, categorical_results) %>%
  select(predictor,term, estimate, std.error, statistic, p.value)

all_results
## # A tibble: 8 × 6
##   predictor    term         estimate std.error statistic  p.value
##   <chr>        <chr>           <dbl>     <dbl>     <dbl>    <dbl>
## 1 Sepal.Width  (Intercept)    3.42      0.254      13.5  1.55e-27
## 2 Sepal.Width  Sepal.Length  -0.0619    0.0430     -1.44 1.52e- 1
## 3 Petal.Length (Intercept)   -7.10      0.507     -14.0  6.13e-29
## 4 Petal.Length Sepal.Length   1.86      0.0859     21.6  1.04e-47
## 5 Petal.Width  (Intercept)   -3.20      0.257     -12.5  8.14e-25
## 6 Petal.Width  Sepal.Length   0.753     0.0435     17.3  2.33e-37
## 7 Species      (Intercept)  -27.8       4.83       -5.76 8.19e- 9
## 8 Species      Sepal.Length   5.18      0.893       5.79 6.90e- 9



##### Reference: