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.
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_varianceviaapply_priorand see how the prior shapes the embedding. - weights.ipynb — per-(user, item)
weightsand thescale_weightsflag for sparse responses. - features.ipynb — non-linear decision boundaries via the
kernelargument (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, andsampling_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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bf1f647f1dd98313af28af7e74c0a602d7b9e3d2fa294e34cb760ed21958dc22
|
|
| MD5 |
6fdc46ff7e9159cb9423430f665b6c89
|
|
| BLAKE2b-256 |
e84be26642993a0b19712359ba38bde71a4ffe09f3f8bae9c9609a3cd96e75cf
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e0da6d42214ddde957f1afd97f7ee48233107036d786862c8dd8a67b84113392
|
|
| MD5 |
ea3fae479c1c82a8c73611bb30c76fb9
|
|
| BLAKE2b-256 |
172c28ef56cb22dd8ae85cf16bd2a95760b5e7ced22742a2c4a85b5063c173c2
|