File size: 2,801 Bytes
71c85a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import pandas as pd


def preprocess_data(df:pd.DataFrame):
    """Clean and feature-engineer the PaySim dataset for a flat-row fraud model.



    Steps, in order:

    - Copies the input (no mutation of the caller's frame)

    - Trims whitespace from column names

    - Drops duplicate rows and the near-useless isFlaggedFraud column

    - Adds orig/dest balance-missing flags (data-quality markers: balance

      is a placeholder zero while amount is non-zero)

    - Drops nameOrig/nameDest (unique per row, no signal for a flat model)

    - Derives errorBalanceOrig/errorBalanceDest (accounting inconsistencies;

      highest-signal engineered features)

    - Keeps only TRANSFER and CASH_OUT rows, the only types where fraud

      occurs in PaySim (first-pass choice)

    - One-hot encodes the remaining transaction types



    Note: does not split, scale, or handle class imbalance — do those

    downstream on the training split only, to avoid leakage.



    Args:

        df: raw PaySim dataframe

    Returns:

        cleaned, feature-engineered dataframe (features + isFraud label)

    """

    df = df.copy()
    df.columns = df.columns.str.strip() # remove leading/trailing whitespaces


    #There were no duplicates but remove anyway just in case
    df = df.drop_duplicates()

    #the column isFlaggedFraud is near useless so its better to remove
    df = df.drop(columns=['isFlaggedFraud'], errors = 'ignore')

    #a classic case of fraud is when the balance is zero and a transaction happens
    #so we create a new column of boolean (0/1)
    df['orig_balance_missing'] = ((df['oldbalanceOrg'] == 0) &
                                  (df['newbalanceOrig'] ==0) &
                                  (df['amount'] > 0)).astype(int)

    df['dest_balance_missing'] = ((df['oldbalanceDest'] == 0) &
                                  (df['newbalanceDest'] ==0) &
                                  (df['amount'] > 0)).astype(int)

    #names are noise since they are unique
    df = df.drop(columns=['nameOrig','nameDest'],errors = 'ignore')     

    #Balance-error features are the highest-signal engineered columns.
    # Rather than "cleaning," the standard move is deriving
    df['errorBalanceOrig'] = df['newbalanceOrig'] + df['amount'] - df['oldbalanceOrg']
    df['errorBalanceDest'] = df['oldbalanceDest'] + df['amount'] - df['newbalanceDest']

    #I will also keep only the two types of transaction that have frauds (Transfer and cash out)
    #at least for the first pass
    df = df[df['type'].isin(['TRANSFER','CASH_OUT'])].copy()

       
    #Last important to use one hot encoding in the type since it is varchars and four classes
    df = pd.get_dummies(df, columns=['type'], dtype=int)
    

    return df