# 8th place solution

I would like to express my gratitude to Kaggle and University College London for hosting this competition. I learned a great deal, and it was truly an exciting experience.
Summary
Preprocess : noise reduction
Stage 1 :  transit time prediction & filtering bad samples
Stage2 : physics-based transit modeling & 2d global fitting
Postprocess : ml-based sigma prediction & simple refinements
Preprocess : noise reduction
Applied all standard preprocessing functions except for the mask_hot_dead function.
For time binning, 30 is used for airs_ch0 channels, and 30 × 12 is used for fgs1 channel.
The spatial dimension is cropped to the range 8 ~ 24.
The remove_outlier function modifies outliers in the data using the local mean.
The effect of the remove_outlier function can be confirmed in the following figure.
():
    df = pd.DataFrame(data)

    mean = df.rolling(window = window, center = , min_periods = ).mean()
    std = df.rolling(window = window, center = , min_periods = ).std()

    mask = (df - mean).() > (threshold * std)

    df[mask] = mean[mask]
    data = df.to_numpy()
     data
![](assets/b561c5c7af99d004c06e4e74.png)
Stage1 : transit time prediction & filtering bad samples
The input is obtained by averaging all channels and then normalizing.
Transit and system are modeled with simple polynomials to predict T1, T2, T3, T4.
Additionally, Success or not is predicted to prevent negative scores from bad samples.
If success == False, wl and sigma are set to the train dataset’s mean and std.
Criteria for success = False:
Fitting error > 1e-3
T1 < 0.01 or T4 > 0.99
The Stage 1 fitting results is visualized in the following figure.
![](assets/eacf934a5ea86288c8b0fc6b.png)
Stage2 : physics-based transit modeling & 2d global fitting
Implemented a custom nonlinear limb darkening pipeline (c1, c2, c3, c4), inspired by the batman library.
To avoid unrealistic cases (where c1 + c2 + c3 + c4 > 1), each coefficient is capped at 0.25.
Used least_squares for global fitting across all channels with parameters: rp, c1, c2, c3, c4, P, sma, i, t0.
Applied tanh to c1–c4 (range -1 to 1) for better convergence stability and easier max constraints.
Constructed jac_sparsity to predefine parameter correlations and speed up convergence.
Adopted the previous competition’s 1st-place approach: system = 1 + f(time) × g(wavelength).
To mitigate degeneracy between limb darkening effect and system effect, a 2-step fitting is used:
Step 1 : f & g modeled as 3rd-order polynomials.
Step 2 : f & g modeled as 4th-order polynomials.
This allows limb darkening coefficients to converge first, reducing errors from degeneracy.
For faster computation, wavelength binning of 4 is applied.
Since channels beyond 200 in airs_ch0 have severe noise, a window size of 50 is used.
The target of the Stage 2 fitting is visualized in the following figure.
Pool(processes = os.cpu_count())  pool:
    res = least_squares(
        fun = fun,
        x0 = x0,
        bounds = bounds,
        method = ,
        jac_sparsity = jac_sparsity,
        args = (time, star_info, param_info, targets),
        workers = pool.,
        verbose =   plot  ,
    )
![](assets/9efc8541a07a81b417b4880d.png)
![](assets/28de81cd8fbf98b59b9d2788.png)
Postprocess : ml-based sigma prediction & simple refinements
inputs = parameters obtained from stage 1 and stage 2 for the training data.
targets =  optimal sigma per sample, computed over np.linspace(5e-5, 2e-3, 500).
Applied a 4-fold ensemble of GradientBoostingRegressor for inference.
For the fgs1 channel, results are simply scaled by ×2 from airs_ch0 channels.
Additional refinements -> wavelength scaling, gaussian_filter1d, and PCA.
inputs = np.concatenate([
    T,
    T[:, :] - T[:, :],
    T[:, :] - T[:, :],

    rp[:, :-].mean(, keepdims = ),
    c1[:, :-].mean(, keepdims = ),
    c2[:, :-].mean(, keepdims = ),
    c3[:, :-].mean(, keepdims = ),
    c4[:, :-].mean(, keepdims = ),

    rp[:, :-].std(, keepdims = ),
    c1[:, :-].std(, keepdims = ),
    c2[:, :-].std(, keepdims = ),
    c3[:, :-].std(, keepdims = ),
    c4[:, :-].std(, keepdims = ),

    cost,
    nfev,

    star_info.values,
], axis = )
Code
[https://github.com/siwooyong/NeurIPS-Ariel-Data-Challenge-2025](https://github.com/siwooyong/NeurIPS-Ariel-Data-Challenge-2025)