# Kontrola danych w Pythonie: funkcja do śledzenia zmian sumy kolumny
Podczas przetwarzania danych w pandas łatwo przeoczyć zmianę kluczowych metryk z powodu błędów w operacjach merge lub groupby. Przedstawiamy funkcję det_sum, która nie tylko oblicza sumę kolumny, ale także rejestruje odchylenia między wywołaniami, pomagając wykrywać anomalie w procesie ETL.
Po co monitorować sumę kolumny?
W procesie przetwarzania danych kluczowe jest śledzenie stabilności najważniejszych wskaźników. Na przykład operacja merge z błędnie podanymi kluczami może niespodziewanie zwiększyć rozmiar danych, a groupby z brakującymi wartościami — go zmniejszyć. Szczególnie narażone są metryki finansowe: jeśli suma sprzedaży nagle zmieni się o 15%, to sygnał alarmowy w potoku przetwarzania. Standardowe podejście z df['sales'].sum() tego nie rozwiązuje — pokazuje surową liczbę bez kontekstu zmian i wymaga ręcznego porównywania między etapami.
Funkcja det_sum automatyzuje ten proces, dodając:
- Automatyczne formatowanie dużych liczb
- Porównanie z poprzednią wartością
- Wygodną identyfikację tabel
- Czytelne powiadomienia o odchyleniach
Architektura funkcji det_sum
Implementacja opiera się na trzech zmiennych globalnych do przechowywania stanu między wywołaniami:
_previous_sum = None
_case_number = 0
_table_names = {}
Funkcja przyjmuje dwa parametry: DataFrame i nazwę kolumny. Kluczowe etapy działania:
- Sprawdzenie istnienia kolumny
- Obliczenie bieżącej sumy
- Formatowanie liczby
- Generowanie unikalnego kodu tabeli
- Porównanie z poprzednią wartością
Kod funkcji zachowuje równowagę między funkcjonalnością a prostotą:
def det_sum(table, column):
import pandas as pd
import random
global _previous_sum, _case_number, _table_names
_case_number += 1
if column not in table.columns:
print(f"\n🔍 Summary №{_case_number}: Andryukha! The column disappeared '{column}'! Possible crime! Saddle up!")
return
current_sum = table[column].sum()
def format_number(num):
abs_num = abs(num)
if abs_num >= 1_000_000_000:
return f"{num/1_000_000_000:.1f} billion"
elif abs_num >= 1_000_000:
return f"{num/1_000_000:.1f} mln"
elif abs_num >= 1_000:
return f"{num/1_000:.1f} tys"
else:
return f"{num:.1f}"
formatted_current = format_number(current_sum)
table_id = id(table)
if table_id not in _table_names:
animals = ["Antelope", "Beaver", "Badger", "Wolf", "Otter", "Cheetah", "Gorilla", "Porcupine", "Dolphin",
"Raccoon", "Giraffe", "Zebra", "Hare", "Iguana", "Yemeni chameleon", "Cat", "Kangaroo",
"Lev", "Llama", "Bear", "Walrus", "Rhinoceros", "Deer", "Panda", "Puma",
"Lynx", "Elephant", "Meerkat", "Tiger", "Seal", "Platypus", "Flaamimingo", "Hamster",
"Heron", "Turtle", "Chimpanzee", "Pike", "Emu", "Brambling", "Yak"]
_table_names[table_id] = random.choice(animals)
table_code = _table_names[table_id]
print(f"\n🕵️ Summary AN №{_case_number}: Onservation for object '{table_code}' by column '{column}'")
print("=" * 50)
if _previous_sum is None:
print(f"🔎 Object '{table_animal}' was accepted under observation! Amount: {formatted_current}")
print("📋 Fiksiruem in svodke...")
else:
print(f"💼 Tekuschaya sum: {formatted_current}")
difference = current_sum - _previous_sum
if difference != 0:
formatted_diff = f"{abs(difference):,.0f}".replace(',', ' ')
if difference > 0:
print(f"⬆️ Hmm... Amount wzrosla on {formatted_diff}")
print("🕵️ Looks like, we missed new connection! Sending crew for kontrole!")
else:
print(f"⬇️ Wow! Amount decreased on {formatted_diff}")
print("🔍 Who-then is trying to cover tracks! Need check by cameras!")
else:
print("🤔 Amount not changed...")
print("📝 Zanosim in svodku: Object not outputil, meetings not recorded")
print("=" * 50)
_previous_sum = current_sum
Formatowanie liczb dla lepszej czytelności
Główny problem ze standardowym wyświetlaniem sumy to nieczytelność dużych liczb. Funkcja format_number rozwiązuje to, konwertując 1500000 na „1.5 mln”. Algorytm przetwarzania:
- Dla wartości ≥ 1 mld: dzielenie przez 10^9 z zaokrągleniem do dziesiątych
- Dla wartości ≥ 1 mln: dzielenie przez 10^6
- Dla tysięcy: dzielenie przez 10^3
- Pozostałe — wyświetlanie z jednym miejscem po przecinku
To szczególnie ważne przy pracy z danymi finansowymi, gdzie operacje na miliardach to codzienność. Formatowanie odbywa się automatycznie bez dodatkowych wywołań, co przyspiesza analizę.
Unikalna identyfikacja tabel
Ponieważ w pandas nie da się bezpośrednio pobrać nazwy zmiennej DataFrame, funkcja stosuje sprytny trik — identyfikator pamięci (id(table)). Przy pierwszym obraschenii do tabeli generowane jest losowe zwierzę z predefiniowanej listy. Pozwala to:
- Śledzić wiele tabel w jednym skrypcie
- Zachowywać historię zmian dla każdej tabeli niezależnie
- Unikać pomyłek przy transformacjach (nazwa oryginalnej tabeli często się zmienia)
Lista zwierząt zawiera 40 wariantów, od Antylopy po Jaka. Przy ponownym wywołaniu dla tej samej tabeli używana jest zapisana nazwa, co zapewnia spójność logów.
Integracja z procesem pracy
Aby używać funkcji na stałe, warto wyodrębnić ją do osobnego modułu. Zalecana struktura:
- Utworzyć plik config/func.py
- Umieścić tam kod det_sum
- Zaimportować w głównym skrypcie:
import importlib
import config.func
importlib.reload(config.func)
from config.func import det_sum
Przeładowanie modułu przez importlib.reload pozwala na wprowadzanie zmian bez restartu kernela. Taki sposób:
- Zmniejsza duplikację kodu
- Centralizuje narzędzia
- Zachowuje czystość głównego skryptu
Co ważne
- Zmienne globalne jako stan: Użycie _previous_sum i _case_number pozwala śledzić zmiany między wywołaniami, ale wymaga ostrożności w środowiskach wielowątkowych.
- Bezpieczeństwo sprawdzeń: Funkcja najpierw weryfikuje obecność kolumny, zapobiegając krytycznym błędom w procesie przetwarzania.
- Wyjście zorientowane na człowieka: Formatowanie liczb i tematyczne komunikaty przyspieszają diagnozę problemów bez ręcznej analizy logów.
- Identyfikacja przez id(): Metoda omijająca ograniczenia pandas do śledzenia tabel działa stabilnie w ramach jednej sesji wykonania.
- Elastyczność integracji: Funkcja nie wymaga zmian w istniejących potokach — wystarczy dodać wywołanie det_sum w kluczowych miejscach.
Narzędzie jest szczególnie przydatne przy debugowaniu złożonych procesów ETL, gdzie zmiany danych powinny być przewidywalne. Implementacja zajmuje zaledwie 30 linii kodu, ale zaspokaja kluczową potrzebę monitoringu danych.
— Editorial Team
Brak komentarzy.