MyNixOS website logo
Description

Simulate the Multi-Attribute Search and Choice (MASC) Model.

Simulates the Multi-Attribute Search and Choice (MASC) model of Gluth, Deakin and Rieskamp (2026) <doi:10.1037/rev0000614> for multi-attribute decision-making, including sequential information search, Bayesian belief updating, and choice. Beliefs may be treated as univariate (independent attributes), or multivariate over correlated attributes ('MASC-C'), in which observing one attribute updates beliefs about correlated attributes via a Kalman filter.

masc: Multi-Attribute Search and Choice Model in R

Lifecycle:experimental

Overview

The masc package implements the Multi-Attribute Search and Choice (MASC) model, a hierarchical Bayesian framework for understanding how people make decisions between options with multiple attributes. Based on the work by Gluth, Deakin, & Rieskamp (2026), this package simulates:

  • Information search patterns in multi-attribute decisions
  • Belief updating about attribute values (univariate, or multivariate over correlated attributes via MASC-C)
  • Choice dynamics and decision termination
  • The interplay between attention and valuation

Installation

# Install from CRAN
install.packages("masc")

# Or the development version from GitHub
devtools::install_github("kiante-fernandez/masc")

Basic Usage

Simple Random Trials

library(masc)

# Generate 5 random trials
results <- rMASC(
  n = 5,
  w = c(0.5, 0.3, 0.2)  # weights for attributes
)

# View results
print(results$results)

Custom Attribute Values

# Create trial data
trial_data <- data.frame(
  # Option 1's attributes across 3 trials
  opt1_att1 = c(4.5, 4.2, 4.8),  # Attribute 1 values
  opt1_att2 = c(3.2, 3.5, 3.1),  # Attribute 2 values
  opt1_att3 = c(2.8, 2.9, 2.7),  # Attribute 3 values
  # Option 2's attributes across 3 trials
  opt2_att1 = c(3.8, 3.9, 3.7),  # Attribute 1 values
  opt2_att2 = c(4.1, 4.0, 4.2),  # Attribute 2 values
  opt2_att3 = c(3.1, 3.3, 3.0)   # Attribute 3 values
)

# Run model with custom data
results <- rMASC(
  data = trial_data,
  w = c(0.5, 0.3, 0.2)  # weights for attributes
)

Correlated Attributes (MASC-C)

By default attributes are treated as independent, reproducing the original MASC model. Supplying a correlation structure switches on the multivariate MASC-C belief update, in which observing one attribute updates beliefs about correlated attributes (“belief spread”). Positive correlations speed up decisions; negative correlations slow them down.

# Decision maker exploits a positive correlation structure (rho = 0.6)
results <- rMASC(
  n = 100,
  w = c(0.5, 0.3, 0.2),
  Sigma_true   = 0.6,   # stimuli are positively correlated
  Sigma_belief = 0.6    # and the agent knows it (matched beliefs)
)

# Full correlation matrices are also accepted, and the agent's beliefs may
# differ from the true environment (belief-environment mismatch):
Sigma <- matrix(c(1.0, 0.5, -0.3,
                  0.5, 1.0,  0.0,
                 -0.3, 0.0,  1.0), 3, 3, byrow = TRUE)
results <- rMASC(n = 100, w = c(0.5, 0.3, 0.2),
                 Sigma_true = Sigma, Sigma_belief = 0)  # agent assumes independence

When Sigma_belief is diagonal (or 0) the model reduces exactly to the original univariate MASC update.

Example use

library(masc)
library(ggplot2)
library(dplyr)
library(tidyr)
library(patchwork)

# Set simulation parameters
params <- list(
  n = 100,              # Number of trials
  n_options = 2,        # Number of choice options
  n_attributes = 3,     # Number of attributes per option
  w = c(0.5, 0.3, 0.2), # Weights for each attribute
  sigma = 1,            # Sampling noise
  alpha = 3,            # Search sensitivity
  delta = 0.01,         # Threshold increment
  theta = 0.01,         # Initial threshold
  lambda = 1,           # Prior precision
  max_steps = 100       # Maximum fixations allowed
)

# Run simulation
set.seed(123)
results <- do.call(rMASC, params)

# Process choice proportions
choice_props <- results$results %>%
  mutate(Choice = factor(response, labels = c("Option1", "Option2"))) %>%
  group_by(Choice) %>%
  summarise(Proportion = n()/100)

# Process fixation data
total_counts <- numeric(params$n_options * params$n_attributes)
for(trial in results$raw) {
  fix_seq <- trial$fix_sequence
  trial_counts <- table(factor(fix_seq, 
                               levels = 1:(params$n_options * params$n_attributes)))
  total_counts <- total_counts + as.numeric(trial_counts)
}

# Create Option-Attribute-Pair (OAP) data
all_props <- total_counts / sum(total_counts)
oap_data <- data.frame(Proportion = all_props) %>%
  mutate(oap_number = 1:length(all_props)) %>%
  mutate(
    Option = paste0("Opt", ceiling(oap_number/3)),
    Attribute = paste0("Att", ((oap_number-1) %% 3) + 1)
  ) %>%
  select(Option, Attribute, Proportion)

# Define consistent theme
theme_masc <- function() {
  theme_classic() +
    theme(
      plot.title = element_text(size = 16, face = "bold", hjust = 0.5),
      axis.title = element_text(size = 12, face = "bold"),
      axis.text = element_text(size = 11),
      legend.position = "bottom"
    )
}

# Create visualizations
# 1. Fixation Distribution
p1 <- ggplot(results$results, aes(x = rt, fill = factor(response))) +
  geom_histogram(position = "dodge", bins = 30, color = "white") +
  scale_fill_manual(values = c("#2171B5", "#CB181D"),
                    labels = c("Choice = Option1", "Choice = Option2")) +
  labs(title = "Number of Fixations", x = "Number of Fixations", y = "Count") +
  theme_masc()

# 2. Choice Probability
p2 <- ggplot(choice_props, aes(x = "", y = Proportion, fill = Choice)) +
  geom_bar(stat = "identity", width = 0.6) +
  geom_text(aes(label = sprintf("%.2f", Proportion)), 
            position = position_stack(vjust = 0.5),
            color = "white", 
            size = 4) +
  scale_fill_manual(values = c("#2171B5", "#CB181D")) +
  labs(title = "Choice Probability", y = "Proportion", x = "Response") +
  theme_masc() +
  scale_y_continuous(limits = c(0, 1), breaks = seq(0, 1, 0.1))

# 3. OAP Heatmap
p3 <- ggplot(oap_data, aes(x = Attribute, y = Option, fill = Proportion)) +
  geom_tile(color = "white") +
  scale_fill_distiller(palette = "Blues", direction = 1) +
  geom_text(aes(label = sprintf("%.4f", Proportion)), size = 4) +
  labs(title = "Fixation Proportions by Option-Attribute Pair") +
  theme_masc()

# Combine plots
combined_plot <- p1 + p2 + p3 +
  plot_layout(ncol = 2) +
  plot_annotation(
    title = "Example: MASC Model Simulation Results",
    theme = theme(plot.title = element_text(size = 20, face = "bold", hjust = 0.5))
  )

# Display plot
print(combined_plot)

Function Parameters

The rMASC() function accepts the following parameters:

  • data: Optional data frame with trial-wise attribute values
  • n: Number of trials to generate when data is NULL (default: 1)
  • n_options: Number of choice options (default: 2)
  • n_attributes: Number of attributes per option (default: 3)
  • w: Attribute weights summing to 1 (if NULL, randomly generated)
  • sigma: Standard deviation of sampling noise (default: 1)
  • alpha: Search rule sensitivity (default: 3)
  • delta: Decision threshold increase per fixation (default: 0.01)
  • theta: Initial decision threshold (default: 0.01)
  • lambda: Precision of prior beliefs about attributes (default: 1)
  • max_steps: Maximum number of fixations allowed (default: 100)
  • Sigma_true: Correlation/covariance structure of the generated stimuli — a matrix, or a single number giving a uniform off-diagonal correlation (default: NULL, i.e. independent attributes). Ignored when data is supplied.
  • Sigma_belief: The decision maker’s assumed correlation structure between attributes. This enables the multivariate (“MASC-C”) belief update, where observing one attribute spreads information to correlated attributes. NULL (default) matches Sigma_true; 0 forces independent (original MASC) beliefs.

Output Structure

The function returns a list containing:

  • results: Data frame with trial-by-trial results including:
    • trial number
    • chosen option
    • best option
    • choice consistency
    • number of fixations (rt)
    • fixation proportions
  • weights: Vector of attribute weights used
  • parameters: List of model parameters used
  • raw: List containing detailed raw data for each trial

The raw component contains a list where each element corresponds to a trial and includes:

  • trial: Trial number
  • response: The option chosen by the model (1 to n_options)
  • best_option: The option with the highest weighted value
  • correct: Boolean indicating if response matches best_option
  • rt: Number of fixations taken to reach a decision
  • x: Matrix of true attribute values for all options
  • opt_values: Vector of computed option values (weighted sums)
  • weights: Vector of attribute weights used in this trial
  • sigma: Sampling noise parameter used
  • alpha: Search sensitivity parameter used
  • delta: Threshold increment parameter used
  • theta: Initial threshold parameter used
  • fix_sequence: Vector showing the sequence of fixations made
  • prop_fix_opt: Vector of proportions of fixations to each option
  • prop_fix_att: Vector of proportions of fixations to each attribute

Reference

Gluth, S., Deakin, J., & Rieskamp, J. (2026). A theory of multiattribute search and choice. Psychological Review. https://doi.org/10.1037/rev0000614

License

This package is released under the MIT License.

Metadata

Version

0.1.0

License

Unknown

Platforms (79)

    Darwin
    FreeBSD
    Genode
    GHCJS
    Linux
    MMIXware
    NetBSD
    none
    OpenBSD
    Redox
    Solaris
    uefi
    wasip1
    Windows
Show all
  • aarch64-darwin
  • aarch64-freebsd
  • aarch64-genode
  • aarch64-linux
  • aarch64-netbsd
  • aarch64-none
  • aarch64-uefi
  • aarch64-windows
  • aarch64_be-none
  • arc-linux
  • arm-none
  • armv5tel-linux
  • armv6l-linux
  • armv6l-netbsd
  • armv6l-none
  • armv7a-linux
  • armv7a-netbsd
  • armv7l-linux
  • armv7l-netbsd
  • avr-none
  • i686-cygwin
  • i686-freebsd
  • i686-genode
  • i686-linux
  • i686-netbsd
  • i686-none
  • i686-openbsd
  • i686-windows
  • javascript-ghcjs
  • loongarch64-linux
  • m68k-linux
  • m68k-netbsd
  • m68k-none
  • microblaze-linux
  • microblaze-none
  • microblazeel-linux
  • microblazeel-none
  • mips-linux
  • mips-none
  • mips64-linux
  • mips64-none
  • mips64el-linux
  • mipsel-linux
  • mipsel-netbsd
  • mmix-mmixware
  • msp430-none
  • or1k-none
  • powerpc-linux
  • powerpc-netbsd
  • powerpc-none
  • powerpc64-linux
  • powerpc64le-linux
  • powerpcle-none
  • riscv32-linux
  • riscv32-netbsd
  • riscv32-none
  • riscv64-linux
  • riscv64-netbsd
  • riscv64-none
  • rx-none
  • s390-linux
  • s390-none
  • s390x-linux
  • s390x-none
  • sh4-linux
  • vc4-none
  • wasm32-wasip1
  • wasm64-wasip1
  • x86_64-cygwin
  • x86_64-freebsd
  • x86_64-genode
  • x86_64-linux
  • x86_64-netbsd
  • x86_64-none
  • x86_64-openbsd
  • x86_64-redox
  • x86_64-solaris
  • x86_64-uefi
  • x86_64-windows