Conjoint Experiment

Author

João C. L. Camargos And Nick Antolick

1 Replication Task

In this exercise, we will replicate the study by O’Campo-Roland. The article’s research question guiding the conjoint design is whether and how white and Black Americans differ in their perceptions of which Latinos are considered “American.” The design systematically varies Latino characteristics such as phenotype, immigrant background/legal status, language, socioeconomic markers, religion, and political affiliation. The goal is to uncover how prototypical (white) versus peripheral (Black) group members draw national boundaries of inclusion, and which Latino subgroups are deemed part of the American imagined community.

The study employs a paired-profile conjoint survey experiment to assess perceptions of Latinos’ Americanness. A sample of 750 white and 750 Black respondents (total N=1,500) was recruited through YouGov. Each respondent evaluated ten Latino profiles, presented in five side-by-side tasks, and for each task was asked (1) to choose which profile they thought was “more American” and (2) to rate each profile on a 1–7 Americanness scale.

The profiles were randomly generated by varying a set of attributes that capture Latino heterogeneity, including:

  • Physical appearance (photos from the Chicago Face Database spanning skin tones and genders);

  • Immigrant background/legal status (undocumented, legal, naturalized, or U.S.-born descendants of legal/undocumented immigrants);

  • Language ability (only English, bilingual, limited English, no English);

  • Socioeconomic characteristics (education and occupation levels);

  • Ethnic/national identification (American, Latino, Mexican, etc.);

  • Religion (Catholic, Evangelical, Presbyterian, not religious);

  • Political affiliation (Democrat, Republican, Independent, non-political);

  • Spousal background (American, white, Black, same as profile)

Design restrictions prevented unrealistic combinations (e.g., inconsistent education and occupation pairings). Analytically, the study estimated Average Marginal Component Effects (AMCEs) across all respondents, then compared marginal mean differences and attribute salience between white and Black respondents, as well as variation within groups by strength of American or racial identity. We will replicate the article findings on the main analysis.

1.1 Instaling necessary packages and functions

Show Answer
#remotes::install_github("leeper/cregg")
library(gtools)
library(tidyverse)
library(xtable)
library(cjoint)
library(cregg)
library(sjPlot)
library(ggplot2)
library(graphics)
library(scales)
library(estimatr)

set.seed(12345678)

1.2 Data Cleaning

Download the data set.

Show Answer
###SET WORKING DIRECTORY and LOAD FILES ----

onedrive_url <- "https://1drv.ms/x/c/0f7d0a7f95ac8880/Edv5Acp5lARJicU-cxAi6q0B9Zn36gXoE4Syl_gv-8MCLw?e=x3jCEe&download=1"

#full <- read.csv(url(onedrive_url))

full <- read.csv('C:/Users/jmcar/OneDrive/Documentos/JOAO/UNC/2025 Fall/Advanced Causal/Class 1/Replication Material OCampo 2025/b_data/1_raw_data_for_long.csv')

##### Recoding key analytic variables----


full <- full %>%
  mutate(#Race of respondent
        race = 
           case_when(race == 1 ~ 'White respondent',
                     race == 2 ~  'Black respondent'),
         race = factor(race, levels = c("White respondent", "Black respondent")),
         #American identity importance
         amerimport = 
           case_when(Q7_2 == 1 ~ 1,
                     Q7_2 == 2 ~ 2,
                     Q7_2 == 3 ~ 3,
                     Q7_2 == 4 ~ 4),
        #Racial identity importance
         raceimport = 
           case_when(Q7_3 == 1 ~ 1,
                     Q7_3 == 2 ~ 2,
                     Q7_3 == 3 ~ 3,
                     Q7_3 == 4 ~ 4),
         #Perceived prototypicality
         prototypical = 
           case_when(Q7_4 == 1 ~ 1,
                     Q7_4 == 2 ~ 2,
                     Q7_4 == 3 ~ 3,
                     Q7_4 == 4 ~ 4))


###Splitting identity perception variables into two and recoding the variables into high and low ----
full$amerimport_high <- quantcut(full$amerimport, q=2)
full$raceimport_high <- quantcut(full$raceimport, q=2)
full$prototypical_high <- quantcut(full$prototypical, q=2)


full <- full %>%
  mutate(amerimport_high =
           case_when(amerimport_high == "[1,3]" ~ 'Low',
                     amerimport_high == "(3,4]" ~ 'High'),
         raceimport_high =
           case_when(raceimport_high == "[1,3]" ~ 'Low',
                     raceimport_high == "(3,4]" ~ 'High'),
         prototypical_high =
           case_when(prototypical_high == "[1,3]" ~ 'Low',
                     prototypical_high == "(3,4]" ~ 'High'))

1.3 Reshaping Data

In order to perform the conjoint analysis, the data frame must be in a long format, which means that each row will contain a task, instead of a respondent itself. The function cj_tidy from the package cregg can help with it. We just need to specify a list containing the characteristics of each task and a list with the outcome.

Show Answer
####Reshaping data into long form ----

full <- full %>%
  rename (choice_a = Q3_2,
          choice_b = Q3_5,
          choice_c = Q3_8,
          choice_d = Q3_11,
          choice_e = Q3_14,
          rating_a1 = Q3_3_1,
          rating_a2 = Q3_3_2,
          rating_b1 = Q3_6_1,
          rating_b2 = Q3_6_2,
          rating_c1 = Q3_9_1,
          rating_c2 = Q3_9_2,
          rating_d1 = Q3_12_1,
          rating_d2 = Q3_12_2,
          rating_e1 = Q3_15_1,
          rating_e2 = Q3_15_2)

# Characteriscs list
list1 <- list(
  Faces = list(
    ~ Q3A_1_Faces + Q3B_1_Faces + Q3C_1_Faces + Q3D_1_Faces + Q3E_1_Faces,
    ~ Q3A_2_Faces + Q3B_2_Faces + Q3C_2_Faces + Q3D_2_Faces + Q3E_2_Faces
  ),
  Background = list(
    ~ Q3A_1_Background + Q3B_1_Background + Q3C_1_Background + Q3D_1_Background + Q3E_1_Background,
    ~ Q3A_2_Background + Q3B_2_Background + Q3C_2_Background + Q3D_2_Background + Q3E_2_Background
  ),
  Identifies = list(
    ~ Q3A_1_Identifies + Q3B_1_Identifies + Q3C_1_Identifies + Q3D_1_Identifies + Q3E_1_Identifies,
    ~ Q3A_2_Identifies + Q3B_2_Identifies + Q3C_2_Identifies + Q3D_2_Identifies + Q3E_2_Identifies
  ),
  Language = list(
    ~ Q3A_1_Language + Q3B_1_Language + Q3C_1_Language + Q3D_1_Language + Q3E_1_Language,
    ~ Q3A_2_Language + Q3B_2_Language + Q3C_2_Language + Q3D_2_Language + Q3E_2_Language
  ),
  Education = list(
    ~ Q3A_1_Education + Q3B_1_Education + Q3C_1_Education + Q3D_1_Education + Q3E_1_Education,
    ~ Q3A_2_Education + Q3B_2_Education + Q3C_2_Education + Q3D_2_Education + Q3E_2_Education
  ),
  Profession = list(
    ~ Q3A_1_Profession + Q3B_1_Profession + Q3C_1_Profession + Q3D_1_Profession + Q3E_1_Profession,
    ~ Q3A_2_Profession + Q3B_2_Profession + Q3C_2_Profession + Q3D_2_Profession + Q3E_2_Profession
  ),
  Religion = list(
    ~ Q3A_1_Religion + Q3B_1_Religion + Q3C_1_Religion + Q3D_1_Religion + Q3E_1_Religion,
    ~ Q3A_2_Religion + Q3B_2_Religion + Q3C_2_Religion + Q3D_2_Religion + Q3E_2_Religion
  ),
  Political = list(
    ~ Q3A_1_Political + Q3B_1_Political + Q3C_1_Political + Q3D_1_Political + Q3E_1_Political,
    ~ Q3A_2_Political + Q3B_2_Political + Q3C_2_Political + Q3D_2_Political + Q3E_2_Political
  ),
  Spouse = list(
    ~ Q3A_1_Spouse + Q3B_1_Spouse + Q3C_1_Spouse + Q3D_1_Spouse + Q3E_1_Spouse,
    ~ Q3A_2_Spouse + Q3B_2_Spouse + Q3C_2_Spouse + Q3D_2_Spouse + Q3E_2_Spouse
  ),
  rating = list(
    ~ rating_a1 + rating_b1 + rating_c1 + rating_d1 + rating_e1,
    ~ rating_a2 + rating_b2 + rating_c2 + rating_d2 + rating_e2
  )
)

# Response list
list2 <- list(choice = ~ choice_a + choice_b + choice_c + choice_d + choice_e)

# Aplying the function
long <- cregg::cj_tidy(full,
                    profile_variables = list1,
                    task_variables = list2,
                    id = ~ caseid)

# Need to save and reload the data in order for code to run
write.csv(long, 'long.csv')

long <- read.csv('long.csv')

After reshaping the data set, we will recode the categories of each group in meaningfull terms, transforming the numerical codes into actual categories.

Show Answer
# Recoding profile characteristics as factors

long <- long %>%
  mutate(
    # Ensure numeric codes (or character across-the-board)
    across(c(Faces, Background, Identifies, Language, Education, Profession, Religion, Political, Spouse),
           ~ as.integer(.x)),

    Faces = factor(recode(Faces,
      `1`="White man", `2`="White woman", `3`="Light man", `4`="Light woman",
      `5`="Medium man", `6`="Medium woman", `7`="Brown man", `8`="Brown woman",
      `9`="Black man", `10`="Black woman",
      .default = NA_character_
    ), levels = c("White man","White woman","Light man","Light woman",
                  "Medium man","Medium woman","Brown man","Brown woman",
                  "Black man","Black woman"), ordered = TRUE),

    Background = factor(recode(Background,
      `1`="Legal immigrant", `2`="Undoc. immigrant", `3`="Naturalized citizen",
      `4`="US-born legal parents", `5`="US-born undoc. parents",
      `6`="US-born legal grandparents", `7`="US-born undoc. grandparents",
      .default = NA_character_
    ), levels = c("Legal immigrant","Undoc. immigrant","Naturalized citizen",
                  "US-born legal parents","US-born undoc. parents",
                  "US-born legal grandparents","US-born undoc. grandparents")),

    Identifies = factor(recode(Identifies,
      `1`="American", `2`="Latino", `3`="Hispanic", `4`="Mexican-American",
      `5`="Mexican", `6`="White", `7`="Black", .default = NA_character_
    ), levels = c("American","Latino","Hispanic","Mexican-American","Mexican","White","Black")),

    Language = factor(recode(Language,
      `1`="Only English", `2`="Bilingual", `3`="Limited English", `4`="No English",
      .default = NA_character_
    ), levels = c("Only English","Bilingual","Limited English","No English"),
      ordered = TRUE),

    Education = factor(recode(Education,
      `1`="High school dropout", `2`="High school graduate",
      `3`="Bachelor's degree", `4`="Advanced/graduate degree",
      .default = NA_character_
    ), levels = c("High school dropout","High school graduate",
                  "Bachelor's degree","Advanced/graduate degree"),
      ordered = TRUE),

    Profession = factor(recode(Profession,
      `1`="Waiter", `2`="Janitor", `3`="Sales manager", `4`="Teacher",
      `5`="IT professional", `6`="Doctor", `7`="Small business owner",
      .default = NA_character_
    ), levels = c("Waiter","Janitor","Sales manager","Teacher",
                  "IT professional","Doctor","Small business owner")),

    Religion = factor(recode(Religion,
      `1`="Presbyterian", `2`="Evangelical", `3`="Catholic", `4`="Not religious",
      .default = NA_character_
    ), levels = c("Presbyterian","Evangelical","Catholic","Not religious")),

    Political = factor(recode(Political,
      `1`="Democrat", `2`="Republican", `3`="Independent", `4`="Not political",
      .default = NA_character_
    ), levels = c("Democrat","Republican","Independent","Not political")),

    Spouse = factor(recode(Spouse,
      `1`="Not married", `2`="S. is American", `3`="S. is Black",
      `4`="S. is White", `5`="S. is same as profile",
      .default = NA_character_
    ), levels = c("Not married","S. is American","S. is Black",
                  "S. is White","S. is same as profile"))
  )

# cleaning American rating variable
df <- long %>% 
  mutate(rating = 
           case_when(rating == 1 ~ 1,
                     rating == 2 ~ 2,
                     rating == 3 ~ 3,
                     rating == 4 ~ 4,
                     rating == 5 ~ 5,
                     rating == 6 ~ 6,
                     rating == 7 ~ 7))

df$rating_r <- rescale(df$rating) #rescales American ratings from 0 to 1

tab_df(df |> slice_head(n= 10))
X caseid weight race educ gender employ faminc_new religpew birthyr ideo5 pid7 presvote16post votereg Q7_2 Q7_3 Q7_4 Q4_3 Q4_5_2 amerimport raceimport prototypical amerimport_high raceimport_high prototypical_high task profile Faces Background Identifies Language Education Profession Religion Political Spouse rating choice pair rating_r
1.10 1203595341 1.55 White respondent 5 1 1 6 1 1963 4 6 2 1 3 2 3 1 1 3 2 3 Low Low Low 1 A Brown woman Undoc. immigrant Hispanic Limited English High school dropout Waiter Presbyterian Not political S. is same as profile 2 2 1 0.17
2.10 1204107895 1.03 White respondent 2 2 7 1 1 1988 6 8 7 2 1 1 2 1 1 1 1 2 Low Low Low 1 A Medium man Undoc. immigrant Hispanic Bilingual Bachelor's degree Janitor Presbyterian Independent Not married 5 1 2 0.67
3.10 1204107941 0.76 White respondent 1 1 6 6 1 1953 6 5 7 2 4 4 4 1 1 4 4 4 High High High 1 A Medium woman US-born legal parents American Only English Bachelor's degree Small business owner Presbyterian Republican S. is same as profile 5 1 3 0.67
4.10 1204110451 0.90 White respondent 3 2 1 4 9 1977 2 3 1 1 2 2 3 1 1 2 2 3 Low Low Low 1 A Brown man US-born legal parents Mexican Only English High school dropout Sales manager Not religious Independent S. is Black 7 1 4 1.00
5.10 1204116055 0.72 White respondent 6 1 8 6 1 1994 3 4 5 1 2 2 3 1 1 2 2 3 Low Low Low 1 A Brown woman US-born undoc. grandparents Mexican-American Only English High school graduate Janitor Presbyterian Independent S. is same as profile 7 1 5 1.00
6.10 1204116247 1.17 White respondent 2 1 1 13 11 1966 4 7 2 1 2 1 4 1 1 2 1 4 Low Low High 1 A Black man Undoc. immigrant American Bilingual Bachelor's degree Sales manager Evangelical Republican Not married 2 2 6 0.17
7.10 1204116147 0.79 White respondent 2 2 5 2 11 1955 2 4 7 2 4 4 4 1 1 4 4 4 High High High 1 A Medium man US-born legal parents Mexican-American Only English Advanced/graduate degree Janitor Not religious Not political S. is American 2 1 7 0.17
8.10 1204116213 1.41 White respondent 4 1 1 4 2 1969 5 7 2 1 4 2 1 1 1 4 2 1 High Low Low 1 A White woman US-born legal parents American Limited English Bachelor's degree Teacher Evangelical Not political S. is Black 6 1 8 0.83
9.10 1204117027 1.03 White respondent 4 2 6 1 9 1958 1 1 7 1 4 2 3 1 1 4 2 3 High Low Low 1 A Light woman US-born undoc. grandparents White Limited English Advanced/graduate degree Teacher Presbyterian Independent S. is same as profile 7 2 9 1.00
10.10 1204116383 0.98 White respondent 3 1 5 2 2 1948 3 4 7 1 4 4 4 1 1 4 4 4 High High High 1 A White woman Naturalized citizen American No English High school dropout Janitor Evangelical Independent Not married 6 1 10 0.83

To end, we will transform the data again so the factor categories are well defined within the conjoint desing. It is important to notice that, due to a package bug, we need to repeat this process exporting the previous file and downloading it back again.

Show Answer
write.csv(df, 'df.csv')

df <- read.csv('df.csv')

####Reshaping data as factors ----

df$race <- as.factor(df$race)

df <- df %>% 
  mutate(Faces = factor(Faces, levels = c("White man", "White woman", "Light man", "Light woman", 
                                          "Medium man",   "Medium woman", "Brown man", 
                                          "Brown woman", "Black man", "Black woman"), 
                        ordered = is.ordered(Faces)))


df <- df %>%
  mutate(Background = factor(Background, levels = c("Legal immigrant",
                                                    "Undoc. immigrant",
                                                    "Naturalized citizen",
                                                    "US-born legal parents",
                                                    "US-born undoc. parents",
                                                    "US-born legal grandparents",
                                                    "US-born undoc. grandparents")))

df <- df %>%
  mutate(Identifies = factor(Identifies, levels = c("American",
                                                    "Latino",
                                                    "Hispanic",
                                                    "Mexican-American",
                                                    "Mexican",
                                                    "White",
                                                    "Black")))

df <- df %>%
  mutate(Language = factor(Language, levels = c("Only English",
                                                "Bilingual",
                                                "Limited English",
                                                "No English"),
                           ordered = is.ordered(Language)))

df <- df %>%
  mutate(Education = factor(Education, levels = c("High school dropout",
                                                  "High school graduate",
                                                  "Bachelor's degree",
                                                  "Advanced/graduate degree"),
                            ordered = is.ordered(Education)))

df <- df %>%
  mutate(Profession = factor(Profession, levels = c("Waiter", "Janitor",
                                                    "Sales manager", "Teacher",
                                                    "IT professional", "Doctor",
                                                    "Small business owner")))

df <- df %>%
  mutate(Religion = factor(Religion, levels = c("Presbyterian", "Evangelical",
                                                "Catholic", "Not religious")))

df <- df %>%
  mutate(Political = factor(Political, levels = c("Democrat", "Republican",
                                                  "Independent", "Not political")))

df <- df %>%
  mutate(Spouse = factor(Spouse, levels = c("Not married",
                                            "S. is American",
                                            "S. is Black",
                                            "S. is White",
                                            "S. is same as profile")))

1.4 Identify the conjoint design by OLS

Accounting for a simple conjoint design, the model can be identified with a simple OLS regression with clustered standard errors by respondent. We can easily estimate this using the lm_robust method from the estimatr package. We just need to specify the equation, the clusters and the weights within it.

Show Answer
fit_ols <- lm_robust(rating_r ~ Faces + Background + Identifies +
                               Language + Education + Profession + Religion 
                             + Political + Spouse, 
                     data=df, clusters = caseid, weights = weight)

tab_model(fit_ols)
  rating r
Predictors Estimates CI p
(Intercept) 0.77 0.73 – 0.80 <0.001
Faces [White woman] -0.01 -0.03 – 0.01 0.363
Faces [Light man] -0.01 -0.03 – 0.02 0.607
Faces [Light woman] -0.01 -0.03 – 0.01 0.379
Faces [Medium man] -0.01 -0.03 – 0.01 0.398
Faces [Medium woman] -0.01 -0.03 – 0.01 0.316
Faces [Brown man] -0.03 -0.05 – -0.01 0.003
Faces [Brown woman] -0.02 -0.04 – 0.00 0.082
Faces [Black man] -0.01 -0.03 – 0.01 0.394
Faces [Black woman] 0.01 -0.01 – 0.03 0.362
Background [Undoc.
immigrant]
-0.16 -0.19 – -0.14 <0.001
Background [Naturalized
citizen]
0.02 -0.00 – 0.04 0.053
Background [US-born legal
parents]
0.05 0.03 – 0.07 <0.001
Background [US-born
undoc. parents]
-0.01 -0.03 – 0.01 0.330
Background [US-born legal
grandparents]
0.03 0.02 – 0.05 <0.001
Background [US-born
undoc. grandparents]
0.01 -0.01 – 0.03 0.489
Identifies [Latino] -0.02 -0.04 – -0.00 0.032
Identifies [Hispanic] -0.02 -0.04 – -0.00 0.018
Identifies
[Mexican-American]
-0.02 -0.03 – 0.00 0.055
Identifies [Mexican] -0.03 -0.04 – -0.01 0.005
Identifies [White] -0.01 -0.03 – 0.02 0.616
Identifies [Black] -0.01 -0.03 – 0.02 0.600
Language [Bilingual] -0.01 -0.02 – 0.01 0.447
Language [Limited
English]
-0.04 -0.05 – -0.02 <0.001
Language [No English] -0.06 -0.09 – -0.04 <0.001
Education [High school
graduate]
0.00 -0.01 – 0.02 0.730
Education [Bachelor's
degree]
0.02 0.00 – 0.03 0.033
Education
[Advanced/graduate
degree]
0.01 -0.00 – 0.03 0.113
Profession [Janitor] -0.01 -0.03 – 0.00 0.160
Profession [Sales
manager]
-0.00 -0.02 – 0.01 0.631
Profession [Teacher] -0.01 -0.03 – 0.01 0.361
Profession [IT
professional]
0.00 -0.02 – 0.02 0.711
Profession [Doctor] 0.00 -0.02 – 0.03 0.900
Profession [Small
business owner]
0.00 -0.01 – 0.02 0.522
Religion [Evangelical] -0.00 -0.02 – 0.01 0.708
Religion [Catholic] 0.00 -0.01 – 0.02 0.545
Religion [Not religious] -0.00 -0.01 – 0.01 0.828
Political [Republican] -0.00 -0.02 – 0.01 0.575
Political [Independent] 0.01 -0.01 – 0.02 0.472
Political [Not political] -0.01 -0.02 – 0.01 0.248
Spouse [S. is American] 0.00 -0.01 – 0.02 0.617
Spouse [S. is Black] -0.01 -0.02 – 0.01 0.335
Spouse [S. is White] -0.01 -0.02 – 0.01 0.458
Spouse [S. is same as
profile]
-0.00 -0.02 – 0.01 0.467
Observations 14979
R2 / R2 adjusted 0.060 / 0.057

1.5 Defining the conjoint design

However, we can incorporate more characteristics of the design by using specific packages. For instance, we can run the function amce from the package cjoint to specify design restrictions, such as impossible combinations and a relative probability of each category to be presented.

Show Answer
###CONJOINT DESIGN ----

# Providing a list with all the attributes available by each feature

attribute_list <- list()

attribute_list[["Faces"]] <-c("White man", "White woman", "Light man", "Light woman",
                              "Medium man", "Medium woman", "Brown man", "Brown woman",
                              "Black man", "Black woman")
attribute_list[["Background"]] <- c("Legal immigrant", "Undoc. immigrant",
                                    "Naturalized citizen", 
                                    "US-born legal parents",
                                    "US-born undoc. parents",
                                    "US-born legal grandparents",
                                    "US-born undoc. grandparents")
attribute_list[["Identifies"]] <- c("American", "Latino",
                                    "Hispanic", "Mexican-American",
                                    "Mexican", "White", "Black")
attribute_list[["Language"]] <- c("Only English",
                                  "Bilingual",
                                  "Limited English",
                                  "No English")
attribute_list[["Education"]] <- c("High school dropout",
                                   "High school graduate",
                                   "Bachelor's degree",
                                   "Advanced/graduate degree")
attribute_list[["Profession"]] <- c("Waiter", "Janitor",
                                    "Sales manager", "Teacher",
                                    "IT professional", "Doctor",
                                    "Small business owner")
attribute_list[["Religion"]] <- c("Presbyterian", "Evangelical",
                                  "Catholic", "Not religious")
attribute_list[["Political"]] <- c("Democrat", "Republican",
                                   "Independent", "Not political")
attribute_list[["Spouse"]] <- c("Not married", "S. is American",
                                "S. is Black", "S. is White",
                                "S. is same as profile")

# Providing a list with all the impossible attributes to be combined 
# (example a doctor that don't have highschool)

constraint_list <- list()

constraint_list[[1]] <- list()
constraint_list[[1]][["Education"]] <- c("High school dropout",
                                         "High school graduate")
constraint_list[[1]][["Profession"]] <- c("Doctor",
                                          "IT professional", "Teacher")

constraint_list[[2]] <- list()
constraint_list[[2]][["Education"]] <- c("Bachelor's degree")
constraint_list[[2]][["Profession"]] <- c("Doctor")


constraint_list[[3]] <- list()
constraint_list[[3]][["Background"]] <- c("US-born legal parents",
                                          "US-born undoc. parents",
                                          "US-born legal grandparents",
                                          "US-born undoc. grandparents")
constraint_list[[3]][["Language"]] <- c("No English")

constraint_list[[4]] <- list()
constraint_list[[4]][["Background"]] <- c("Legal immigrant",
                                          "Naturalized citizen",
                                          "Undoc. immigrant")
constraint_list[[4]][["Language"]] <- c("Only English")

constraint_list[[5]] <- list()
constraint_list[[5]][["Profession"]] <- c("IT professional",
                                          "Doctor",
                                          "Sales manager",
                                          "Teacher")
constraint_list[[5]][["Language"]] <- c("No English")

constraint_list[[6]] <- list()
constraint_list[[6]][["Faces"]] <- c("White man", "White woman", 
                                     "Light man", "Light woman",
                                     "Medium man", "Medium woman")
constraint_list[[6]][["Identifies"]] <- c("Black")

constraint_list[[7]] <- list()
constraint_list[[7]][["Faces"]] <- c("Brown man", "Brown woman",
                                     "Black man", "Black woman")
constraint_list[[7]][["Identifies"]] <- c("White")

# To end, we need to provide a list with the 

marginal_weights <- list()
marginal_weights[["Faces"]] <- rep(1/length(attribute_list[["Faces"]]),
                                   length(attribute_list[["Faces"]]))
marginal_weights[["Background"]] <- rep(1/length(attribute_list[["Background"]]),
                                        length(attribute_list[["Background"]]))
marginal_weights[["Identifies"]] <- rep(1/length(attribute_list[["Identifies"]]),
                                        length(attribute_list[["Identifies"]]))
marginal_weights[["Language"]] <- rep(1/length(attribute_list[["Language"]]),
                                      length(attribute_list[["Language"]]))
marginal_weights[["Education"]] <- rep(1/length(attribute_list[["Education"]]),
                                       length(attribute_list[["Education"]]))
marginal_weights[["Profession"]] <- rep(1/length(attribute_list[["Profession"]]),
                                        length(attribute_list[["Profession"]]))
marginal_weights[["Religion"]] <- rep(1/length(attribute_list[["Religion"]]),
                                      length(attribute_list[["Religion"]]))
marginal_weights[["Political"]] <- c(2/9, 2/9, 2/9, 3/9)
marginal_weights[["Spouse"]] <- c(5/15, 3/15, 2/15, 2/15, 3/15)

conjointdesign <- cjoint::makeDesign(type='constraints', attribute.levels=attribute_list,
                                     constraints=constraint_list, level.probs=marginal_weights)
baselines <- list()
baselines$Education <- "High school graduate"
baselines$Background <- "US-born legal grandparents"
baselines$Profession <- "Teacher"
baselines$Political <- "Not political"
baselines$Religion <- "Not religious"


#### AMCE of Perceptions of Americanness (Amerian rating) ----
####NOTE: This model is computationally intensive and may not run on all computers


ratingamce_r <- cjoint::amce(rating_r ~ Faces + Background + Identifies +
                               Language + Education + Profession + Religion 
                             + Political + Spouse, data=df, cluster=TRUE, 
                             respondent.id="caseid", design=conjointdesign,
                             weights="weight", baselines=baselines,
                             na.ignore=TRUE)

### Creating a table to visualize the results

sum_amce_r <- summary(ratingamce_r)
amce_r <-sum_amce_r$amce
amcer_table <- amce_r[c(2:6)]
tab_df(amcer_table)
Level Estimate Std..Err z.value Pr...z..
Legal immigrant -0.04 0.01 -2.96 0.00
Undoc. immigrant -0.21 0.02 -13.67 0.00
Naturalized citizen -0.02 0.01 -1.35 0.18
US-born legal parents 0.02 0.01 2.19 0.03
US-born undoc. parents -0.05 0.01 -4.10 0.00
US-born undoc. grandparents -0.02 0.01 -2.46 0.01
High school dropout -0.00 0.01 -0.40 0.69
Bachelor's degree 0.01 0.01 0.95 0.34
Advanced/graduate degree 0.01 0.01 0.67 0.51
White woman -0.01 0.01 -0.91 0.36
Light man -0.00 0.01 -0.22 0.83
Light woman -0.01 0.01 -0.82 0.41
Medium man -0.01 0.01 -0.79 0.43
Medium woman -0.01 0.01 -0.77 0.44
Brown man -0.03 0.01 -2.41 0.02
Brown woman -0.01 0.01 -1.09 0.28
Black man -0.00 0.01 -0.34 0.74
Black woman 0.01 0.01 0.62 0.53
Latino -0.02 0.01 -2.03 0.04
Hispanic -0.02 0.01 -2.57 0.01
Mexican-American -0.02 0.01 -1.78 0.07
Mexican -0.03 0.01 -2.80 0.01
White -0.01 0.01 -0.98 0.33
Black 0.00 0.01 0.15 0.88
Bilingual 0.01 0.01 0.56 0.58
Limited English -0.02 0.01 -2.03 0.04
No English -0.04 0.04 -1.01 0.31
Democrat 0.01 0.01 1.17 0.24
Republican 0.00 0.01 0.46 0.65
Independent 0.01 0.01 2.15 0.03
Waiter -0.00 0.02 -0.12 0.91
Janitor 0.01 0.01 0.58 0.56
Sales manager 0.00 0.01 0.15 0.88
IT professional 0.01 0.01 0.80 0.43
Doctor 0.01 0.02 0.35 0.73
Small business owner 0.02 0.01 1.59 0.11
Presbyterian 0.00 0.01 0.07 0.95
Evangelical -0.00 0.01 -0.38 0.70
Catholic 0.00 0.01 0.61 0.54
S. is American 0.00 0.01 0.71 0.48
S. is Black -0.01 0.01 -0.68 0.49
S. is White -0.01 0.01 -0.75 0.45
S. is same as profile -0.00 0.01 -0.69 0.49

1.6 Visualizations

Now let’s visualize the results on a figure based on the plot method. In order to do so, we will use a a combination of the method plot, implemented in cjoint with the ggplot in order to make the graph more presentable.

Show Answer
# Figure Limits
lim <- c(-.25, .05)

# Feature order 
c_order <- c("Faces", "Identifies", "Religion", "Background", "Language",
             "Spouse", "Education", "Profession", "Political")


figure_1 <- plot(ratingamce_r, main = "Average Marginal Component Effect of Perceived Americanness Among All Respondents",
  xlab = "Change: American Rating (0 'not at all American' - 1 'very much American')", group.order= c_order,
  colors="black",
  xlim=lim,
   point.size=0.4, 
   dodge.size=1,
  plot.theme = (ggplot2::theme_bw() + theme(legend.position="none", plot.title= element_text(size=10)))) 

1.7 Extra features

The cregg package also allow us to perform the conjoint in a comparative way, seeing how different groups within the sample using the function mm_diffs. We again need to specify the design and add the argument by, which will specify the respondent groups that we are comparing.

Show Answer
###Marginal mean differences in perceptions of Americanness by race model----
american_diff <- cregg::mm_diffs(na.omit(df),
                                 rating_r ~ Faces + Background
                                 + Education + Profession
                                 + Language + Political
                                 + Religion + Spouse
                                 + Identifies,
                                 by = ~ race,
                                 id= ~caseid,
                                 weights = ~weight, 
                                 feature_order = c_order)

american_diff_table <- american_diff[c(5:9)]
tab_df(american_diff_table)
level estimate std.error z p
White man 0.05 0.02 2.38 0.02
White woman 0.05 0.02 2.72 0.01
Light man 0.02 0.02 0.96 0.33
Light woman 0.05 0.02 2.65 0.01
Medium man 0.05 0.02 2.73 0.01
Medium woman 0.06 0.02 3.31 0.00
Brown man 0.04 0.02 1.90 0.06
Brown woman 0.03 0.02 1.36 0.17
Black man 0.03 0.02 1.86 0.06
Black woman 0.02 0.02 1.21 0.23
American 0.04 0.02 2.28 0.02
Latino 0.05 0.02 2.99 0.00
Hispanic 0.03 0.02 2.06 0.04
Mexican-American 0.03 0.02 2.10 0.04
Mexican 0.02 0.02 1.53 0.13
White 0.08 0.02 4.52 0.00
Black 0.03 0.02 1.26 0.21
Presbyterian 0.05 0.01 3.34 0.00
Evangelical 0.03 0.01 2.34 0.02
Catholic 0.03 0.01 2.12 0.03
Not religious 0.05 0.01 3.32 0.00
Legal immigrant 0.07 0.02 3.92 0.00
Undoc. immigrant -0.07 0.02 -3.22 0.00
Naturalized citizen 0.07 0.02 3.80 0.00
US-born legal parents 0.07 0.01 4.50 0.00
US-born undoc. parents 0.00 0.02 0.14 0.89
US-born legal grandparents 0.07 0.02 4.47 0.00
US-born undoc. grandparents 0.04 0.02 2.73 0.01
Only English 0.06 0.01 3.93 0.00
Bilingual 0.04 0.01 3.55 0.00
Limited English 0.03 0.01 1.94 0.05
No English 0.03 0.02 1.10 0.27
Not married 0.05 0.01 3.48 0.00
S. is American 0.03 0.01 1.96 0.05
S. is Black 0.04 0.02 2.31 0.02
S. is White 0.05 0.02 3.25 0.00
S. is same as profile 0.03 0.01 1.83 0.07
High school dropout 0.03 0.02 1.86 0.06
High school graduate 0.04 0.02 2.44 0.01
Bachelor's degree 0.04 0.01 3.04 0.00
Advanced/graduate degree 0.05 0.01 3.36 0.00
Waiter 0.02 0.02 1.30 0.19
Janitor 0.05 0.02 2.89 0.00
Sales manager 0.05 0.02 2.88 0.00
Teacher 0.04 0.02 1.90 0.06
IT professional 0.05 0.02 2.37 0.02
Doctor 0.08 0.03 3.02 0.00
Small business owner 0.04 0.01 2.48 0.01
Democrat 0.03 0.01 1.95 0.05
Republican 0.05 0.01 3.59 0.00
Independent 0.04 0.01 2.56 0.01
Not political 0.04 0.01 2.88 0.00

To end, let`s visualize those differences.

Show Answer
american_diff_p <- plot(american_diff)

figure_2 <- american_diff_p + ggplot2::theme_bw() + 
  theme(legend.position="none", plot.title= element_text(size=10)) +
  scale_color_manual(values = rep("black", 9)) +
  geom_point(size = 2) + 
  ggplot2::labs(x = "Estimated Difference Between White and Black Respondents",
                title = "Marginal Mean Differences in Perceived Americanness Between White and Black Respondents") 

figure_2