
# ========================================
# Name            : Sravanthi Sangam
# Student number  : 2536458
# Module code     : ACCA7022
# Assessment title: Data Manipulation
# Date            : 22/10/2026
# ========================================




#=======================** TASK 1A **==========================

# Task 1A: Generate Warehouse Data
# Define dimensions
days <- 1:28
zones <- 1:5
categories <- 1:6

total_rows <- length(days) * length(zones) * length(categories) # 840 rows

# Initialize empty vectors to store data
day_vec <- numeric(total_rows)
zone_vec <- numeric(total_rows)
category_vec <- numeric(total_rows)
units_processed_vec <- numeric(total_rows)
processing_time_vec <- numeric(total_rows)

# Use nested loops to generate and populate row values
row_index <- 1

for (d in days) {
  for (z in zones) {
    for (c in categories) {
      
      # Constants and calculations per row
      base_units <- 100
      # Generate a random integer between -12 and +12
      random_variation <- sample(-12:12, 1)
      
      # Formula for UnitsProcessed
      units_processed <- base_units + (z * 8) + (c * 5) + random_variation
      
      # Formula for ProcessingTime (TimePerUnit = 0.85)
      time_per_unit <- 0.85
      processing_time <- units_processed * time_per_unit
      
      # Assign values to the vectors at the current row index
      day_vec[row_index] <- d
      zone_vec[row_index] <- z
      category_vec[row_index] <- c
      units_processed_vec[row_index] <- units_processed
      processing_time_vec[row_index] <- processing_time
      
      # Increment row index
      row_index <- row_index + 1
    }
  }
}

# Construct the warehouse_data dataframe
warehouse_data <- data.frame(
  Day = day_vec,
  Zone = zone_vec,
  Category = category_vec,
  UnitsProcessed = units_processed_vec,
  ProcessingTime = processing_time_vec
)

# Display the first 10 rows of the dataframe
print("First 10 rows of warehouse_data:")
print(head(warehouse_data, 10))

# Display the total number of rows and columns
cat("\nTotal number of rows:", nrow(warehouse_data))
cat("\nTotal number of columns:", ncol(warehouse_data), "\n")



#========================** TASK 1B **===============================


# Task 1B: Total Units Processed per Zone

# Define the unique zones
zones <- 1:5
num_zones <- length(zones)

# Initialize empty vectors for the new dataframe
zone_id_vec <- numeric(num_zones)
total_units_vec <- numeric(num_zones)

# Use loops to calculate total units per zone
for (i in 1:num_zones) {
  current_zone <- zones[i]
  zone_sum <- 0 # Reset sum for the current zone
  
  # Loop through every row in warehouse_data
  for (j in 1:nrow(warehouse_data)) {
    # Check if the row belongs to the current zone
    if (warehouse_data$Zone[j] == current_zone) {
      zone_sum <- zone_sum + warehouse_data$UnitsProcessed[j]
    }
  }
  
  # Store the results in the vectors
  zone_id_vec[i] <- current_zone
  total_units_vec[i] <- zone_sum
}

# Create the zone_totals dataframe
zone_totals <- data.frame(
  Zone = zone_id_vec,
  TotalUnits = total_units_vec
)

# Display the completed zone_totals dataframe
print("Completed zone_totals dataframe:")
print(zone_totals)




#=======================** TASK 1C **=======================


# Task 1C: Average Processing Time per Product Category

# Define the unique categories
categories <- 1:6
num_categories <- length(categories)

# Initialize empty vectors for the new dataframe
category_id_vec <- numeric(num_categories)
avg_processing_time_vec <- numeric(num_categories)

# Use loops to calculate total time and count per category, then compute average
for (i in 1:num_categories) {
  current_category <- categories[i]
  category_time_sum <- 0 # Accumulator for processing time
  record_count <- 0      # Counter for matching records
  
  # Loop through every row in warehouse_data
  for (j in 1:nrow(warehouse_data)) {
    # Check if the row belongs to the current category
    if (warehouse_data$Category[j] == current_category) {
      category_time_sum <- category_time_sum + warehouse_data$ProcessingTime[j]
      record_count <- record_count + 1
    }
  }
  
  # Calculate the average (Total Time / Number of Records)
  average_time <- category_time_sum / record_count
  
  # Store the results in the vectors
  category_id_vec[i] <- current_category
  avg_processing_time_vec[i] <- average_time
}

# Create the category_average_time dataframe
category_average_time <- data.frame(
  Category = category_id_vec,
  AverageProcessingTime = avg_processing_time_vec
)

# Display the completed category_average_time dataframe
print("Completed category_average_time dataframe:")
print(category_average_time)



#========================** TASK 1D **======================

# Task 1D: Identify Stock Processing Anomalies
# Initialize vectors to store results for all rows in warehouse_data
total_rows <- nrow(warehouse_data)

day_anomaly_vec <- numeric(total_rows)
zone_anomaly_vec <- numeric(total_rows)
category_anomaly_vec <- numeric(total_rows)
units_anomaly_vec <- numeric(total_rows)
daily_zone_avg_vec <- numeric(total_rows)
anomaly_status_vec <- logical(total_rows)

# Use loops to calculate DailyZoneAverage and detect anomalies for every row
days <- 1:28
zones <- 1:5
categories <- 1:6

row_counter <- 1

for (d in days) {
  for (z in zones) {
    
    # Step A: Calculate DailyZoneAverage for this specific Day and Zone across all 6 categories
    zone_day_sum <- 0
    category_count <- length(categories)
    
    # First inner loop pass to sum units for this day and zone
    for (c in categories) {
      # Find the matching row index in warehouse_data
      # Since data is structured sequentially: Day -> Zone -> Category
      # We can calculate the exact row index directly or search via loop:
      for (k in 1:total_rows) {
        if (warehouse_data$Day[k] == d && warehouse_data$Zone[k] == z && warehouse_data$Category[k] == c) {
          zone_day_sum <- zone_day_sum + warehouse_data$UnitsProcessed[k]
        }
      }
    }
    
    daily_zone_average <- zone_day_sum / category_count
    
    # Step B: Evaluate each category record for this day and zone against thresholds
    for (c in categories) {
      # Locate the row for current day, zone, category
      current_units <- 0
      for (k in 1:total_rows) {
        if (warehouse_data$Day[k] == d && warehouse_data$Zone[k] == z && warehouse_data$Category[k] == c) {
          current_units <- warehouse_data$UnitsProcessed[k]
        }
      }
      
      # Determine if it's an anomaly ( > 1.15 * avg OR < 0.85 * avg )
      is_anomaly <- FALSE
      if (current_units > (daily_zone_average * 1.15) || current_units < (daily_zone_average * 0.85)) {
        is_anomaly <- TRUE
      }
      
      # Store in our tracking vectors
      day_anomaly_vec[row_counter] <- d
      zone_anomaly_vec[row_counter] <- z
      category_anomaly_vec[row_counter] <- c
      units_anomaly_vec[row_counter] <- current_units
      daily_zone_avg_vec[row_counter] <- daily_zone_average
      anomaly_status_vec[row_counter] <- is_anomaly
      
      row_counter <- row_counter + 1
    }
  }
}

# Construct the processing_anomalies dataframe
processing_anomalies <- data.frame(
  Day = day_anomaly_vec,
  Zone = zone_anomaly_vec,
  Category = category_anomaly_vec,
  UnitsProcessed = units_anomaly_vec,
  DailyZoneAverage = daily_zone_avg_vec,
  Anomaly = anomaly_status_vec
)

# Extract and display the first 10 rows where Anomaly is TRUE using a loop-based filter
print("First 10 rows where Anomaly is TRUE:")

true_anomaly_count <- 0
displayed_count <- 0
max_display <- 10

for (i in 1:nrow(processing_anomalies)) {
  if (processing_anomalies$Anomaly[i] == TRUE) {
    true_anomaly_count <- true_anomaly_count + 1
    
    if (displayed_count < max_display) {
      displayed_count <- displayed_count + 1
      cat(sprintf("Row %d -> Day: %d, Zone: %d, Category: %d, Units: %.1f, DailyAvg: %.2f, Anomaly: %s\n",
                  i,
                  processing_anomalies$Day[i],
                  processing_anomalies$Zone[i],
                  processing_anomalies$Category[i],
                  processing_anomalies$UnitsProcessed[i],
                  processing_anomalies$DailyZoneAverage[i],
                  as.character(processing_anomalies$Anomaly[i])))
    }
  }
}

cat("\nTotal number of anomalies found:", true_anomaly_count, "\n")




#=========================** TASK 1E **======================

# Task 1E: Identify the Most Productive Day for Each Zone
# Define dimensions
zones <- 1:5
days <- 1:28
categories <- 1:6

num_zones <- length(zones)

# Initialize empty vectors for the peak_zone_days dataframe
zone_peak_vec <- numeric(num_zones)
day_peak_vec <- numeric(num_zones)
max_units_vec <- numeric(num_zones)

# Loop through each zone to find the peak day
for (i in 1:num_zones) {
  current_zone <- zones[i]
  
  highest_units <- -1  # Initialize with a low placeholder
  best_day <- -1       # Initialize best day tracker
  
  # Loop through each day to calculate total units processed in this zone
  for (d in days) {
    daily_zone_sum <- 0
    
    # Sum across all categories for this specific day and zone
    for (c in categories) {
      # Scan warehouse_data for matching row
      for (k in 1:nrow(warehouse_data)) {
        if (warehouse_data$Day[k] == d && 
            warehouse_data$Zone[k] == current_zone && 
            warehouse_data$Category[k] == c) {
          daily_zone_sum <- daily_zone_sum + warehouse_data$UnitsProcessed[k]
        }
      }
    }
    
    # Check if this day's total exceeds the current highest for this zone
    if (daily_zone_sum > highest_units) {
      highest_units <- daily_zone_sum
      best_day <- d
    }
  }
  
  # Store the peak findings for the current zone
  zone_peak_vec[i] <- current_zone
  day_peak_vec[i] <- best_day
  max_units_vec[i] <- highest_units
}

# Construct the peak_zone_days dataframe
peak_zone_days <- data.frame(
  Zone = zone_peak_vec,
  Day = day_peak_vec,
  TotalUnits = max_units_vec
)

# Display the completed peak_zone_days dataframe
print("Completed peak_zone_days dataframe:")
print(peak_zone_days)
