Detecting fraud on e-commerce orders with Benford's law

In this article by Andrea Cirillo, author of the book RStudio for R Statistical Computing Cookbook, has explained how to detect fraud on e-commerce orders.

Benford's law is a popular empirical law that states that the first digits of a population of data will follow a specific logarithmic distribution.

This law was observed by Frank Benford around 1938 and since then has gained increasing popularity as a way to detect anomalous alteration of population of data.

Basically, testing a population against Benford's law means verifying that the given population respects this law. If deviations are discovered, the law performs further analysis for items related to those deviations.

In this recipe, we will test a population of e-commerce orders against the law, focusing on items deviating from the expected distribution.

(For more resources related to this topic, see here.)

Getting ready

This recipe will use functions from the well-documented benford.analysis package by Carlos Cinelli.

We therefore need to install and load this package:

install.packages("benford.analysis")

library(benford.analysis)

In our example, we will use a data frame that stores e-commerce orders, provided within the book as an .Rdata file.

In order to make it available within your environment, we need to load this file by running the following command (assuming the file is within your current working directory):

load("ecommerce_orders_list.Rdata")

How to do it...

  1. Perform Benford test on the order amounts:
    benford_test <- benford(ecommerce_orders_list$order_amount,1)
  2. Plot test analysis:
    plot(benford_test)
    

    This will result in the following plot:

  3. Highlights supectes digits:
    suspectsTable(benford_test)

    This will produce a table showing for each digit absolute differences between expected and observed frequencies. The first digits will therefore be more anomalous ones:

    > suspectsTable(benford_test)

       digits absolute.diff

    1:      5     4860.8974

    2:      9     3764.0664

    3:      1     2876.4653

    4:      2     2870.4985

    5:      3     2856.0362

    6:      4     2706.3959

    7:      7     1567.3235

    8:      6     1300.7127

    9:      8      200.4623

  4. Define a function to extrapolate the first digit from each amount:

    left = function (string,char){
    
      substr(string,1,char)}
  5. Extrapolate the first digit from each amount:
    ecommerce_orders_list$first_digit <- left(ecommerce_orders_list$order_amount,1)
  6. Filter amounts starting with the suspected digit:
    suspects_orders <- subset(ecommerce_orders_list,first_digit == 5)

How it works

Step 1 performs the Benford test on the order amounts. In this step, we applied the benford() function to the amounts. Applying this function means evaluating the distribution of the first digits of amounts against the expected Benford distribution.

The function will result in the production of the following objects:

Object

Description

Info

This object covers the following general information:

  • data.name: This shows the name of the data used
  • n: This shows the number of observations used
  • n.second.order: This shows the number of observations used for second-order analysis
  • number.of.digits: This shows the number of first digits analyzed

Data

This is a data frame with the following subobjects:

  • lines.used: This shows  the original lines of the dataset
  • data.used: This shows the data used
  • data.mantissa: This shows the log data's mantissa
  • data.digits: This shows the first digits of the data

s.o.data

This is a data frame with the following subobjects:

  • data.second.order: This shows the differences of the ordered data 
  • data.second.order.digits: This shows the first digits of the second-order analysis

Bfd

This is a data frame with the following subobjects:

  • digits: This highlights the groups of digits analyzed
  • data.dist: This highlights the distribution of the first digits of the data
  • data.second.order.dist: This highlights the distribution of the first digits of the second-order analysis
  • benford.dist: This shows the theoretical Benford distribution
  • data.second.order.dist.freq: This shows the frequency distribution of the first digits of the second-order analysis
  • data.dist.freq: This shows the frequency distribution of the first digits of the data
  • benford.dist.freq: This shows the theoretical Benford frequency distribution
  • benford.so.dist.freq: This shows the theoretical Benford frequency distribution of the second order analysis.
  • data.summation: This shows the summation of the data values grouped by first digits
  • abs.excess.summation: This shows the absolute excess summation of the data values grouped by first digits
  • difference: This highlights the difference between the data and Benford frequencies
  • squared.diff: This shows the chi-squared difference between the data and Benford frequencies
  • absolute.diff: This highlights the absolute difference between the data and Benford frequencies

Mantissa

This is a data frame with the following subobjects:

  • mean.mantissa: This shows the mean of the mantissa
  • var.mantissa: This shows the variance of the mantissa
  • ek.mantissa: This shows the excess kurtosis of the mantissa
  • sk.mantissa: This highlights the skewness of the mantissa

MAD

This object depicts the mean absolute deviation.

distortion.factor

This object talks about the distortion factor.

Stats

This object lists of htest class statistics as follows:

  • chisq: This lists the Pearson's Chi-squared test.
  • mantissa.arc.test: This lists the Mantissa Arc test

Step 2 plots test results. Running plot on the object resulting from the benford() function will result in a plot showing the following (from upper-left corner to bottom-right corner):

  • First digit distribution
  • Results of second-order test
  • Summation distribution for each digit
  • Results of chi-squared test
  • Summation differences

If you look carefully at these plots, you will understand which digits show up a distribution significantly different from the one expected from the Benford law. Nevertheless, in order to have a sounder base for our consideration, we need to look at the suspects table, showing absolute differences between expected and observed frequencies. This is what we will do in the next step.

Step 3 highlights suspects digits. Using suspectsTable() we can easily discover which digits presents the greater deviation from the expected distribution.

Looking at the so-called suspects table, we can see that number 5 shows up as the first digit within our table. In the next step, we will focus our attention on the orders with amounts having this digit as the first digit.

Step 4 defines a function to extrapolate the first digit from each amount. This function leverages the substr() function from the stringr() package and extracts the first digit from the number passed to it as an argument.

Step 5 adds a new column to the investigated dataset where the first digit is extrapolated.

Step 6 filters amounts starting with the suspected digit.

After applying the left function to our sequence of amounts, we can now filter the dataset, retaining only rows whose amounts have 5 as the first digit. We will now be able to perform analytical, testing procedures on those items.

Summary

In this article, you learned how to apply the R language to an e-commerce fraud detection system.

Resources for Article:


Further resources on this subject:


You've been reading an excerpt of:

RStudio for R Statistical Computing Cookbook

Explore Title