Set A - Question 3
3. a) Define NumPy arrays and describe their role in data analysis. Answer the following with suitable examples:
i. How does a NumPy array differ from a standard Python list in terms of structure and functionality? ii. Show two different ways to create a NumPy array with code examples. iii. Demonstrate how arithmetic and vectorized operations can be applied directly on NumPy arrays. iv. Highlight the benefits of using NumPy arrays when working with large-scale numerical datasets.
Answer
Definition of NumPy Arrays
A NumPy array is a homogeneous multidimensional container (ndarray) of elements of the same data type. It is the core library for scientific computing in Python.
Role in Data Analysis
NumPy arrays act as the foundational layer of the scientific Python ecosystem (e.g., Pandas, Scikit-Learn, SciPy), enabling fast linear algebra, matrix manipulation, and vector calculations on structured data.
i. Structural & Functional Differences: NumPy Arrays vs. Python Lists
| Feature | Standard Python List | NumPy Array (ndarray) |
|---|---|---|
| Data Homogeneity | Heterogeneous: Can contain mixed types (e.g., [1, "text", 3.14]). | Homogeneous: Must contain identical types (e.g., all float64). |
| Memory Layout | Non-contiguous: Stores an array of pointers to scattered objects. | Contiguous: Stores raw values in a single continuous block of memory. |
| Operations | Operates on container (e.g., + concatenates lists). Needs loops for math. | Vectorized (e.g., + adds elements). Runs at hardware speed without loops. |
| Slicing | Slicing creates a new list (memory copy). | Slicing creates a "view" of the original array (zero memory overhead). |
ii. Two Different Ways to Create a NumPy Array
Method 1: From a Python List
import numpy as np
my_list = [10, 20, 30, 40]
arr_from_list = np.array(my_list)
print("Array from list:", arr_from_list)
Method 2: Using Intrinsic Array Generators (e.g., arange, zeros, linspace)
import numpy as np
# Create an array of 5 equally spaced values between 0 and 1
lin_spaced = np.linspace(0, 1, 5)
# Create a 2x3 array of zeros
zeros_arr = np.zeros((2, 3))
print("Linspace array:", lin_spaced)
print("Zeros array:\n", zeros_arr)
iii. Vectorized and Arithmetic Operations Demonstration
Vectorization allows element-wise operations on arrays without explicit for loops in Python.
import numpy as np
A = np.array([1, 2, 3, 4])
B = np.array([10, 20, 30, 40])
# Element-wise addition
print("Addition (A + B):", A + B) # [11, 22, 33, 44]
# Scalar multiplication
print("Scalar multiplication (A * 5):", A * 5) # [5, 10, 15, 20]
# Element-wise division
print("Division (B / A):", B / A) # [10., 10., 10., 10.]
# Universal trigonometric/exponential function
print("Exponential (exp(A)):", np.exp(A))
iv. Benefits of NumPy for Large-Scale Numerical Datasets
- Speed (Vectorization): Offloads loops to compiled, optimized C code, running up to 100x faster than pure Python loops.
- Memory Efficiency: Consumes significantly less memory (up to 80% less) by omitting object references and pointers.
- Cache Locality: Contiguous memory placement leverages modern CPU cache hierarchies for faster memory access.
- Broadcasting: Allows element-wise math on arrays of different dimensions without physically replicating data.
3. b) Demonstrate how to load a dataset into a Pandas DataFrame and clean the data (handle missing values, drop unnecessary columns) and perform group-by operations and calculate summary statistics, display the first 10 rows, and perform basic data manipulation (sorting, filtering).
Dataset Schema: Date, Store, Product, Sales, Quantity (Use simple mock dataset inside code).
Demonstrate how to create different types of plots (line plot, bar plot, scatter plot) using Matplotlib. Provide code example. Explain how to add titles, labels (x and y axis), and customize colours in these plots. Include examples of customization in your plots.
Answer
Part 1: Pandas Data Manipulation & Cleaning Code
import pandas as pd
import numpy as np
# 1. Loading mock dataset into a DataFrame
raw_data = {
"Date": ["2026-01-01", "2026-01-01", "2026-01-02", "2026-01-02", "2026-01-03",
"2026-01-03", "2026-01-04", "2026-01-04", "2026-01-05", "2026-01-05", "2026-01-06", "2026-01-06"],
"Store": ["East", "West", "East", "West", "East", "West", "East", "West", "East", "West", "East", "West"],
"Product": ["Apples", "Bananas", "Apples", "Cherries", "Bananas", "Cherries", "Apples", "Bananas", None, "Cherries", "Bananas", "Cherries"],
"Sales": [1200, 850, np.nan, 950, 1100, 1400, 1300, 900, 1500, np.nan, 1250, 1600],
"Quantity": [120, 85, 45, 95, 110, 140, 130, 90, 150, 160, 125, 160],
"Unnecessary_Column": ["X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X"]
}
df = pd.DataFrame(raw_data)
# 2. Cleaning Data
# A. Drop unnecessary columns
df_cleaned = df.drop(columns=["Unnecessary_Column"])
# B. Handle missing values
# Drop rows with missing Product (categorical)
df_cleaned = df_cleaned.dropna(subset=["Product"])
# Impute missing Sales (numerical) with the median of that specific product
df_cleaned["Sales"] = df_cleaned.groupby("Product")["Sales"].transform(lambda x: x.fillna(x.median()))
# 3. Filtering and Sorting
# Filter for Sales > 1000 and Store == "East"
filtered_df = df_cleaned[(df_cleaned["Sales"] > 1000) & (df_cleaned["Store"] == "East")]
# Sort cleaned data by Sales descending
sorted_df = df_cleaned.sort_values(by="Sales", ascending=False)
# 4. Group-by and Summary Statistics
grouped_store = df_cleaned.groupby("Store").agg(
Total_Sales=("Sales", "sum"),
Average_Quantity=("Quantity", "mean"),
Transaction_Count=("Sales", "count")
)
# 5. Display First 10 Rows
print("--- Cleaned DataFrame (First 10 Rows) ---")
print(df_cleaned.head(10))
print("\n--- Filtered DataFrame ---")
print(filtered_df)
print("\n--- Group-By Store Metrics ---")
print(grouped_store)
Part 2: Matplotlib Multi-Plot Dashboard & Explanation
import matplotlib.pyplot as plt
# Data arrays for plotting
days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
temperature = [22, 24, 21, 25, 27, 28, 26]
sales_units = [120, 150, 90, 180, 220, 310, 270]
advertising_cost = [10, 12, 8, 15, 20, 25, 22]
# Create a 1x3 subplot grid
fig, axes = plt.subplots(1, 3, figsize=(16, 4.5))
# 1. Line Plot (Trend visualization)
axes[0].plot(days, temperature, color='darkorange', linestyle='-', linewidth=2.5, marker='o', label='Temp (°C)')
axes[0].set_title('Weekly Temperature Trend', fontsize=11, fontweight='bold')
axes[0].set_xlabel('Days of Week', fontsize=9)
axes[0].set_ylabel('Temperature (°C)', fontsize=9)
axes[0].grid(True, linestyle=':', alpha=0.6)
axes[0].legend()
# 2. Bar Plot (Categorical comparisons)
axes[1].bar(days, sales_units, color='royalblue', edgecolor='navy', width=0.5, label='Units Sold')
axes[1].set_title('Weekly Sales Volume', fontsize=11, fontweight='bold')
axes[1].set_xlabel('Days of Week', fontsize=9)
axes[1].set_ylabel('Units Sold', fontsize=9)
axes[1].grid(True, axis='y', linestyle='--', alpha=0.5)
axes[1].legend()
# 3. Scatter Plot (Correlation analysis)
sc = axes[2].scatter(advertising_cost, sales_units, c=sales_units, cmap='viridis', s=100, edgecolor='black', label='Sales Nodes')
axes[2].set_title('Ad Cost vs. Sales Correlation', fontsize=11, fontweight='bold')
axes[2].set_xlabel('Ad Spend ($)', fontsize=9)
axes[2].set_ylabel('Sales Units', fontsize=9)
axes[2].grid(True, linestyle='--', alpha=0.5)
axes[2].legend()
plt.tight_layout()
plt.show()
Explanation of Customizations:
- Titles & Axis Labels: Added using
set_title(),set_xlabel(), andset_ylabel(). Font sizes and weights (fontweight='bold') make them readable. - Colors: Customized via string identifiers (
color='darkorange','royalblue') or gradient colormaps (cmap='viridis') to represent numerical sizes on scatter plots. - Markers & Line Styles: Tailored using
linestyle='-',linewidth=2.5, andmarker='o'to highlight key coordinates. - Legends & Grids: Enabled via
legend()andgrid(True)to add context and coordinate guides.