ReMoDe#
ReMoDe (Reproducible Mode Detection) is a method for finding meaningful peaks (modes) in ordinal or discrete count data.
This is useful in many applied settings, for example identifying polarization in survey responses or separating symptom-incidence groups in clinical scales. Existing modality methods are often descriptive only or not designed for ordinal data. ReMoDe addresses this by combining recursive mode detection with statistical significance testing.
For each detected mode, ReMoDe reports statistical evidence (p-values and approximate Bayes factors) and includes a stability check to assess how robust results are to data perturbations.
Install#
pip install remode
Quickstart#
from remode import ReMoDe
xt_count = [8, 20, 5, 2, 6, 2, 30]
remode = ReMoDe()
result = remode.fit(xt_count)
print(result)
from remode import *
import numpy as np
import matplotlib.pyplot as plt
Tutorial#
## Histogram counts
xt_count = [8, 20, 5, 2, 6, 2, 30]
# Start ReMoDe
remode = ReMoDe( definition="shape_based", # default; set "peak_based" to exclude uniform distributions
)
# Fit the data and plot maxima
result = remode.fit(xt_count)
print(result)
remode.plot_maxima()
{'nr_of_modes': 2, 'modes': array([1, 6]), 'xt': [8, 20, 5, 2, 6, 2, 30], 'alpha_after_correction': 0.05}
# Test robustness using jackknife
robustness = remode.evaluate_stability(percentage_steps=50)
print(robustness["stable_until"])
81.63265306122449
## Converting individual points to count data
# Generate random data
np.random.seed(0 definition="shape_based", # default; set "peak_based" to exclude uniform distributions
)
data = np.random.choice(range(3, 8), 200, p=[0.2, 0.2, 0.3, 0.2, 0.1])
# Start ReMoDe
remode = ReMoDe()
# Create count data
xt_count = remode.format_data(data)
# Fit the data and plot maxima
result = remode.fit(xt_count, levels=np.arange(3, 8))
print(result)
remode.plot_maxima()
{'nr_of_modes': 1, 'modes': array([2]), 'xt': array([41, 34, 71, 36, 18]), 'alpha_after_correction': 0.05}
Configuration Options for ReMoDe()#
remode = ReMoDe(
alpha=0.05, # alpha level desired (adjusted recursively for multiple testing)
alpha_correction="descriptive_peaks", # default; or "max_modes", "none", or custom function
statistical_test="bootstrap", # default; or "fisher", "binomial", or custom function
definition="shape_based", # default; set "peak_based" to exclude uniform distributions
)
remode = ReMoDe(
alpha=0.05,
alpha_correction="max_modes", # alternative built-in correction
statistical_test="fisher",
definition="peak_based",
)
Uniformity Test (peak_based)#
With definition="peak_based", ReMoDe applies a Pearson Chi-square test for uniformity.
If the distribution is not significantly different from uniform (p > 0.05), it returns zero modes.
uniform_counts = np.array([10, 10, 10, 10, 10])
uniform_result = ReMoDe(
statistical_test="fisher",
definition="peak_based",
).fit(uniform_counts)
print("Number of modes:", uniform_result["nr_of_modes"])
print("Uniform distribution:", uniform_result["uniform_distribution"])
print("Chi-square p-value:", uniform_result["uniformity_p_value"])
# Example of custom statistical test (this is actually `perform_binomial_test`)
from scipy.stats import binomtest
def custom_test(x, candidate, left_min, right_min):
"""Return left/right p-values for a candidate maximum."""
n_left = x[candidate] + x[left_min]
n_right = x[candidate] + x[right_min]
p_left = binomtest(x[candidate], n_left, alternative="greater").pvalue
p_right = binomtest(x[candidate], n_right, alternative="greater").pvalue
return p_left, p_right
# Example of custom alpha adjustment
def custom_adjustment(segment_or_length, alpha):
"""Adjust alpha as a function of segment size."""
if hasattr(segment_or_length, "__len__"):
length = len(segment_or_length)
else:
length = int(segment_or_length)
return alpha / max(1, length)
remode = ReMoDe(
alpha=0.05,
statistical_test=custom_test,
alpha_correction=custom_adjustment,
)