Skip to main content

IXPLORE

Bounded Ideal Point Estimation with Grid-Based Uncertainty Quantification: A Python package for embedding users and questionnaire items in a shared 2D latent space.

IXPLORE overview

Overview

IXPLORE is a Python package that jointly embeds users and questionnaire items in a shared 2D latent space. It is designed for user-item reaction matrices commonly found in political questionnaires, where each row represents a user and each column represents an item (question). Responses can be binary (agree/disagree) or Likert-scale values, which are automatically normalized to the [0, 1] range.

The core idea is simple: a user's position in 2D space should predict their responses to all items. Each item defines a logistic regression decision boundary in this space, separating regions of agreement from disagreement. The model iteratively refines both the user positions and the item boundaries until they are mutually consistent.

IXPLORE produces:

  • User embeddings (N x 2): a 2D coordinate for each user representing their latent preferences
  • Item parameters (K x 3): logistic regression coefficients (beta1, beta2, intercept) defining each item's decision boundary
  • Posterior distributions: full probability distributions over the 2D space quantifying uncertainty about each user's position

This enables interpretable visualization of preference landscapes, principled uncertainty quantification, and missing value imputation grounded in the learned geometry.

For the design rationale behind these choices, see docs/motivation.md. For a feature-by-feature walkthrough and the full API reference, see docs/documentation.md.

Features

  • User Embedding: Compute posterior distributions for users based on their reactions
  • Item Models: Define decision boundaries for each question with logistic regression models
  • Iterative Refinement: Jointly optimize user embeddings and item models through iterative updates
  • Flexible Initialization: Initialize embeddings via PCA, random values, or load pretrained embeddings
  • Missing Data Handling: Robust to missing values in the user-item reaction matrix
  • Answer Imputation: Predict answers based on positions in latent space
  • New User Embedding: Embed new users in the latent space with uncertainty quantification
  • Visualization Tools: Built-in plotting functions for embeddings, posteriors, and item decision boundaries

Installation

pip install ixplore

Or install from source:

git clone https://github.com/fsvbach/ixplore.git
cd ixplore
pip install -e .

Quick Start

import pandas as pd
from ixplore import IXPLORE

# Load reaction data (users × items matrix, values in {0, 1} or Likert-scale)
reactions = pd.read_csv('../data/likert_reactions.csv', index_col=0)

# Initialize and fit the model
model = IXPLORE(reactions, pca_initialization=True)

# Refine with a few iterations of joint optimization
model.iterate(n_iterations=1)

# Get user embeddings and item parameters
embedding  = model.get_embedding()    # User positions (N × 2) with columns ['x', 'y']
parameters = model.get_parameters()   # Item parameters (K × 3): ['beta1', 'beta2', 'alpha']

# Embed a new user based on their answers
new_user_answers = pd.Series({'Q1': 0.8, 'Q2': 0.2, 'Q3': 0.6}, name='new_user')
position = model.embed(new_user_answers)

# Impute all answers for a user
predicted = model.impute_answers(new_user_answers)

Custom Configuration

import numpy as np

model = IXPLORE(
    reactions,
    prior_variance=1.0,                         # Prior regularization
    sampling_resolution=200,                    # Grid resolution for posteriors
    limits=(-1, 1),                             # Bounds for both axes (square space)
    pca_initialization=True,                    # Initialize with PCA
    random_state=17                             # For reproducibility
)

Loading Pretrained Models

# Load pretrained embedding and model parameters
pretrained_embedding = pd.read_csv('../data/pretrained_embedding_likert.csv', index_col=0)
pretrained_models = pd.read_csv('../data/pretrained_models_likert.csv', index_col=0)

model = IXPLORE(
    reactions,
    pretrained_embedding=pretrained_embedding,
    pretrained_models=pretrained_models
)

Visualization

from ixplore.visualization import plot_overview

# Load user metadata (e.g., colors for plotting)
users = pd.read_csv('../data/synthetic_users.csv', index_col=0)

# Plot user embeddings
_ = plot_overview(model, question='Q12', user='159', colors=users.color)

For the full constructor parameters and method reference, see docs/documentation.md.

Notebooks

Runnable examples live in notebooks/:

  • demo.ipynb — end-to-end workflow: load data, fit, visualize, embed new users, impute answers.
  • data.ipynb — generate the synthetic users, binary/Likert/categorical reactions used by the other notebooks.
  • prior.ipynb — sweep prior_variance via apply_prior and see how the prior shapes the embedding.
  • weights.ipynb — per-(user, item) weights and the scale_weights flag for sparse responses.
  • features.ipynb — non-linear decision boundaries via the kernel argument (interaction, quadratic, RFF) on categorical data.
  • distortion.ipynb — quantify latent-space distortion with ixplore.metrics.compute_distortion.
  • timing.ipynb — runtime breakdown and scaling in n_users, n_items, and sampling_resolution.

Dependencies

  • numpy
  • pandas
  • scikit-learn
  • scipy
  • matplotlib

License

This project is licensed under the MIT License - see the LICENSE file for details.

Citation

If you use IXPLORE in your research, please cite:

@software{bachmann2026ixplore,
  author       = {Bachmann, Fynn},
  title        = {IXPLORE: Bounded Ideal Point Estimation with Grid-Based Uncertainty Quantification},
  year         = {2026},
  publisher    = {GitHub},
  url          = {https://github.com/fsvbach/ixplore}
}

Or in text format:

Bachmann, F. (2026). IXPLORE: Bounded Ideal Point Estimation with Grid-Based Uncertainty Quantification. GitHub. https://github.com/fsvbach/ixplore

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ixplore-1.2.tar.gz (22.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ixplore-1.2-py3-none-any.whl (21.1 kB view details)

Uploaded Python 3

File details

Details for the file ixplore-1.2.tar.gz.

File metadata

  • Download URL: ixplore-1.2.tar.gz
  • Upload date:
  • Size: 22.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for ixplore-1.2.tar.gz
Algorithm Hash digest
SHA256 1d7b2525b0bfd8ad09aed98f130222e0578d6924c392c445bbea25279bbb0a5a
MD5 6517817ee0dd0c54cdc0ce59150f3864
BLAKE2b-256 38980992d86b0d28619b2f41092bc4baa7b1a47204721ebe4cc9927f7117d57a

See more details on using hashes here.

File details

Details for the file ixplore-1.2-py3-none-any.whl.

File metadata

  • Download URL: ixplore-1.2-py3-none-any.whl
  • Upload date:
  • Size: 21.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for ixplore-1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 ee17fdb87c0250641081ac30022504778c8fbf9d96f16acdc770d231db90a579
MD5 582e08300b4d00dd0662a39de3a7fc22
BLAKE2b-256 e416afe2d36f6cb079ece56e985e09739ac40196db6875849121eacf7f849828

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

1.2 This release

2 files

1.1

2 files

1.0

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page