4.1. Basic Data¶
This tutorial covers how to load and prepare data for curve fitting.
4.1.1. Data Requirements¶
NLSQ requires:
x: Independent variable (1D array)
y: Dependent variable (1D array, same length as x)
p0: Initial parameter guess (list or array)
import numpy as np
from nlsq import fit
x = np.array([0, 1, 2, 3, 4])
y = np.array([1.0, 0.6, 0.4, 0.2, 0.1])
p0 = [1.0, 0.5]
popt, pcov = fit(model, x, y, p0=p0)
4.1.2. Loading from Files¶
CSV files:
import numpy as np
# Simple CSV with columns: x, y
data = np.loadtxt("data.csv", delimiter=",", skiprows=1)
x, y = data[:, 0], data[:, 1]
# With pandas
import pandas as pd
df = pd.read_csv("data.csv")
x, y = df["x"].values, df["y"].values
NumPy files:
data = np.load("data.npz")
x, y = data["x"], data["y"]
HDF5 files:
import h5py
with h5py.File("data.h5", "r") as f:
x = f["x"][:]
y = f["y"][:]
4.1.3. Data Types¶
NLSQ accepts various array types:
# Python lists (converted internally)
popt, pcov = fit(model, [0, 1, 2], [1.0, 0.6, 0.4], p0=[1, 0.5])
# NumPy arrays (recommended)
x = np.array([0, 1, 2])
y = np.array([1.0, 0.6, 0.4])
# JAX arrays
import jax.numpy as jnp
x = jnp.array([0, 1, 2])
y = jnp.array([1.0, 0.6, 0.4])
Float64 is used internally for numerical precision.
4.1.4. Handling Missing Data¶
Remove NaN values before fitting:
# Method 1: Boolean indexing
mask = ~(np.isnan(x) | np.isnan(y))
x_clean = x[mask]
y_clean = y[mask]
# Method 2: Use nan_policy parameter
popt, pcov = fit(model, x, y, p0=[...], nan_policy="omit")
4.1.5. Data Scaling¶
For best numerical stability, scale data if values are very large or small:
# Scale x to [0, 1] range
x_min, x_max = x.min(), x.max()
x_scaled = (x - x_min) / (x_max - x_min)
# Scale y to reasonable range
y_mean = y.mean()
y_scaled = y / y_mean
# Fit scaled data
popt, pcov = fit(model, x_scaled, y_scaled, p0=[...])
# Adjust parameters back (depends on model)
NLSQ can also automatically rescale data:
popt, pcov = fit(model, x, y, p0=[...], rescale_data=True)
4.1.6. Multi-dimensional X¶
For models with multiple independent variables:
import jax.numpy as jnp
def surface(xy, a, b, c):
"""2D surface: z = a*x + b*y + c"""
x, y = xy
return a * x + b * y + c
# Pack x, y into tuple
xdata = (x_array, y_array)
popt, pcov = fit(surface, xdata, z_array, p0=[1, 1, 0])
4.1.7. Complete Example¶
import numpy as np
import jax.numpy as jnp
from nlsq import fit
# Load data
df = pd.read_csv("experiment.csv")
x = df["time"].values
y = df["signal"].values
# Remove any invalid data
mask = np.isfinite(x) & np.isfinite(y)
x, y = x[mask], y[mask]
# Define model
def exponential(x, A, k, c):
return A * jnp.exp(-k * x) + c
# Initial guess based on data inspection
p0 = [
y.max() - y.min(), # Amplitude
1.0 / (x.max() / 3), # Rough decay rate
y.min(),
] # Offset
# Fit
popt, pcov = fit(exponential, x, y, p0=p0)
A, k, c = popt
print(f"Amplitude: {A:.3f}")
print(f"Decay rate: {k:.3f}")
print(f"Offset: {c:.3f}")
4.1.8. Next Steps¶
Uncertainties (Sigma) - Add measurement errors
Parameter Bounds - Constrain parameters