| Title: | Conjoint Analysis with Reliability Correction and Visualization |
|---|---|
| Description: | Provides tools for analyzing data generated from conjoint survey experiments, a method widely used in the social sciences for studying multidimensional preferences. The package implements estimation of marginal means (MMs) and average marginal component effects (AMCEs), with corrections for measurement error. Methods include profile-level and choice-level estimators, bias correction using intra-respondent reliability (IRR), and visualization utilities. For details on the methodology, see Clayton, Horiuchi, Kaufman, King, and Komisarchik (2025) <https://gking.harvard.edu/conjointE>. |
| Authors: | Yusaku Horiuchi [aut, cre] (ORCID: <https://orcid.org/0000-0003-0295-4089>), Aaron Kaufman [aut] (ORCID: <https://orcid.org/0000-0003-3688-0428>), Gary King [aut] (ORCID: <https://orcid.org/0000-0002-5327-7631>) |
| Maintainer: | Yusaku Horiuchi <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 1.1.1 |
| Built: | 2026-06-01 09:56:38 UTC |
| Source: | https://github.com/yhoriuchi/projoint |
A cleaned Qualtrics export from a conjoint study that compares two potential new building developments. Each respondent completed 8 standard tasks as well as a repeated version of the first task (flipped), which can be used to calculate intra-respondent reliability (response instability).
data(exampleData1)data(exampleData1)
A data frame with 400 rows and 185 columns. Contains survey responses
including demographic information, outcome choices, and conjoint attribute
values identified by K-*-* variable names.
# Load the dataset data(exampleData1) # Inspect the first few rows head(exampleData1) # Number of rows and columns dim(exampleData1) # Display column names (truncated here) names(exampleData1)[1:10]# Load the dataset data(exampleData1) # Inspect the first few rows head(exampleData1) # Number of rows and columns dim(exampleData1) # Display column names (truncated here) names(exampleData1)[1:10]
A cleaned tibble where each attribute corresponds to a separate column with a descriptive attribute name. The unit of observation is each of two profiles in each task for each respondent.
data(exampleData1_labelled_tibble)data(exampleData1_labelled_tibble)
A tibble with 6,400 rows and 14 columns. Contains survey responses including outcome choices and conjoint attribute values (columns typically named with a 'K-*-*' convention).
This dataset is intended for illustrating reading, reshaping, and
analysis workflows in projoint. Column names are compatible with
reshape_projoint().
Qualtrics survey on Prolific; see Clayton et al. replication materials.
# Load the data data(exampleData1_labelled_tibble) # Basic inspection (fast and always runnable) head(exampleData1_labelled_tibble) dim(exampleData1_labelled_tibble) # Optional: quick structure peek (names only) names(exampleData1_labelled_tibble)# Load the data data(exampleData1_labelled_tibble) # Basic inspection (fast and always runnable) head(exampleData1_labelled_tibble) dim(exampleData1_labelled_tibble) # Optional: quick structure peek (names only) names(exampleData1_labelled_tibble)
A cleaned Qualtrics export from a conjoint study that compares two potential new building developments. Each respondent completed 8 standard tasks as well as a repeated version of the first task, where the profiles were presented in the same left-right positions (not flipped).
data(exampleData2)data(exampleData2)
A data frame with 400 rows and 185 columns. Contains survey responses
including demographic information, outcome choices, and conjoint attribute
values identified by K-*-* variable names.
# Load the dataset data(exampleData2) # Inspect the first few rows head(exampleData2) # Number of rows and columns dim(exampleData2) # Display first 10 column names names(exampleData2)[1:10]# Load the dataset data(exampleData2) # Inspect the first few rows head(exampleData2) # Number of rows and columns dim(exampleData2) # Display first 10 column names names(exampleData2)[1:10]
A cleaned Qualtrics export from a conjoint study that compares two potential new building developments. Each respondent completed 8 standard tasks only; no repeated tasks are included in this dataset.
data(exampleData3)data(exampleData3)
A data frame with 400 rows and 184 columns. Contains survey responses
including demographic information, outcome choices, and conjoint attribute
values identified by K-*-* variable names.
# Load the dataset data(exampleData3) # Inspect the first few rows head(exampleData3) # Number of rows and columns dim(exampleData3) # Display first 10 column names names(exampleData3)[1:10]# Load the dataset data(exampleData3) # Inspect the first few rows head(exampleData3) # Number of rows and columns dim(exampleData3) # Display first 10 column names names(exampleData3)[1:10]
projoint_data object from a labelled tibbleConverts a labelled tibble/data frame (one column per attribute) into an object
of class projoint_data that downstream functions (e.g., projoint)
can consume. The unit of observation should be each of two profiles in each task
for each respondent.
make_projoint_data( .dataframe, .attribute_vars, .id_var = "id", .task_var = "task", .profile_var = "profile", .selected_var = "selected", .selected_repeated_var = NULL, .fill = FALSE )make_projoint_data( .dataframe, .attribute_vars, .id_var = "id", .task_var = "task", .profile_var = "profile", .selected_var = "selected", .selected_repeated_var = NULL, .fill = FALSE )
.dataframe |
A data frame (or tibble). One row per profile per task per respondent. |
.attribute_vars |
Character vector of attribute column names. |
.id_var |
Column name (character) with respondent IDs. Default |
.task_var |
Column name (character) with task numbers. Default |
.profile_var |
Column name (character) with profile numbers. Default |
.selected_var |
Column name (character) with the binary choice for each task
(values in |
.selected_repeated_var |
Optional column name (character) with the binary choice
for the repeated task. Default |
.fill |
Logical. If |
A projoint_data object (a list-like object containing a labels
tibble and a data tibble) ready to pass to projoint and related
functions.
# Example: build a projoint_data object from the labelled-tibble example data(exampleData1_labelled_tibble) att_cols <- c("School Quality", "Violent Crime Rate (Vs National Rate)", "Racial Composition", "Housing Cost", "Presidential Vote (2020)", "Total Daily Driving Time for Commuting and Errands", "Type of Place") pj_dat <- make_projoint_data( .dataframe = exampleData1_labelled_tibble, .attribute_vars = att_cols, .id_var = "id", .task_var = "task", .profile_var = "profile", .selected_var = "selected", .selected_repeated_var = "selected_repeated", .fill = FALSE ) class(pj_dat) # [1] "projoint_data"# Example: build a projoint_data object from the labelled-tibble example data(exampleData1_labelled_tibble) att_cols <- c("School Quality", "Violent Crime Rate (Vs National Rate)", "Racial Composition", "Housing Cost", "Presidential Vote (2020)", "Total Daily Driving Time for Commuting and Errands", "Type of Place") pj_dat <- make_projoint_data( .dataframe = exampleData1_labelled_tibble, .attribute_vars = att_cols, .id_var = "id", .task_var = "task", .profile_var = "profile", .selected_var = "selected", .selected_repeated_var = "selected_repeated", .fill = FALSE ) class(pj_dat) # [1] "projoint_data"
A toy projoint_data object showing what happens after labels
have been manually rearranged (e.g., via save_labels /
read_labels).
data(out1_arranged)data(out1_arranged)
An object of class projoint_data.
Contains two elements:
$labels: reordered attribute–level mapping
$data: the corresponding profile-level dataset
Useful for illustrating workflows without requiring users to re-run the reordering themselves.
projoint_results
Creates publication-ready plots from a projoint_results object produced by
projoint. Supports both profile-level and choice-level analyses,
with plotting options tailored to each structure.
## S3 method for class 'projoint_results' plot( x, .estimates = "corrected", .by_var = FALSE, .labels = NULL, .base_size = 12, .base_family = "", .type = c("bar", "pointrange"), .show_attribute = TRUE, .remove_xaxis = FALSE, .xlim = c(0, 1), .plot.margin = c(0, 3, 0, 3), ... )## S3 method for class 'projoint_results' plot( x, .estimates = "corrected", .by_var = FALSE, .labels = NULL, .base_size = 12, .base_family = "", .type = c("bar", "pointrange"), .show_attribute = TRUE, .remove_xaxis = FALSE, .xlim = c(0, 1), .plot.margin = c(0, 3, 0, 3), ... )
x |
A |
.estimates |
Character: which estimates to plot. One of
|
.by_var |
Logical (profile-level only). Whether to plot subgroup differences.
Default |
.labels |
Character vector of length 2 (choice-level only). Custom x-axis
labels for bar/pointrange plots. If |
.base_size |
Numeric. Base font size. Default |
.base_family |
Character. Base font family. Default |
.type |
Character (choice-level only). One of |
.show_attribute |
Logical (choice-level only). Show the attribute name as the
title when both levels belong to the same attribute. Default |
.remove_xaxis |
Logical (choice-level only). Remove x-axis line, ticks, and labels.
Default |
.xlim |
Numeric length-2 vector (choice-level only). X-axis limits. Default |
.plot.margin |
Numeric length-4 vector (choice-level only). Plot margins in cm:
|
... |
Additional arguments passed to downstream plotting helpers. |
For profile-level results, only .by_var, .base_size, and .base_family
are relevant. For choice-level results, only .type, .labels,
.show_attribute, .remove_xaxis, .xlim, and .plot.margin
are relevant. Irrelevant arguments are ignored with a warning.
A ggplot2 object.
projoint, plot_projoint_profile_level,
plot_projoint_choice_level_mm
data(exampleData1) # Two base tasks (1 & 2) + repeated of task 1 (last) dat <- reshape_projoint( exampleData1, .outcomes = c("choice1", "choice2", "choice1_repeated_flipped") ) # Build a valid QOI from the labels att <- unique(dat$labels$attribute_id)[1] levs <- subset(dat$labels, attribute_id == att)$level_id lev_names <- sub(".*:", "", levs) q <- set_qoi( .structure = "choice_level", .estimand = "mm", .att_choose = att, .lev_choose = lev_names[2], .att_notchoose = att, .lev_notchoose = lev_names[1] ) fit <- projoint(dat, .qoi = q) # Plot method plot(fit)data(exampleData1) # Two base tasks (1 & 2) + repeated of task 1 (last) dat <- reshape_projoint( exampleData1, .outcomes = c("choice1", "choice2", "choice1_repeated_flipped") ) # Build a valid QOI from the labels att <- unique(dat$labels$attribute_id)[1] levs <- subset(dat$labels, attribute_id == att)$level_id lev_names <- sub(".*:", "", levs) q <- set_qoi( .structure = "choice_level", .estimand = "mm", .att_choose = att, .lev_choose = lev_names[2], .att_notchoose = att, .lev_notchoose = lev_names[1] ) fit <- projoint(dat, .qoi = q) # Plot method plot(fit)
projoint_tau
Visualizes the estimated intra-respondent reliability () produced
by the extrapolation method and stored in a projoint_tau object.
## S3 method for class 'projoint_tau' plot(x, ...)## S3 method for class 'projoint_tau' plot(x, ...)
x |
A |
... |
Optional arguments (currently unused). |
A ggplot2 object representing the IRR () visualization.
The plot is drawn for its side effect and also returned (invisibly).
# Estimate tau, then plot: # dat <- reshape_projoint(exampleData1, .outcomes = c("choice1","choice2")) # tau_fit <- projoint_tau(dat) # or predict_tau(dat) # p <- plot(tau_fit) # also returns the ggplot object (invisibly)# Estimate tau, then plot: # dat <- reshape_projoint(exampleData1, .outcomes = c("choice1","choice2")) # tau_fit <- projoint_tau(dat) # or predict_tau(dat) # p <- plot(tau_fit) # also returns the ggplot object (invisibly)
Uses the extrapolation method to estimate intra-respondent reliability (IRR, )
when your conjoint design does not include an explicit repeated task. The input is a
projoint_data object (e.g., produced by reshape_projoint).
predict_tau(.data, .title = NULL)predict_tau(.data, .title = NULL)
.data |
A |
.title |
Optional character string used as the plot title prefix. |
The procedure constructs pairs of base tasks within respondent, computes the proportion
of identical choices as a function of how many attributes differ between the two tasks,
fits a weighted regression of agreement on the number of differing attributes, and
extrapolates to zero differences to obtain .
A projoint_tau object (list-like) with components:
$irr: a tibble with columns x (number of differing attributes) and
predicted (fitted agreement), including x = 0 which is the estimate
of .
$figure: a ggplot2 object visualizing observed agreement by x
and the fitted line with the extrapolated point at x = 0.
plot.projoint_tau, summary.projoint_tau,
reshape_projoint
# Example workflow: data(exampleData1) outcomes <- c(paste0("choice", 1:8), "choice1_repeated_flipped") # Even if your real study lacks a repeated task, this shows the API: pj <- reshape_projoint(exampleData1, outcomes, .repeated = TRUE) tau_fit <- predict_tau(pj, .title = "IRR (tau): ") # Inspect the extrapolated tau (row where x == 0) tau_fit$irr[tau_fit$irr$x == 0, ] # Plot (also available via plot(tau_fit)) print(tau_fit$figure)# Example workflow: data(exampleData1) outcomes <- c(paste0("choice", 1:8), "choice1_repeated_flipped") # Even if your real study lacks a repeated task, this shows the API: pj <- reshape_projoint(exampleData1, outcomes, .repeated = TRUE) tau_fit <- predict_tau(pj, .title = "IRR (tau): ") # Inspect the extrapolated tau (row where x == 0) tau_fit$irr[tau_fit$irr$x == 0, ] # Plot (also available via plot(tau_fit)) print(tau_fit$figure)
Custom print method for objects of class projoint_data.
## S3 method for class 'projoint_data' print(x, ...)## S3 method for class 'projoint_data' print(x, ...)
x |
A |
... |
Additional arguments (currently unused). |
No return value, called for its side effect of printing a summary of
the projoint_data object to the console.
data(exampleData1) dat <- reshape_projoint( exampleData1, .outcomes = c("choice1", "choice2") ) print(dat)data(exampleData1) dat <- reshape_projoint( exampleData1, .outcomes = c("choice1", "choice2") ) print(dat)
Custom print method for objects of class projoint_results.
## S3 method for class 'projoint_results' print(x, ...)## S3 method for class 'projoint_results' print(x, ...)
x |
An object of class |
... |
Additional arguments (ignored). |
No return value, called for its side effect of printing a summary of
the projoint_results object to the console.
data(exampleData1) dat <- reshape_projoint( exampleData1, .outcomes = c("choice1", "choice2", "choice1_repeated_flipped") ) att <- unique(dat$labels$attribute_id)[1] levs <- subset(dat$labels, attribute_id == att)$level_id lev_names <- sub(".*:", "", levs) q <- set_qoi( .structure = "choice_level", .estimand = "mm", .att_choose = att, .lev_choose = lev_names[2], .att_notchoose = att, .lev_notchoose = lev_names[1] ) fit <- projoint(dat, .qoi = q) print(fit)data(exampleData1) dat <- reshape_projoint( exampleData1, .outcomes = c("choice1", "choice2", "choice1_repeated_flipped") ) att <- unique(dat$labels$attribute_id)[1] levs <- subset(dat$labels, attribute_id == att)$level_id lev_names <- sub(".*:", "", levs) q <- set_qoi( .structure = "choice_level", .estimand = "mm", .att_choose = att, .lev_choose = lev_names[2], .att_notchoose = att, .lev_notchoose = lev_names[1] ) fit <- projoint(dat, .qoi = q) print(fit)
Custom print method for objects of class projoint_tau, typically created
by projoint or related functions.
## S3 method for class 'projoint_tau' print(x, ...)## S3 method for class 'projoint_tau' print(x, ...)
x |
An object of class |
... |
Additional arguments (currently unused). |
No return value; called for its side effect of printing a summary of
the estimated intra-respondent reliability ().
toy_tau <- structure( list(irr = data.frame(predicted = 0.413, se = 0.02, n = 200)), class = "projoint_tau" ) print(toy_tau)toy_tau <- structure( list(irr = data.frame(predicted = 0.413, se = 0.02, n = 200)), class = "projoint_tau" ) print(toy_tau)
Computes marginal means (MMs) or average marginal component effects (AMCEs)
with correction for intra-respondent reliability (IRR, ). When a
repeated task is present, IRR is estimated unless a fixed value is supplied.
Results are returned in a structured object ready for plotting and summary.
projoint( .data, .qoi = NULL, .by_var = NULL, .structure = "choice_level", .estimand = "mm", .se_method = "analytical", .irr = NULL, .remove_ties = TRUE, .ignore_position = NULL, .n_sims = NULL, .n_boot = NULL, .weights_1 = NULL, .clusters_1 = NULL, .se_type_1 = NULL, .weights_2 = NULL, .clusters_2 = NULL, .se_type_2 = NULL, .auto_cluster = TRUE, .seed = NULL )projoint( .data, .qoi = NULL, .by_var = NULL, .structure = "choice_level", .estimand = "mm", .se_method = "analytical", .irr = NULL, .remove_ties = TRUE, .ignore_position = NULL, .n_sims = NULL, .n_boot = NULL, .weights_1 = NULL, .clusters_1 = NULL, .se_type_1 = NULL, .weights_2 = NULL, .clusters_2 = NULL, .se_type_2 = NULL, .auto_cluster = TRUE, .seed = NULL )
.data |
A |
.qoi |
Optional |
.by_var |
Optional column name (character) for subgroup analysis; must be
logical (TRUE/FALSE) or numeric/integer coded as 0/1. Only supported for
|
.structure |
Either |
.estimand |
Either |
.se_method |
Standard-error method: |
.irr |
Numeric or |
.remove_ties |
Logical; remove ties in choice data before estimation?
Default |
.ignore_position |
Logical; choice-level only. Ignore profile position
(left/right)? Default |
.n_sims |
Integer; required when |
.n_boot |
Integer; required when |
.weights_1, .clusters_1, .se_type_1
|
Passed to
|
.weights_2, .clusters_2, .se_type_2
|
Passed to
|
.auto_cluster |
Logical. If |
.seed |
Optional integer. Sets a temporary RNG seed for reproducible simulation/bootstrap inside the call; restores the previous RNG state on exit. |
Most users will pass a projoint_data object (from
reshape_projoint or make_projoint_data). Advanced
users may specify custom quantities via projoint_qoi; if provided,
its structure and estimand override .structure and
.estimand.
Valid se_type_* values depend on clustering:
Without clusters: "classical", "HC0", "HC1", "HC2", "HC3"
With clusters: "CR0", "CR1", "CR2", "stata", "none"
If NULL, estimatr defaults are used (HC2 when unclustered;
CR2 when clustered).
A projoint_results object (list-like) with components such as:
$estimand, $structure, $se_method, $irr, $tau
$labels: attribute/level mapping used in estimation
$estimates: a data frame of estimates with columns like
att_level_choose, att_level_notchoose (if choice-level),
estimate, se / std.error, conf.low, conf.high,
and an estimand label such as "mm_corrected" or "amce_uncorrected".
This object is suitable for downstream use in plot.projoint_results,
summary.projoint_results, and related helpers.
reshape_projoint, projoint_qoi,
plot.projoint_results, summary.projoint_results
# Prepare example data data(exampleData1) outcomes <- c(paste0("choice", 1:8), "choice1_repeated_flipped") pj <- reshape_projoint(exampleData1, outcomes) # Choice-level QoI based on pj$labels att <- unique(pj$labels$attribute_id)[1] lev_ids <- pj$labels$level_id[pj$labels$attribute_id == att] lev_names <- sub(".*:", "", lev_ids) q <- set_qoi( .structure = "choice_level", .estimand = "mm", .att_choose = att, .lev_choose = lev_names[2], .att_notchoose = att, .lev_notchoose = lev_names[1] ) # Choice-level, marginal means (fast example: fix IRR) fit_choice <- projoint( pj, .qoi = q, .structure = "choice_level", .estimand = "mm", .irr = 0.80, # skip IRR estimation for a quick example .se_method = "analytical" ) head(summary(fit_choice)) # Profile-level AMCEs fit_profile <- projoint( pj, .structure = "profile_level", .estimand = "amce", .se_method = "analytical" ) # Plot using the S3 plot method p <- plot(fit_profile, .estimates = "both") print(p)# Prepare example data data(exampleData1) outcomes <- c(paste0("choice", 1:8), "choice1_repeated_flipped") pj <- reshape_projoint(exampleData1, outcomes) # Choice-level QoI based on pj$labels att <- unique(pj$labels$attribute_id)[1] lev_ids <- pj$labels$level_id[pj$labels$attribute_id == att] lev_names <- sub(".*:", "", lev_ids) q <- set_qoi( .structure = "choice_level", .estimand = "mm", .att_choose = att, .lev_choose = lev_names[2], .att_notchoose = att, .lev_notchoose = lev_names[1] ) # Choice-level, marginal means (fast example: fix IRR) fit_choice <- projoint( pj, .qoi = q, .structure = "choice_level", .estimand = "mm", .irr = 0.80, # skip IRR estimation for a quick example .se_method = "analytical" ) head(summary(fit_choice)) # Profile-level AMCEs fit_profile <- projoint( pj, .structure = "profile_level", .estimand = "amce", .se_method = "analytical" ) # Plot using the S3 plot method p <- plot(fit_profile, .estimates = "both") print(p)
Reads a CSV containing a revised ordering of attributes and levels and applies it
to an existing projoint_data object. Typical workflow: first save the
current labels to CSV (e.g., with save_labels), manually reorder rows
(and/or the attribute grouping) in the CSV, then call read_labels() to apply.
read_labels(.data, .filename)read_labels(.data, .filename)
.data |
A |
.filename |
Path to the revised labels CSV (originally produced from the package's labels). |
A projoint_data object with the same content as .data but with
attributes and levels reordered to match the CSV. The returned object contains:
$labels: a tibble with new attribute_id and level_id reflecting the chosen order
$data: a tibble whose att* columns have been remapped to the new level_ids
# Create a projoint_data object from the example dataset data(exampleData1) outcomes <- c(paste0("choice", 1:8), "choice1_repeated_flipped") pj <- reshape_projoint(exampleData1, outcomes) # Write current labels to a temporary CSV, adding an 'order' column tmp <- tempfile(fileext = ".csv") pj$labels |> dplyr::mutate(order = dplyr::row_number()) |> readr::write_csv(tmp) # (User would reorder rows in 'tmp' manually; we just read it back) pj_reordered <- read_labels(pj, tmp) # Inspect the updated label order head(pj_reordered$labels)# Create a projoint_data object from the example dataset data(exampleData1) outcomes <- c(paste0("choice", 1:8), "choice1_repeated_flipped") pj <- reshape_projoint(exampleData1, outcomes) # Write current labels to a temporary CSV, adding an 'order' column tmp <- tempfile(fileext = ".csv") pj$labels |> dplyr::mutate(order = dplyr::row_number()) |> readr::write_csv(tmp) # (User would reorder rows in 'tmp' manually; we just read it back) pj_reordered <- read_labels(pj, tmp) # Inspect the updated label order head(pj_reordered$labels)
Reads a CSV file exported from Qualtrics (with "Use choice text" enabled) and
returns a data frame formatted for downstream processing with
reshape_projoint.
read_Qualtrics(.file)read_Qualtrics(.file)
.file |
A character string giving the path to a Qualtrics CSV file. |
A data frame where column names are preserved from the Qualtrics export. The first two rows of Qualtrics metadata are skipped automatically.
# Write a tiny dummy Qualtrics-style CSV to a temp file tmp <- tempfile(fileext = ".csv") readr::write_csv( data.frame(Q1 = c("Choice Text", "Choice Text", "A", "B")), tmp ) # Read it back in df <- read_Qualtrics(tmp) head(df)# Write a tiny dummy Qualtrics-style CSV to a temp file tmp <- tempfile(fileext = ".csv") readr::write_csv( data.frame(Q1 = c("Choice Text", "Choice Text", "A", "B")), tmp ) # Read it back in df <- read_Qualtrics(tmp) head(df)
Takes a wide survey data frame (e.g., from read_Qualtrics) and reshapes
it so that each row corresponds to a single respondent–task–profile. Supports arbitrary
ordering of base tasks and a single repeated task per respondent. The repeated base task
is inferred from the first base outcome in .outcomes, and the repeated outcome
must be the last element of .outcomes.
reshape_projoint( .dataframe, .outcomes, .choice_labels = c("A", "B"), .alphabet = "K", .idvar = "ResponseId", .repeated = TRUE, .flipped = TRUE, .covariates = NULL, .fill = FALSE )reshape_projoint( .dataframe, .outcomes, .choice_labels = c("A", "B"), .alphabet = "K", .idvar = "ResponseId", .repeated = TRUE, .flipped = TRUE, .covariates = NULL, .fill = FALSE )
.dataframe |
A data frame, preferably from |
.outcomes |
Character vector of outcome column names in the asked order. If a repeated task is used, its outcome must be the last element. |
.choice_labels |
Character vector (default |
.alphabet |
Single character (default |
.idvar |
Character (default |
.repeated |
Logical (default |
.flipped |
Logical (default |
.covariates |
Optional character vector of respondent-level covariate column names to carry through. |
.fill |
Logical (default |
Scope and assumptions
One set of conjoint tasks with exactly two profiles per task (profiles 1 and 2).
For multi-set designs, call reshape_projoint() once per set and bind the results.
Expected input (Qualtrics K-codes)
Wide columns named K-<task>-<attribute> (attribute names) and
K-<task>-<profile>-<attribute> (level names), with <task> in 1..n
and <profile> in 1,2.
Rows with missing K-1-1 are dropped as empty tables (server hiccup safeguard).
Outcome columns (.outcomes)
List all choice variables in the order asked. If you include a repeated task, its outcome must be the last element.
For base tasks (all but the last element), the function extracts the base task id by
reading the digits in each outcome name (e.g., "choice4", "Q4",
"task04" -> task 4).
The set of base task ids extracted from .outcomes must exactly match the set of
task ids present in the K-codes; otherwise an error is thrown.
The repeated base task is inferred as the digits in the first base outcome
(i.e., the first element of .outcomes, excluding the final repeated outcome).
Choice parsing
The selected profile is parsed from the last character of each outcome string
and matched to .choice_labels. Ensure outcomes end with these labels (e.g.,
"Candidate A"/"Candidate B"). If outcomes are numeric or differently
formatted, pre-process or adjust .choice_labels accordingly.
Output
A projoint_data object with:
$labels: map from human-readable attribute/level to stable ids
(attribute_id = "att1","att2",..., level_id = "attX:levelY").
$data: tibble with one row per id–task–profile, attribute
columns (named att*) storing level_id, selected (1 if that profile
was chosen; 0 otherwise), agree (1/0/NA for repeated-task agreement after flip logic),
and any .covariates. id is coerced to character; attribute columns are factors.
Filling agreement
If .fill = TRUE, agree is filled within respondent across tasks in task order,
propagating the observed repeated-task agreement to all tasks for that respondent. This assumes
IRR is respondent-specific and independent of table content.
Diagnostics
dplyr::count(reshaped$data, task, profile) should show exactly two rows per task.
If pj_estimate() later reports “No rows match the specified attribute/level”, construct QoIs
from reshaped$labels (use the exact attX:levelY ids).
A projoint_data object with elements $labels and $data; see Details.
# Base tasks asked in numeric order; repeated task corresponds to task 1 data(exampleData1) outcomes <- c(paste0("choice", 1:8), "choice1_repeated_flipped") reshaped <- reshape_projoint(exampleData1, outcomes) dplyr::count(reshaped$data, task, profile) # should be 2 per task# Base tasks asked in numeric order; repeated task corresponds to task 1 data(exampleData1) outcomes <- c(paste0("choice", 1:8), "choice1_repeated_flipped") reshaped <- reshape_projoint(exampleData1, outcomes) dplyr::count(reshaped$data, task, profile) # should be 2 per task
Saves the attributes and levels (and their order) from a projoint_data
object, as generated by reshape_projoint, to a CSV file. This
enables manual reordering and later re-import via read_labels.
save_labels(.data, .filename)save_labels(.data, .filename)
.data |
A |
.filename |
A character string giving the name of a CSV file to be written. |
No return value, called for side effects (writes a CSV file).
library(projoint) data(exampleData1) reshaped <- reshape_projoint( exampleData1, .outcomes = c(paste0("choice", 1:8), "choice1_repeated_flipped") ) tmpfile <- tempfile(fileext = ".csv") save_labels(reshaped, tmpfile) readLines(tmpfile, n = 5) # show first few lineslibrary(projoint) data(exampleData1) reshaped <- reshape_projoint( exampleData1, .outcomes = c(paste0("choice", 1:8), "choice1_repeated_flipped") ) tmpfile <- tempfile(fileext = ".csv") save_labels(reshaped, tmpfile) readLines(tmpfile, n = 5) # show first few lines
Constructs a quantities-of-interest (QoI) specification for projoint. Use this to request specific estimands—marginal means (MMs) or average marginal component effects (AMCEs)—at either the choice- or profile-level, and to declare which attribute levels are compared (including baselines).
set_qoi( .structure = "choice_level", .estimand = "mm", .att_choose, .lev_choose, .att_notchoose = NULL, .lev_notchoose = NULL, .att_choose_b = NULL, .lev_choose_b = NULL, .att_notchoose_b = NULL, .lev_notchoose_b = NULL )set_qoi( .structure = "choice_level", .estimand = "mm", .att_choose, .lev_choose, .att_notchoose = NULL, .lev_notchoose = NULL, .att_choose_b = NULL, .lev_choose_b = NULL, .att_notchoose_b = NULL, .lev_notchoose_b = NULL )
.structure |
Either |
.estimand |
Either |
.att_choose |
Character scalar: the attribute (column) for the level(s) that are chosen. |
.lev_choose |
Character vector: the level id(s) for the chosen
side. Length 1 for profile-level, |
.att_notchoose |
Character scalar: the attribute (column) for the level(s)
that are not chosen. Only used for |
.lev_notchoose |
Character vector: the level id(s) for the not chosen
side. Length 1 for profile-level, |
.att_choose_b |
Character scalar: baseline attribute for the chosen side when computing AMCEs. |
.lev_choose_b |
Character vector: baseline level id(s) for the
chosen side when computing AMCEs. Length 1 for profile-level, |
.att_notchoose_b |
Character scalar: baseline attribute for the not-chosen side (choice-level only) when computing AMCEs. |
.lev_notchoose_b |
Character vector: baseline level id(s) for the not-chosen side (choice-level only) when computing AMCEs. |
A projoint_qoi object (list-like) containing fields such as:
structure, estimand, attribute_of_interest,
levels_of_interest, and their baseline counterparts. This object
can be supplied to downstream estimation helpers that accept a QoI spec.
# Specify a simple choice-level MM comparison for att1 levels: q_mm <- set_qoi( .structure = "choice_level", .estimand = "mm", .att_choose = "att1", .lev_choose = c("att1:lev2"), .att_notchoose = "att1", .lev_notchoose = c("att1:lev1") ) str(q_mm) # Example AMCE with explicit baselines (profile-level): q_amce <- set_qoi( .structure = "profile_level", .estimand = "amce", .att_choose = "att2", .lev_choose = "att2:lev3", .att_choose_b = "att2", .lev_choose_b = "att2:lev1" ) str(q_amce)# Specify a simple choice-level MM comparison for att1 levels: q_mm <- set_qoi( .structure = "choice_level", .estimand = "mm", .att_choose = "att1", .lev_choose = c("att1:lev2"), .att_notchoose = "att1", .lev_notchoose = c("att1:lev1") ) str(q_mm) # Example AMCE with explicit baselines (profile-level): q_amce <- set_qoi( .structure = "profile_level", .estimand = "amce", .att_choose = "att2", .lev_choose = "att2:lev3", .att_choose_b = "att2", .lev_choose_b = "att2:lev1" ) str(q_amce)
projoint_data
Custom summary method for objects of class projoint_data. Prints a brief
overview of the main data and attribute-level labels contained in the object.
## S3 method for class 'projoint_data' summary(object, ...)## S3 method for class 'projoint_data' summary(object, ...)
object |
A |
... |
Additional arguments (currently unused). |
No return value, called for its side effect of printing a summary of the
projoint_data object to the console.
data(exampleData1) dat <- reshape_projoint( exampleData1, .outcomes = c("choice1", "choice2") ) summary(dat)data(exampleData1) dat <- reshape_projoint( exampleData1, .outcomes = c("choice1", "choice2") ) summary(dat)
projoint_results
Creates a concise tabular summary of a projoint_results object,
including the chosen estimand, analysis structure, standard-error settings,
and a data frame of estimates.
## S3 method for class 'projoint_results' summary(object, ...)## S3 method for class 'projoint_results' summary(object, ...)
object |
An object of class |
... |
Additional arguments (ignored). |
A data frame (often a tibble) summarizing the estimated effects.
At minimum, it contains the columns produced in object$estimates
(e.g., attribute/level identifiers and the point estimate with its
standard error and confidence interval in columns such as
estimate, std.error, conf.low, conf.high).
This table is suitable for further processing or printing.
data(exampleData1) # Reshape data for two base tasks + repeated (for IRR estimation) dat <- reshape_projoint( exampleData1, .outcomes = c("choice1", "choice2", "choice1_repeated_flipped") ) # Build a valid choice-level QoI att <- unique(dat$labels$attribute_id)[1] lev_ids <- dat$labels$level_id[dat$labels$attribute_id == att] lev_names <- sub(".*:", "", lev_ids) q <- set_qoi( .structure = "choice_level", .estimand = "mm", .att_choose = att, .lev_choose = lev_names[2], .att_notchoose = att, .lev_notchoose = lev_names[1] ) # Fit model fit <- projoint(dat, .qoi = q) # Get the tabular summary of estimates tab <- summary(fit) head(tab)data(exampleData1) # Reshape data for two base tasks + repeated (for IRR estimation) dat <- reshape_projoint( exampleData1, .outcomes = c("choice1", "choice2", "choice1_repeated_flipped") ) # Build a valid choice-level QoI att <- unique(dat$labels$attribute_id)[1] lev_ids <- dat$labels$level_id[dat$labels$attribute_id == att] lev_names <- sub(".*:", "", lev_ids) q <- set_qoi( .structure = "choice_level", .estimand = "mm", .att_choose = att, .lev_choose = lev_names[2], .att_notchoose = att, .lev_notchoose = lev_names[1] ) # Fit model fit <- projoint(dat, .qoi = q) # Get the tabular summary of estimates tab <- summary(fit) head(tab)
Custom summary method for objects of class projoint_tau, typically created
by projoint or related functions. Summarizes intra-respondent
reliability (IRR) estimates.
## S3 method for class 'projoint_tau' summary(object, ...)## S3 method for class 'projoint_tau' summary(object, ...)
object |
An object of class |
... |
Additional arguments (currently unused). |
A tibble (data frame) showing IRR estimates, typically by the number
of differing attributes, as stored in object$irr.
toy_tau <- structure( list(irr = data.frame(predicted = 0.413, se = 0.02, n = 200)), class = "projoint_tau" ) summary(toy_tau)toy_tau <- structure( list(irr = data.frame(predicted = 0.413, se = 0.02, n = 200)), class = "projoint_tau" ) summary(toy_tau)