Skip to main content

Bounded Ideal Point Estimation with Iterative Logistic Regression Embeddings

Project description

IXPLORE

Bounded Ideal Point Estimation with Iterative Logistic Regression Embeddings: 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 Iterative Logistic Regression Embeddings},
  year         = {2026},
  publisher    = {GitHub},
  url          = {https://github.com/fsvbach/ixplore}
}

Or in text format:

Bachmann, F. (2026). IXPLORE: Bounded Ideal Point Estimation with Iterative Logistic Regression Embeddings. GitHub. https://github.com/fsvbach/ixplore

Project details


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.1.tar.gz (22.7 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.1-py3-none-any.whl (21.0 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for ixplore-1.1.tar.gz
Algorithm Hash digest
SHA256 bf1f647f1dd98313af28af7e74c0a602d7b9e3d2fa294e34cb760ed21958dc22
MD5 6fdc46ff7e9159cb9423430f665b6c89
BLAKE2b-256 e84be26642993a0b19712359ba38bde71a4ffe09f3f8bae9c9609a3cd96e75cf

See more details on using hashes here.

File details

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

File metadata

  • Download URL: ixplore-1.1-py3-none-any.whl
  • Upload date:
  • Size: 21.0 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 e0da6d42214ddde957f1afd97f7ee48233107036d786862c8dd8a67b84113392
MD5 ea3fae479c1c82a8c73611bb30c76fb9
BLAKE2b-256 172c28ef56cb22dd8ae85cf16bd2a95760b5e7ced22742a2c4a85b5063c173c2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page