Powrót do strony głównej

Koliderowy bias w data science: symulacja efektu

Artykuł omawia collider bias — zniekształcenie korelacji przy filtrowaniu po koliderze. Przedstawiono symulacje Python na danych uniform i normal z wizualizacją. Zalecenia unikania w analizie.

Collider bias: fałszywe powiązania od filtrowania danych
Advertisement 728x90

Collider bias: jak filtrowanie danych tworzy fałszywe korelacje

Często filtrowanie danych według wyniku prowadzi do mylących wniosków: zmienne niezależne zaczynają się negatywnie korelować. To właśnie collider bias — efekt, gdy warunkowanie na zmiennych typu "kolizja" zniekształca statystyki. W praktyce pojawia się przy analizie przeżywających obiektów, sukcesów czy wyfiltrowanych próbek. Symulacja w Pythonie pokazuje mechanizm na losowych danych.

Definicja kolizji

Kolizja to zmienna C, do której prowadzą ścieżki przyczynowe od A i B: A → C ← B. Warunkowanie na C (filtracja lub stratyfikacja) otwiera fałszywą zależność między A i B.

Przykłady z praktyki:

Google AdInline article slot
  • Wiek → Używanie → Jakość (przeżyłe przedmioty wydają się lepsze).
  • Inteligencja → Wejście do uczelni → Bogactwo rodziców (średnio wśród studentów bogaci wydają się mniej inteligentni).
  • Talent → Sukces → Pracowitość (udane osoby z talentem wydają się leniwe).

W data science prowadzi to do błędnych modeli: gradient boosting na wyfiltrowanych danych dziedziczy ten błąd.

Symulacja na danych jednostajnych

Generujemy 1000 punktów A i B z rozkładu uniform(0,100), niezależnych. Kolizja flag = (A > 50) | (B > 50).

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
from statsmodels.nonparametric.smoothers_lowess import lowess

NUMBER_OF_VALUES = 1000
A_RANGE = (0, 100)
B_RANGE = (0, 100)
A_THRESHOLD = 50
B_THRESHOLD = 50

a = np.random.uniform(*A_RANGE, NUMBER_OF_VALUES)
b = np.random.uniform(*B_RANGE, NUMBER_OF_VALUES)
data = pd.DataFrame({'a': a, 'b': b})
data['flag'] = (data['a'] > A_THRESHOLD) | (data['b'] > B_THRESHOLD)
print(f'Korelacja między A i B: {data["a"].corr(data["b"]):.2f}')
print(f'Korelacja dla flag == True: {data[data["flag"]]["a"].corr(data[data["flag"]]["b"]):.2f}')

Wynik:

Google AdInline article slot
  • Całość: corr ≈ 0.00
  • Wyfiltrowana: corr ≈ -0.34

Wykres rozrzutu pokazuje, jak usunięcie "nieprzeżyłych" punktów (niebieskie) przesuwa trend w dół.

sns.scatterplot(data=data, x='a', y='b', hue='flag', palette={True: 'red', False: 'blue'}, alpha=0.5)
sns.regplot(data=data, x='a', y='b', scatter=False, color='black')
plt.show()

Po filtracji lmplot pokazuje ujemny nachylenie: prawy górny róg "pusty", równowaga naruszona.

Realistyczny scenariusz: rozkład normalny

Przechodzimy do np.random.normal: A ~ N(50,5), B ~ N(30,5). Kolizja probabilistyczna: normalizujemy, selection_prob = max(a_norm, b_norm)*1.5, clip(0,1), flag = rand < prob.

Google AdInline article slot
a = np.random.normal(loc=50, scale=5, size=NUMBER_OF_VALUES)
b = np.random.normal(loc=30, scale=5, size=NUMBER_OF_VALUES)
normal_data = pd.DataFrame({'a': a, 'b': b})
a_norm = (normal_data['a'] - normal_data['a'].min()) / (normal_data['a'].max() - normal_data['a'].min() + 1e-6)
b_norm = (normal_data['b'] - normal_data['b'].min()) / (normal_data['b'].max() - normal_data['b'].min() + 1e-6)
selection_prob = np.maximum(a_norm, b_norm) * 1.5
selection_prob = np.clip(selection_prob, 0, 1)
normal_data['flag'] = np.random.random(len(normal_data)) < selection_prob
print(f'Korelacja w całym zbiorze: {normal_data["a"].corr(normal_data["b"]):.2f}')
print(f'Korelacja gdy flag == True: {normal_data[normal_data["flag"]]["a"].corr(normal_data[normal_data["flag"]]["b"]):.2f}')

Wynik:

  • Całość: corr ≈ 0.03
  • Wyfiltrowana: corr ≈ -0.09

Efekt słabszy, ale istotny w A/B testach, farmach lub ML.

Jak uniknąć w analizie

  • Rysuj DAG: grafy przyczynowe (directed acyclic graphs) pomagają wykryć kolizje.
  • Nie filtrować po wyniku: analizuj pełną populację lub użyj IPCW (inverse probability censoring weighting).
  • Sprawdzaj korelacje: porównuj dane przed i po stratyfikacji.
  • Zmienne instrumentalne: do wnioskowania przyczynowego.
  • Sieci bayesowskie: modelują zależności jawnie.

Kluczowe informacje

  • Collider bias pojawia się przy warunkowaniu na wspólnej przyczynie — tworzy fałszywą negatywną korelację.
  • Symulacja na rozkładzie jednostajnym daje corr -0.34, na normalnym -0.09; efekt skaluje się.
  • Powiązany z błędem przeżywającego, paradoksem Berksa, biasem selekcji.
  • Sprawdź: narysuj DAG przed filtrowaniem.
  • W ML: wyfiltrowane dane → modele z biasem, niedoszacowanie/przeszacowanie cech.

— Editorial Team

Advertisement 728x90

Czytaj dalej