""" DataMind AI — Simulated Dataset Generators Produces two realistic, slightly messy 500-row datasets for demo purposes. """ import pandas as pd import numpy as np from datetime import datetime, timedelta def generate_retail_dataset() -> pd.DataFrame: """Generate a 500-row Retail Sales dataset with realistic, slightly messy data.""" np.random.seed(42) n = 500 # Date range: 2 years of sales start_date = datetime(2023, 1, 1) dates = [start_date + timedelta(days=np.random.randint(0, 730)) for _ in range(n)] products = [ "Laptop", "Wireless Mouse", "Keyboard", "Monitor", "USB Cable", "Headphones", "Webcam", "Desk Lamp", "Phone Case", "Charger", "Tablet", "Smartwatch", "Speaker", "Power Bank", "Router" ] categories_map = { "Laptop": "Electronics", "Wireless Mouse": "Accessories", "Keyboard": "Accessories", "Monitor": "Electronics", "USB Cable": "Accessories", "Headphones": "Audio", "Webcam": "Electronics", "Desk Lamp": "Office", "Phone Case": "Accessories", "Charger": "Accessories", "Tablet": "Electronics", "Smartwatch": "Wearables", "Speaker": "Audio", "Power Bank": "Accessories", "Router": "Electronics" } regions = ["North", "South", "East", "West", "Central"] genders = ["Male", "Female", "Other"] product_choices = np.random.choice(products, n) categories = [categories_map[p] for p in product_choices] region_choices = np.random.choice(regions, n) units = np.random.randint(1, 50, n) # Realistic price ranges per product price_map = { "Laptop": (600, 1500), "Wireless Mouse": (15, 60), "Keyboard": (25, 150), "Monitor": (200, 800), "USB Cable": (5, 20), "Headphones": (30, 300), "Webcam": (40, 150), "Desk Lamp": (20, 80), "Phone Case": (8, 40), "Charger": (10, 50), "Tablet": (200, 900), "Smartwatch": (100, 500), "Speaker": (50, 250), "Power Bank": (15, 70), "Router": (30, 200) } unit_prices = [round(np.random.uniform(*price_map[p]), 2) for p in product_choices] revenue = [round(u * p, 2) for u, p in zip(units, unit_prices)] customer_ids = [f"CUST-{np.random.randint(1000, 2000)}" for _ in range(n)] customer_ages = np.random.randint(18, 72, n).astype(float) customer_genders = np.random.choice(genders, n) return_flags = np.random.choice([0, 1], n, p=[0.88, 0.12]) df = pd.DataFrame({ "Date": dates, "Product": product_choices, "Category": categories, "Region": region_choices, "Units_Sold": units, "Unit_Price": unit_prices, "Revenue": revenue, "Customer_ID": customer_ids, "Customer_Age": customer_ages, "Customer_Gender": customer_genders, "Return_Flag": return_flags }) # --- Inject messiness --- # 1. Nulls (~5%) null_indices_age = np.random.choice(n, size=20, replace=False) df.loc[null_indices_age, "Customer_Age"] = np.nan null_indices_price = np.random.choice(n, size=10, replace=False) df.loc[null_indices_price, "Unit_Price"] = np.nan null_indices_region = np.random.choice(n, size=8, replace=False) df.loc[null_indices_region, "Region"] = np.nan null_indices_gender = np.random.choice(n, size=7, replace=False) df.loc[null_indices_gender, "Customer_Gender"] = np.nan null_indices_date = np.random.choice(n, size=5, replace=False) df.loc[null_indices_date, "Date"] = pd.NaT # 2. Duplicates (~2%) dup_indices = np.random.choice(n, size=12, replace=False) duplicates = df.iloc[dup_indices].copy() df = pd.concat([df, duplicates], ignore_index=True) # 3. Inconsistent capitalisation messy_indices = np.random.choice(len(df), size=40, replace=False) case_funcs = [str.upper, str.lower, str.swapcase] for idx in messy_indices: func = np.random.choice(case_funcs) if pd.notna(df.at[idx, "Product"]): df.at[idx, "Product"] = func(df.at[idx, "Product"]) if pd.notna(df.at[idx, "Region"]): df.at[idx, "Region"] = func(df.at[idx, "Region"]) if pd.notna(df.at[idx, "Category"]): df.at[idx, "Category"] = func(df.at[idx, "Category"]) # Shuffle rows df = df.sample(frac=1, random_state=42).reset_index(drop=True) return df def generate_ecommerce_dataset() -> pd.DataFrame: """Generate a 500-row E-Commerce Orders dataset with realistic, slightly messy data.""" np.random.seed(99) n = 500 start_date = datetime(2023, 1, 1) order_dates = [start_date + timedelta(days=np.random.randint(0, 730)) for _ in range(n)] ship_dates = [od + timedelta(days=np.random.randint(1, 10)) for od in order_dates] segments = ["Consumer", "Corporate", "Home Office"] countries = ["United States", "United Kingdom", "Canada", "Germany", "Australia"] cities_map = { "United States": ["New York", "Los Angeles", "Chicago", "Houston", "Seattle", "Miami"], "United Kingdom": ["London", "Manchester", "Birmingham", "Leeds"], "Canada": ["Toronto", "Vancouver", "Montreal", "Ottawa"], "Germany": ["Berlin", "Munich", "Hamburg", "Frankfurt"], "Australia": ["Sydney", "Melbourne", "Brisbane", "Perth"] } categories_structure = { "Technology": { "sub_categories": ["Phones", "Laptops", "Accessories", "Tablets"], "products": { "Phones": ["iPhone 15", "Galaxy S24", "Pixel 8", "OnePlus 12"], "Laptops": ["MacBook Pro", "Dell XPS 15", "ThinkPad X1", "HP Spectre"], "Accessories": ["AirPods Pro", "Magic Mouse", "USB-C Hub", "Laptop Stand"], "Tablets": ["iPad Air", "Galaxy Tab S9", "Surface Pro", "Kindle Fire"] }, "price_range": (50, 2000) }, "Furniture": { "sub_categories": ["Chairs", "Tables", "Bookcases", "Storage"], "products": { "Chairs": ["Ergonomic Office Chair", "Gaming Chair", "Mesh Task Chair", "Executive Chair"], "Tables": ["Standing Desk", "Conference Table", "Corner Desk", "Folding Table"], "Bookcases": ["5-Shelf Bookcase", "Floating Shelves", "Glass Bookcase", "Cube Organizer"], "Storage": ["Filing Cabinet", "Storage Ottoman", "Drawer Unit", "Wardrobe"] }, "price_range": (30, 1200) }, "Office Supplies": { "sub_categories": ["Paper", "Binders", "Art Supplies", "Envelopes"], "products": { "Paper": ["Copy Paper A4", "Cardstock", "Photo Paper", "Sticky Notes"], "Binders": ["3-Ring Binder", "Report Cover", "Presentation Folder", "Clip Board"], "Art Supplies": ["Marker Set", "Sketch Pad", "Colored Pencils", "Paint Set"], "Envelopes": ["Manila Envelope", "Padded Mailer", "Window Envelope", "Bubble Wrap Roll"] }, "price_range": (2, 80) } } rows = [] for i in range(n): order_id = f"ORD-{10000 + i}" segment = np.random.choice(segments) country = np.random.choice(countries) city = np.random.choice(cities_map[country]) category = np.random.choice(list(categories_structure.keys())) cat_info = categories_structure[category] sub_cat = np.random.choice(cat_info["sub_categories"]) product_name = np.random.choice(cat_info["products"][sub_cat]) low, high = cat_info["price_range"] quantity = np.random.randint(1, 12) unit_sale = round(np.random.uniform(low, high), 2) sales = round(unit_sale * quantity, 2) discount = round(np.random.choice([0, 0, 0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3]), 2) profit = round(sales * np.random.uniform(-0.15, 0.45) * (1 - discount), 2) shipping_cost = round(np.random.uniform(3, 50), 2) cust_id = f"CID-{np.random.randint(100, 500)}" rows.append({ "Order_ID": order_id, "Order_Date": order_dates[i], "Ship_Date": ship_dates[i], "Customer_ID": cust_id, "Segment": segment, "Country": country, "City": city, "Category": category, "Sub_Category": sub_cat, "Product_Name": product_name, "Sales": sales, "Quantity": quantity, "Discount": discount, "Profit": profit, "Shipping_Cost": shipping_cost }) df = pd.DataFrame(rows) # --- Inject messiness --- # 1. Nulls (~5%) null_sales = np.random.choice(n, size=15, replace=False) df.loc[null_sales, "Sales"] = np.nan null_profit = np.random.choice(n, size=12, replace=False) df.loc[null_profit, "Profit"] = np.nan null_ship = np.random.choice(n, size=10, replace=False) df.loc[null_ship, "Ship_Date"] = pd.NaT null_segment = np.random.choice(n, size=8, replace=False) df.loc[null_segment, "Segment"] = np.nan null_city = np.random.choice(n, size=7, replace=False) df.loc[null_city, "City"] = np.nan null_discount = np.random.choice(n, size=6, replace=False) df.loc[null_discount, "Discount"] = np.nan # 2. Duplicates dup_indices = np.random.choice(n, size=10, replace=False) duplicates = df.iloc[dup_indices].copy() df = pd.concat([df, duplicates], ignore_index=True) # 3. Inconsistent capitalisation messy_indices = np.random.choice(len(df), size=35, replace=False) case_funcs = [str.upper, str.lower, str.swapcase] for idx in messy_indices: func = np.random.choice(case_funcs) if pd.notna(df.at[idx, "Segment"]): df.at[idx, "Segment"] = func(df.at[idx, "Segment"]) if pd.notna(df.at[idx, "Category"]): df.at[idx, "Category"] = func(df.at[idx, "Category"]) if pd.notna(df.at[idx, "City"]): df.at[idx, "City"] = func(df.at[idx, "City"]) if pd.notna(df.at[idx, "Country"]): df.at[idx, "Country"] = func(df.at[idx, "Country"]) # Shuffle df = df.sample(frac=1, random_state=99).reset_index(drop=True) return df