# Data Monitoring in Python: Function for Tracking Column Sum Changes
When processing data in pandas, it's easy to overlook changes in key metrics due to errors in merge or groupby operations. Introducing the det_sum function, which not only calculates the column sum but also tracks deviations between calls, helping detect anomalies in the ETL process.
Why Monitor Column Sums?
In data processing, it's crucial to track the stability of key metrics. For example, a merge operation with incorrect keys can unexpectedly inflate the data volume, while groupby with missing values can shrink it. Financial metrics are especially vulnerable: if the sales sum suddenly shifts by 15%, it's a red flag for an issue in the processing pipeline. The standard approach with df['sales'].sum() falls short—it spits out a raw number without change context and demands manual comparisons across stages.
The det_sum function automates this by adding:
- Automatic formatting of large numbers
- Comparison with the previous value
- Handy table identification
- Human-readable deviation alerts
Architecture of the det_sum Function
The implementation relies on three global variables to maintain state between calls:
_previous_sum = None
_case_number = 0
_table_names = {}
The function takes two parameters: a DataFrame and a column name. Key stages:
- Check if the column exists
- Calculate the current sum
- Format the number
- Generate a unique table code name
- Compare with the previous value
The code strikes a balance between functionality and simplicity:
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} thousand"
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
Number Formatting for Readability
A key issue with standard sum output is the unreadability of large numbers. The format_number function fixes this by turning 1500000 into "1.5 mln". The algorithm:
- For values ≥ 1 billion: divide by 10^9, round to tenths
- For values ≥ 1 million: divide by 10^6
- For thousands: divide by 10^3
- Others: display with one decimal place
This is especially vital for financial data, where billions are par for the course. Formatting happens automatically without extra calls, speeding up analysis.
Unique Table Identification
Since pandas doesn't let you directly get a DataFrame variable's name, the function uses a clever trick: the memory ID (id(table)). On first access, it picks a random animal from a preset list. This enables:
- Tracking multiple tables in one script
- Maintaining change history per table independently
- Avoiding confusion during transformations (source table names often change)
The animal list has 40 options, from Antelope to Yak. Repeat calls for the same table reuse the saved name for log consistency.
Integration into Workflow
For ongoing use, put the function in a separate module. Recommended setup:
- Create config/func.py
- Add the det_sum code there
- Import in the main script:
import importlib
import config.func
importlib.reload(config.func)
from config.func import det_sum
Reloading via importlib.reload applies changes without restarting the kernel. This approach:
- Reduces code duplication
- Centralizes utilities
- Keeps the main script clean
What Matters
- Global variables for state: Using _previous_sum and _case_number tracks changes across calls but calls for caution in multithreaded environments.
- Safe checks: The function verifies column existence first, preventing crashes during processing.
- Human-focused output: Number formatting and themed messages speed up issue diagnosis without manual log digging.
- Identification via id(): This pandas workaround reliably tracks tables within a single session.
- Flexible integration: No need to overhaul pipelines—just insert det_sum calls where needed.
This tool shines for debugging complex ETL processes where data changes must be predictable. The implementation is just 30 lines but fills a critical data monitoring gap.
— Editorial Team
No comments yet.