KajoData / Praca i rozwój
Czyszczenie danych: pięć kroków, które nie zgubią sensu analizy
Przycisk Usuń duplikaty działa szybko. Odpowiedź na pytanie, co właściwie jest duplikatem, wymaga myślenia. Pokazuję pięć etapów czyszczenia danych oraz sposób sprawdzenia, czy porządki nie zepsuły wyniku.

W pracy z danymi łatwo zakochać się w ostatnim etapie. Wykres wygląda dobrze, dashboard reaguje na kliknięcia, a liczba na górze ekranu ma odpowiednio duży font. Tylko że wcześniej ktoś musiał ustalić, dlaczego jedno zamówienie występuje trzy razy. Ja właśnie od tego zaczynam.
Czyszczenie danych, czyli data cleaning, obejmuje wykrywanie problemów oraz przygotowanie danych do konkretnego zastosowania. Słowo „konkretnego” ma znaczenie. Zbiór wystarczający do policzenia zamówień może nadal nie nadawać się do obliczenia ich wartości. Brak ceny nie przeszkadza w każdym pytaniu tak samo.
Pokażę pięć kroków: duplikaty, braki, ujednolicenie zapisu, walidację i anomalie. Dołożę kontrolę przed i po zmianach, bo po sprzątaniu chcę wiedzieć, co zniknęło oraz dlaczego. Wszystkie liczby i zamówienia w przykładzie są syntetyczne. Służą do ćwiczenia decyzji, bez udawania raportu z prawdziwej firmy.
Zanim zacznę poprawiać, ustalam znaczenie wiersza
Wyobraź sobie plik z identyfikatorem zamówienia, nazwą miasta i wartością zakupu. Widzę dwa wiersze z tym samym numerem. Ręka już leci do usuwania duplikatów. Zatrzymuję ją, dopóki nie wiem, czy wiersz oznacza całe zamówienie, jego pozycję, płatność czy zmianę statusu. Każda z tych tabel ma inną ziarnistość.
W tabeli pozycji poprawnym kluczem może być para numer zamówienia i numer pozycji. W historii statusów potrzebuję jeszcze informacji o zdarzeniu. Sama powtarzalność numeru zamówienia niczego nie rozstrzyga. Usuwanie według niewłaściwego klucza potrafi pozbawić tabelę prawidłowych produktów, płatności albo historii.
Zapisuję więc jedno zdanie: „Jeden wiersz reprezentuje jedno zamówienie po zakończeniu jego rejestracji”. Następnie ustalam oczekiwany klucz, zakres czasu, jednostkę wartości i znaczenie statusów. Jeżeli źródło nie gwarantuje jednego wiersza na zamówienie, muszę zmienić to założenie albo przygotować agregację. Dokumentacja ma opisywać dane, które dostałem, a nie dane z moich marzeń.
Zachowuję punkt odniesienia
Pracuję na kopii albo w osobnym etapie przetwarzania. Oryginał powinien zostać dostępny w kontrolowanym miejscu, z datą pobrania i informacją o źródle. Dzięki temu mogę wrócić do rekordu oraz rozdzielić zmianę w systemie źródłowym od własnego przekształcenia. W firmie obowiązują przy tym jej zasady przechowywania danych.
Przed zmianami notuję liczbę wierszy, liczbę różnych kluczy, udział braków i zakres dat. Dla wartości liczbowych sprawdzam minimum, maksimum oraz sumę, o ile dodawanie ma sens. Sumowanie identyfikatorów klientów niczego użytecznego mi nie powie. Sumowanie kwot w różnych walutach również wymaga wcześniejszego ustalenia zasad.
Oglądam także rozkład kategorii. Gdy większość wierszy ma status „completed”, a kilkanaście zawiera „Completed ”, podejrzewam różnicę zapisu. Gdy pojawia się zupełnie nowy status, nie wciskam go automatycznie do najbliższej starej szufladki. Być może zmienił się proces, a raport właśnie o tym informuje.
Krok pierwszy: rozstrzygam duplikaty
Rozróżniam identyczną kopię całego rekordu od konfliktu pod tym samym kluczem. Dwie identyczne linie mogą wynikać z ponownego załadowania pliku. Dwa zamówienia o tym samym ID, lecz różnych kwotach, wymagają innego postępowania. Zostawienie pierwszego wiersza nie jest uniwersalnym rozwiązaniem konfliktu.
W przykładzie zakładam, że potwierdziłem regułę źródła: każdy identyfikator oznacza jedno zamówienie, a powtórzenie identycznych pól jest techniczną kopią. Dopiero w tych warunkach zachowuję jedną kopię. Gdyby dane reprezentowały zdarzenia, dwa identycznie wyglądające wpisy mogłyby oznaczać dwa osobne zdarzenia i potrzebowałbym ich identyfikatorów.
Narzędzie wykonuje wybraną regułę. W pandas metoda drop_duplicates pozwala wskazać kolumny porównania oraz sposób zachowania powtórzeń. To ja muszę zdecydować, które kolumny opisują tożsamość rekordu. Przy konflikcie zwykle wydzielam rekordy do wyjaśnienia i pokazuję skalę problemu osobie znającej źródło.
Sprawdzam też, czy duplikaty nie pojawiły się dopiero po połączeniu tabel. Poprawna tabela zamówień może rozmnożyć wiersze po dołączeniu wielu kontaktów klienta. Wtedy naprawiam sposób łączenia, zamiast maskować jego skutek kolejnym usuwaniem duplikatów. Ten problem pokazuję szerzej przy rodzajach JOIN w SQL.
Krok drugi: brakom nadaję znaczenie
Puste pole jest informacją, że czegoś nie ma w danym miejscu. Powód może być bardzo różny: pytanie nie dotyczy rekordu, użytkownik nie odpowiedział, system nie przesłał wartości albo proces jeszcze się nie zakończył. Pusty numer zwrotu sam w sobie nie dowodzi, że zwrotu nie było. Potrzebuję znajomości procesu i kompletności źródła.
Najbardziej kuszący skrót to zastąpienie wszystkich braków zerem. Dla ceny prowadzi to do dopisania darmowych zamówień, których nikt nie zarejestrował. Dla czasu dostawy tworzy dostawy natychmiastowe. Dla oceny klienta zmienia brak odpowiedzi w bardzo konkretną ocenę. Tabela wygląda schludniej, a znaczenie liczb odpływa.
W dokumentacji pandas o brakach znajdziesz rozróżnienie ich reprezentacji oraz metody isna i notna. Na poziomie analizy najpierw zapisuję decyzję: zachowuję brak, wyłączam rekord z określonej miary czy stosuję uzasadnione uzupełnienie. Uzupełnione wartości oznaczam, aby później dało się je odróżnić od obserwacji.
Przy awarii zbierania danych samo usunięcie pustych rekordów nie usuwa awarii. Może za to ukryć przerwę w pomiarze. Sprawdzam, czy braki skupiają się w konkretnym dniu, kanale albo typie zamówienia. W raporcie podaję zakres dostępnych danych. Gdy braków nie potrafię wyjaśnić, ograniczam wniosek zamiast dodawać precyzję po przecinku.
Krok trzeci: ujednolicam zapis, jednostki i typy
„Warszawa”, „warszawa” i „ Warszawa ” mogą opisywać tę samą miejscowość. Usunięcie skrajnych spacji i ujednolicenie wielkości liter pomaga, ale szersze mapowanie wymaga ostrożności. Skrót „WA” może znaczyć coś innego w różnych źródłach. Używam jawnego słownika, którego pozycje można przejrzeć i poprawić.
Nie zamieniam bezmyślnie każdej kolumny na liczbę. Kod produktu 0012 może potrzebować zer na początku, podobnie jak kod pocztowy. O typie decyduje znaczenie, a nie wygląd komórki. Przy datach ustalam format i kontekst strefy czasowej. Zapis 03/04/2026 bez dodatkowej informacji pozostaje niejednoznaczny.
Wartości pieniężne wymagają waluty, a pomiary jednostki. Samo przemianowanie nagłówka z dolarów na złote nie jest przeliczeniem. Nawet przy jednej walucie trzeba rozróżnić kwotę netto i brutto. W moim ćwiczeniu kwoty są całkowitą liczbą groszy, co pozwala prześledzić rachunek bez problemu reprezentacji ułamków.
Słowo „normalizacja” bywa używane w kilku znaczeniach. W projektowaniu baz dotyczy organizacji tabel i zależności. W przygotowaniu cech modelu może oznaczać zmianę skali. Tutaj przede wszystkim ujednolicam zapis oraz typy. Rozdzielenie tych pojęć pomaga uniknąć rozmowy, w której wszyscy przytakują, ale każdy planuje inną operację.

Krok czwarty: sprawdzam reguły biznesowe
Poprawny typ nie gwarantuje sensu. Data dostawy może być wcześniejsza od zamówienia, mimo że obie komórki są prawidłowymi datami. Status może należeć do słownika, ale nie pasować do innych pól. Dlatego obok testu pojedynczej kolumny potrzebuję sprawdzeń relacji między wartościami.
Najpierw zapisuję regułę językiem biznesowym. Na przykład: „W tym zestawie zakończone zamówienie musi mieć datę zakończenia”. Dopiero później wybieram formułę, warunek SQL albo kod. Tak sformułowaną zasadę może ocenić ktoś, kto zna proces, nawet jeśli nie pisze zapytań. Bywa, że już podczas tej rozmowy wychodzi na jaw ważny wyjątek.
Ujemna kwota może być błędem, zwrotem lub korektą. Zanim uznam ją za niepoprawną, ustalam zakres tabeli. W danych wyłącznie o nowych zamówieniach oczekiwania są inne niż w rejestrze rozliczeń. Reguła powinna mieć opis, właściciela oraz sposób obsługi naruszenia. Inaczej lista błędów szybko staje się kolejnym plikiem, którego nikt nie otwiera.
Krok piąty: badam anomalie zamiast je odruchowo kasować
Duże zamówienie może pochodzić od klienta hurtowego. Nagły spadek stanu magazynowego może oznaczać realny brak towaru. Nietypowy rekord często niesie właśnie tę informację, po którą przyszliśmy. Automatyczne wygładzenie wszystkich odstępstw daje przyjemny wykres, lecz może ukryć problem operacyjny.
NIST opisuje wartości odstające jako wymagające zbadania i zwraca uwagę na założenia metod ich wykrywania. Ja traktuję próg jako alarm do sprawdzenia. Porównuję rekord z podobną grupą, zaglądam do źródła i dopiero wtedy wybieram dalsze postępowanie. Ten sam próg może źle działać dla klientów detalicznych i hurtowych.
Gdy potwierdzę pomyłkę, poprawiam ją na podstawie dowodu albo wyłączam rekord z jasno określonej analizy. Jeżeli wartości nie da się rozstrzygnąć, mogę pokazać wynik z nią oraz bez niej jako analizę wrażliwości. Obie wersje opisuję. Czytelnik powinien widzieć wpływ decyzji, zamiast zgadywać, dlaczego liczba zmieniła się między spotkaniami.
Mały przykład: co zostało po sprzątaniu
Mam sześć wierszy. Zamówienie A występuje dwukrotnie z identyczną kwotą 10000 groszy. B ma wartość 20000, C nie ma kwoty, D ma zero, a E ma 500000. Wszystkie dotyczą jednej waluty. Dla tego ćwiczenia potwierdzam techniczną kopię A, dopuszczalne darmowe zamówienie D i poprawne duże zamówienie E.
rows = [
("A", 10000), ("A", 10000), ("B", 20000),
("C", None), ("D", 0), ("E", 500000),
]
unique = list(dict.fromkeys(rows))
known = [amount for _, amount in unique if amount is not None]
print(len(rows), len(unique), len(known)) # 6 5 4
print(sum(known)) # 530000
print(sum(known) / len(known)) # 132500.0
Kod w Pythonie usuwa identyczne pary, zgodnie z przyjętą regułą ćwiczenia. Nie rozwiązuje konfliktów kwot pod tym samym ID. Przed użyciem takiego podejścia na innym zbiorze trzeba sprawdzić klucz i pola. Podstawowe zachowanie None wyjaśniam w artykule o typach danych w Pythonie.
Po zmianie mam pięć zamówień, ale tylko cztery znane kwoty. Ich suma to 5300 zł, a średnia znanych wartości to 1325 zł. To nie pozwala określić pełnej wartości pięciu zamówień, dopóki nie poznam kwoty C. Zero D bierze udział w średniej, bo jest znaną wartością. Brak C nie dostaje wymyślonego zera.
Gdybym jednak zastąpił brak zerem, otrzymałbym średnią 1060 zł. W osobnym wariancie, bez uzupełniania braku, usunięcie dużego E dałoby średnią pozostałych znanych wartości równą 100 zł. Żaden z tych zabiegów nie jest kosmetyką. Zmienia odpowiedź biznesową. Właśnie dlatego przy opisie średniej podaję, które rekordy weszły do mianownika.
Rejestr zmian pomaga wyjaśnić wynik
Do raportu dopisuję: sześć wierszy wejściowych, jedna usunięta kopia, pięć zamówień po deduplikacji, jedna nieznana kwota, zero zachowane, duża wartość sprawdzona i zachowana. Kompletność kwot wynosi cztery z pięciu, czyli 80 procent. Osobno podaję, że suma dotyczy wyłącznie znanych kwot.
Taki zapis pozwala szybko uzgodnić liczby. Jeżeli ktoś oczekuje sześciu zamówień, wskazuję duplikat. Jeżeli pyta o pełną wartość sprzedaży, pokazuję nierozstrzygnięty brak. Nie muszę odtwarzać kliknięć z pamięci. Przy większym zbiorze podobny rejestr powinien wynikać z procesu, z identyfikatorami rekordów i powodami decyzji.
Ważne, żeby rekord z problemem nie znikał bez śladu. Może pozostać w osobnym zestawie do wyjaśnienia albo mieć flagę jakości. Sposób zależy od zastosowania i zasad firmy. Chcę jednak móc odpowiedzieć, ile danych odłożono, czego dotyczą i czy ich brak zmienia interpretację raportu.
Ustalam, które problemy blokują odpowiedź
Zanim ustawię alarmy, określam, które błędy blokują daną odpowiedź. Brak identyfikatora zamówienia może uniemożliwiać deduplikację, ale brak opcjonalnej notatki zwykle nie zmienia sumy. Nie nadaję obu problemom tej samej wagi. Kolejność napraw wynika z wpływu na pytanie, częstotliwości oraz możliwości odzyskania informacji ze źródła.
Na przykład literówka w mieście dotykająca dwóch małych zamówień może mieć niewielki wpływ na sumę całego sklepu, a ogromny na raport dla tej miejscowości. Dlatego ocenę jakości wiążę z poziomem raportowania. Tabela potrafi być wystarczająca do jednego wykresu i niewystarczająca do drugiego, choć fizycznie jest dokładnie tym samym plikiem.
Ustalam z odbiorcą, co zrobić po przekroczeniu progu: zatrzymać publikację, pokazać ostrzeżenie czy opublikować tylko część miar. Próg sam w sobie nie rozwiązuje problemu. Potrzebna jest osoba, która dostanie informację, oraz jasny opis oczekiwanej reakcji. Inaczej automatyzacja będzie codziennie produkować ostrzeżenie, które z czasem stanie się niewidzialne.
Wreszcie rozdzielam naprawę raportu od naprawy źródła. Słownik może na dziś scalić trzy warianty nazwy miasta, ale ograniczenie błędów w formularzu zmniejszy ich liczbę w przyszłości. Zapisuję więc również przyczynę i propozycję poprawy procesu. Dobre czyszczenie pomaga wykonać bieżące zadanie, a zebrane przy nim obserwacje mogą ograniczyć konieczność ponownego sprzątania za miesiąc.
Sprawdzam powtarzalność całego procesu
Dobry wynik dzisiaj nie wystarcza, gdy jutro dostanę nowy plik. Zapisuję kolejność kroków i warunki, przy których proces powinien się zatrzymać. Brak wymaganej kolumny, nowa waluta albo nagły wzrost konfliktów pod kluczem zasługują na wyraźny sygnał. Ciche pomijanie problemów tylko przesuwa moment ich odkrycia.
Sprawdzam też drugi przebieg na już przygotowanych danych. Jeśli ponowne czyszczenie znów usuwa rekordy albo po raz kolejny przelicza jednostki, szukam przyczyny. W wielu procesach oczekuję, że ponowienie nie zmieni poprawnego wyniku. Wyjątki trzeba nazwać, na przykład pobranie nowszej wersji słownika czy aktualizacji źródła.
Na końcu porównuję wartości z niezależnym punktem odniesienia: małą ręcznie policzoną próbką, kontrolnym raportem albo znaną liczbą zdarzeń. Zgodność nie dowodzi jeszcze poprawności każdego rekordu, ale rozbieżność daje konkretne miejsce do szukania. Wolę znaleźć ją przed prezentacją dashboardu. Publiczne odkrywanie podwójnie policzonej sprzedaży ma ograniczony urok.
Ćwiczenie: dodaj konflikt i brak całej grupy
Do przykładu dopisz A z kwotą 12000. Zastanów się, czy identyczność całego wiersza nadal wystarcza do kontroli klucza. Powinny zostać wykryte dwie różne kwoty jednego zamówienia. Bez dodatkowej wiedzy nie wybieraj automatycznie większej, mniejszej ani ostatniej. Zapisz, jakiej informacji potrzebujesz do rozstrzygnięcia.
Następnie wyobraź sobie, że wszystkie zamówienia z jednego kanału mają brakującą kwotę. Łączna kompletność może wyglądać znośnie, a porównanie kanałów będzie poważnie ograniczone. Policz kompletność osobno dla grup. To ćwiczenie pokazuje, dlaczego pojedynczy procent jakości całego zbioru potrafi uspokoić zbyt wcześnie.
Na własnym projekcie zacznij od jednej tabeli, jednej miary i pięciu zapisanych reguł. Przy każdej podaj wynik kontroli, decyzję oraz liczbę dotkniętych rekordów. Dopiero potem buduj wizualizację. Gdy potrafisz wyjaśnić drogę od źródła do liczby, czyszczenie danych staje się częścią argumentacji, którą odbiorca może sprawdzić.
Film o czyszczeniu danych
W nagraniu omawiam pięć podstawowych obszarów pracy z jakością danych. Przykład zamówień i kontrole opisane wyżej rozwijają tekst artykułu.
Najważniejsze w pigułce
Czy każdy powtarzający się rekord trzeba usunąć?
Nie. Najpierw określ, co oznacza wiersz i jaki klucz powinien go identyfikować. Dwie pozycje jednego zamówienia są poprawne, a dwie kopie tej samej pozycji mogą być błędem importu.
Czy brakujące wartości można zastąpić zerem?
Tylko gdy znaczenie danych uzasadnia zero. Brak ceny oznacza nieznaną cenę, a nie darmowy produkt. Warto zachować powód braku i osobno mierzyć kompletność.
Czy wartości odstające należy usuwać?
Samo odstawanie jest powodem do sprawdzenia. Rekord usuwam z konkretnej analizy dopiero po ustaleniu błędu albo uzasadnionego wykluczenia, zachowując ślad decyzji.
