Wayfair · Data Analytics

E-commerce Sales Analysis

The work

  1. import pandas as pd
    
    df1 = pd.read_csv("Datasets/customers.csv")
    df2 = pd.read_csv("Datasets/order_items.csv")
    df3 = pd.read_csv("Datasets/orders.csv")
    df4 = pd.read_csv("Datasets/products.csv")
  2. datasets = {
        "customers": df1,
        "order-items":df2,
        "orders":df3,
        "products":df4
    }
  3. for name, df in datasets.items():
        print(name)
        print(f"Shape: {df.shape}")
        print()
        print(df.head())
        print()
        print(f"Info: {df.info()}")
        print()
        print(f"Missing Values: {df.isnull().sum()}")
        print()
        print(f"Duplicated Values: {df.duplicated().sum()}")
        print("----------------------------------------")
    customers
    Shape: (3600, 4)
    
       customer_id signup_date state acquisition_channel
    0            1  2024-09-13    CO      organic_search
    1            2  2025-10-21    IL         paid_search
    2            3  2025-01-17    AZ            referral
    3            4  2024-06-23    FL         paid_social
    4            5  2025-04-08    IL         paid_social
    
    <class 'pandas.core.frame.DataFrame'>
    RangeIndex: 3600 entries, 0 to 3599
    Data columns (total 4 columns):
     #   Column               Non-Null Count  Dtype 
    ---  ------               --------------  ----- 
     0   customer_id          3600 non-null   int64 
     1   signup_date          3600 non-null   object
     2   state                3600 non-null   object
     3   acquisition_channel  3600 non-null   object
    dtypes: int64(1), object(3)
    memory usage: 112.6+ KB
    Info: None
    
    Missing Values: customer_id            0
    signup_date            0
    state                  0
    acquisition_channel    0
    dtype: int64
    
    Duplicated Values: 0
    ----------------------------------------
    order-items
    Shape: (15608, 5)
    
       order_id  product_id  quantity  unit_price  discount_amount
    0    100001        1031         1       38.99              0.0
    1    100002        1087         1       45.99              0.0
    2    100002        1072         1      141.99              0.0
    3    100003        1048         1      139.99              0.0
    4    100004        1083         1       19.99              2.0
    
    <class 'pandas.core.frame.DataFrame'>
    RangeIndex: 15608 entries, 0 to 15607
    Data columns (total 5 columns):
     #   Column           Non-Null Count  Dtype  
    ---  ------           --------------  -----  
     0   order_id         15608 non-null  int64  
     1   product_id       15608 non-null  int64  
     2   quantity         15608 non-null  int64  
     3   unit_price       15409 non-null  float64
     4   discount_amount  15608 non-null  float64
    dtypes: float64(2), int64(3)
    memory usage: 609.8 KB
    Info: None
    
    Missing Values: order_id             0
    product_id           0
    quantity             0
    unit_pri
  4. df1.describe(include='all')
            customer_id signup_date state acquisition_channel
    count   3600.000000        3600  3600                3600
    unique          NaN         811    35                   6
    top             NaN  2025-10-29    CA      organic_search
    freq            NaN          17   451                1230
    mean    1800.500000         NaN   NaN                 NaN
    std     1039.374812         NaN   NaN                 NaN
    min        1.000000         NaN   NaN                 NaN
    25%      900.750000         NaN   NaN                 NaN
    50%     1800.500000         NaN   NaN                 NaN
    75%     2700.250000         NaN   NaN                 NaN
    max     3600.000000         NaN   NaN                 NaN
  5. df2.describe(include='all')
                order_id    product_id      quantity    unit_price  \
    count   15608.000000  15608.000000  15608.000000  15409.000000   
    mean   104570.429011   1046.471040      1.244810    113.084088   
    std      2659.674531     28.278148      0.577641    108.590935   
    min    100001.000000   1001.000000      1.000000      7.990000   
    25%    102269.750000   1020.000000      1.000000     49.990000   
    50%    104554.500000   1048.000000      1.000000     86.090000   
    75%    106867.250000   1073.000000      1.000000    141.740000   
    max    109207.000000   1090.000000      4.000000    812.690000   
    
           discount_amount  
    count     15608.000000  
    mean          3.201492  
    std          11.154638  
    min           0.000000  
    25%           0.000000  
    50%           0.000000  
    75%           0.000000  
    max         196.560000  
  6. df3.describe(include='all')
                 order_id  customer_id           order_date     status  \
    count     9245.000000  9245.000000                 9245       9245   
    unique            NaN          NaN                 9206          3   
    top               NaN          NaN  2025-08-05 10:07:36  completed   
    freq              NaN          NaN                    2       8490   
    mean    104604.530882  1784.054840                  NaN        NaN   
    std       2657.471479  1062.469902                  NaN        NaN   
    min     100001.000000     2.000000                  NaN        NaN   
    25%     102304.000000   863.000000                  NaN        NaN   
    50%     104605.000000  1786.000000                  NaN        NaN   
    75%     106907.000000  2738.000000                  NaN        NaN   
    max     109207.000000  3600.000000                  NaN        NaN   
    
           payment_method discount_code  shipping_fee  
    count            9245          1411   9245.000000  
    unique              4             3           NaN  
    top       credit_card     HOLIDAY20           NaN  
    freq             5332           782           NaN  
    mean              NaN           NaN      1.544862  
    std               NaN           NaN      2.889826  
    min               NaN           NaN      0.000000  
    25%               NaN           NaN      0.000000  
    50%               NaN           NaN      0.000000  
    75%               NaN           NaN      0.000000  
    max               NaN           NaN      6.950000  
  7. df4.describe(include='all')
             product_id      product_name category  list_price   unit_cost
    count     90.000000                90       90   90.000000   90.000000
    unique          NaN                90       24         NaN         NaN
    top             NaN  Brass Chef Knife  Kitchen         NaN         NaN
    freq            NaN                 1       10         NaN         NaN
    mean    1045.500000               NaN      NaN  134.378889   69.563222
    std       26.124701               NaN      NaN  136.899512   76.820180
    min     1001.000000               NaN      NaN    7.990000    4.760000
    25%     1023.250000               NaN      NaN   52.490000   24.547500
    50%     1045.500000               NaN      NaN   95.490000   49.275000
    75%     1067.750000               NaN      NaN  151.490000   74.330000
    max     1090.000000               NaN      NaN  773.990000  445.470000
  8. orders has 38 duplicates
    In order_items unit_price column has 199 missing values.

  9. df3[df3.duplicated()].head()
          order_id  customer_id           order_date     status payment_method  \
    2476    102345           68  2024-08-12 18:24:57  completed    credit_card   
    2747    101380         2821  2024-05-15 16:28:12  completed    credit_card   
    3222    103209          875  2024-11-03 09:28:56  completed         paypal   
    3280    103267         1466  2024-11-07 17:50:28   returned         paypal   
    3992    102000          840  2024-07-12 09:50:27  completed    credit_card   
    
         discount_code  shipping_fee  
    2476           NaN          0.00  
    2747           NaN          0.00  
    3222     WELCOME10          0.00  
    3280           NaN          6.95  
    3992           NaN          0.00  
  10. print("Remaining exact duplicates:", df3.duplicated().sum())
    print("Remaining rows:", len(df3))
    Remaining exact duplicates: 38
    Remaining rows: 9245
    
  11. #duplicates are dropped
    df3.drop_duplicates(inplace=True)
  12. df2[df2.unit_price.isna()]
    #the unit price can be done simply on lookup table in excel
           order_id  product_id  quantity  unit_price  discount_amount
    55       100033        1086         1         NaN             0.00
    110      100062        1020         1         NaN             0.00
    197      100116        1087         2         NaN             0.00
    366      100210        1014         1         NaN             0.00
    626      100361        1077         1         NaN             0.00
    ...         ...         ...       ...         ...              ...
    15205    108954        1088         1         NaN             4.41
    15228    108966        1071         2         NaN             4.20
    15326    109024        1083         1         NaN             0.00
    15354    109043        1074         2         NaN             8.40
    15504    109136        1082         1         NaN             0.00
    
    [199 rows x 5 columns]
  13. df3.head()
       order_id  customer_id           order_date     status payment_method  \
    0    100001         1530  2024-01-01 09:24:45  completed    credit_card   
    1    100002         3531  2024-01-01 10:37:49  completed    credit_card   
    2    100003          840  2024-01-01 10:59:09  completed    credit_card   
    3    100004          725  2024-01-01 12:48:34  completed      apple_pay   
    4    100005         3452  2024-01-01 17:44:03  completed      gift_card   
    
      discount_code  shipping_fee  
    0           NaN          6.95  
    1           NaN          0.00  
    2           NaN          0.00  
    3     WELCOME10          0.00  
    4           NaN          0.00  
  14. df3.discount_code.unique()
    array([nan, 'WELCOME10', 'SPRING15', 'HOLIDAY20'], dtype=object)
  15. df3['discount_code'] = df3["discount_code"].fillna("No Discount")
  16. df3.discount_code.unique()
    array(['No Discount', 'WELCOME10', 'SPRING15', 'HOLIDAY20'], dtype=object)
  17. df4.head()
       product_id            product_name  category  list_price  unit_cost
    0        1001        Brass Chef Knife   Kitchen       61.99      38.13
    1        1002    Walnut Cutting Board   Kitchen      138.99      74.76
    2        1003  Matte Black Dutch Oven   Kitchen       22.99      13.50
    3        1004         Classic Skillet   Kitchen       75.99      39.86
    4        1005   Linen Mixing Bowl Set  Kitchen        79.99      41.11
  18. df4.category.unique()
    array(['Kitchen', ' Kitchen', 'Kitchen ', 'kitchen', 'KITCHEN',
           ' Bedding', 'Bedding', 'bedding', 'BEDDING', 'Bath', 'bath',
           'Furniture', 'Furniture ', 'furniture', 'FURNITURE', 'Lighting',
           'LIGHTING', 'Outdoor', 'OUTDOOR', ' Outdoor', 'Decor', 'decor',
           'Storage', 'Storage '], dtype=object)
  19. df4['category'] = df4['category'].str.lower().str.replace(" ", "", regex=True)
    df4['category'].unique()
    array(['kitchen', 'bedding', 'bath', 'furniture', 'lighting', 'outdoor',
           'decor', 'storage'], dtype=object)
  20. product_lookup = df4.set_index("product_id")["list_price"]
    product_lookup
    product_id
    1001     61.99
    1002    138.99
    1003     22.99
    1004     75.99
    1005     79.99
             ...  
    1086     97.99
    1087     45.99
    1088     20.99
    1089     53.99
    1090     17.99
    Name: list_price, Length: 90, dtype: float64
  21. df2["unit_price"] = df2["unit_price"].fillna(
        df2["product_id"].map(product_lookup)
    )
    df2.isna().sum()
    order_id           0
    product_id         0
    quantity           0
    unit_price         0
    discount_amount    0
    dtype: int64
  22. import pandas as pd
    from sqlalchemy import create_engine, text
    from sqlalchemy.engine import URL
    
    # MySQL connection details
    username = "YOUR-USERNAME"
    password = "YOUR-PASSWORD"
    host = "localhost"
    port = 3306
    database = "YOUR-DB"
    
    # Create connection URL safely
    connection_url = URL.create(
        drivername="mysql+pymysql",
        username=username,
        password=password,
        host=host,
        port=port,
        database=database
    )
    
    # Create SQLAlchemy engine
    engine = create_engine(connection_url)
    
    # Test the connection
    try:
        with engine.connect() as connection:
            result = connection.execute(text("SELECT VERSION()"))
            print("Connected successfully!")
            print("MySQL version:", result.scalar())
    
    except Exception as e:
        print("Connection failed:", e)
    Connected successfully!
    MySQL version: 8.0.45
    
  23. df1.to_sql(
        name="customers",
        con=engine,
        if_exists="replace",
        index=False
    )
    
    print("Data uploaded successfully!")
    Data uploaded successfully!
    
  24. df2.to_sql(
        name="order-items",
        con=engine,
        if_exists="replace",
        index=False
    )
    
    print("Data uploaded successfully!")
    Data uploaded successfully!
    
  25. df3.to_sql(
        name="orders",
        con=engine,
        if_exists="replace",
        index=False
    )
    
    print("Data uploaded successfully!")
    Data uploaded successfully!
    
  26. df4.to_sql(
        name="products",
        con=engine,
        if_exists="replace",
        index=False
    )
    
    print("Data uploaded successfully!")
    Data uploaded successfully!
    

The brief this answers

Clean two years of online store orders and turn them into a clear sales review for next year's plan.

Try it yourself

Other answers to this brief

The same problem, solved by somebody else.

Discussion

Nothing here yet. Ask how they approached it, or tell them what you would have done differently.