DATA & ANALYTICS · FOUNDATION PROJECT

FinMonitorSmall scripts.
Clearer signals.

Turning messy transaction records into a cleaner dataset, explainable flags, and a concise review summary.

A simplified demonstration of a small part of the SQL/Python-related work I did at Deloitte, focused here on foundational Python scripting.

Explore the pipeline Try the rule logic ↗
A BATCH, MADE REVIEWABLE ↗
01 / INPUTTransaction batch
Missing amount—
Duplicate record×2
Unusual spend?
02 / THROUGH THE PIPELINE
Clean→Detect→Report
03 / READY FOR REVIEWTrace the flag. Review the record.

Illustrative workflow · simulated transactions

IMPLEMENTEDPython + pandas
STRUCTURE3 focused scripts
DATASimulated CSV batch
APPROACHExplicit rule logic

Make the exceptions
easier to inspect.

Before an analyst can interpret an unusual transaction, the underlying data needs to be usable. Missing amounts, duplicated rows, and inconsistent formats can obscure the story.

This demo breaks the work into three readable stages: prepare the records, apply a transparent decision, and summarise what needs review. A flag is a prompt for investigation, not proof of fraud.

Where SQL fits

SQL supports the wider investigation skill set: checking completeness, reconciling counts, and grouping records. The supplied implementation is Python/pandas and CSV; no SQL query or database integration was provided for this demo.

One batch. Three deliberate steps.

Each script writes the file the next script reads. Run order matters; orchestration is a future extension.

  1. INPUTtransactions.csvSimulated records
  2. 01 / CLEANclean_data.pyPrepare the dataset
  3. 02 / DETECTanalyze_data.pyCalculate and flag
  4. 03 / REPORTreport.pySummarise for review
01 DATA QUALITY

Start with cleaner inputs.

Standardise column names, remove exact duplicate rows, and coerce dates and amounts into usable types.

  • Mark missing or invalid amounts before median imputation.
  • Drop null transaction IDs, customer IDs, and merchants.
  • Keep positive amounts; sort by customer and date.
What the cleaning does not guarantee

Invalid dates become missing dates (NaT) and are not removed. Blank strings are not explicitly normalised. Duplicates are removed across full rows, not by transaction ID. If every amount is missing, the median cannot fill them.

→ cleaned_data.csv
02 ANOMALY LOGIC

Give every flag a reason.

Calculate each customer’s mean amount across the loaded cleaned batch. Compare each transaction with that baseline.

  • Spend spike: amount > 2 × customer mean.
  • High value: amount > ₹20,000.
  • Merchant match: exact match to a demo list.

The demo list contains Apple and Dell as arbitrary examples. It is not an assessment of those businesses.

→ anomalies.csv
03 REPORTING

Make the review manageable.

For a non-empty flagged set, calculate transaction count, distinct customers, total amount, and mean amount.

  • Print leading merchants by flag count.
  • Print leading customers by flagged spend.
  • Write the four summary metrics to CSV.
How an empty result behaves

The script prints a no-anomalies message and skips writing the summary CSV. Existing summary files are not cleared by the supplied code.

→ daily_anomaly_summary.csv

Three checks.
One combined decision.

A spend spike alone triggers a flag. Otherwise, both a high value and a merchant-list match are required. High value alone does not trigger the final flag.

Spend spikeOR(High value AND Merchant match)

INTERACTIVE EXPLANATION

Follow one transaction.

Choose an example to see how the same decision plays out.

Fixed examples, not an execution of the Python pipeline. Baselines shown are assumed batch means.

A baseline limitation worth showing.

The current transaction is included in its customer’s batch mean. This is not a historical-only or rolling baseline, so a large amount can raise its own comparison threshold. Both amount comparisons use strict “greater than”; equality does not pass.

The Python behind the pipeline.

The three code excerpts supplied for this demo, with formatting restored for readability. They use pandas as pd; the excerpts omit the import and assume the input files and output folder already exist.

Original comments and output labels are retained. “Company logic” labels this illustrative decision; it does not establish Deloitte ownership. “High-risk” merchant labels and “amount at risk” are demo terminology, not verified risk assessments or losses.

clean_data.py

# 1. Load raw data
df = pd.read_csv('../data/transactions.csv')

# 2. Standardize column names
df.columns = df.columns.str.lower().str.strip()

# 3. Remove exact duplicate rows
df = df.drop_duplicates()

# 4. Convert date column to datetime
df['date'] = pd.to_datetime(df['date'], errors='coerce')

# 5. Handle missing or invalid amounts
df['amount'] = pd.to_numeric(df['amount'], errors='coerce')
df['amount_was_missing'] = df['amount'].isna()
median_amount = df['amount'].median()
df['amount'] = df['amount'].fillna(median_amount)

# 6. Remove transactions with missing critical identifiers
df = df.dropna(subset=['transaction_id', 'customer_id', 'merchant'])

# 7. Remove negative or zero-value transactions
df = df[df['amount'] > 0]

# 8. Sort for consistency (audit-friendly)
df = df.sort_values(by=['customer_id', 'date'])

# 9. Save cleaned data
df.to_csv('../output/cleaned_data.csv', index=False)

analyze_data.py

# 1. Load cleaned data
df = pd.read_csv('../output/cleaned_data.csv')

# 2. Customer-level baseline
avg_spend = df.groupby('customer_id')['amount'].mean()
df['avg_customer_spend'] = df['customer_id'].map(avg_spend)

# 3. Rule 1: Spend spike anomaly
df['spend_anomaly'] = df['amount'] > (2 * df['avg_customer_spend'])

# 4. Rule 2: High absolute transaction value
df['high_value_anomaly'] = df['amount'] > 20000

# 5. Rule 3: High-risk merchant
high_risk_merchants = ['Apple', 'Dell']
df['merchant_risk'] = df['merchant'].isin(high_risk_merchants)

# 6. Final anomaly flag (company logic)
df['final_anomaly'] = (
    df['spend_anomaly'] |
    (df['high_value_anomaly'] & df['merchant_risk'])
)

# 7. Save outputs
df[df['final_anomaly']].to_csv('../output/anomalies.csv', index=False)

report.py

# 1. Load anomaly data
df = pd.read_csv('../output/anomalies.csv')

# 2. Handle case where no anomalies exist
if df.empty:
    print("No anomalies detected for this period.")
else:
    # 3. Summary metrics
    total_anomalies = len(df)
    unique_customers = df['customer_id'].nunique()
    total_amount = df['amount'].sum()
    avg_anomaly_amount = df['amount'].mean()

    print(f"Total flagged transactions : {total_anomalies}")
    print(f"Unique customers impacted : {unique_customers}")
    print(f"Total amount at risk (₹)  : {total_amount:,.2f}")
    print(f"Average anomaly amount    : {avg_anomaly_amount:,.2f}")

    # 4. Top risky merchants
    print("\nTop merchants by anomaly count:")
    print(df['merchant'].value_counts().head())

    # 5. Top customers by total anomaly spend
    print("\nTop customers by anomaly spend:")
    print(
        df.groupby('customer_id')['amount']
          .sum()
          .sort_values(ascending=False)
          .head()
    )

    # 6. Save daily summary
    summary_df = pd.DataFrame([{
        'total_anomalies': total_anomalies,
        'unique_customers': unique_customers,
        'total_amount_at_risk': total_amount,
        'average_anomaly_amount': avg_anomaly_amount
    }])
    summary_df.to_csv('../output/daily_anomaly_summary.csv', index=False)

A useful handoff, not a black box.

01 / CSV

Cleaned records

cleaned_data.csv

Retained rows with standardised fields and an amount-missing indicator.

02 / CSV

Flagged transactions

anomalies.csv

Flagged rows retain the customer mean and individual rule columns.

03 / CONDITIONAL CSV

Batch summary

daily_anomaly_summary.csv

Four aggregate metrics when flags exist. The filename says “daily”; the script does not filter by day.

+ Console output adds the summary and top merchants/customers. This is a reporting channel, not a fourth output file. Flagged amounts are review totals, not confirmed financial losses.

The next layer
is reliability.

The value of this foundation is its inspectability: each transformation and decision can be explained. Moving toward production would require more than adding detection rules.

BUILT TO BE UNDERSTOOD

Clean the data.
Explain the exception.

A small example of turning analytical work into a reviewable process.