Repository files navigation

📊 ReadyToFit — PeakFit Functions

A flexible and modular Python toolkit for multi-peak curve fitting, parameter management, visualization, and area analysis using scipy.optimize.curve_fit.

⭐ Author Notes

I had to dig into scipy too many times over the years without using it on a daily basis. I decided that I needed a more handy peak-fitting tool (based on scipy) that would speed up my workflow. This project is designed as a modular research-grade fitting system, not a black-box tool. Each component can be reused independently in scientific workflows.

This package is designed to handle arbitrary numbers of peaks with different models (Gaussian, Voigt, Asymmetric, Skewed), including support for fixed parameters (e.g., fixed peak centers μ).

🚀 Features

🔹 Automatic peak detection — Intelligently finds peaks and estimates initial parameters
🔹 Multi-peak fitting with arbitrary peak count
🔹 Multiple peak models:
- Gaussian
- Voigt
- Asymmetric
- Skewed
🔹 Support for fixed parameters (e.g., μ fixed per peak)
🔹 Automatic parameter flattening/unflattening
🔹 Intelligent initial guess generation from data
🔹 Flexible bounds handling
🔹 Full peak decomposition after fitting
🔹 Area integration for peaks and total signal
🔹 Clean visualization with residuals and RMSE

⚙️ Installation

Install from source:

git clone https://github.com/your-username/ReadyToFit.git
cd ReadyToFit
pip install .

Or install directly from GitHub:

pip install git+https://github.com/your-username/ReadyToFit.git

Dependencies: numpy, scipy, matplotlib (automatically installed).

🧠 Core Concept

Each peak is defined as a dictionary:

peaks= [
{"model": "gauss"},
{"model": "voigt", "mu": 50}, # fixed center at x=50
{"model": "asym"}
]

The system automatically:

  • Detects peaks in data using scipy.signal.find_peaks
  • Estimates initial parameters (amplitude, position, width) from data
  • Builds the composite model (sum of all peaks)
  • Flattens parameters for optimization
  • Handles fixed parameters (removed from optimization, reinstated after fit)
  • Reconstructs fitted peaks

🔍 Automatic p0 Generation & Peak Detection

The Problem: Multi-peak fitting requires good initial guesses (p0). Bad guesses lead to poor convergence or fitting the wrong peaks.

The Solution: ReadyToFit includes intelligent automatic peak detection:

  1. Peak Detection (detect_peaks()):

    • Finds local maxima using local gradient analysis
    • Filters by prominence (relative height above baseline)
    • Estimates peak positions, amplitudes, and widths
  2. Parameter Estimation (estimate_initial_parameters()):

    • Converts detected peaks into initial parameter guesses
    • Estimates widths from Full-Width-Half-Maximum (FWHM)
    • Respects user-defined fixed parameters (not overridden)
  3. Automatic p0:

    • When fit_model() is called without p0, it auto-generates it
    • Much more robust than naive guesses (e.g., same μ for all peaks)
    • Can be overridden by providing manual p0 if needed

Example:

fromreadytofitimportdetect_peaks# Detect peaks manually (optional — fit_model() does this automatically)detected=detect_peaks(x, y, n_peaks=3)
# Returns: [{"mu": 25.5, "A": 4.8, "sigma": 5.1}, ...]

📈 Usage Examples

Automatic Peak Detection & Fitting (Recommended)

The simplest approach: ReadyToFit automatically detects peaks and estimates initial parameters.

fromreadytofitimportfit_modelimportnumpyasnpimportmatplotlib.pyplotasplt# Your noisy multi-peak datax=np.array([...])
y=np.array([...])
# Define peak structure (models only — parameters auto-detected)peaks= [
{"model": "gauss"},
{"model": "gauss"},
{"model": "voigt"}
]
# Fit — p0 and initial parameters are auto-generated!result=fit_model(x, y, peaks)
print(f"RMSE: {result['rmse']:.4f}")
print("Fitted parameters per peak:")
fori, peak_paramsinenumerate(result["params"]):
print(f" Peak {i+1}: {peak_params}")

How auto-detection works:

  1. Peak finding: Uses scipy.signal.find_peaks to locate local maxima
  2. Parameter estimation: Estimates amplitude (A), center (μ), and width (σ) from data
  3. Smart initial guess: Provides excellent starting point for optimization
  4. Respects fixed parameters: If you specify "mu": 50, it's locked and not overridden

With Fixed Parameters

Lock specific parameter values (e.g., peak center):

peaks= [
{"model": "gauss"}, # All parameters free
{"model": "gauss", "mu": 50}, # Center fixed at x=50
{"model": "voigt", "mu": 80} # Center fixed at x=80
]
result=fit_model(x, y, peaks)
# View results (fixed parameters are preserved in result["params"])assertresult["params"][1]["mu"] ==50# mu is lockedassertresult["params"][1]["A"] isnotNone# A and sigma are fitted

Manual Initial Guess (Advanced)

Override automatic detection with manual p0:

# Manual initial guesses (if you know better than auto-detection!)p0= [
{"A": 100, "mu": 25, "sigma": 5},
{"A": 80, "mu": 75, "sigma": 6}
]
result=fit_model(x, y, peaks, p0=p0)

Plotting Results

fromreadytofitimportplot_fit_resultfig, ax=plt.subplots(figsize=(10, 6))
plot_fit_result(x, y, result, show_residual=True, show_rmse=True, fig=fig, ax=ax)
fig.suptitle("Multi-Peak Fitting Results")
plt.savefig("fit_result.png")
plt.close()

Example output:

Fitting Results

Computing Peak Areas

fromreadytofitimportevaluate_peak_areasareas=evaluate_peak_areas(x, result)
print(f"Total signal area: {areas['total']:.2f}")
print(f"Individual peak areas: {areas['peaks']}")

Automatic Peak Detection (Standalone)

Detect peaks without fitting:

fromreadytofitimportdetect_peaksdetected=detect_peaks(x, y, n_peaks=3)
fori, peakinenumerate(detected):
print(f"Peak {i+1}:")
print(f" Center (μ): {peak['mu']:.2f}")
print(f" Amplitude (A): {peak['A']:.3f}")
print(f" Width (σ): {peak['sigma']:.3f}")

📊 fit_model() Output

The fit_model() function returns a comprehensive results dictionary:

result= {
# Parameters"popt": [...], # Optimized parameters (flat, free only)"params": [ # Structured parameters per peak
{"A": 5.0, "mu": 30.0, "sigma": 5.1},
{"A": 3.0, "mu": 70.0, "sigma": 3.0}
],
# Fitted curves"total_fit": [...], # Full reconstructed signal"peak_fits": [...], # Individual peaks# Diagnostics"residual": [...], # y - total_fit"rmse": 0.1006, # Root mean square error# Metadata"param_names": [...], # Names of free parameters"param_slices": [...], # Index mapping per peak"model_function": callable, # Composite model function"p0": [...], # Initial guess used"bounds": (lower, upper) # Bounds used in optimization
}

Access results:

# Check fit qualityprint(f"RMSE: {result['rmse']:.4f}")
# Get all parameters (including fixed ones)fori, peakinenumerate(result["params"]):
print(f"Peak {i}: {peak}")
# Get fitted curve and residualsy_fitted=result["total_fit"]
residuals=result["residual"]
# Get individual peak contributionsfori, peak_curveinenumerate(result["peak_fits"]):
plt.plot(x, peak_curve, label=f"Peak {i}")

🔬 Supported Peak Models

ModelParametersUse Case
gaussA, μ, σSymmetric peaks (most common)
voigtA, μ, σ, γSymmetric peaks with natural broadening
asymA, μ, σL, σR, γAsymmetric peaks (different left/right widths)
skewA, μ, σ, γ, αPeaks with asymmetric tails

Legend:

  • A: Amplitude (peak height above baseline)
  • μ: Center position (can be fixed: {"model": "gauss", "mu": 50})
  • σ: Standard deviation (width parameter)
  • σL, σR: Left/right widths for asymmetric model
  • γ: Lorentz width (natural line broadening)
  • α: Skew parameter (asymmetry control)

Fixed parameters are removed from optimization. Example:

{"model": "gauss", "mu": 50}
# → Only A and sigma are fitted; mu is locked at 50

📐 Key Features in Detail

🔹 Automatic parameter handling
No manual indexing required — parameters are flattened internally and fixed parameters are properly managed.

🔹 Robust fitting pipeline
Handles missing p0, partial bounds, and invalid input gracefully with intelligent fallbacks.

🔹 Full decomposition
Inspect total fit, individual peaks, residuals, and peak areas independently.

🔹 Peak detection
detect_peaks() and estimate_initial_parameters() enable automatic initial guess generation.

📚 Public API Reference

Core Fitting

  • fit_model(x, y, peaks, p0=None, bounds=None, debug=False)
    Main fitting function. Automatically detects peaks and estimates p0 if not provided.

Visualization

  • plot_fit_result(x, y, result, show_residual=True, show_rmse=True, fig=None, ax=None)
    Plot raw data, total fit, individual peaks, residuals, and RMSE.

Peak Detection

  • detect_peaks(x, y, n_peaks=None, height_threshold=0.1, prominence_threshold=None, distance=None)
    Automatically detect peaks. Returns list of peak dictionaries with estimated parameters.

  • estimate_initial_parameters(x, y, peaks)
    Estimate initial parameters from data. Respects fixed parameters in peak definitions.

Area Integration

  • evaluate_peak_areas(x, result)
    Compute areas of total fit and individual peaks using trapezoidal rule.

  • area_integration(y, x=None)
    Low-level numerical integration utility.

� Dependencies

numpy
scipy
matplotlib

To add new peak types

To add new functions you need the following changes:

  • Insert the function definition into functions.py
  • Add dedicated section in get_model() inside functions.py
  • Add dedicated entry inside the PARAM_ORDER dictionary inside parameters.py
  • Add dedicated section in generate_default_p0() inside parameters.py
  • Add the new peak name into peak_types variable inside test.py in the peaks import section

About

A flexible and modular Python toolkit for multi-peak curve fitting, parameter management, visualization, and area analysis using scipy.optimize.curve_fit.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

📊 ReadyToFit — PeakFit Functions

A flexible and modular Python toolkit for multi-peak curve fitting, parameter management, visualization, and area analysis using scipy.optimize.curve_fit.

⭐ Author Notes

I had to dig into scipy too many times over the years without using it on a daily basis. I decided that I needed a more handy peak-fitting tool (based on scipy) that would speed up my workflow. This project is designed as a modular research-grade fitting system, not a black-box tool. Each component can be reused independently in scientific workflows.

This package is designed to handle arbitrary numbers of peaks with different models (Gaussian, Voigt, Asymmetric, Skewed), including support for fixed parameters (e.g., fixed peak centers μ).

🚀 Features

🔹 Automatic peak detection — Intelligently finds peaks and estimates initial parameters
🔹 Multi-peak fitting with arbitrary peak count
🔹 Multiple peak models:
- Gaussian
- Voigt
- Asymmetric
- Skewed
🔹 Support for fixed parameters (e.g., μ fixed per peak)
🔹 Automatic parameter flattening/unflattening
🔹 Intelligent initial guess generation from data
🔹 Flexible bounds handling
🔹 Full peak decomposition after fitting
🔹 Area integration for peaks and total signal
🔹 Clean visualization with residuals and RMSE

⚙️ Installation

Install from source:

git clone https://github.com/your-username/ReadyToFit.git
cd ReadyToFit
pip install .

Or install directly from GitHub:

pip install git+https://github.com/your-username/ReadyToFit.git

Dependencies: numpy, scipy, matplotlib (automatically installed).

🧠 Core Concept

Each peak is defined as a dictionary:

peaks= [
{"model": "gauss"},
{"model": "voigt", "mu": 50}, # fixed center at x=50
{"model": "asym"}
]

The system automatically:

  • Detects peaks in data using scipy.signal.find_peaks
  • Estimates initial parameters (amplitude, position, width) from data
  • Builds the composite model (sum of all peaks)
  • Flattens parameters for optimization
  • Handles fixed parameters (removed from optimization, reinstated after fit)
  • Reconstructs fitted peaks

🔍 Automatic p0 Generation & Peak Detection

The Problem: Multi-peak fitting requires good initial guesses (p0). Bad guesses lead to poor convergence or fitting the wrong peaks.

The Solution: ReadyToFit includes intelligent automatic peak detection:

  1. Peak Detection (detect_peaks()):

    • Finds local maxima using local gradient analysis
    • Filters by prominence (relative height above baseline)
    • Estimates peak positions, amplitudes, and widths
  2. Parameter Estimation (estimate_initial_parameters()):

    • Converts detected peaks into initial parameter guesses
    • Estimates widths from Full-Width-Half-Maximum (FWHM)
    • Respects user-defined fixed parameters (not overridden)
  3. Automatic p0:

    • When fit_model() is called without p0, it auto-generates it
    • Much more robust than naive guesses (e.g., same μ for all peaks)
    • Can be overridden by providing manual p0 if needed

Example:

fromreadytofitimportdetect_peaks# Detect peaks manually (optional — fit_model() does this automatically)detected=detect_peaks(x, y, n_peaks=3)
# Returns: [{"mu": 25.5, "A": 4.8, "sigma": 5.1}, ...]

📈 Usage Examples

Automatic Peak Detection & Fitting (Recommended)

The simplest approach: ReadyToFit automatically detects peaks and estimates initial parameters.

fromreadytofitimportfit_modelimportnumpyasnpimportmatplotlib.pyplotasplt# Your noisy multi-peak datax=np.array([...])
y=np.array([...])
# Define peak structure (models only — parameters auto-detected)peaks= [
{"model": "gauss"},
{"model": "gauss"},
{"model": "voigt"}
]
# Fit — p0 and initial parameters are auto-generated!result=fit_model(x, y, peaks)
print(f"RMSE: {result['rmse']:.4f}")
print("Fitted parameters per peak:")
fori, peak_paramsinenumerate(result["params"]):
print(f" Peak {i+1}: {peak_params}")

How auto-detection works:

  1. Peak finding: Uses scipy.signal.find_peaks to locate local maxima
  2. Parameter estimation: Estimates amplitude (A), center (μ), and width (σ) from data
  3. Smart initial guess: Provides excellent starting point for optimization
  4. Respects fixed parameters: If you specify "mu": 50, it's locked and not overridden

With Fixed Parameters

Lock specific parameter values (e.g., peak center):

peaks= [
{"model": "gauss"}, # All parameters free
{"model": "gauss", "mu": 50}, # Center fixed at x=50
{"model": "voigt", "mu": 80} # Center fixed at x=80
]
result=fit_model(x, y, peaks)
# View results (fixed parameters are preserved in result["params"])assertresult["params"][1]["mu"] ==50# mu is lockedassertresult["params"][1]["A"] isnotNone# A and sigma are fitted

Manual Initial Guess (Advanced)

Override automatic detection with manual p0:

# Manual initial guesses (if you know better than auto-detection!)p0= [
{"A": 100, "mu": 25, "sigma": 5},
{"A": 80, "mu": 75, "sigma": 6}
]
result=fit_model(x, y, peaks, p0=p0)

Plotting Results

fromreadytofitimportplot_fit_resultfig, ax=plt.subplots(figsize=(10, 6))
plot_fit_result(x, y, result, show_residual=True, show_rmse=True, fig=fig, ax=ax)
fig.suptitle("Multi-Peak Fitting Results")
plt.savefig("fit_result.png")
plt.close()

Example output:

Fitting Results

Computing Peak Areas

fromreadytofitimportevaluate_peak_areasareas=evaluate_peak_areas(x, result)
print(f"Total signal area: {areas['total']:.2f}")
print(f"Individual peak areas: {areas['peaks']}")

Automatic Peak Detection (Standalone)

Detect peaks without fitting:

fromreadytofitimportdetect_peaksdetected=detect_peaks(x, y, n_peaks=3)
fori, peakinenumerate(detected):
print(f"Peak {i+1}:")
print(f" Center (μ): {peak['mu']:.2f}")
print(f" Amplitude (A): {peak['A']:.3f}")
print(f" Width (σ): {peak['sigma']:.3f}")

📊 fit_model() Output

The fit_model() function returns a comprehensive results dictionary:

result= {
# Parameters"popt": [...], # Optimized parameters (flat, free only)"params": [ # Structured parameters per peak
{"A": 5.0, "mu": 30.0, "sigma": 5.1},
{"A": 3.0, "mu": 70.0, "sigma": 3.0}
],
# Fitted curves"total_fit": [...], # Full reconstructed signal"peak_fits": [...], # Individual peaks# Diagnostics"residual": [...], # y - total_fit"rmse": 0.1006, # Root mean square error# Metadata"param_names": [...], # Names of free parameters"param_slices": [...], # Index mapping per peak"model_function": callable, # Composite model function"p0": [...], # Initial guess used"bounds": (lower, upper) # Bounds used in optimization
}

Access results:

# Check fit qualityprint(f"RMSE: {result['rmse']:.4f}")
# Get all parameters (including fixed ones)fori, peakinenumerate(result["params"]):
print(f"Peak {i}: {peak}")
# Get fitted curve and residualsy_fitted=result["total_fit"]
residuals=result["residual"]
# Get individual peak contributionsfori, peak_curveinenumerate(result["peak_fits"]):
plt.plot(x, peak_curve, label=f"Peak {i}")

🔬 Supported Peak Models

ModelParametersUse Case
gaussA, μ, σSymmetric peaks (most common)
voigtA, μ, σ, γSymmetric peaks with natural broadening
asymA, μ, σL, σR, γAsymmetric peaks (different left/right widths)
skewA, μ, σ, γ, αPeaks with asymmetric tails

Legend:

  • A: Amplitude (peak height above baseline)
  • μ: Center position (can be fixed: {"model": "gauss", "mu": 50})
  • σ: Standard deviation (width parameter)
  • σL, σR: Left/right widths for asymmetric model
  • γ: Lorentz width (natural line broadening)
  • α: Skew parameter (asymmetry control)

Fixed parameters are removed from optimization. Example:

{"model": "gauss", "mu": 50}
# → Only A and sigma are fitted; mu is locked at 50

📐 Key Features in Detail

🔹 Automatic parameter handling
No manual indexing required — parameters are flattened internally and fixed parameters are properly managed.

🔹 Robust fitting pipeline
Handles missing p0, partial bounds, and invalid input gracefully with intelligent fallbacks.

🔹 Full decomposition
Inspect total fit, individual peaks, residuals, and peak areas independently.

🔹 Peak detection
detect_peaks() and estimate_initial_parameters() enable automatic initial guess generation.

📚 Public API Reference

Core Fitting

  • fit_model(x, y, peaks, p0=None, bounds=None, debug=False)
    Main fitting function. Automatically detects peaks and estimates p0 if not provided.

Visualization

  • plot_fit_result(x, y, result, show_residual=True, show_rmse=True, fig=None, ax=None)
    Plot raw data, total fit, individual peaks, residuals, and RMSE.

Peak Detection

  • detect_peaks(x, y, n_peaks=None, height_threshold=0.1, prominence_threshold=None, distance=None)
    Automatically detect peaks. Returns list of peak dictionaries with estimated parameters.

  • estimate_initial_parameters(x, y, peaks)
    Estimate initial parameters from data. Respects fixed parameters in peak definitions.

Area Integration

  • evaluate_peak_areas(x, result)
    Compute areas of total fit and individual peaks using trapezoidal rule.

  • area_integration(y, x=None)
    Low-level numerical integration utility.

� Dependencies

numpy
scipy
matplotlib

To add new peak types

To add new functions you need the following changes:

  • Insert the function definition into functions.py
  • Add dedicated section in get_model() inside functions.py
  • Add dedicated entry inside the PARAM_ORDER dictionary inside parameters.py
  • Add dedicated section in generate_default_p0() inside parameters.py
  • Add the new peak name into peak_types variable inside test.py in the peaks import section

About

A flexible and modular Python toolkit for multi-peak curve fitting, parameter management, visualization, and area analysis using scipy.optimize.curve_fit.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

📊 ReadyToFit — PeakFit Functions

A flexible and modular Python toolkit for multi-peak curve fitting, parameter management, visualization, and area analysis using scipy.optimize.curve_fit.

⭐ Author Notes

I had to dig into scipy too many times over the years without using it on a daily basis. I decided that I needed a more handy peak-fitting tool (based on scipy) that would speed up my workflow. This project is designed as a modular research-grade fitting system, not a black-box tool. Each component can be reused independently in scientific workflows.

This package is designed to handle arbitrary numbers of peaks with different models (Gaussian, Voigt, Asymmetric, Skewed), including support for fixed parameters (e.g., fixed peak centers μ).

🚀 Features

🔹 Automatic peak detection — Intelligently finds peaks and estimates initial parameters
🔹 Multi-peak fitting with arbitrary peak count
🔹 Multiple peak models:
- Gaussian
- Voigt
- Asymmetric
- Skewed
🔹 Support for fixed parameters (e.g., μ fixed per peak)
🔹 Automatic parameter flattening/unflattening
🔹 Intelligent initial guess generation from data
🔹 Flexible bounds handling
🔹 Full peak decomposition after fitting
🔹 Area integration for peaks and total signal
🔹 Clean visualization with residuals and RMSE

⚙️ Installation

Install from source:

git clone https://github.com/your-username/ReadyToFit.git
cd ReadyToFit
pip install .

Or install directly from GitHub:

pip install git+https://github.com/your-username/ReadyToFit.git

Dependencies: numpy, scipy, matplotlib (automatically installed).

🧠 Core Concept

Each peak is defined as a dictionary:

peaks= [
{"model": "gauss"},
{"model": "voigt", "mu": 50}, # fixed center at x=50
{"model": "asym"}
]

The system automatically:

  • Detects peaks in data using scipy.signal.find_peaks
  • Estimates initial parameters (amplitude, position, width) from data
  • Builds the composite model (sum of all peaks)
  • Flattens parameters for optimization
  • Handles fixed parameters (removed from optimization, reinstated after fit)
  • Reconstructs fitted peaks

🔍 Automatic p0 Generation & Peak Detection

The Problem: Multi-peak fitting requires good initial guesses (p0). Bad guesses lead to poor convergence or fitting the wrong peaks.

The Solution: ReadyToFit includes intelligent automatic peak detection:

  1. Peak Detection (detect_peaks()):

    • Finds local maxima using local gradient analysis
    • Filters by prominence (relative height above baseline)
    • Estimates peak positions, amplitudes, and widths
  2. Parameter Estimation (estimate_initial_parameters()):

    • Converts detected peaks into initial parameter guesses
    • Estimates widths from Full-Width-Half-Maximum (FWHM)
    • Respects user-defined fixed parameters (not overridden)
  3. Automatic p0:

    • When fit_model() is called without p0, it auto-generates it
    • Much more robust than naive guesses (e.g., same μ for all peaks)
    • Can be overridden by providing manual p0 if needed

Example:

fromreadytofitimportdetect_peaks# Detect peaks manually (optional — fit_model() does this automatically)detected=detect_peaks(x, y, n_peaks=3)
# Returns: [{"mu": 25.5, "A": 4.8, "sigma": 5.1}, ...]

📈 Usage Examples

Automatic Peak Detection & Fitting (Recommended)

The simplest approach: ReadyToFit automatically detects peaks and estimates initial parameters.

fromreadytofitimportfit_modelimportnumpyasnpimportmatplotlib.pyplotasplt# Your noisy multi-peak datax=np.array([...])
y=np.array([...])
# Define peak structure (models only — parameters auto-detected)peaks= [
{"model": "gauss"},
{"model": "gauss"},
{"model": "voigt"}
]
# Fit — p0 and initial parameters are auto-generated!result=fit_model(x, y, peaks)
print(f"RMSE: {result['rmse']:.4f}")
print("Fitted parameters per peak:")
fori, peak_paramsinenumerate(result["params"]):
print(f" Peak {i+1}: {peak_params}")

How auto-detection works:

  1. Peak finding: Uses scipy.signal.find_peaks to locate local maxima
  2. Parameter estimation: Estimates amplitude (A), center (μ), and width (σ) from data
  3. Smart initial guess: Provides excellent starting point for optimization
  4. Respects fixed parameters: If you specify "mu": 50, it's locked and not overridden

With Fixed Parameters

Lock specific parameter values (e.g., peak center):

peaks= [
{"model": "gauss"}, # All parameters free
{"model": "gauss", "mu": 50}, # Center fixed at x=50
{"model": "voigt", "mu": 80} # Center fixed at x=80
]
result=fit_model(x, y, peaks)
# View results (fixed parameters are preserved in result["params"])assertresult["params"][1]["mu"] ==50# mu is lockedassertresult["params"][1]["A"] isnotNone# A and sigma are fitted

Manual Initial Guess (Advanced)

Override automatic detection with manual p0:

# Manual initial guesses (if you know better than auto-detection!)p0= [
{"A": 100, "mu": 25, "sigma": 5},
{"A": 80, "mu": 75, "sigma": 6}
]
result=fit_model(x, y, peaks, p0=p0)

Plotting Results

fromreadytofitimportplot_fit_resultfig, ax=plt.subplots(figsize=(10, 6))
plot_fit_result(x, y, result, show_residual=True, show_rmse=True, fig=fig, ax=ax)
fig.suptitle("Multi-Peak Fitting Results")
plt.savefig("fit_result.png")
plt.close()

Example output:

Fitting Results

Computing Peak Areas

fromreadytofitimportevaluate_peak_areasareas=evaluate_peak_areas(x, result)
print(f"Total signal area: {areas['total']:.2f}")
print(f"Individual peak areas: {areas['peaks']}")

Automatic Peak Detection (Standalone)

Detect peaks without fitting:

fromreadytofitimportdetect_peaksdetected=detect_peaks(x, y, n_peaks=3)
fori, peakinenumerate(detected):
print(f"Peak {i+1}:")
print(f" Center (μ): {peak['mu']:.2f}")
print(f" Amplitude (A): {peak['A']:.3f}")
print(f" Width (σ): {peak['sigma']:.3f}")

📊 fit_model() Output

The fit_model() function returns a comprehensive results dictionary:

result= {
# Parameters"popt": [...], # Optimized parameters (flat, free only)"params": [ # Structured parameters per peak
{"A": 5.0, "mu": 30.0, "sigma": 5.1},
{"A": 3.0, "mu": 70.0, "sigma": 3.0}
],
# Fitted curves"total_fit": [...], # Full reconstructed signal"peak_fits": [...], # Individual peaks# Diagnostics"residual": [...], # y - total_fit"rmse": 0.1006, # Root mean square error# Metadata"param_names": [...], # Names of free parameters"param_slices": [...], # Index mapping per peak"model_function": callable, # Composite model function"p0": [...], # Initial guess used"bounds": (lower, upper) # Bounds used in optimization
}

Access results:

# Check fit qualityprint(f"RMSE: {result['rmse']:.4f}")
# Get all parameters (including fixed ones)fori, peakinenumerate(result["params"]):
print(f"Peak {i}: {peak}")
# Get fitted curve and residualsy_fitted=result["total_fit"]
residuals=result["residual"]
# Get individual peak contributionsfori, peak_curveinenumerate(result["peak_fits"]):
plt.plot(x, peak_curve, label=f"Peak {i}")

🔬 Supported Peak Models

ModelParametersUse Case
gaussA, μ, σSymmetric peaks (most common)
voigtA, μ, σ, γSymmetric peaks with natural broadening
asymA, μ, σL, σR, γAsymmetric peaks (different left/right widths)
skewA, μ, σ, γ, αPeaks with asymmetric tails

Legend:

  • A: Amplitude (peak height above baseline)
  • μ: Center position (can be fixed: {"model": "gauss", "mu": 50})
  • σ: Standard deviation (width parameter)
  • σL, σR: Left/right widths for asymmetric model
  • γ: Lorentz width (natural line broadening)
  • α: Skew parameter (asymmetry control)

Fixed parameters are removed from optimization. Example:

{"model": "gauss", "mu": 50}
# → Only A and sigma are fitted; mu is locked at 50

📐 Key Features in Detail

🔹 Automatic parameter handling
No manual indexing required — parameters are flattened internally and fixed parameters are properly managed.

🔹 Robust fitting pipeline
Handles missing p0, partial bounds, and invalid input gracefully with intelligent fallbacks.

🔹 Full decomposition
Inspect total fit, individual peaks, residuals, and peak areas independently.

🔹 Peak detection
detect_peaks() and estimate_initial_parameters() enable automatic initial guess generation.

📚 Public API Reference

Core Fitting

  • fit_model(x, y, peaks, p0=None, bounds=None, debug=False)
    Main fitting function. Automatically detects peaks and estimates p0 if not provided.

Visualization

  • plot_fit_result(x, y, result, show_residual=True, show_rmse=True, fig=None, ax=None)
    Plot raw data, total fit, individual peaks, residuals, and RMSE.

Peak Detection

  • detect_peaks(x, y, n_peaks=None, height_threshold=0.1, prominence_threshold=None, distance=None)
    Automatically detect peaks. Returns list of peak dictionaries with estimated parameters.

  • estimate_initial_parameters(x, y, peaks)
    Estimate initial parameters from data. Respects fixed parameters in peak definitions.

Area Integration

  • evaluate_peak_areas(x, result)
    Compute areas of total fit and individual peaks using trapezoidal rule.

  • area_integration(y, x=None)
    Low-level numerical integration utility.

� Dependencies

numpy
scipy
matplotlib

To add new peak types

To add new functions you need the following changes:

  • Insert the function definition into functions.py
  • Add dedicated section in get_model() inside functions.py
  • Add dedicated entry inside the PARAM_ORDER dictionary inside parameters.py
  • Add dedicated section in generate_default_p0() inside parameters.py
  • Add the new peak name into peak_types variable inside test.py in the peaks import section

About

A flexible and modular Python toolkit for multi-peak curve fitting, parameter management, visualization, and area analysis using scipy.optimize.curve_fit.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

📊 ReadyToFit — PeakFit Functions

A flexible and modular Python toolkit for multi-peak curve fitting, parameter management, visualization, and area analysis using scipy.optimize.curve_fit.

⭐ Author Notes

I had to dig into scipy too many times over the years without using it on a daily basis. I decided that I needed a more handy peak-fitting tool (based on scipy) that would speed up my workflow. This project is designed as a modular research-grade fitting system, not a black-box tool. Each component can be reused independently in scientific workflows.

This package is designed to handle arbitrary numbers of peaks with different models (Gaussian, Voigt, Asymmetric, Skewed), including support for fixed parameters (e.g., fixed peak centers μ).

🚀 Features

🔹 Automatic peak detection — Intelligently finds peaks and estimates initial parameters
🔹 Multi-peak fitting with arbitrary peak count
🔹 Multiple peak models:
- Gaussian
- Voigt
- Asymmetric
- Skewed
🔹 Support for fixed parameters (e.g., μ fixed per peak)
🔹 Automatic parameter flattening/unflattening
🔹 Intelligent initial guess generation from data
🔹 Flexible bounds handling
🔹 Full peak decomposition after fitting
🔹 Area integration for peaks and total signal
🔹 Clean visualization with residuals and RMSE

⚙️ Installation

Install from source:

git clone https://github.com/your-username/ReadyToFit.git
cd ReadyToFit
pip install .

Or install directly from GitHub:

pip install git+https://github.com/your-username/ReadyToFit.git

Dependencies: numpy, scipy, matplotlib (automatically installed).

🧠 Core Concept

Each peak is defined as a dictionary:

peaks= [
{"model": "gauss"},
{"model": "voigt", "mu": 50}, # fixed center at x=50
{"model": "asym"}
]

The system automatically:

  • Detects peaks in data using scipy.signal.find_peaks
  • Estimates initial parameters (amplitude, position, width) from data
  • Builds the composite model (sum of all peaks)
  • Flattens parameters for optimization
  • Handles fixed parameters (removed from optimization, reinstated after fit)
  • Reconstructs fitted peaks

🔍 Automatic p0 Generation & Peak Detection

The Problem: Multi-peak fitting requires good initial guesses (p0). Bad guesses lead to poor convergence or fitting the wrong peaks.

The Solution: ReadyToFit includes intelligent automatic peak detection:

  1. Peak Detection (detect_peaks()):

    • Finds local maxima using local gradient analysis
    • Filters by prominence (relative height above baseline)
    • Estimates peak positions, amplitudes, and widths
  2. Parameter Estimation (estimate_initial_parameters()):

    • Converts detected peaks into initial parameter guesses
    • Estimates widths from Full-Width-Half-Maximum (FWHM)
    • Respects user-defined fixed parameters (not overridden)
  3. Automatic p0:

    • When fit_model() is called without p0, it auto-generates it
    • Much more robust than naive guesses (e.g., same μ for all peaks)
    • Can be overridden by providing manual p0 if needed

Example:

fromreadytofitimportdetect_peaks# Detect peaks manually (optional — fit_model() does this automatically)detected=detect_peaks(x, y, n_peaks=3)
# Returns: [{"mu": 25.5, "A": 4.8, "sigma": 5.1}, ...]

📈 Usage Examples

Automatic Peak Detection & Fitting (Recommended)

The simplest approach: ReadyToFit automatically detects peaks and estimates initial parameters.

fromreadytofitimportfit_modelimportnumpyasnpimportmatplotlib.pyplotasplt# Your noisy multi-peak datax=np.array([...])
y=np.array([...])
# Define peak structure (models only — parameters auto-detected)peaks= [
{"model": "gauss"},
{"model": "gauss"},
{"model": "voigt"}
]
# Fit — p0 and initial parameters are auto-generated!result=fit_model(x, y, peaks)
print(f"RMSE: {result['rmse']:.4f}")
print("Fitted parameters per peak:")
fori, peak_paramsinenumerate(result["params"]):
print(f" Peak {i+1}: {peak_params}")

How auto-detection works:

  1. Peak finding: Uses scipy.signal.find_peaks to locate local maxima
  2. Parameter estimation: Estimates amplitude (A), center (μ), and width (σ) from data
  3. Smart initial guess: Provides excellent starting point for optimization
  4. Respects fixed parameters: If you specify "mu": 50, it's locked and not overridden

With Fixed Parameters

Lock specific parameter values (e.g., peak center):

peaks= [
{"model": "gauss"}, # All parameters free
{"model": "gauss", "mu": 50}, # Center fixed at x=50
{"model": "voigt", "mu": 80} # Center fixed at x=80
]
result=fit_model(x, y, peaks)
# View results (fixed parameters are preserved in result["params"])assertresult["params"][1]["mu"] ==50# mu is lockedassertresult["params"][1]["A"] isnotNone# A and sigma are fitted

Manual Initial Guess (Advanced)

Override automatic detection with manual p0:

# Manual initial guesses (if you know better than auto-detection!)p0= [
{"A": 100, "mu": 25, "sigma": 5},
{"A": 80, "mu": 75, "sigma": 6}
]
result=fit_model(x, y, peaks, p0=p0)

Plotting Results

fromreadytofitimportplot_fit_resultfig, ax=plt.subplots(figsize=(10, 6))
plot_fit_result(x, y, result, show_residual=True, show_rmse=True, fig=fig, ax=ax)
fig.suptitle("Multi-Peak Fitting Results")
plt.savefig("fit_result.png")
plt.close()

Example output:

Fitting Results

Computing Peak Areas

fromreadytofitimportevaluate_peak_areasareas=evaluate_peak_areas(x, result)
print(f"Total signal area: {areas['total']:.2f}")
print(f"Individual peak areas: {areas['peaks']}")

Automatic Peak Detection (Standalone)

Detect peaks without fitting:

fromreadytofitimportdetect_peaksdetected=detect_peaks(x, y, n_peaks=3)
fori, peakinenumerate(detected):
print(f"Peak {i+1}:")
print(f" Center (μ): {peak['mu']:.2f}")
print(f" Amplitude (A): {peak['A']:.3f}")
print(f" Width (σ): {peak['sigma']:.3f}")

📊 fit_model() Output

The fit_model() function returns a comprehensive results dictionary:

result= {
# Parameters"popt": [...], # Optimized parameters (flat, free only)"params": [ # Structured parameters per peak
{"A": 5.0, "mu": 30.0, "sigma": 5.1},
{"A": 3.0, "mu": 70.0, "sigma": 3.0}
],
# Fitted curves"total_fit": [...], # Full reconstructed signal"peak_fits": [...], # Individual peaks# Diagnostics"residual": [...], # y - total_fit"rmse": 0.1006, # Root mean square error# Metadata"param_names": [...], # Names of free parameters"param_slices": [...], # Index mapping per peak"model_function": callable, # Composite model function"p0": [...], # Initial guess used"bounds": (lower, upper) # Bounds used in optimization
}

Access results:

# Check fit qualityprint(f"RMSE: {result['rmse']:.4f}")
# Get all parameters (including fixed ones)fori, peakinenumerate(result["params"]):
print(f"Peak {i}: {peak}")
# Get fitted curve and residualsy_fitted=result["total_fit"]
residuals=result["residual"]
# Get individual peak contributionsfori, peak_curveinenumerate(result["peak_fits"]):
plt.plot(x, peak_curve, label=f"Peak {i}")

🔬 Supported Peak Models

ModelParametersUse Case
gaussA, μ, σSymmetric peaks (most common)
voigtA, μ, σ, γSymmetric peaks with natural broadening
asymA, μ, σL, σR, γAsymmetric peaks (different left/right widths)
skewA, μ, σ, γ, αPeaks with asymmetric tails

Legend:

  • A: Amplitude (peak height above baseline)
  • μ: Center position (can be fixed: {"model": "gauss", "mu": 50})
  • σ: Standard deviation (width parameter)
  • σL, σR: Left/right widths for asymmetric model
  • γ: Lorentz width (natural line broadening)
  • α: Skew parameter (asymmetry control)

Fixed parameters are removed from optimization. Example:

{"model": "gauss", "mu": 50}
# → Only A and sigma are fitted; mu is locked at 50

📐 Key Features in Detail

🔹 Automatic parameter handling
No manual indexing required — parameters are flattened internally and fixed parameters are properly managed.

🔹 Robust fitting pipeline
Handles missing p0, partial bounds, and invalid input gracefully with intelligent fallbacks.

🔹 Full decomposition
Inspect total fit, individual peaks, residuals, and peak areas independently.

🔹 Peak detection
detect_peaks() and estimate_initial_parameters() enable automatic initial guess generation.

📚 Public API Reference

Core Fitting

  • fit_model(x, y, peaks, p0=None, bounds=None, debug=False)
    Main fitting function. Automatically detects peaks and estimates p0 if not provided.

Visualization

  • plot_fit_result(x, y, result, show_residual=True, show_rmse=True, fig=None, ax=None)
    Plot raw data, total fit, individual peaks, residuals, and RMSE.

Peak Detection

  • detect_peaks(x, y, n_peaks=None, height_threshold=0.1, prominence_threshold=None, distance=None)
    Automatically detect peaks. Returns list of peak dictionaries with estimated parameters.

  • estimate_initial_parameters(x, y, peaks)
    Estimate initial parameters from data. Respects fixed parameters in peak definitions.

Area Integration

  • evaluate_peak_areas(x, result)
    Compute areas of total fit and individual peaks using trapezoidal rule.

  • area_integration(y, x=None)
    Low-level numerical integration utility.

� Dependencies

numpy
scipy
matplotlib

To add new peak types

To add new functions you need the following changes:

  • Insert the function definition into functions.py
  • Add dedicated section in get_model() inside functions.py
  • Add dedicated entry inside the PARAM_ORDER dictionary inside parameters.py
  • Add dedicated section in generate_default_p0() inside parameters.py
  • Add the new peak name into peak_types variable inside test.py in the peaks import section

About

A flexible and modular Python toolkit for multi-peak curve fitting, parameter management, visualization, and area analysis using scipy.optimize.curve_fit.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

📊 ReadyToFit — PeakFit Functions

A flexible and modular Python toolkit for multi-peak curve fitting, parameter management, visualization, and area analysis using scipy.optimize.curve_fit.

⭐ Author Notes

I had to dig into scipy too many times over the years without using it on a daily basis. I decided that I needed a more handy peak-fitting tool (based on scipy) that would speed up my workflow. This project is designed as a modular research-grade fitting system, not a black-box tool. Each component can be reused independently in scientific workflows.

This package is designed to handle arbitrary numbers of peaks with different models (Gaussian, Voigt, Asymmetric, Skewed), including support for fixed parameters (e.g., fixed peak centers μ).

🚀 Features

🔹 Automatic peak detection — Intelligently finds peaks and estimates initial parameters
🔹 Multi-peak fitting with arbitrary peak count
🔹 Multiple peak models:
- Gaussian
- Voigt
- Asymmetric
- Skewed
🔹 Support for fixed parameters (e.g., μ fixed per peak)
🔹 Automatic parameter flattening/unflattening
🔹 Intelligent initial guess generation from data
🔹 Flexible bounds handling
🔹 Full peak decomposition after fitting
🔹 Area integration for peaks and total signal
🔹 Clean visualization with residuals and RMSE

⚙️ Installation

Install from source:

git clone https://github.com/your-username/ReadyToFit.git
cd ReadyToFit
pip install .

Or install directly from GitHub:

pip install git+https://github.com/your-username/ReadyToFit.git

Dependencies: numpy, scipy, matplotlib (automatically installed).

🧠 Core Concept

Each peak is defined as a dictionary:

peaks= [
{"model": "gauss"},
{"model": "voigt", "mu": 50}, # fixed center at x=50
{"model": "asym"}
]

The system automatically:

  • Detects peaks in data using scipy.signal.find_peaks
  • Estimates initial parameters (amplitude, position, width) from data
  • Builds the composite model (sum of all peaks)
  • Flattens parameters for optimization
  • Handles fixed parameters (removed from optimization, reinstated after fit)
  • Reconstructs fitted peaks

🔍 Automatic p0 Generation & Peak Detection

The Problem: Multi-peak fitting requires good initial guesses (p0). Bad guesses lead to poor convergence or fitting the wrong peaks.

The Solution: ReadyToFit includes intelligent automatic peak detection:

  1. Peak Detection (detect_peaks()):

    • Finds local maxima using local gradient analysis
    • Filters by prominence (relative height above baseline)
    • Estimates peak positions, amplitudes, and widths
  2. Parameter Estimation (estimate_initial_parameters()):

    • Converts detected peaks into initial parameter guesses
    • Estimates widths from Full-Width-Half-Maximum (FWHM)
    • Respects user-defined fixed parameters (not overridden)
  3. Automatic p0:

    • When fit_model() is called without p0, it auto-generates it
    • Much more robust than naive guesses (e.g., same μ for all peaks)
    • Can be overridden by providing manual p0 if needed

Example:

fromreadytofitimportdetect_peaks# Detect peaks manually (optional — fit_model() does this automatically)detected=detect_peaks(x, y, n_peaks=3)
# Returns: [{"mu": 25.5, "A": 4.8, "sigma": 5.1}, ...]

📈 Usage Examples

Automatic Peak Detection & Fitting (Recommended)

The simplest approach: ReadyToFit automatically detects peaks and estimates initial parameters.

fromreadytofitimportfit_modelimportnumpyasnpimportmatplotlib.pyplotasplt# Your noisy multi-peak datax=np.array([...])
y=np.array([...])
# Define peak structure (models only — parameters auto-detected)peaks= [
{"model": "gauss"},
{"model": "gauss"},
{"model": "voigt"}
]
# Fit — p0 and initial parameters are auto-generated!result=fit_model(x, y, peaks)
print(f"RMSE: {result['rmse']:.4f}")
print("Fitted parameters per peak:")
fori, peak_paramsinenumerate(result["params"]):
print(f" Peak {i+1}: {peak_params}")

How auto-detection works:

  1. Peak finding: Uses scipy.signal.find_peaks to locate local maxima
  2. Parameter estimation: Estimates amplitude (A), center (μ), and width (σ) from data
  3. Smart initial guess: Provides excellent starting point for optimization
  4. Respects fixed parameters: If you specify "mu": 50, it's locked and not overridden

With Fixed Parameters

Lock specific parameter values (e.g., peak center):

peaks= [
{"model": "gauss"}, # All parameters free
{"model": "gauss", "mu": 50}, # Center fixed at x=50
{"model": "voigt", "mu": 80} # Center fixed at x=80
]
result=fit_model(x, y, peaks)
# View results (fixed parameters are preserved in result["params"])assertresult["params"][1]["mu"] ==50# mu is lockedassertresult["params"][1]["A"] isnotNone# A and sigma are fitted

Manual Initial Guess (Advanced)

Override automatic detection with manual p0:

# Manual initial guesses (if you know better than auto-detection!)p0= [
{"A": 100, "mu": 25, "sigma": 5},
{"A": 80, "mu": 75, "sigma": 6}
]
result=fit_model(x, y, peaks, p0=p0)

Plotting Results

fromreadytofitimportplot_fit_resultfig, ax=plt.subplots(figsize=(10, 6))
plot_fit_result(x, y, result, show_residual=True, show_rmse=True, fig=fig, ax=ax)
fig.suptitle("Multi-Peak Fitting Results")
plt.savefig("fit_result.png")
plt.close()

Example output:

Fitting Results

Computing Peak Areas

fromreadytofitimportevaluate_peak_areasareas=evaluate_peak_areas(x, result)
print(f"Total signal area: {areas['total']:.2f}")
print(f"Individual peak areas: {areas['peaks']}")

Automatic Peak Detection (Standalone)

Detect peaks without fitting:

fromreadytofitimportdetect_peaksdetected=detect_peaks(x, y, n_peaks=3)
fori, peakinenumerate(detected):
print(f"Peak {i+1}:")
print(f" Center (μ): {peak['mu']:.2f}")
print(f" Amplitude (A): {peak['A']:.3f}")
print(f" Width (σ): {peak['sigma']:.3f}")

📊 fit_model() Output

The fit_model() function returns a comprehensive results dictionary:

result= {
# Parameters"popt": [...], # Optimized parameters (flat, free only)"params": [ # Structured parameters per peak
{"A": 5.0, "mu": 30.0, "sigma": 5.1},
{"A": 3.0, "mu": 70.0, "sigma": 3.0}
],
# Fitted curves"total_fit": [...], # Full reconstructed signal"peak_fits": [...], # Individual peaks# Diagnostics"residual": [...], # y - total_fit"rmse": 0.1006, # Root mean square error# Metadata"param_names": [...], # Names of free parameters"param_slices": [...], # Index mapping per peak"model_function": callable, # Composite model function"p0": [...], # Initial guess used"bounds": (lower, upper) # Bounds used in optimization
}

Access results:

# Check fit qualityprint(f"RMSE: {result['rmse']:.4f}")
# Get all parameters (including fixed ones)fori, peakinenumerate(result["params"]):
print(f"Peak {i}: {peak}")
# Get fitted curve and residualsy_fitted=result["total_fit"]
residuals=result["residual"]
# Get individual peak contributionsfori, peak_curveinenumerate(result["peak_fits"]):
plt.plot(x, peak_curve, label=f"Peak {i}")

🔬 Supported Peak Models

ModelParametersUse Case
gaussA, μ, σSymmetric peaks (most common)
voigtA, μ, σ, γSymmetric peaks with natural broadening
asymA, μ, σL, σR, γAsymmetric peaks (different left/right widths)
skewA, μ, σ, γ, αPeaks with asymmetric tails

Legend:

  • A: Amplitude (peak height above baseline)
  • μ: Center position (can be fixed: {"model": "gauss", "mu": 50})
  • σ: Standard deviation (width parameter)
  • σL, σR: Left/right widths for asymmetric model
  • γ: Lorentz width (natural line broadening)
  • α: Skew parameter (asymmetry control)

Fixed parameters are removed from optimization. Example:

{"model": "gauss", "mu": 50}
# → Only A and sigma are fitted; mu is locked at 50

📐 Key Features in Detail

🔹 Automatic parameter handling
No manual indexing required — parameters are flattened internally and fixed parameters are properly managed.

🔹 Robust fitting pipeline
Handles missing p0, partial bounds, and invalid input gracefully with intelligent fallbacks.

🔹 Full decomposition
Inspect total fit, individual peaks, residuals, and peak areas independently.

🔹 Peak detection
detect_peaks() and estimate_initial_parameters() enable automatic initial guess generation.

📚 Public API Reference

Core Fitting

  • fit_model(x, y, peaks, p0=None, bounds=None, debug=False)
    Main fitting function. Automatically detects peaks and estimates p0 if not provided.

Visualization

  • plot_fit_result(x, y, result, show_residual=True, show_rmse=True, fig=None, ax=None)
    Plot raw data, total fit, individual peaks, residuals, and RMSE.

Peak Detection

  • detect_peaks(x, y, n_peaks=None, height_threshold=0.1, prominence_threshold=None, distance=None)
    Automatically detect peaks. Returns list of peak dictionaries with estimated parameters.

  • estimate_initial_parameters(x, y, peaks)
    Estimate initial parameters from data. Respects fixed parameters in peak definitions.

Area Integration

  • evaluate_peak_areas(x, result)
    Compute areas of total fit and individual peaks using trapezoidal rule.

  • area_integration(y, x=None)
    Low-level numerical integration utility.

� Dependencies

numpy
scipy
matplotlib

To add new peak types

To add new functions you need the following changes:

  • Insert the function definition into functions.py
  • Add dedicated section in get_model() inside functions.py
  • Add dedicated entry inside the PARAM_ORDER dictionary inside parameters.py
  • Add dedicated section in generate_default_p0() inside parameters.py
  • Add the new peak name into peak_types variable inside test.py in the peaks import section

About

A flexible and modular Python toolkit for multi-peak curve fitting, parameter management, visualization, and area analysis using scipy.optimize.curve_fit.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

📊 ReadyToFit — PeakFit Functions

A flexible and modular Python toolkit for multi-peak curve fitting, parameter management, visualization, and area analysis using scipy.optimize.curve_fit.

⭐ Author Notes

I had to dig into scipy too many times over the years without using it on a daily basis. I decided that I needed a more handy peak-fitting tool (based on scipy) that would speed up my workflow. This project is designed as a modular research-grade fitting system, not a black-box tool. Each component can be reused independently in scientific workflows.

This package is designed to handle arbitrary numbers of peaks with different models (Gaussian, Voigt, Asymmetric, Skewed), including support for fixed parameters (e.g., fixed peak centers μ).

🚀 Features

🔹 Automatic peak detection — Intelligently finds peaks and estimates initial parameters
🔹 Multi-peak fitting with arbitrary peak count
🔹 Multiple peak models:
- Gaussian
- Voigt
- Asymmetric
- Skewed
🔹 Support for fixed parameters (e.g., μ fixed per peak)
🔹 Automatic parameter flattening/unflattening
🔹 Intelligent initial guess generation from data
🔹 Flexible bounds handling
🔹 Full peak decomposition after fitting
🔹 Area integration for peaks and total signal
🔹 Clean visualization with residuals and RMSE

⚙️ Installation

Install from source:

git clone https://github.com/your-username/ReadyToFit.git
cd ReadyToFit
pip install .

Or install directly from GitHub:

pip install git+https://github.com/your-username/ReadyToFit.git

Dependencies: numpy, scipy, matplotlib (automatically installed).

🧠 Core Concept

Each peak is defined as a dictionary:

peaks= [
{"model": "gauss"},
{"model": "voigt", "mu": 50}, # fixed center at x=50
{"model": "asym"}
]

The system automatically:

  • Detects peaks in data using scipy.signal.find_peaks
  • Estimates initial parameters (amplitude, position, width) from data
  • Builds the composite model (sum of all peaks)
  • Flattens parameters for optimization
  • Handles fixed parameters (removed from optimization, reinstated after fit)
  • Reconstructs fitted peaks

🔍 Automatic p0 Generation & Peak Detection

The Problem: Multi-peak fitting requires good initial guesses (p0). Bad guesses lead to poor convergence or fitting the wrong peaks.

The Solution: ReadyToFit includes intelligent automatic peak detection:

  1. Peak Detection (detect_peaks()):

    • Finds local maxima using local gradient analysis
    • Filters by prominence (relative height above baseline)
    • Estimates peak positions, amplitudes, and widths
  2. Parameter Estimation (estimate_initial_parameters()):

    • Converts detected peaks into initial parameter guesses
    • Estimates widths from Full-Width-Half-Maximum (FWHM)
    • Respects user-defined fixed parameters (not overridden)
  3. Automatic p0:

    • When fit_model() is called without p0, it auto-generates it
    • Much more robust than naive guesses (e.g., same μ for all peaks)
    • Can be overridden by providing manual p0 if needed

Example:

fromreadytofitimportdetect_peaks# Detect peaks manually (optional — fit_model() does this automatically)detected=detect_peaks(x, y, n_peaks=3)
# Returns: [{"mu": 25.5, "A": 4.8, "sigma": 5.1}, ...]

📈 Usage Examples

Automatic Peak Detection & Fitting (Recommended)

The simplest approach: ReadyToFit automatically detects peaks and estimates initial parameters.

fromreadytofitimportfit_modelimportnumpyasnpimportmatplotlib.pyplotasplt# Your noisy multi-peak datax=np.array([...])
y=np.array([...])
# Define peak structure (models only — parameters auto-detected)peaks= [
{"model": "gauss"},
{"model": "gauss"},
{"model": "voigt"}
]
# Fit — p0 and initial parameters are auto-generated!result=fit_model(x, y, peaks)
print(f"RMSE: {result['rmse']:.4f}")
print("Fitted parameters per peak:")
fori, peak_paramsinenumerate(result["params"]):
print(f" Peak {i+1}: {peak_params}")

How auto-detection works:

  1. Peak finding: Uses scipy.signal.find_peaks to locate local maxima
  2. Parameter estimation: Estimates amplitude (A), center (μ), and width (σ) from data
  3. Smart initial guess: Provides excellent starting point for optimization
  4. Respects fixed parameters: If you specify "mu": 50, it's locked and not overridden

With Fixed Parameters

Lock specific parameter values (e.g., peak center):

peaks= [
{"model": "gauss"}, # All parameters free
{"model": "gauss", "mu": 50}, # Center fixed at x=50
{"model": "voigt", "mu": 80} # Center fixed at x=80
]
result=fit_model(x, y, peaks)
# View results (fixed parameters are preserved in result["params"])assertresult["params"][1]["mu"] ==50# mu is lockedassertresult["params"][1]["A"] isnotNone# A and sigma are fitted

Manual Initial Guess (Advanced)

Override automatic detection with manual p0:

# Manual initial guesses (if you know better than auto-detection!)p0= [
{"A": 100, "mu": 25, "sigma": 5},
{"A": 80, "mu": 75, "sigma": 6}
]
result=fit_model(x, y, peaks, p0=p0)

Plotting Results

fromreadytofitimportplot_fit_resultfig, ax=plt.subplots(figsize=(10, 6))
plot_fit_result(x, y, result, show_residual=True, show_rmse=True, fig=fig, ax=ax)
fig.suptitle("Multi-Peak Fitting Results")
plt.savefig("fit_result.png")
plt.close()

Example output:

Fitting Results

Computing Peak Areas

fromreadytofitimportevaluate_peak_areasareas=evaluate_peak_areas(x, result)
print(f"Total signal area: {areas['total']:.2f}")
print(f"Individual peak areas: {areas['peaks']}")

Automatic Peak Detection (Standalone)

Detect peaks without fitting:

fromreadytofitimportdetect_peaksdetected=detect_peaks(x, y, n_peaks=3)
fori, peakinenumerate(detected):
print(f"Peak {i+1}:")
print(f" Center (μ): {peak['mu']:.2f}")
print(f" Amplitude (A): {peak['A']:.3f}")
print(f" Width (σ): {peak['sigma']:.3f}")

📊 fit_model() Output

The fit_model() function returns a comprehensive results dictionary:

result= {
# Parameters"popt": [...], # Optimized parameters (flat, free only)"params": [ # Structured parameters per peak
{"A": 5.0, "mu": 30.0, "sigma": 5.1},
{"A": 3.0, "mu": 70.0, "sigma": 3.0}
],
# Fitted curves"total_fit": [...], # Full reconstructed signal"peak_fits": [...], # Individual peaks# Diagnostics"residual": [...], # y - total_fit"rmse": 0.1006, # Root mean square error# Metadata"param_names": [...], # Names of free parameters"param_slices": [...], # Index mapping per peak"model_function": callable, # Composite model function"p0": [...], # Initial guess used"bounds": (lower, upper) # Bounds used in optimization
}

Access results:

# Check fit qualityprint(f"RMSE: {result['rmse']:.4f}")
# Get all parameters (including fixed ones)fori, peakinenumerate(result["params"]):
print(f"Peak {i}: {peak}")
# Get fitted curve and residualsy_fitted=result["total_fit"]
residuals=result["residual"]
# Get individual peak contributionsfori, peak_curveinenumerate(result["peak_fits"]):
plt.plot(x, peak_curve, label=f"Peak {i}")

🔬 Supported Peak Models

ModelParametersUse Case
gaussA, μ, σSymmetric peaks (most common)
voigtA, μ, σ, γSymmetric peaks with natural broadening
asymA, μ, σL, σR, γAsymmetric peaks (different left/right widths)
skewA, μ, σ, γ, αPeaks with asymmetric tails

Legend:

  • A: Amplitude (peak height above baseline)
  • μ: Center position (can be fixed: {"model": "gauss", "mu": 50})
  • σ: Standard deviation (width parameter)
  • σL, σR: Left/right widths for asymmetric model
  • γ: Lorentz width (natural line broadening)
  • α: Skew parameter (asymmetry control)

Fixed parameters are removed from optimization. Example:

{"model": "gauss", "mu": 50}
# → Only A and sigma are fitted; mu is locked at 50

📐 Key Features in Detail

🔹 Automatic parameter handling
No manual indexing required — parameters are flattened internally and fixed parameters are properly managed.

🔹 Robust fitting pipeline
Handles missing p0, partial bounds, and invalid input gracefully with intelligent fallbacks.

🔹 Full decomposition
Inspect total fit, individual peaks, residuals, and peak areas independently.

🔹 Peak detection
detect_peaks() and estimate_initial_parameters() enable automatic initial guess generation.

📚 Public API Reference

Core Fitting

  • fit_model(x, y, peaks, p0=None, bounds=None, debug=False)
    Main fitting function. Automatically detects peaks and estimates p0 if not provided.

Visualization

  • plot_fit_result(x, y, result, show_residual=True, show_rmse=True, fig=None, ax=None)
    Plot raw data, total fit, individual peaks, residuals, and RMSE.

Peak Detection

  • detect_peaks(x, y, n_peaks=None, height_threshold=0.1, prominence_threshold=None, distance=None)
    Automatically detect peaks. Returns list of peak dictionaries with estimated parameters.

  • estimate_initial_parameters(x, y, peaks)
    Estimate initial parameters from data. Respects fixed parameters in peak definitions.

Area Integration

  • evaluate_peak_areas(x, result)
    Compute areas of total fit and individual peaks using trapezoidal rule.

  • area_integration(y, x=None)
    Low-level numerical integration utility.

� Dependencies

numpy
scipy
matplotlib

To add new peak types

To add new functions you need the following changes:

  • Insert the function definition into functions.py
  • Add dedicated section in get_model() inside functions.py
  • Add dedicated entry inside the PARAM_ORDER dictionary inside parameters.py
  • Add dedicated section in generate_default_p0() inside parameters.py
  • Add the new peak name into peak_types variable inside test.py in the peaks import section

About

A flexible and modular Python toolkit for multi-peak curve fitting, parameter management, visualization, and area analysis using scipy.optimize.curve_fit.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

📊 ReadyToFit — PeakFit Functions

A flexible and modular Python toolkit for multi-peak curve fitting, parameter management, visualization, and area analysis using scipy.optimize.curve_fit.

⭐ Author Notes

I had to dig into scipy too many times over the years without using it on a daily basis. I decided that I needed a more handy peak-fitting tool (based on scipy) that would speed up my workflow. This project is designed as a modular research-grade fitting system, not a black-box tool. Each component can be reused independently in scientific workflows.

This package is designed to handle arbitrary numbers of peaks with different models (Gaussian, Voigt, Asymmetric, Skewed), including support for fixed parameters (e.g., fixed peak centers μ).

🚀 Features

🔹 Automatic peak detection — Intelligently finds peaks and estimates initial parameters
🔹 Multi-peak fitting with arbitrary peak count
🔹 Multiple peak models:
- Gaussian
- Voigt
- Asymmetric
- Skewed
🔹 Support for fixed parameters (e.g., μ fixed per peak)
🔹 Automatic parameter flattening/unflattening
🔹 Intelligent initial guess generation from data
🔹 Flexible bounds handling
🔹 Full peak decomposition after fitting
🔹 Area integration for peaks and total signal
🔹 Clean visualization with residuals and RMSE

⚙️ Installation

Install from source:

git clone https://github.com/your-username/ReadyToFit.git
cd ReadyToFit
pip install .

Or install directly from GitHub:

pip install git+https://github.com/your-username/ReadyToFit.git

Dependencies: numpy, scipy, matplotlib (automatically installed).

🧠 Core Concept

Each peak is defined as a dictionary:

peaks= [
{"model": "gauss"},
{"model": "voigt", "mu": 50}, # fixed center at x=50
{"model": "asym"}
]

The system automatically:

  • Detects peaks in data using scipy.signal.find_peaks
  • Estimates initial parameters (amplitude, position, width) from data
  • Builds the composite model (sum of all peaks)
  • Flattens parameters for optimization
  • Handles fixed parameters (removed from optimization, reinstated after fit)
  • Reconstructs fitted peaks

🔍 Automatic p0 Generation & Peak Detection

The Problem: Multi-peak fitting requires good initial guesses (p0). Bad guesses lead to poor convergence or fitting the wrong peaks.

The Solution: ReadyToFit includes intelligent automatic peak detection:

  1. Peak Detection (detect_peaks()):

    • Finds local maxima using local gradient analysis
    • Filters by prominence (relative height above baseline)
    • Estimates peak positions, amplitudes, and widths
  2. Parameter Estimation (estimate_initial_parameters()):

    • Converts detected peaks into initial parameter guesses
    • Estimates widths from Full-Width-Half-Maximum (FWHM)
    • Respects user-defined fixed parameters (not overridden)
  3. Automatic p0:

    • When fit_model() is called without p0, it auto-generates it
    • Much more robust than naive guesses (e.g., same μ for all peaks)
    • Can be overridden by providing manual p0 if needed

Example:

fromreadytofitimportdetect_peaks# Detect peaks manually (optional — fit_model() does this automatically)detected=detect_peaks(x, y, n_peaks=3)
# Returns: [{"mu": 25.5, "A": 4.8, "sigma": 5.1}, ...]

📈 Usage Examples

Automatic Peak Detection & Fitting (Recommended)

The simplest approach: ReadyToFit automatically detects peaks and estimates initial parameters.

fromreadytofitimportfit_modelimportnumpyasnpimportmatplotlib.pyplotasplt# Your noisy multi-peak datax=np.array([...])
y=np.array([...])
# Define peak structure (models only — parameters auto-detected)peaks= [
{"model": "gauss"},
{"model": "gauss"},
{"model": "voigt"}
]
# Fit — p0 and initial parameters are auto-generated!result=fit_model(x, y, peaks)
print(f"RMSE: {result['rmse']:.4f}")
print("Fitted parameters per peak:")
fori, peak_paramsinenumerate(result["params"]):
print(f" Peak {i+1}: {peak_params}")

How auto-detection works:

  1. Peak finding: Uses scipy.signal.find_peaks to locate local maxima
  2. Parameter estimation: Estimates amplitude (A), center (μ), and width (σ) from data
  3. Smart initial guess: Provides excellent starting point for optimization
  4. Respects fixed parameters: If you specify "mu": 50, it's locked and not overridden

With Fixed Parameters

Lock specific parameter values (e.g., peak center):

peaks= [
{"model": "gauss"}, # All parameters free
{"model": "gauss", "mu": 50}, # Center fixed at x=50
{"model": "voigt", "mu": 80} # Center fixed at x=80
]
result=fit_model(x, y, peaks)
# View results (fixed parameters are preserved in result["params"])assertresult["params"][1]["mu"] ==50# mu is lockedassertresult["params"][1]["A"] isnotNone# A and sigma are fitted

Manual Initial Guess (Advanced)

Override automatic detection with manual p0:

# Manual initial guesses (if you know better than auto-detection!)p0= [
{"A": 100, "mu": 25, "sigma": 5},
{"A": 80, "mu": 75, "sigma": 6}
]
result=fit_model(x, y, peaks, p0=p0)

Plotting Results

fromreadytofitimportplot_fit_resultfig, ax=plt.subplots(figsize=(10, 6))
plot_fit_result(x, y, result, show_residual=True, show_rmse=True, fig=fig, ax=ax)
fig.suptitle("Multi-Peak Fitting Results")
plt.savefig("fit_result.png")
plt.close()

Example output:

Fitting Results

Computing Peak Areas

fromreadytofitimportevaluate_peak_areasareas=evaluate_peak_areas(x, result)
print(f"Total signal area: {areas['total']:.2f}")
print(f"Individual peak areas: {areas['peaks']}")

Automatic Peak Detection (Standalone)

Detect peaks without fitting:

fromreadytofitimportdetect_peaksdetected=detect_peaks(x, y, n_peaks=3)
fori, peakinenumerate(detected):
print(f"Peak {i+1}:")
print(f" Center (μ): {peak['mu']:.2f}")
print(f" Amplitude (A): {peak['A']:.3f}")
print(f" Width (σ): {peak['sigma']:.3f}")

📊 fit_model() Output

The fit_model() function returns a comprehensive results dictionary:

result= {
# Parameters"popt": [...], # Optimized parameters (flat, free only)"params": [ # Structured parameters per peak
{"A": 5.0, "mu": 30.0, "sigma": 5.1},
{"A": 3.0, "mu": 70.0, "sigma": 3.0}
],
# Fitted curves"total_fit": [...], # Full reconstructed signal"peak_fits": [...], # Individual peaks# Diagnostics"residual": [...], # y - total_fit"rmse": 0.1006, # Root mean square error# Metadata"param_names": [...], # Names of free parameters"param_slices": [...], # Index mapping per peak"model_function": callable, # Composite model function"p0": [...], # Initial guess used"bounds": (lower, upper) # Bounds used in optimization
}

Access results:

# Check fit qualityprint(f"RMSE: {result['rmse']:.4f}")
# Get all parameters (including fixed ones)fori, peakinenumerate(result["params"]):
print(f"Peak {i}: {peak}")
# Get fitted curve and residualsy_fitted=result["total_fit"]
residuals=result["residual"]
# Get individual peak contributionsfori, peak_curveinenumerate(result["peak_fits"]):
plt.plot(x, peak_curve, label=f"Peak {i}")

🔬 Supported Peak Models

ModelParametersUse Case
gaussA, μ, σSymmetric peaks (most common)
voigtA, μ, σ, γSymmetric peaks with natural broadening
asymA, μ, σL, σR, γAsymmetric peaks (different left/right widths)
skewA, μ, σ, γ, αPeaks with asymmetric tails

Legend:

  • A: Amplitude (peak height above baseline)
  • μ: Center position (can be fixed: {"model": "gauss", "mu": 50})
  • σ: Standard deviation (width parameter)
  • σL, σR: Left/right widths for asymmetric model
  • γ: Lorentz width (natural line broadening)
  • α: Skew parameter (asymmetry control)

Fixed parameters are removed from optimization. Example:

{"model": "gauss", "mu": 50}
# → Only A and sigma are fitted; mu is locked at 50

📐 Key Features in Detail

🔹 Automatic parameter handling
No manual indexing required — parameters are flattened internally and fixed parameters are properly managed.

🔹 Robust fitting pipeline
Handles missing p0, partial bounds, and invalid input gracefully with intelligent fallbacks.

🔹 Full decomposition
Inspect total fit, individual peaks, residuals, and peak areas independently.

🔹 Peak detection
detect_peaks() and estimate_initial_parameters() enable automatic initial guess generation.

📚 Public API Reference

Core Fitting

  • fit_model(x, y, peaks, p0=None, bounds=None, debug=False)
    Main fitting function. Automatically detects peaks and estimates p0 if not provided.

Visualization

  • plot_fit_result(x, y, result, show_residual=True, show_rmse=True, fig=None, ax=None)
    Plot raw data, total fit, individual peaks, residuals, and RMSE.

Peak Detection

  • detect_peaks(x, y, n_peaks=None, height_threshold=0.1, prominence_threshold=None, distance=None)
    Automatically detect peaks. Returns list of peak dictionaries with estimated parameters.

  • estimate_initial_parameters(x, y, peaks)
    Estimate initial parameters from data. Respects fixed parameters in peak definitions.

Area Integration

  • evaluate_peak_areas(x, result)
    Compute areas of total fit and individual peaks using trapezoidal rule.

  • area_integration(y, x=None)
    Low-level numerical integration utility.

� Dependencies

numpy
scipy
matplotlib

To add new peak types

To add new functions you need the following changes:

  • Insert the function definition into functions.py
  • Add dedicated section in get_model() inside functions.py
  • Add dedicated entry inside the PARAM_ORDER dictionary inside parameters.py
  • Add dedicated section in generate_default_p0() inside parameters.py
  • Add the new peak name into peak_types variable inside test.py in the peaks import section

About

A flexible and modular Python toolkit for multi-peak curve fitting, parameter management, visualization, and area analysis using scipy.optimize.curve_fit.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

📊 ReadyToFit — PeakFit Functions

A flexible and modular Python toolkit for multi-peak curve fitting, parameter management, visualization, and area analysis using scipy.optimize.curve_fit.

⭐ Author Notes

I had to dig into scipy too many times over the years without using it on a daily basis. I decided that I needed a more handy peak-fitting tool (based on scipy) that would speed up my workflow. This project is designed as a modular research-grade fitting system, not a black-box tool. Each component can be reused independently in scientific workflows.

This package is designed to handle arbitrary numbers of peaks with different models (Gaussian, Voigt, Asymmetric, Skewed), including support for fixed parameters (e.g., fixed peak centers μ).

🚀 Features

🔹 Automatic peak detection — Intelligently finds peaks and estimates initial parameters
🔹 Multi-peak fitting with arbitrary peak count
🔹 Multiple peak models:
- Gaussian
- Voigt
- Asymmetric
- Skewed
🔹 Support for fixed parameters (e.g., μ fixed per peak)
🔹 Automatic parameter flattening/unflattening
🔹 Intelligent initial guess generation from data
🔹 Flexible bounds handling
🔹 Full peak decomposition after fitting
🔹 Area integration for peaks and total signal
🔹 Clean visualization with residuals and RMSE

⚙️ Installation

Install from source:

git clone https://github.com/your-username/ReadyToFit.git
cd ReadyToFit
pip install .

Or install directly from GitHub:

pip install git+https://github.com/your-username/ReadyToFit.git

Dependencies: numpy, scipy, matplotlib (automatically installed).

🧠 Core Concept

Each peak is defined as a dictionary:

peaks= [
{"model": "gauss"},
{"model": "voigt", "mu": 50}, # fixed center at x=50
{"model": "asym"}
]

The system automatically:

  • Detects peaks in data using scipy.signal.find_peaks
  • Estimates initial parameters (amplitude, position, width) from data
  • Builds the composite model (sum of all peaks)
  • Flattens parameters for optimization
  • Handles fixed parameters (removed from optimization, reinstated after fit)
  • Reconstructs fitted peaks

🔍 Automatic p0 Generation & Peak Detection

The Problem: Multi-peak fitting requires good initial guesses (p0). Bad guesses lead to poor convergence or fitting the wrong peaks.

The Solution: ReadyToFit includes intelligent automatic peak detection:

  1. Peak Detection (detect_peaks()):

    • Finds local maxima using local gradient analysis
    • Filters by prominence (relative height above baseline)
    • Estimates peak positions, amplitudes, and widths
  2. Parameter Estimation (estimate_initial_parameters()):

    • Converts detected peaks into initial parameter guesses
    • Estimates widths from Full-Width-Half-Maximum (FWHM)
    • Respects user-defined fixed parameters (not overridden)
  3. Automatic p0:

    • When fit_model() is called without p0, it auto-generates it
    • Much more robust than naive guesses (e.g., same μ for all peaks)
    • Can be overridden by providing manual p0 if needed

Example:

fromreadytofitimportdetect_peaks# Detect peaks manually (optional — fit_model() does this automatically)detected=detect_peaks(x, y, n_peaks=3)
# Returns: [{"mu": 25.5, "A": 4.8, "sigma": 5.1}, ...]

📈 Usage Examples

Automatic Peak Detection & Fitting (Recommended)

The simplest approach: ReadyToFit automatically detects peaks and estimates initial parameters.

fromreadytofitimportfit_modelimportnumpyasnpimportmatplotlib.pyplotasplt# Your noisy multi-peak datax=np.array([...])
y=np.array([...])
# Define peak structure (models only — parameters auto-detected)peaks= [
{"model": "gauss"},
{"model": "gauss"},
{"model": "voigt"}
]
# Fit — p0 and initial parameters are auto-generated!result=fit_model(x, y, peaks)
print(f"RMSE: {result['rmse']:.4f}")
print("Fitted parameters per peak:")
fori, peak_paramsinenumerate(result["params"]):
print(f" Peak {i+1}: {peak_params}")

How auto-detection works:

  1. Peak finding: Uses scipy.signal.find_peaks to locate local maxima
  2. Parameter estimation: Estimates amplitude (A), center (μ), and width (σ) from data
  3. Smart initial guess: Provides excellent starting point for optimization
  4. Respects fixed parameters: If you specify "mu": 50, it's locked and not overridden

With Fixed Parameters

Lock specific parameter values (e.g., peak center):

peaks= [
{"model": "gauss"}, # All parameters free
{"model": "gauss", "mu": 50}, # Center fixed at x=50
{"model": "voigt", "mu": 80} # Center fixed at x=80
]
result=fit_model(x, y, peaks)
# View results (fixed parameters are preserved in result["params"])assertresult["params"][1]["mu"] ==50# mu is lockedassertresult["params"][1]["A"] isnotNone# A and sigma are fitted

Manual Initial Guess (Advanced)

Override automatic detection with manual p0:

# Manual initial guesses (if you know better than auto-detection!)p0= [
{"A": 100, "mu": 25, "sigma": 5},
{"A": 80, "mu": 75, "sigma": 6}
]
result=fit_model(x, y, peaks, p0=p0)

Plotting Results

fromreadytofitimportplot_fit_resultfig, ax=plt.subplots(figsize=(10, 6))
plot_fit_result(x, y, result, show_residual=True, show_rmse=True, fig=fig, ax=ax)
fig.suptitle("Multi-Peak Fitting Results")
plt.savefig("fit_result.png")
plt.close()

Example output:

Fitting Results

Computing Peak Areas

fromreadytofitimportevaluate_peak_areasareas=evaluate_peak_areas(x, result)
print(f"Total signal area: {areas['total']:.2f}")
print(f"Individual peak areas: {areas['peaks']}")

Automatic Peak Detection (Standalone)

Detect peaks without fitting:

fromreadytofitimportdetect_peaksdetected=detect_peaks(x, y, n_peaks=3)
fori, peakinenumerate(detected):
print(f"Peak {i+1}:")
print(f" Center (μ): {peak['mu']:.2f}")
print(f" Amplitude (A): {peak['A']:.3f}")
print(f" Width (σ): {peak['sigma']:.3f}")

📊 fit_model() Output

The fit_model() function returns a comprehensive results dictionary:

result= {
# Parameters"popt": [...], # Optimized parameters (flat, free only)"params": [ # Structured parameters per peak
{"A": 5.0, "mu": 30.0, "sigma": 5.1},
{"A": 3.0, "mu": 70.0, "sigma": 3.0}
],
# Fitted curves"total_fit": [...], # Full reconstructed signal"peak_fits": [...], # Individual peaks# Diagnostics"residual": [...], # y - total_fit"rmse": 0.1006, # Root mean square error# Metadata"param_names": [...], # Names of free parameters"param_slices": [...], # Index mapping per peak"model_function": callable, # Composite model function"p0": [...], # Initial guess used"bounds": (lower, upper) # Bounds used in optimization
}

Access results:

# Check fit qualityprint(f"RMSE: {result['rmse']:.4f}")
# Get all parameters (including fixed ones)fori, peakinenumerate(result["params"]):
print(f"Peak {i}: {peak}")
# Get fitted curve and residualsy_fitted=result["total_fit"]
residuals=result["residual"]
# Get individual peak contributionsfori, peak_curveinenumerate(result["peak_fits"]):
plt.plot(x, peak_curve, label=f"Peak {i}")

🔬 Supported Peak Models

ModelParametersUse Case
gaussA, μ, σSymmetric peaks (most common)
voigtA, μ, σ, γSymmetric peaks with natural broadening
asymA, μ, σL, σR, γAsymmetric peaks (different left/right widths)
skewA, μ, σ, γ, αPeaks with asymmetric tails

Legend:

  • A: Amplitude (peak height above baseline)
  • μ: Center position (can be fixed: {"model": "gauss", "mu": 50})
  • σ: Standard deviation (width parameter)
  • σL, σR: Left/right widths for asymmetric model
  • γ: Lorentz width (natural line broadening)
  • α: Skew parameter (asymmetry control)

Fixed parameters are removed from optimization. Example:

{"model": "gauss", "mu": 50}
# → Only A and sigma are fitted; mu is locked at 50

📐 Key Features in Detail

🔹 Automatic parameter handling
No manual indexing required — parameters are flattened internally and fixed parameters are properly managed.

🔹 Robust fitting pipeline
Handles missing p0, partial bounds, and invalid input gracefully with intelligent fallbacks.

🔹 Full decomposition
Inspect total fit, individual peaks, residuals, and peak areas independently.

🔹 Peak detection
detect_peaks() and estimate_initial_parameters() enable automatic initial guess generation.

📚 Public API Reference

Core Fitting

  • fit_model(x, y, peaks, p0=None, bounds=None, debug=False)
    Main fitting function. Automatically detects peaks and estimates p0 if not provided.

Visualization

  • plot_fit_result(x, y, result, show_residual=True, show_rmse=True, fig=None, ax=None)
    Plot raw data, total fit, individual peaks, residuals, and RMSE.

Peak Detection

  • detect_peaks(x, y, n_peaks=None, height_threshold=0.1, prominence_threshold=None, distance=None)
    Automatically detect peaks. Returns list of peak dictionaries with estimated parameters.

  • estimate_initial_parameters(x, y, peaks)
    Estimate initial parameters from data. Respects fixed parameters in peak definitions.

Area Integration

  • evaluate_peak_areas(x, result)
    Compute areas of total fit and individual peaks using trapezoidal rule.

  • area_integration(y, x=None)
    Low-level numerical integration utility.

� Dependencies

numpy
scipy
matplotlib

To add new peak types

To add new functions you need the following changes:

  • Insert the function definition into functions.py
  • Add dedicated section in get_model() inside functions.py
  • Add dedicated entry inside the PARAM_ORDER dictionary inside parameters.py
  • Add dedicated section in generate_default_p0() inside parameters.py
  • Add the new peak name into peak_types variable inside test.py in the peaks import section

About

A flexible and modular Python toolkit for multi-peak curve fitting, parameter management, visualization, and area analysis using scipy.optimize.curve_fit.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages