Author: NISHTHA ASHARA

  • Relational Database

    Case Study – Oracle Database

    Data Architecture to store inventory data for transaction and reporting

    The goal of this project is to create a database for a grocery store to facilitate inventory management. The database will track inventory stock, re-stocking levels, vendor list for products and status of purchase orders.

    This is a prototype of a clean database with clearly defined entity attributes and entity relationship.

    The project will include forms and reports to both enter information in the database and to allow reporting.

    The following is a list of the application’s expected functionality:

    1. Product (add and update).
    2. Inventory (add and update).
    3. Vendor (add, update, and delete).
    4. Purchase Order (add and update)

    The following are some of the questions that the database will be able to answer:

    1. Available products, inventory stock and inventory value (reporting).
    2. Available vendors to order given product (reporting).
    3. Status and Value of a Purchase Order (record retrieval).

    Data Model

    Entity Diagram

    Record Diagram

    Food Item
    PK  
    food_item_idfood_item_namecategory_name
    10001Moong DalLantil
    10002RajmaBean
    10003Khaman MixInstant Cook
    10004Plain ParathaFrozen
    10005Ratlami SevReady-to-eat-Snack
    Brand
    PK  
    brand_idbrand_namemanufacturer
    10001Mirchi MasalaDeep Foods, Inc
    10002UdupiDeep Foods, Inc
    10003Deep DairyDeep Foods, Inc
    10004SwadPatel Brothers
    10005DeepDeep Foods, Inc
    Product
    Assumptions
    unit_cost is based on last shipment
    unit_cost and unit_price are in USD

    PKFKFK          
    pdt_idfood_item_idbrand_idsize_measuresize_unitpacket_sizestock_unitcurrent_stock_levellast_modifiedunit_costunit_pricemin_stock_levelmax_stock_level
    1001001001000110004weightLB4each5010/27/20183420100
    1001001011000510001weightgm250each2510/27/201811.530100
    1001001021000210005weightLB4each1010/27/20184520200
    Vendor
    PK            
    vendor_idvendor_nameaddress_lineaddress_cityaddress_stateaddress_countryaddress_zipcontact_namecontact_phone_1contact_phone_2contact_emailcontact_faxis_active
    62345890Food Corp1058, Expensive StPhiladelphiaPAUSA12385Mike Green123-897-1234128-965-7456mg@green.com Y
    62345891Snack Special Packeges, Inc.1059, Expensive StNew YorkNYUSA56498John Dane458-896-4789 jd@gmail.com N
    Pdt_vendor_xref
    PK
    FKFK
    pdt_idvendor_id
    10010010162345891
    10010010062345890
    Purchase Order
    Assumption
    payment_amt is in USD
    PKFK    
    po_idvendor_idpayment_amtpayment_due_dtpayment_dtpayment_method
    70001236234589095010/31/201810/27/2018cheque
    70001246234589057011/15/2018  
    PO Item
    PK     
    FK FK   FK
    po_idline_nopdt_idqtyunit_pricediscountshipment_id
    7000123110010010050302560789456
    70001232100100102200402560789457
    7000124110010010020035%2560789458
    Shipment
    PK  
    shipment_idexpected_arrival_dtactual_arriaval_dt
    256078945610/31/201810/27/2018
    256078945710/31/2018 
    256078945811/20/2018 

    Database Schema

    -- ******************************************************
    --   1: CREATE TABLES
    -- ******************************************************
    
    /* Drop tables if already created */
    drop table tbPOitems;
    drop table tbShipments;
    drop table tbPO;
    drop table tbProdVendorXref;
    drop table tbProd;
    drop table tbVendors;
    drop table tbFoodItem;
    drop table tbBrand;

    drop table tbCategory;

    /* Create table */

    CREATE table tbCategory (
    categoryID char (4) not null,
    categoryName varchar2(200) not null,
    constraint pk_category primary key (categoryID)
    );
    CREATE table tbFoodItem (
    foodItemNo char(5) not null,
    constraint pk_foodItem primary key (foodItemNo),
    foodItemName varchar2(200) not null,
    categoryID char (4) not null,
    constraint fk_categoryID_tbFoodItem foreign key (categoryID) references tbCategory (categoryID)
    );
    CREATE table tbBrand (
    brandNo char(5) not null,
    constraint pk_Brand primary key (brandNo),
    brandName varchar2(200) not null,
    manufacturer char (200) null
    );
    CREATE table tbProd (
     prodNo char(9) not null,
     constraint pk_tbProd primary key (prodNo),
     foodItemNo char(5) not null,
     constraint fk_foodItemNo_tbProd foreign key (foodItemNo)      references tbFoodItem (foodItemNo),
     brandNo char(5) not null,
     constraint fk_brandNo_tbProd foreign key (brandNo) references
    tbBrand (brandNo),
     sizeMeasure char (100) not null,
     sizeUnit char (50) not null,
     packetSize number(11,2) not null,
     stockUnit char (50) null,
     currentStock number(11,2) null,
     unitCost number(11,2) null,
     unitPrice number(11,2) null,
     minStockLevel number(11,2) null,
     maxStockLevel number(11,2) null,
     lastModifiedOn date default CURRENT_DATE not null,
     constraint constraintcheck_packetSize check (packetSize>0),
     constraint constraintcheck_currentStock check (currentStock>0 OR currentStock=0),
     constraint constraintcheck_unitCost check (unitCost>0 OR unitCost=0),
     constraint constraintcheck_unitPrice check (unitPrice>0),
     constraint constraintcheck_minStockLevel check (minStockLevel>0),
     constraint constraintcheck_maxStockLevel check (maxStockLevel>0)
    );
    CREATE table tbVendors (
     vendorNo char(8) not null,
     constraint pk_tbVendors primary key (vendorNo),
     vendorName varchar2(225) not null,
     vendorAddressLine varchar2(500) null,
     vendorCity varchar2(15) not null,
     vendorState varchar2(100) not null,
     vendorCountry varchar2(150) not null,
     vendorZip varchar2(15) null,
     vendorContact varchar2(200) not null,
     vendorContactPhone varchar2(15) null,
     vendorContactPhone2 varchar2(15) null,
     vendorContactEmail varchar2(150) null,
     vendorContactFax varchar2(15) null,
     venodrIsActive varchar2(1) not null,
     constraint constraintcheck_venodrIsActive check (venodrIsActive in ('Y','N'))
    );
    CREATE table tbProdVendorXref (
     prodNo char(9) not null,
     constraint fk_prodNo_tbProdVendorXref foreign key (prodNo) references
     tbProd (prodNo) on delete cascade,
     vendorNo char(8) not null,
     constraint fk_venodrNo_tbVendors foreign key (vendorNo) references
     tbVendors (vendorNo) on delete cascade,
     constraint pk_tbProdVendorXref primary key (prodNo,vendorNo)
    );
    CREATE table tbPO (
     POno char(7) not null,
     constraint pk_tbPO primary key (POno),
     vendorNo char(8) not null,
     constraint fk_venodrNo_tbVendors_intotbPO foreign key (vendorNo) references
     tbVendors (vendorNo),
     PaymentAmount number(11,2) null,
     constraint constraintcheck_PaymentAmount check (PaymentAmount>0),
     PaymentDueDate date null,
     PaymentDate date null,
     PaymentMehtod char(100) null
    );
    CREATE table tbShipments (
     shipmentNo number (11,0) not null,
     constraint pk_tbShipments primary key (shipmentNo),
     expectedArrivalDate date null,
     actualArrivalDate date null
    );
    CREATE table tbPOitems (
     POno char(7) not null,
     constraint fk_POno_tbPO foreign key (POno) references
     tbPO(POno),
     lineNo number(3,0) not null,
     prodNo char(9) not null,
     constraint fk_prodNo_tbProd_intotbPOitems foreign key (prodNo) references
     tbProd (prodNo) on delete cascade,
     qty number(11,0) not null,
     constraint constraintcheck_qty check (qty>0),
     unitPrice number(11,2) not null,
     constraint constraintcheck_POunitPrice check (unitPrice>0),
     discount number(5,2) null,
     constraint constraintcheck_POdiscount check (discount between 0 and 100),
     shipmentNo number(11,0) null,
     constraint fk_shipmentNo_tbShipments foreign key (shipmentNo) references
     tbShipments(shipmentNo),
     constraint pk_tbPOitems primary key (POno, lineNo)
    );
    -- ******************************************************
    -- VIEW TABLES
    -- Note: Appropiate commands to show data
    -- ******************************************************
    
    SELECT count(*) FROM tbCategory;
    SELECT count(*) FROM tbBrand;
    SELECT count(*) FROM tbFoodItem;
    SELECT count(*) FROM tbProd;
    SELECT count(*) FROM tbVendors;
    SELECT count(*) FROM tbProdVendorXref;
    SELECT count(*) FROM tbPO;
    SELECT count(*) FROM tbShipments;
    SELECT count(*) FROM tbPOitems;
    SELECT * FROM tbCategory;
    SELECT * FROM tbBrand;
    SELECT * FROM tbFoodItem;
    SELECT * FROM tbProd;
    SELECT * FROM tbVendors;
    SELECT * FROM tbProdVendorXref;
    SELECT * FROM tbPO;
    SELECT * FROM tbShipments;
    SELECT * FROM tbPOitems;

    -- ******************************************************
    -- CHECK CONSTRAINTS
    -- QUALITY CONTROLS
    -- Note: Test only 2 constraints of each of
    -- the following types:
    -- *) Entity integrity
    -- *) Referential integrity
    -- *) Column constraints
    -- ******************************************************
    
    /*Entity Integrity: No Duplicate PK*/
    INSERT into tbCategory values ( '1234' , 'Dairy');
    
    /*Entity Integrity: No Null PK*/
    INSERT into tbBrand values ( '', 'Amul' , 'Amul');
    
    /*Referential Integrity: Inserting a FK which does not match a PK */
    INSERT into tbFoodItem values ( '10041', 'Ghee (Clarified Butter)' , '1111');
    
    /*Referential Integrity: Updating a PK with existing FK */
    UPDATE tbVendors SET vendorNo = '72345890' where vendorNo = '62345890';
    
    /* Column Constraint */
    INSERT into tbPO values ( '7000125', '62345890', -1000, to_date('12-15-2018','mm-dd-yyyy'), to_date('12-12-2018','mm-dd-yyyy'), '');
    INSERT into tbProd values ( '100100378', '10037', '10014', 'weight', 'gm', -250, 'each', 95, 1, 1.89, -10, 100,to_date('10-27-2018','mm-dd-yyyy'));

    SQL Queries

    -- ******************************************************
    -- ASKING QUESTIONS TO DATABASE
    -- SELECT STATEMENTS
    -- ******************************************************
    
    /* Query 1: List of Products with inventory less than minimum stock */
    select prodNo, (tbBrand.brandName || ' ' || tbFoodItem.foodItemName || ' ' || tbProd.packetSize || ' ' || tbProd.sizeUnit) as ProductName, currentStock , minStockLevel from tbProd
    inner join tbBrand on tbProd.BrandNo = tbBrand.BrandNo
    inner join tbFoodItem on tbProd.foodItemNo = tbFoodItem.foodItemNo
    where currentStock < minStockLevel;
    
    /* QUERY 2: List of Products with inventory less than minimum stock and still PO is not created */
    
    select prodNo, (tbBrand.brandName || ' ' || tbFoodItem.foodItemName || ' ' || tbProd.packetSize || ' ' || tbProd.sizeUnit) as ProductName, currentStock , minStockLevel from tbProd
    inner join tbBrand on tbProd.BrandNo = tbBrand.BrandNo
    inner join tbFoodItem on tbProd.foodItemNo = tbFoodItem.foodItemNo
    where currentStock < minStockLevel
    and prodNo not in (SELECT prodNo from tbPOitems);
    
    /* QUERY 3: List of Products with inventory less than minimum stock and still PO is not created, include vendors of respective products */
    
    select
    tbProd.prodNo,
    (tbBrand.brandName || ' ' || tbFoodItem.foodItemName || ' ' || tbProd.packetSize || ' ' || tbProd.sizeUnit) as ProductName,
    currentStock , minStockLevel,
    tbVendors.vendorName
    from tbProd
    left outer join tbProdVendorXref on tbProdVendorXref.prodNo = tbProd.prodNo
    inner join tbVendors on tbProdVendorXref.vendorNo = tbVendors.vendorNo
    inner join tbBrand on tbProd.BrandNo = tbBrand.BrandNo
    inner join tbFoodItem on tbProd.foodItemNo = tbFoodItem.foodItemNo
    where currentStock < minStockLevel
    and tbProd.prodNo not in (SELECT prodNo from tbPOitems)
    order by tbProd.prodNo, tbProdVendorXref.vendorNo;
    
    /* Query 4: List of POs due in next 2 days or overdue
    assumption: all the POs for which payment is made will have paymentDate populated */
    SELECT
    POno,
    vendorName
    from tbPO
    inner join tbVendors on tbVendors.vendorNo = tbPO.vendorNo
    where
    paymentDate is null
    AND paymentDueDate < (sysdate + 2);
    
    /* Query 5: List of Brand with number of food items and inventory value in descending order */
    SELECT
    tbBrand.brandNo, tbBrand.brandName, count(distinct foodItemNo), sum(unitCost*currentStock) as inventory_value
    from tbProd
    inner join tbBrand on tbBrand.brandNo = tbProd.brandNo
    group by tbBrand.brandNo, tbBrand.brandName
    order by inventory_value desc;

    Triggers and Transaction

    -- ******************************************************
    -- TRIGGER 1
    -- Trigger fires when there are products for which
    -- inventory is lower than minimum stock level
    -- ******************************************************
    
    /* dropping the trigger if it already exists */
    drop trigger low_stock;
    
    /* Creating the trigger*/
    CREATE OR REPLACE trigger low_stock
     AFTER insert OR update OR delete
     on tbProd
    
    /*declaring the variable*/
    declare x number(10); /* PL/SQL variable */
    begin
     select count(*) into x
     from tbProd
     inner join tbBrand on tbProd.BrandNo = tbBrand.BrandNo
     inner join tbFoodItem on tbProd.foodItemNo = tbFoodItem.foodItemNo
     where currentStock < minStockLevel;
    if x > 0
     THEN
     dbms_output.put_line (
     'Action Alert: Generate POs *** Warning: low_stock TRIGGER - Number of Products with Inventory less than minimum stock level = ' || x );
    END if;
    end low_stock;
    /
    
    /*debug the trigger in case of error*/
    SHOW ERRORS TRIGGER low_stock;
    
    /*testing trigger for insert statements*/
    INSERT into tbProd values ( '100100378', '10001', '10007', 'weight', 'LB', 10, 'each', 50, 3, 4, 20, 100,to_date('10-27-2018','mm-dd-yyyy'));
    
    INSERT into tbProd values ( '100100379', '10005', '10005', 'weight', 'kg', 1, 'each', 25, 1, 1.5, 30, 100,to_date('10-27-2018','mm-dd-yyyy'));
    
    INSERT into tbProd values ( '100100380', '10002', '10005', 'weight', 'LB', 10, 'each', 10, 4, 5, 20, 200,to_date('10-27-2018','mm-dd-yyyy'));
    
    /*testing trigger for update statement*/
    update tbProd
    set currentStock = 80
    where prodNo = '100100379';
    
    /*testing trigger for delete statements*/
    delete from tbProd
    where prodNo = '100100378';
    -- ******************************************************
    -- TRIGGER 2: CASCADE UPDATE TRIGGER
    -- When prodNo is updated in tbProd, update prodNo in subsequent children table
    -- tbProdVendorXref, tbPOitems
    -- ******************************************************
    
    /* dropping the trigger if it already exists */
    drop trigger cascade_update_prodNo;
    
    /* Creating the trigger*/
    CREATE OR REPLACE trigger cascade_update_prodno
     AFTER update
     on tbProd
    for each row
    begin
     update tbProdVendorXref
     set prodNo = :NEW.prodNo
     where prodNo = :old.prodNo;
     update tbPOitems
     set prodNo = :NEW.prodNo
     where prodNo = :old.prodNo;
    end cascade_update_prodno;
    /
    
    /*debug the trigger in case of error*/
    SHOW ERRORS TRIGGER cascade_update_prodno;
    
    /*testing the trigger by updating prodNo in tbProduct */
    select * from tbProd where prodNo = '100100100';
    select * from tbProdVendorXref where prodNo = '100100100';
    select * from tbPOitems where prodNo = '100100100';
    
    update tbProd
    set prodNo = 'A00100100'
    where prodNo = '100100100';
    
    select * from tbProd where prodNo = 'A00100100';
    select * from tbProdVendorXref where prodNo = 'A00100100';
    select * from tbPOitems where prodNo = 'A00100100';
    
    
    -- ******************************************************
    -- PL/SQL TRANSACTION
    -- Insert into tbPO, tbPOitems
    -- ******************************************************
    
    /*Values before the transaction*/
    select * from tbPO;
    select * from tbPOitems;
    
    --*** successful PO and PO Items inserts****
    
    declare
     tranState char(10); /* variable to keep track of the state */
    
    begin
     tranState := 'PO'; /* begin new project process */
     
     INSERT into tbPO 
     values ( '7000125', '62345895', '815', to_date('12-14-2018','mm-dd-yyyy'),'' , '');
    
     
     commit;
     
     savepoint po_in; /* define savepoint */
     
     tranState := 'POitems'; /* begin phone numbers process */
     
     INSERT into tbPOitems values ( '7000125', '1', '100100318', '100', '2', 10, '' );
     INSERT into tbPOitems values ( '7000125', '2', '100100328', '35', '5', 20, '');
     INSERT into tbPOitems values ( '7000125', '3', '100100342', '50', '7', 0, '');
     INSERT into tbPOitems values ( '7000125', '4', '100100353', '40', '2.25', 0, '' );
    
     
     commit;
     
     EXCEPTION
     when DUP_VAL_ON_INDEX then
     if tranState = 'POitems' then
     dbms_output.put_line ('*** Failed attempt to enter PO Items in tbPOitems');
     ROLLBACK to po_in;
     else
     dbms_output.put_line ('*** A PO with this NUMBER already exists');
     end if;
    
     when OTHERS then
     if tranState = 'POitems' then
     dbms_output.put_line ('*** Problems inserting PO Items');
     ROLLBACK to po_in;
     else
     dbms_output.put_line ('*** Problems creating new PO');
     end if; 
    end;
    /
    
    /*Values after the transaction*/
    select * from tbPO;
    select * from tbPOitems;
    
    -- ******************************************************
    -- STORED Procedure
    -- Reporting on Current Financial Summary of the Store
    -- ******************************************************
    
    create or replace procedure financial_summary 
    is
     civ number(15,2);
     er number(15,2);
     ep number(15,2);
     po number(15,2);
     
    begin
    
     -- Current Inventory Value
     select
     sum(currentStock*unitCost) 
     into civ
     from tbProd;
    
     -- Expected Revenue
     select
     sum(currentStock*unitPrice) 
     into er
     from tbProd;
    
     -- Expected Profit
     select
     sum(currentStock*(unitPrice-unitCost)) 
     into ep
     from tbProd;
    
     -- PO Amount Due
     select
     sum(paymentAmount) 
     into po
     from tbPO
     WHERE paymentDate is NULL;
    
     
     /* set output */
     dbms_output.put_line('Current Inventory Value = ' || civ);
     dbms_output.put_line('Expected Revenue = ' || er);
     dbms_output.put_line('Expected Profit = ' || ep);
     dbms_output.put_line('PO Amount Due = ' || po);
     
    end financial_summary;
    /
    
    show errors
    
    exec financial_summary;
    
    

  • Programming Language R

    Coding Exercise

    Plotting binomical distribution, scatter plot, box-plot, probability distribution, and Gaussian Curve

    Problem 1: Binomial Distribution

    n <- 60
    # for p=0.3, generating statistical summary
    p <- 0.3
    bin_dist_0.3 <- dbinom(0:n, n, p)
    
    summary(bin_dist_0.3)
    ##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
    ##    0.00    0.00    0.00    0.02    0.01    0.11
    sd(bin_dist_0.3)

    ## [1] 0.03

    # for p=0.5, generating statistical summary

    p <- 0.5
    bin_dist_0.5 <- dbinom(0:n, n, p)
    summary(bin_dist_0.5)

    ##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
    ##    0.00    0.00    0.00    0.02    0.01    0.10

    sd(bin_dist_0.5)

    ## [1] 0.03

    # for p=0.8, generating statistical summary

    p <- 0.8
    bin_dist_0.8 <- dbinom(0:n, n, p)
    summary(bin_dist_0.8)

    ##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
    ##    0.00    0.00    0.00    0.02    0.01    0.13

    sd(bin_dist_0.8)

    ## [1] 0.04

    preparing for the plot

    x <- seq(0,n,1)
    X <- c(x,x,x)
    bin_dist <- c(bin_dist_0.3,bin_dist_0.5,bin_dist_0.8)
    binomial.distribution <- data.frame(X,bin_dist)
    n <- as.numeric(rownames(binomial.distribution))
    binomial.distribution <- binomial.distribution %>%
      mutate(p = ifelse(n<61, "p=0.3", ifelse(n<122, "p=0.5", "p=0.8")))

    Plotting Binomial Distribution

    binomial.distribution %>%
      ggplot() +
      geom_line(aes(x=X, y=bin_dist, col=p)) +
      ggtitle("Binomial Distribution") +
      ylab("")

    Plotting Boxplot

    boxplot(bin_dist ~ p,
            data = binomial.distribution,
            names = c("p=0.3", "p=0.5", "p=0.8"))

    Problem 2: Relationship between waiting and duration of eruptions

    Getting data

    faithful.data <- as.data.frame(faithful)
    attach(faithful.data)

    Plotting a scatter plot waiting vs. duration of eruptions

    plot(x = eruptions,
         y = waiting,
         main = "waiting vs. duration",
         xlab = "duration")

    linear model fit

    model <- lm(waiting ~ eruptions)
    model
    ##
    ## Call:
    ## lm(formula = waiting ~ eruptions)
    ##
    ## Coefficients:
    ## (Intercept)    eruptions 
    ##        33.5         10.7

    linear association between duration of eruptions and waiting between eruptions

    plot(x = eruptions,
         y = waiting,
         main = "waiting vs. duration",
         xlab = "duration")
    abline(model,
           col = "red",
           lwd = 3)

    Problem 3: short vs. long eruptions

    detach(faithful.data)
    faithful.data <- faithful.data %>%
      mutate(type = ifelse(eruptions<3.1, "short", "long" ))

    boxplot(waiting ~ type,
            data = faithful.data,
            ylab = "waiting",
            main = "Waiting between eruptions: long vs. short eruptions")
    boxplot(eruptions ~ type,
            data = faithful.data,
            ylab = "Duration of eruptions",
            main = "Duration of each eruption: long vs. short eruptions")

    Problem 4: Uniform Probability Distribution

    generating random variable which follows uniform distribution

    n <- 10000
    min <- -1
    max <- 2
    parameter <- runif(n, min = min, max = max)

    plotting distribution of random variable generated above

    breaks <- seq(min, max, (max-min)/20)
    xrange <- range(-2,3)

    hist(parameter,
         breaks = breaks,
         right = FALSE,
         col = "light blue",
         xlim = xrange,
         main = "Distribution of random variable generated using runif")

    Relative cumulative frequency of uniformly distributed variable

    parameter.cut <- cut(parameter, breaks, right = FALSE)
    parameter.freq <- table(parameter.cut)
    parameter.relfreq <- parameter.freq/n
    parameter.cumfreq <- cumsum(parameter.freq)
    parameter.cumrelfreq <- parameter.cumfreq / n
    parameter.cumrelfreq.0 <- c(0, parameter.cumrelfreq)

    Plotting Relative Cumulative Distribution of uniformly distributed random variable

    plot(parameter.cumrelfreq)
    lines(parameter.cumrelfreq)

    When enough values of random variable are generated, the distribution starts resembling uniform distribution.

    Problem 5:

    Generating 100×40 matrix

    matrix <- replicate(40, runif(100,min,max))

    Preparation for plotting (x,y1) and (x,y2)

    y1 <- matrix[,1]
    y2 <- matrix[,2]

    y1.cut <- cut(y1, breaks, right = FALSE)
    y1.freq <- table(y1.cut)
    y1.relfreq <- y1.freq/100

    y1data <- data.frame(distribution = y1.relfreq, variable = "y1")
    y1data <- y1data %>% select(-distribution.y1.cut)

    parameterdata <- data.frame(distribution = parameter.relfreq, variable = "parameter")
    parameterdata <- parameterdata %>% select(-distribution.parameter.cut)

    y2.cut <- cut(y2, breaks, right = FALSE)
    y2.freq <- table(y2.cut)
    y2.relfreq <- y2.freq/100
    y2data <- data.frame(distribution = y2.relfreq, variable = "y2")
    y2data <- y2data %>% select(-distribution.y2.cut)

    data <- bind_rows(parameterdata,y1data,y2data)
    x <- seq(min, max, (max-min)/19)
    X <- c(x,x,x)
    X <- as.data.frame(X)
    data1 <- bind_cols(data, X)
    Plotting (x,y1)
    data1 %>%
      filter(variable == c("y1", "parameter")) %>%
      ggplot() +
      geom_line(aes(x = X , y = distribution.Freq, col = variable), size = 1) +
      ggtitle("Comparing Relative Distribution of y1 and parameter") +
      ylab("Relative Frequency") +
      xlab("")

    Plotting (x,y2)

    data1 %>%
      filter(variable == c("y2", "parameter")) %>%
      ggplot() +
      geom_line(aes(x = X , y = distribution.Freq, col = variable), size = 1) +
      ggtitle("Comparing Relative Distribution of y2 and parameter") +
      ylab("Relative Frequency") +
      xlab("")

    Problem 6: Gaussian Curve

    summation <- rowSums(matrix)
    matrix1 <- cbind(matrix, summation)
    normal <- matrix1[,41]
    hist(normal,
         main = "Plotting summation of Columns",
         col = "light blue")

    It approximates normal distribution

  • Monte-Carlo Simulation

    Case Study

    Head-count planning using Monte-carlo simulation for IT Support Team

    Use Monte Carlo simulations to build the model that predicts head-count needed for the work-load.

    Features:

    FactorLower BoundUpper BoundMost LikelyUnits
    Number of new support tickets205032tickets / week
    Time to resolve new support ticket6248hours / ticket
    Number of support analysts  15analysts
    Hours per analyst per week  40hours / analyst / week

    With the current capacity of support team, there will be a few weeks – around 2 weeks in a year when support analysts will be working full capacity and yet tickets might roll over into next week. Which I believe is ok as it is rare occurrence.

    Normal Weeks

    In a given week, a support analyst spends 21-33 hours to resolve support tickets. And we can say this with 80% confidence interval.

    A support analyst ends up spending 3 and half days or more, working on tickets in more than half of the weeks. Which is very close to our company policy which requires 80% of resource utilization for support team – that is 4 out of 5 working days in a normal week.

    Unusual Weeks – bad and good

    There can be 10 weeks in a given year when all the analysts will end up spending more than 32 hours each. Which might be ok in context of maintaining sustainable pace for the team. As there will be 10 weeks in a given year when an analyst will spend 20 hours or less on support tickets.

    Long range explained

    It is just the unpredictable nature of the job. It depends on tickets influx and complexity of the tickets which drives resolve time for the ticket. Support team does not have direct control over both of these factors which drive its utilization levels.

    Customer needs resolution in a week

    We also know that as per industry standard, support team needs to resolve a ticket in a week. Customer shouldn’t be waiting more than a week for the resolution. Hence, it is best to complete the ticket in the same week it came in. We do not want tickets to roll over into the next week.

    14 is too less for sustainable pace of the team

    Please note that even if one support analyst leaves, the team will start losing the sustainable pace. Each analyst will be working 22-35 hours a week 80% of the time. There will be 25% of the time an analyst will end up working for more than 32 hours. We will have 13 weeks in a year when an analyst will be utilized more than 80% for support work.

    14 is too less to keep customers happy

    It is worth noting that if team reduces to 14 analysts, there will be 6 weeks in a year when tickets might roll over to next week. Which might cause reputation issues with customers.

    To avoid the long-tail impact, keep the team count at 15.

    16 is too relaxing

    There will be just 3 weeks in a year when an analyst needs to work more than 32 hours.

    In a normal week analysts would spend 18-30 hours to resolve support tickets.

    Hence, adding one more analyst to the team does not add value – it will just make everyone’s life easier and probably boring.

  • Robotic Process Automation (RPA)

    RPA is a software tool to integrate any application to automate routine, predictable tasks using structured digital data. RPA tool operates by deploying software script (also known as ‘bot’) to imitate human task within a business workflow. These bots act like a human inputting and consuming information from multiple IT systems.

    An RPA tool possess three core competencies – Low-code/no-code development environment for citizen developers to create bots, integration with enterprise applications through UI interaction, APIs, connectors and a orchestrator (a control dashboard) to manage (configuration and monitoring) bots. In addition, RPA tool may also provide – Mechanisms to align itself with planned changes in the enterprise application/system ecosystem, automated disaster recovery, support for various hosting options, data security and role-based privileges, In-built AI (Artificial Intelligence), ML(Machine Learning) and NLP (Natural language processing) capabilities, process mining and discovery capabilities

    RPA tools are used in mainly three areas – getting data into or between systems, consolidating data into reports or standardized formats and automating a structured, predetermined workflow or building a workflow. Financial institutes have been an early adopter of RPA. Many onerous back-office functions, such as ensuring an up-to-date Know Your Client (KYC) form is filed or a recent credit check is included on a loan application, are ideal for RPA. An RPA tool can be triggered manually or automatically, move, or populate data between prescribed locations, document audit trails, conduct calculations, perform actions, and trigger downstream activities.

    Why: Easy to develop bots provide speed to value, high ROI, accuracy, and easy integration

    Application leaders apply robotic process automation as a noninvasive integration method to automate routine, repetitive, predictable tasks to unlock tactical benefits. RPA is designed to play nice with most legacy applications, making it easier to implement compared to other enterprise automation solutions. Organizations can take agile approach for implementing RPA solutions as they are scalable, and work well with existing IT infrastructure. Bots are easy to create and integrate using no-code/low-code development environment. RPA streamlines the process and enables strategic analysis by providing event logs. RPA is the least expensive technology of all cognitive solutions and can be a catalyst to digitally transform the company by providing greater productivity, speed to value and high return on investment.

    How: Mine, identify and prioritize

    An organization can follow below process-flow to deploy RPA solutionFew major vendors for RPA tools are Microsoft Automate and UiPath.

    Recommendations: Bots are augmentation of human-workforce

    Financial institute, specially a wealth management organization can deploy RPA to accomplish client on-boarding, interactive what-if scenarios, and exception handling in trade processing. The organization can benefit by creating bots that can perform reconciliation by retrieving data in numerous forms from external parties and internal accounting/ recordkeeping systems, formatting information, comparing data sets, and making corrections and adjustments based on defined rules. Bots will be augmentation of human-workforce, carrying out tedious tasks much more quickly and accurately, while employees focus on high-value tasks.

    Example of RPA Opportunity

    References

    • Tornbohm, Cathy (2020). When and Where to Use Robotic Process Automation in Finance and Accounting (ID G00377790)

    https://www.gartner.com/document/3902070?ref=solrAll&refval=262289193

    https://www.fiserv.com/en/about-fiserv/resource-center/white-papers/how-digitalization-is-reshaping-wealth-management.html

    • illimity Bank (2020). Illimity Bank simplifies loan process and saves 15 hours a month with Microsoft Power Automate

    https://customers.microsoft.com/en-us/story/821782-illimity-bank-banking-power-automate

    • Standard Bank – South Africa (2018). The power of four: African bank embraces digitalization and increases efficiency with time-saving Microsoft Power Automate, Power Apps, Power BI, and SharePoint

    https://customers.microsoft.com/en-us/story/standard-bank-banking-capital-markets-powerapps

    • Naved Rashid (2020). Critical Capabilities for Robotic Process Automation (ID G00465756)

    https://www.gartner.com/document/3989821?ref=solrAll&refval=262291979

    • Saikat Ray (2020). Magic Quadrant for Robotic Process Automation

    https://www.gartner.com/document/3988021?ref=solrAll&refval=262291998

    • Davenport, T., Ronanki, R. (2018).  Artificial Intelligence in the Real World. Harvard Business Review.

    https://hbr.org/2018/01/artificial-intelligence-for-the-real-world

    • Porter,M., Heppelmann, J. (2015, October). How Smart, Connected Products Are Transforming Companies.

    https://hbr.org/2015/10/how-smart-connected-products-are-transforming-companies

    • Microsoft (2020) Get started with Power Automate

    https://docs.microsoft.com/en-us/power-automate/getting-started

    • Microsoft – Power Automate website

    https://flow.microsoft.com/en-us