Syllabus
Unit 05 • 15 Class HoursHighest Weightage Core

Basic Data Processing & Analysis using Python Libraries

Unit 5 is the largest and highest-scoring module in the Tribhuvan University (TU MCA504) syllabus. It covers the three foundational pillars of scientific and data engineering computing in Python: NumPy (high-performance N-dimensional arrays, vectorization, and broadcasting), SciPy (scientific routines including linear algebra, numerical integration, and clustering), and Pandas (Series, DataFrames, data cleaning, missing value imputation, indexing, and relational joins).

Exam Weightage & Strategy (~20 - 25 Marks):In the TU final examination, this unit accounts for at least two 8-to-10 mark long questions (e.g., "Explain NumPy Broadcasting rules with visual matrix expansion and code" or "Discuss Pandas DataFrame creation, missing value handling, and compare .loc vs .iloc with examples") alongside 2 to 3 short conceptual questions.
👨‍🏫 Teacher's Lecture Desk (Core Intuition):Classroom मा याद राख्नुहोस्: यो Unit बाट 20 देखि 25 Marks को questions आउँछ। Theory मात्र घोकेर हुँदैन, examiner ले Visual Diagram (जस्तै Memory Layout, Broadcasting Expansion, Axis 0 vs 1) र minimal code snippets खोज्छ। जहाँ-जहाँ concept tricky छ, त्यहाँ Teacher's Board Note मा दिएको intuition trick सम्झिनुस्!
Section 01 • Numerical Python Core

NumPy Architecture, Memory Layout & Array Attributes

Standard Python lists store elements as arrays of pointers referencing independent PyObject entities scattered across the heap. For large-scale numerical computation, this causes severe memory overhead and frequent CPU Cache Misses. NumPy (Numerical Python) solves this by introducing the C-based ndarray (N-dimensional array), which allocates a single, contiguous block of homogeneous memory addresses.

👨‍🏫 Teacher's Board Note:Notice गर्नुहोस्: Python List मा प्रत्येक item को आफ्नै छुट्टाछुट्टै Memory Address मा Heap pointer हुन्छ। त्यसैले loop चलाउँदा continuous pointer dereferencing ले CPU Cache Miss गराउँछ। तर NumPy ndarray मा C-style Contiguous Memory Buffer allocate हुन्छ जसमा Homogeneous Data Type मात्र बस्छ। यही कारणले CPU ले SIMD commands प्रयोग गरेर single clock cycle मा 50x देखि 100x fast vector computation गर्न सक्छ।

1. Python List vs NumPy Array (Architectural Comparison)

  • Contiguous Memory Buffer: All elements in an ndarray reside sequentially in raw memory. The CPU pre-fetches contiguous bytes into high-speed L1/L2 caches, enabling SIMD vectorization.
  • Homogeneous Data Types: Every element in an ndarray shares the exact same numeric dtype (e.g., int32, float64), eliminating runtime type checking overhead.
  • Vectorization & ufuncs: Element-wise operations execute compiled C loops under the hood without Python interpreter loop overhead.
FIGURE 5.1: Python List (Scattered Heap) vs NumPy ndarray (Contiguous Buffer)TU EXAM FAVORITE
PYTHON BUILT-IN LIST [10, 20, 30]:
Array of Pointers: [ 0x104A ] ----> [PyObject: Type=int, Value=10, RefCount=1] (28 Bytes)
                   [ 0x89BC ] ----> [PyObject: Type=int, Value=20, RefCount=1] (28 Bytes)
                   [ 0x33DF ] ----> [PyObject: Type=int, Value=30, RefCount=1] (28 Bytes)
===> Memory Address fragmented all over Heap! Massive pointer dereferencing latency!

NUMPY NDARRAY (np.array([10, 20, 30], dtype=np.int32)):
Contiguous Raw Memory Buffer (C-Order):
+---------------+---------------+---------------+
| 00000000 000A | 00000000 0014 | 00000000 001E |  (12 Bytes TOTAL! 4 bytes per int)
+---------------+---------------+---------------+
[Byte Offset 0]  [Byte Offset 4]  [Byte Offset 8]
Metadata Header: shape=(3,), strides=(4,), dtype=int32, data_ptr=0x1000

👨‍🏫 Exam Drawing Tip: When TU asks "Why is NumPy faster than Python List?", sketch this exact comparison diagram (Scattered Pointer vs Contiguous Memory Buffer) to secure full marks.

2. Essential ndarray Attributes (High-Yield for Exam)

An ndarray is an object wrapper over raw memory with the following key attributes:

import numpy as np

# Create a 2x3 2D array of 32-bit integers
arr = np.array([[10, 20, 30], [40, 50, 60]], dtype=np.int32)

print("1. ndim (Dimensions):", arr.ndim)       # 2 (2D Array)
print("2. shape (Rows, Cols):", arr.shape)      # (2, 3)
print("3. size (Total Items):", arr.size)       # 6 elements
print("4. dtype (Data Type):", arr.dtype)       # int32
print("5. itemsize (Bytes/item):", arr.itemsize) # 4 bytes
print("6. nbytes (Total Bytes):", arr.nbytes)   # 6 * 4 = 24 bytes
print("7. strides (Byte steps):", arr.strides)  # (12, 4) -> 12 bytes to next row, 4 to next col
👨‍🏫 Teacher's Board Note: ndim ले number of dimensions (1D, 2D) जनाउँछ, shape ले tuple मा (rows, cols) दिन्छ, dtype ले underlying numeric type देखाउँछ, र strides ले Memory Buffer भित्र अर्को row वा column मा जान कति bytes को step लिनुपर्छ भन्ने point गर्छ।
Section 02

Array Creation, Numerical Ranges & File I/O

NumPy provides several optimized built-in constructors to instantiate arrays without manual loops:

1. np.zeros(shape) & np.ones(shape):Initializes an array filled entirely with 0s or 1s (e.g., np.zeros((3, 4)) allocates a $3 \times 4$ zero matrix).
2. np.empty(shape) & np.full(shape, value):empty() allocates raw uninitialized memory containing garbage values (fastest); full() pre-populates all cells with a specified scalar constant (e.g., 99).
3. np.eye(N) & np.identity(N):Generates an $N \times N$ identity matrix with 1s along the main diagonal and 0s elsewhere.

Numerical Ranges: `arange` vs `linspace` vs `logspace`

High-Yield TU Exam Question: "Differentiate between np.arange() and np.linspace() with syntax and output."

# 1. np.arange(start, stop, step):
# Generates evenly spaced values within half-open interval [start, stop)
# Stop value is strictly EXCLUDED!
r1 = np.arange(0, 10, 2)
print("arange:", r1) # [0 2 4 6 8]

# 2. np.linspace(start, stop, num_samples):
# Generates N evenly spaced numbers over closed interval [start, stop]
# Stop value is strictly INCLUDED!
r2 = np.linspace(0, 1, 5)
print("linspace:", r2) # [0.   0.25 0.5  0.75 1.  ]

# 3. np.logspace(start, stop, num):
# Generates numbers spaced evenly on a log scale (10^start to 10^stop)
r3 = np.logspace(1, 3, 3) # [10^1, 10^2, 10^3]
print("logspace:", r3) # [  10.  100. 1000.]
👨‍🏫 Teacher's Board Note: np.arange() मा stop value EXCLUSIVE हुन्छ (जस्तै 10 आउँदैन), तर np.linspace() मा stop value INCLUSIVE हुन्छ (exact 1 वा 10 समेत समावेश हुन्छ)। Exam मा numerical interval सोध्दा यो point अनिवार्य हाइलाइट गर्नुहोस्।

Array Persistence & File I/O

  • np.save("matrix.npy", arr): Serializes a single ndarray into fast, uncompressed binary format preserving dtype and shape metadata.
  • loaded_arr = np.load("matrix.npy"): Directly restores binary arrays into RAM.
  • np.savetxt("data.csv", arr, delimiter=",") & np.loadtxt("data.csv", delimiter=","): Plaintext CSV export and ingestion.
Section 03

Indexing, Slicing & Boolean Masking

NumPy provides multi-dimensional slicing, integer array indexing (fancy indexing), and conditional boolean masking that operates at hardware speed.

👨‍🏫 Teacher's Board Note: Basic slicing sub = arr[0:2, :] ले मूल array को View (Memory Reference) मात्र दिन्छ। View मा value परिवर्तन गर्दा original array पनि फेरिन्छ! तर Boolean Masking (arr[arr > 50]) वा Fancy Indexing ले नयाँ Copy memory allocate गर्छ।
matrix = np.array([
    [10, 20, 30, 40],
    [50, 60, 70, 80],
    [90, 100, 110, 120]
])

# 1. Multi-dimensional Slicing: [row_start:row_end, col_start:col_end]
sub_matrix = matrix[0:2, 1:3]
print("Rows 0-1, Cols 1-2:\n", sub_matrix)
# Output:
# [[20 30]
#  [60 70]]

# 2. Extract entire column or row
col_2 = matrix[:, 2] # 3rd column across all rows: [30, 70, 110]

# 3. Boolean Masking (Vectorized conditional filtering - High Yield)
mask = matrix > 65
filtered_values = matrix[mask]
print("Values > 65:", filtered_values) # [70 80 90 100 110 120]

# 4. In-place Conditional Replacement
matrix[matrix < 50] = 0 # Sets all numbers below 50 to 0
Section 04 • TU Exam 10-Mark Core

Broadcasting Rules & Vectorized Operations

Broadcasting describes how NumPy treats arrays with different shapes during arithmetic operations (addition, multiplication). Without making unneeded memory copies, NumPy virtually stretches the smaller array across the larger array so that they have compatible shapes.

The Formal Broadcasting Rules (TU Exam Standard)

When operating on two arrays, NumPy compares their shapes element-wise starting from the trailing dimensions (Right to Left). Two dimensions are compatible when:

  1. Their numeric lengths are strictly equal, OR
  2. One of the dimension lengths is exactly 1 (which is virtually stretched to match the other).

If neither condition is met, NumPy triggers a fatal ValueError: operands could not be broadcast together.

👨‍🏫 Teacher's Board Note: Broadcasting भनेको Rubber Band Concept हो। यदि कुनै dimension 1 छ भने त्यो रबरको ब्यान्ड जस्तै तन्किएर अर्को array को आकार बराबर बन्छ (zero memory copy)। तर यदि दायाँबाट हेर्दा dimensions न बराबर छन् न त 1 छन् (जस्तै (4, 3) र (4,)), तब रबर ब्यान्ड च्यातिन्छ र ValueError आउँछ!
FIGURE 5.2: Broadcasting Virtual Stretch Matrix AlignmentCRITICAL 10-MARK SKETCH
EXAMPLE 1: Matrix (3, 3) + 1D Array (3,)
Array A Shape:  (3, 3)  <--- 3 Rows, 3 Columns
Array B Shape:     (3,)  <--- 1D Array of length 3

STEP 1: Prepend 1 to smaller shape: Array B becomes (1, 3)
STEP 2: Compare from Right to Left:
        Dim 1 (Trailing): A=3, B=3 (Match! Equal dimensions)
        Dim 0 (Leading):  A=3, B=1 (Match! Dimension is 1, stretched to 3)

VIRTUAL EXPANSION IN MEMORY:
+----+----+----+       +----+----+----+       +----+----+----+
| 10 | 20 | 30 |       |  1 |  2 |  3 |       | 11 | 22 | 33 |
+----+----+----+       +----+----+----+       +----+----+----+
| 40 | 50 | 60 |   +   |  1 |  2 |  3 |   =   | 41 | 52 | 63 |  (Result: 3x3)
+----+----+----+       +----+----+----+       +----+----+----+
| 70 | 80 | 90 |       |  1 |  2 |  3 |       | 71 | 82 | 93 |
+----+----+----+       +----+----+----+       +----+----+----+
  Array A (3, 3)         Virtual B (3, 3)       Output Matrix

EXAMPLE 2: INCOMPATIBLE SHAPES (Triggers ValueError):
Array A Shape: (4, 3)
Array B Shape:    (4,) ===> Right-to-Left: 3 vs 4 (Neither is 1, Not Equal!)
====> RESULT: FAILS! Cannot broadcast (4, 3) with (4,)!
A = np.array([[10, 20, 30], [40, 50, 60], [70, 80, 90]]) # shape (3, 3)
B = np.array([1, 2, 3])                                     # shape (3,)

# Broadcasting automatically expands B across all 3 rows
result = A + B
print("Broadcasting Addition:\n", result)

# Scalar Broadcasting (Multiply single integer across entire matrix)
scaled = A * 2 # 2 is broadcast virtually across all elements
Section 06 • Scientific Computing

SciPy: Linalg, Integrate, Interpolate, Cluster & IO

SciPy (Scientific Python) is an advanced scientific computing library built directly on top of NumPy. While NumPy supplies raw array data structures and basic vector operations, SciPy provides production-grade mathematical algorithms for engineering and scientific analysis.

👨‍🏫 Teacher's Board Note: NumPy ले raw data structures (bricks) दिन्छ भने SciPy ले advanced mathematical algorithms (engineering tools) दिन्छ। scipy.linalg BLAS/LAPACK सँग compiled भएको हुनाले NumPy भन्दा faster हुन्छ र LU, Cholesky decomposition जस्ता advanced routines support गर्छ।
FIGURE 5.3: SciPy Subpackage Architecture & Mathematical DomainsTU SYLLABUS SCOPE
+-------------------------------------------------------------------------+
|                  SCIPY SCIENTIFIC COMPUTING ECOSYSTEM                   |
+-------------------------------------------------------------------------+
       |                  |                   |                  |
       v                  v                   v                  v
+--------------+   +--------------+   +---------------+   +--------------+
| scipy.linalg |   |scipy.integrate|   |scipy.interpolate| | scipy.cluster|
| Matrix Inv   |   | quad()       |   | interp1d()    |   | vq.kmeans()  |
| Eigenvalues  |   | dblquad()    |   | Spline fitting|   | Vector quant |
| Det, Solve Ax=b  | ODE Solvers  |   | Curve approx  |   | K-Means Clust|
+--------------+   +--------------+   +---------------+   +--------------+
       |                  |                   |                  |
       +------------------+-------------------+------------------+
                                  |
                                  v
+-------------------------------------------------------------------------+
| scipy.constants (pi, c, G, h)   |   scipy.io (loadmat, savemat, wavfile)|
+-------------------------------------------------------------------------+

Core SciPy Subpackages with Working Code

# 1. scipy.constants: Physical and Mathematical Constants
from scipy import constants
print("Speed of Light (c):", constants.c, "m/s")
print("Planck constant (h):", constants.h)
print("Standard Pi:", constants.pi)

# 2. scipy.linalg: Solve System of Equations (2x + 3y = 8, 4x + 9y = 20)
from scipy import linalg
A = np.array([[2, 3], [4, 9]])
b = np.array([8, 20])
solution = linalg.solve(A, b) # Direct LAPACK solver for Ax = b
print("Solution [x, y]:", solution) # [2., 1.33333333]
print("Determinant:", linalg.det(A))
print("Matrix Inverse:\n", linalg.inv(A))

# 3. scipy.integrate: Definite Numerical Integration (Integral of x^2 from 0 to 3)
from scipy import integrate
def integrand(x):
    return x ** 2
area, error = integrate.quad(integrand, 0, 3) # Exact analytical: x^3 / 3 = 27 / 3 = 9
print(f"Integral Result: {area} (Est. Error: {error})")

# 4. scipy.interpolate: Gap Filling & Curve Fitting
from scipy.interpolate import interp1d
x_known = np.array([0, 1, 2, 3, 4])
y_known = np.array([0, 1, 4, 9, 16])
f_interp = interp1d(x_known, y_known, kind="linear")
print("Interpolated value at x=2.5:", f_interp(2.5)) # 6.5

# 5. scipy.cluster.vq: K-Means Clustering
from scipy.cluster.vq import kmeans, vq
data_pts = np.array([[1.1, 2.3], [1.5, 1.9], [8.2, 9.1], [8.9, 8.8]], dtype=float)
centroids, _ = kmeans(data_pts, 2) # Discover 2 cluster centroids
print("Discovered Centroids:\n", centroids)
Section 07 • Pandas Tabular Data

Pandas Ecosystem: Series & DataFrames

Pandas is the gold standard for handling structured, tabular, heterogeneous datasets. It provides two foundational data structures:

Pandas Series (1D Labeled Array)

A 1-dimensional homogeneous array with an explicit, mutable index. Every entry in a Series possesses both a value and a custom index label.

Pandas DataFrame (2D Tabular Matrix)

A 2-dimensional labeled data structure with columns of potentially differing types (integers, strings, floats, booleans, timestamps), sharing a common index.

👨‍🏫 Teacher's Board Note: Structurally, DataFrame भनेको shared index भएका Series हरूको aligned dictionary हो। जब तपाईं df['Marks'] single bracket लेख्नुहुन्छ, यसले Series return गर्छ; तर df[['Marks']] double brackets लेख्नुभयो भने 1-column DataFrame return गर्छ।
FIGURE 5.4: Pandas DataFrame Dual-Axis Coordinate SystemCOORDINATE MODEL
                        COLUMNS (axis=1: horizontal operations / column headers)
                       +-------------------+--------------------+--------------------+
                       | "name" (str)      | "semester" (int64) | "gpa" (float64)    |
+----------------------+-------------------+--------------------+--------------------+
| Index 0 (Row label)  | "Bikram Karki"    | 3                  | 3.82               |
+----------------------+-------------------+--------------------+--------------------+
| Index 1 (Row label)  | "Prajita Sen"     | 3                  | 3.95               |
+----------------------+-------------------+--------------------+--------------------+
| Index 2 (Row label)  | "Aman Sharma"     | 2                  | 3.40               |
+----------------------+-------------------+--------------------+--------------------+
INDEX / ROWS (axis=0: vertical operations / agg over rows)

Golden Rule: df.mean(axis=0) computes the vertical mean of each column. df.drop('gpa', axis=1) removes the horizontal column.

Section 08

Creating DataFrames from Scratch & Inspection

High-Yield TU Exam Question: "Explain four different ways to create a Pandas DataFrame from scratch with syntax."

import pandas as pd
import numpy as np

# Method 1: From Dictionary of Lists
data_dict = {
    "Roll": ["MCA-01", "MCA-02", "MCA-03"],
    "Name": ["Aayush", "Binod", "Chahana"],
    "Score": [88, 94, 76]
}
df1 = pd.DataFrame(data_dict)

# Method 2: From List of Dictionaries (Rows represented as dicts)
rows_list = [
    {"Name": "Deepak", "Age": 24, "City": "Kathmandu"},
    {"Name": "Elina", "Age": 23, "City": "Pokhara"}
]
df2 = pd.DataFrame(rows_list)

# Method 3: From 2D NumPy Array (With custom Index and Column labels)
np_data = np.array([[10, 20], [30, 40], [50, 60]])
df3 = pd.DataFrame(np_data, columns=["Test1", "Test2"], index=["S1", "S2", "S3"])

# Method 4: From Disk Files (CSV / JSON)
# df_csv = pd.read_csv("students.csv")
# df_json = pd.read_json("records.json")

# Essential Inspection Methods:
print(df1.head(2))       # First 2 rows
print(df1.tail(1))       # Last 1 row
print(df1.info())        # Memory usage, dtypes and null counts
print(df1.describe())    # Summary statistics: count, mean, std, min, max
print(df1.shape)         # Dimensions tuple: (rows, cols)
Section 09 • TU Exam High-Yield

Handling Missing Data (NaN Imputation & Dropping)

Real-world datasets contain missing entries represented in Pandas as NaN (Not a Number) or None. In the TU examination, data cleaning involves 3 primary steps:

  1. Detecting Missing Values: df.isna() or df.isnull(). Column null tally: df.isna().sum().
  2. Dropping Missing Values: df.dropna().
    • df.dropna(how="any"): Drops entire row if any column contains NaN.
    • df.dropna(subset=["email"]): Drops row only if specific column contains NaN.
  3. Imputing / Filling Missing Values: df.fillna().
    • Constant Imputation: df.fillna(0)
    • Statistical Imputation: df["marks"].fillna(df["marks"].mean())
    • Propagation: df.ffill() (forward fill) or df.bfill() (backward fill)
👨‍🏫 Teacher's Board Note: Doctor vs Undertaker Model:
- fillna() = Doctor: औषधि (Mean/Median) दिएर record को ज्यान बचाउने (Imputation)।
- dropna() = Undertaker: पूरै row नै delete गरिदिने। सानो dataset मा dropna() गर्दा महत्वपूर्ण data loss हुन सक्छ, त्यसैले exam मा सोध्दा Imputation strategy लाई प्राथमिकता दिनुहोस्।
raw_data = {
    "Name": ["Ram", "Shyam", "Hari", "Gita"],
    "Age": [22, np.nan, 24, 21],
    "Score": [85, 90, np.nan, np.nan]
}
df = pd.DataFrame(raw_data)

# 1. Detect missing value counts per column
print("Missing counts:\n", df.isna().sum())

# 2. Mean Imputation: Impute missing numerical score with column mean
mean_score = df["Score"].mean()
df["Score"] = df["Score"].fillna(mean_score)

# 3. Drop rows where critical identifier (Age) is still missing
clean_df = df.dropna(subset=["Age"])
print("Cleaned DataFrame:\n", clean_df)
Section 10 • Crucial Viva & Exam Difference

Slicing & Subsetting: `.loc` vs `.iloc`

Most Frequent TU Trap: "Differentiate between .loc and .iloc with clear examples and endpoint behavior."

.loc (Label-Based Indexing)
  • • Accesses rows and columns via their explicit labels.
  • • Slicing endpoint is INCLUSIVE (both start and stop labels returned).
  • • Syntax: df.loc['row_label', 'col_label']
.iloc (Integer Position-Based Indexing)
  • • Accesses rows and columns via 0-based integer positions.
  • • Slicing endpoint is EXCLUSIVE (standard Python stop - 1 rule).
  • • Syntax: df.iloc[0:3, 1:4]
👨‍🏫 Teacher's Board Note: Name Tag vs Index Slip:
- .loc = Name Tag: रोल नम्बर "R101" देखि "R103" भन्दा "R103" आफै समावेश हुन्छ (INCLUSIVE)।
- .iloc = Standard Python Slice: 0:3 गर्दा 0, 1, 2 मात्र आउँछ, 3 बाहिरिन्छ (EXCLUSIVE)।
students = pd.DataFrame({
    "name": ["Anil", "Bhawana", "Chirag", "Divya"],
    "gpa": [3.6, 3.9, 3.4, 3.8],
    "semester": [1, 2, 3, 4]
}, index=["R101", "R102", "R103", "R104"])

# 1. loc usage: label-based ('R101' to 'R103' BOTH inclusive!)
print(students.loc["R101":"R103", ["name", "gpa"]])

# 2. iloc usage: integer-based (0 to 2 only, index 2 excluded!)
print(students.iloc[0:2, 0:2])

# 3. Boolean Subsetting (Logical Bitwise Filtering)
high_achievers = students[(students["gpa"] >= 3.7) & (students["semester"] >= 2)]
print("GPA >= 3.7:\n", high_achievers)
Section 11 • Relational Algebra

Merging, Joining & Concatenating DataFrames

When datasets originate from multiple disparate sources, Pandas provides 3 relational operations:

  • `pd.concat([df1, df2], axis=0)`: Stacks tables vertically (row binding) or horizontally (axis=1 column binding).
  • `pd.merge(df1, df2, on='key', how='...')`: Executes relational SQL joins (inner, left, right, outer) on common key columns.
  • `df1.join(df2)`: Combines DataFrames based on their row indexes.
FIGURE 5.5: Pandas Relational Merge Strategies (`how` parameter)SQL COMPARISON
HOW='INNER' (Default):
Intersection only! Only keys existing in BOTH df1 and df2 are retained.

HOW='LEFT':
Preserves ALL rows from Left table (df1). Unmatched columns from df2 filled with NaN.

HOW='RIGHT':
Preserves ALL rows from Right table (df2). Unmatched columns from df1 filled with NaN.

HOW='OUTER' (Full Outer Join):
Union of both! All rows from both sides preserved. Missing values filled with NaN.
# Student Profile Table
students_df = pd.DataFrame({
    "sid": [101, 102, 103, 104],
    "name": ["Kabir", "Laxmi", "Manoj", "Neeta"]
})

# Exam Marks Table
marks_df = pd.DataFrame({
    "sid": [102, 103, 105],
    "subject": ["Python", "Python", "Python"],
    "score": [92, 85, 78]
})

# 1. Inner Join: Intersection only (102, 103)
inner_merged = pd.merge(students_df, marks_df, on="sid", how="inner")

# 2. Left Join: Preserves all students; unattempted exams show NaN score
left_merged = pd.merge(students_df, marks_df, on="sid", how="left")

# 3. Full Outer Join: Union of all records from both tables
outer_merged = pd.merge(students_df, marks_df, on="sid", how="outer")
Section 12 • End-to-End Data Pipeline

Full Data Analysis Exam Project: Student Marks Analytics Pipeline

A recurring 10-mark comprehensive question in the TU MCA examination tests candidate ability to combine NumPy vectorization with Pandas ETL workflows: "Write an end-to-end Python program using NumPy and Pandas to ingest student records, clean missing marks via statistical imputation, compute vectorized metrics, filter high achievers with boolean masks, and export clean CSV reports."

import numpy as np
import pandas as pd

def run_student_analytics():
    # 1. Raw Data Ingestion (Simulated CSV DataFrame)
    raw_records = {
        "Roll": ["MCA-01", "MCA-02", "MCA-03", "MCA-04", "MCA-05", "MCA-06"],
        "Name": ["Aayush", "Bipana", "Chirag", "Deepa", "Eshwar", "Farhan"],
        "Theory": [75.0, np.nan, 82.0, 45.0, 92.0, 68.0],
        "Practical": [22.0, 24.0, np.nan, 18.0, 25.0, 21.0],
        "Attendance": [85, 92, 78, 60, 95, 80]
    }
    df = pd.DataFrame(raw_records)
    print("=== 1. Initial Raw Dataset ===")
    print(df)

    # 2. Data Cleaning & Imputation (Missing Data Handling)
    # Fill Theory NaN with mean; fill Practical NaN with default 20.0
    df["Theory"] = df["Theory"].fillna(df["Theory"].mean())
    df["Practical"] = df["Practical"].fillna(20.0)

    # 3. Feature Engineering (Vectorized Calculation)
    df["Total_Score"] = df["Theory"] + df["Practical"]
    df["Percentage"] = (df["Total_Score"] / 125.0) * 100

    # 4. NumPy Statistical Analysis
    scores_array = df["Total_Score"].values
    mean_val = np.mean(scores_array)
    std_val = np.std(scores_array)
    median_val = np.median(scores_array)

    print(f"\n=== 2. Statistical Summary (NumPy Engine) ===")
    print(f"Mean Score: {mean_val:.2f} | Std Deviation: {std_val:.2f} | Median: {median_val:.2f}")

    # 5. Advanced Filtering (Boolean Masking)
    # Students with Distinction (>= 80%) AND Attendance >= 75%
    distinction_students = df[(df["Percentage"] >= 80.0) & (df["Attendance"] >= 75)]
    print("\n=== 3. Distinction Students (Attendance >= 75%) ===")
    print(distinction_students[["Roll", "Name", "Total_Score", "Percentage"]])

    # 6. Export Processed Report
    df.to_csv("tu_final_processed_marks.csv", index=False)
    print("\n[SUCCESS] Pipeline completed. Exported to 'tu_final_processed_marks.csv'.")

if __name__ == "__main__":
    run_student_analytics()
👨‍🏫 Teacher's Board Note: यो single pipeline मा Data Ingestion, fillna() imputation, vectorized total calculation, NumPy stats (mean, std, median), र boolean masking (& operator) सबै कभर हुन्छ। परीक्षामा १० मार्क्सको code प्रश्न आएमा यो २५-लाइनको सफा pattern लेख्नुभयो भने full marks पाइन्छ!
Special Focus • Final Preparation Hacks

Master Techniques, Mental Models & Exam Writing Hacks

Rote memorization often breaks down under examination conditions. The following 6 Visual Mental Models, 5-Layer Long Answer Formula, and 5 Fatal Examiner Traps will structure your answers to command maximum marks.

1. The 5-Layer Long Answer Formula (Scoring 9+ in 10-Mark Questions)

Examiners evaluate hundreds of copies and quickly scan for technical rigor. Structuring answers into these 5 clear layers guarantees high scores:

Layer 1
Formal Academic Definition (2-3 Sentences):State the exact definition with bold technical keywords (memory architecture, C-level layout, PEP conventions).
Layer 2
Architectural Diagram / ASCII Memory Sketch:Draw a neat memory or dimension diagram (e.g., Heap pointers vs Contiguous buffer, Broadcasting expansion, or Axis reduction).
Layer 3
Minimal Working Code Snippet (10-15 Lines):Provide realistic, syntax-perfect Python code with expected outputs clearly commented.
Layer 4
Rules, Complexity & Operational Mechanics:Bullet points explaining operational rules (e.g., Broadcasting matching rule, C3 MRO order) and time/space complexities (O(1) vs O(N)).
Layer 5
Edge Case & Common Trap Callout:State a subtle caveat (e.g., "Note: Basic slicing produces a VIEW, so mutating it modifies the base array; use .copy() for safety").

2. The 6 Core Mental Models (अवधारणा दिमागमा बसाउने मुख्य सूत्रहरू)

1. The Rubber Band Model (Broadcasting)

सूत्र: 1 Dimension भनेको Rubber Band जस्तै हो — जत्रो size को array आए पनि stretch भएर match हुन्छ। तर यदि 1 नभई different number भयो भने rubber च्यातिन्छ (ValueError: operands could not be broadcast together)!

A: (4, 1, 3)
B:    (2, 3) ➔ (1, 2, 3)
Result: (4, 2, 3) ✅ दुवै 1 हरू stretch भए!
2. Gravity vs Book Reader (Axis 0 vs Axis 1)

सूत्र: axis=0 = Gravity (माथिबाट तल) — सबै rows तल खसेर compress हुन्छन् र vertical column-wise aggregate दिन्छन्। axis=1 = Book Reader (देब्रेबाट दायाँ) — किताब पढेजस्तै row-by-row तेर्सो हिसाब निक्लिन्छ।

df.mean(axis=0) ➔ ठाडो Columns को Average
df.mean(axis=1) ➔ तेर्सो Rows को Average
3. Name Tag vs Python Slice (.loc vs .iloc)

सूत्र: .loc = Name Label (नाम बोलाउने) — शिक्षकले "Anil देखि Chirag सम्म" भन्दा Chirag पनि पर्छ (INCLUSIVE)। .iloc = Python Slice Indexrange(0, 3) मा 3 कहिल्यै नपरे जस्तै (EXCLUSIVE)।

df.loc["A":"C"] ➔ A, B, C तीनवटै आउँछ (Inclusive)
df.iloc[0:3]     ➔ 0, 1, 2 मात्र (3 Excluded)
4. Hotel Rooms vs Scattered Houses (NumPy vs List)

सूत्र: Python List = शहरभरि scatter भएका घरहरू — प्रत्येकलाई भेट्न Heap Memory को Pointer Address पछ्याउनुपर्छ (Cache Misses)। NumPy = होटलका 101, 102, 103 लगातार कोठाहरू (Contiguous Raw Buffer) — CPU ले SIMD द्वारा एकै cycle मा fetch गर्छ।

List: ~28 Bytes/int + Heap Pointer (Slow)
NumPy: 4 Bytes/int raw buffer (50x Fast)
5. Doctor vs Undertaker (fillna vs dropna)

सूत्र: fillna() = Doctor — Mean/Median को औषधि दिएर row को ज्यान बचाउने (Imputation)। dropna() = Undertaker — missing भेट्यो कि सिधै row नै फाल्ने। सानो dataset हुँदा सधैं Doctor (fillna) रणनीति लिनुपर्छ।

df["gpa"].fillna(df["gpa"].mean()) ✅ Row सुरक्षित
df.dropna(how="any") ⚠️ 50% valuable data गुम्न सक्छ
6. Digital Vault Lockdown (ACID Transactions)

सूत्र: Database Transaction भनेको Digital Vault जस्तै हो। सबै query सफल भएपछि conn.commit() ले vault permanently lock गर्छ। बीचमा कुनै Error/Exception आयो भने conn.rollback() ले सबै कुरा reset गर्छ (All-or-Nothing Atomicity)।

try: debit; credit; conn.commit()
except: conn.rollback() ✅ Safe State

3. The 5 Fatal Examiner Traps (परीक्षकले नम्बर काट्ने मुख्य भूलहरू)

Common mistakes where candidates lose 30-50% marks despite writing correct logic:

  • TRAP 1:
    Slicing View vs Copy Trap: In NumPy, basic slicing b = a[0:2] creates a View (Reference). Mutating b[0] = 99 alters a[0]! Always write a[0:2].copy() when independent data is required.
  • TRAP 2:
    Boolean Masking Operator Trap: Writing df[(df.age > 20) and (df.gpa > 3.5)] triggers ValueError: The truth value of a Series is ambiguous. You must use bitwise operators & and |, and wrap every condition in parentheses: (df.age > 20) & (df.gpa > 3.5).
  • TRAP 3:
    Missing Reassignment in Pandas: Simply invoking df.dropna() or df.fillna(0) does not modify the DataFrame! You must reassign (df = df.dropna()) or set inplace=True.
  • TRAP 4:
    arange vs linspace Endpoint Trap: np.arange(0, 10, 2) excludes the endpoint 10 (Output: 0, 2, 4, 6, 8). In contrast, np.linspace(0, 10, 5) includes the stop point 10 (Output: 0, 2.5, 5, 7.5, 10).
  • TRAP 5:
    SQL String Concatenation: Never write f"SELECT * FROM t WHERE id={id}". Always write parameterized queries ("SELECT * FROM t WHERE id=?", (id,)) to prevent SQL Injection attacks.

4. 3-Minute Quick Formula Matrix (Exam Hall Revision Sheet)

Function / MethodLibraryReturn Type / MemoryExam Keywords
np.broadcast()NumPyVirtual expansion (0-copy)Right-to-left, equal or 1
arr[1:3]NumPyView (Shares buffer)Mutating view mutates base
scipy.linalg.solve()SciPyNumPy 1D solutionSolves Ax = b using LAPACK
df.locPandasSeries or DataFrameLabel-based, INCLUSIVE
df.ilocPandasSeries or DataFrameInteger index, EXCLUSIVE
pd.merge()PandasNew DataFrameSQL joins: inner, left, outer
Section 13 • Examination Hall Blueprint

TU Exam Q&A Bank: 10 High-Yield Model Answers

Each model answer provides 5-Point Quick-Grab Memory Anchors for instant recall, an authoritative Examination Paragraph Blueprint in English, and a classroom Teacher's Board Note.

Q1TU Model • NumPy vs Python List Memory Architecture (8 Marks)

Explain the architectural and memory differences between a Python List and a NumPy ndarray. Why is NumPy exponentially faster for scientific computing?

5-POINT QUICK-GRAB MEMORY ANCHORS:
  • Contiguous vs Segmented: NumPy arrays allocate a single contiguous block of C memory; Python lists allocate arrays of pointers pointing to scattered PyObject entities.
  • Memory Footprint: A Python integer object takes 28 bytes minimum; a NumPy `int32` takes exactly 4 raw bytes (7x memory reduction).
  • Type Checking Overhead: Python lists check types dynamically on every single item access; NumPy enforces strict homogeneity, bypassing runtime type checks.
  • CPU Cache Optimization: Contiguous memory ensures high L1/L2 CPU cache hit rates and enables SIMD (Single Instruction Multiple Data) vectorization.
  • Underlying C Implementation: Operations on ndarray execute compiled pre-optimized C loops rather than Python bytecode interpretation.

📝 Examination Paragraph Blueprint: In Python, a standard list is implemented as a contiguous array of pointers to arbitrary objects allocated across the dynamic heap memory. Each individual integer is an instance of PyObject, requiring 28 bytes of metadata (reference count, type pointer, and raw value). Consequently, traversing a Python list requires pointer dereferencing across discontinuous memory locations, leading to frequent CPU L1/L2 cache misses and substantial type-checking overhead at every step.

In contrast, NumPy's ndarray is a C-struct maintaining a pointer to a single, contiguous block of homogeneous primitive data in memory, alongside metadata specifying shape, strides, and dtype. Because data elements reside sequentially with zero wrapper overhead, modern CPUs can preload entire memory lines into high-speed cache and apply SIMD vectorization to process multiple elements per clock cycle, achieving 50x to 100x performance gains over standard Python loops.

👨‍🏫 Teacher's Board Note: Exam paper मा सुरुमै Figure 5.1 जस्तै memory sketch कोर्नुहोस्। Python List भनेको heap भरि छरिएका Pointer Address हरू हुन्, तर NumPy भनेको hotel का consecutive room numbers जस्तै Contiguous Buffer हो। यो रेखाचित्र कोर्नासाथ परीक्षकले Full Marks दिन्छन्।
Q2TU Model • NumPy Broadcasting Rules (10 Marks)

State and thoroughly explain the NumPy Broadcasting Rules. Provide compatible and incompatible shape examples with mathematical matrices.

5-POINT QUICK-GRAB MEMORY ANCHORS:
  • Definition: Broadcasting allows element-wise operations on arrays of differing shapes without making unnecessary memory duplicates.
  • Trailing Alignment: Shape dimensions are paired strictly from Right to Left (trailing dimension backwards).
  • Compatibility Condition 1: Two dimensions are compatible if their numeric lengths are exactly equal.
  • Compatibility Condition 2: Two dimensions are compatible if one of the lengths is exactly 1 (it is virtually stretched).
  • Shape Prepending: If arrays have different rank (ndim), 1s are prepended to the smaller shape until ranks match.

📝 Examination Paragraph Blueprint: NumPy broadcasting describes how arithmetic operations are executed between arrays of differing dimensions without replicating data in physical memory. The operation conforms to two rigorous rules: First, all dimensions are aligned starting from the rightmost (trailing) dimension and working leftward. If array dimensions differ in rank, dimensions of size 1 are prepended to the shorter shape until both tuples have identical length.

Second, two dimensions are compatible if and only if they are equal, or one of them is 1. If a dimension has length 1, it is virtually expanded along that axis by setting its stride to 0 (re-reading the same element without copying bytes). For example, adding an array of shape (3, 3) and (3,) aligns to (3, 3) and (1, 3), yielding a valid broadcast shape of (3, 3). Conversely, shapes (4, 3) and (4,) fail because trailing dimensions 3 and 4 are neither equal nor 1, triggering a ValueError: operands could not be broadcast together.

👨‍🏫 Teacher's Board Note: दायाँबाट (Right-to-Left) हेर्दै आउँदा कि त number equal हुनुपर्छ, कि त एउटा 1 हुनुपर्छ। 1 भएको shape stretch भएर अर्को बराबर बन्छ। Exam मा compatible र incompatible दुवै matrix example देखाउन छुटाउनुहुन्न!
Q3TU Model • Slicing Views vs Copies (5 Marks)

Explain the difference between a View and a Copy in NumPy array slicing. How do `base` and `may_share_memory()` verify this?

5-POINT QUICK-GRAB MEMORY ANCHORS:
  • View Mechanism: Basic slicing (`arr[1:3]`) returns a view that shares the identical underlying data buffer.
  • Copy Mechanism: Advanced/Fancy indexing (`arr[[1, 2]]`) or `.copy()` allocates an entirely separate new memory buffer.
  • Side Effects: Mutating elements in a view mutates the original parent array; mutating a copy has zero side effects.
  • Inspection with base: `sub_arr.base` returns the parent array if it is a view, and `None` if it is an independent copy.
  • Memory Sharing Check: `np.shares_memory(arr, sub_arr)` returns `True` for views and `False` for copies.

📝 Examination Paragraph Blueprint: In NumPy, basic slicing using index notation arr[start:stop:step] produces a View. A view does not allocate a new buffer; rather, it creates a new ndarray header that shares the original memory buffer with adjusted offset, shape, and strides. Because memory is shared, any in-place modification to a view directly mutates the parent array.

Conversely, explicit calls to arr.copy() or advanced indexing with boolean/integer masks produce an independent Copy in a newly allocated memory address. To verify ownership programmatically, inspect the base attribute: if sub.base is None, the array owns its memory (it is a copy); if sub.base is parent, it is a view. Furthermore, np.shares_memory(a, b) returns a boolean confirming whether both arrays reference the same buffer.

👨‍🏫 Teacher's Board Note: View भनेको ऐना हेरेजस्तै हो — data buffer एउटै हुन्छ, त्यसैले slice मा modify गर्दा main array पनि modify हुन्छ। Copy भनेको Photocopy जस्तै हो — new buffer allocate हुन्छ। Exam मा b.base is anp.shares_memory() को code अनिवार्य लेख्नुहोस्।
Q4TU Model • SciPy Subpackages & Linear Algebra (6 Marks)

Discuss the role of SciPy in scientific computing. Differentiate between `scipy.linalg` and `numpy.linalg`.

5-POINT QUICK-GRAB MEMORY ANCHORS:
  • NumPy vs SciPy Role: NumPy provides foundational data structures (`ndarray`); SciPy provides high-level mathematical algorithms built upon it.
  • BLAS/LAPACK Binding: `scipy.linalg` is always compiled with advanced BLAS/LAPACK routines, guaranteeing higher speed.
  • Extended Decompositions: `scipy.linalg` contains advanced matrix factorizations (LU, Schur, Cholesky, QZ) not present in NumPy.
  • Key Subpackages: `integrate` (quad/ODE), `interpolate` (splines), `cluster` (k-means), `constants` (physical constants).
  • Return Format: Almost all SciPy modules return native NumPy arrays, ensuring complete ecosystem compatibility.

📝 Examination Paragraph Blueprint: SciPy is an open-source scientific computing library built atop the NumPy ndarray architecture. While NumPy provides fundamental array operations and elementary mathematical transforms, SciPy implements specialized algorithmic toolboxes spanning numerical integration (scipy.integrate), optimization and curve fitting (scipy.optimize), statistical distributions and hypothesis testing (scipy.stats), signal processing (scipy.signal), and interpolation (scipy.interpolate).

The differentiation between scipy.linalg and numpy.linalg is critical: scipy.linalg contains all functions present in numpy.linalg plus specialized matrix decompositions (LU factorization, Schur decomposition, Cholesky, and Sylvester solvers). Furthermore, scipy.linalg is guaranteed to compile directly against optimized hardware-accelerated BLAS and LAPACK libraries, delivering superior computational throughput for high-dimensional matrix equations.

👨‍🏫 Teacher's Board Note: NumPy ले foundational bricks (Arrays) दिन्छ भने SciPy ले complex engineering algorithms (Integration, Optimization, Linear Algebra) दिन्छ। scipy.linalg.solve(A, b) ले $Ax = b$ system of linear equations कसरी एकै line मा solve गर्छ, त्यो code snippet दिनुहोला।
Q5TU Model • Series vs DataFrame Structural Comparison (5 Marks)

Compare a Pandas Series with a Pandas DataFrame. How are they structurally related?

5-POINT QUICK-GRAB MEMORY ANCHORS:
  • Dimensions: Series is 1-dimensional labeled array; DataFrame is 2-dimensional labeled tabular matrix.
  • Homogeneity: A Series holds values of a single uniform dtype; a DataFrame can hold multiple dtypes across columns.
  • Structural Composition: A DataFrame is essentially an aligned dictionary collection of Series sharing a common index.
  • Extraction Relation: Selecting a single column from a DataFrame (`df["col"]`) yields a Pandas Series.
  • Axis Configuration: Series has only `axis=0` (rows); DataFrame has both `axis=0` (rows) and `axis=1` (columns).

📝 Examination Paragraph Blueprint: In the Pandas library, the Series is a one-dimensional labeled array capable of holding data of any homogeneous type (integers, strings, floats, Python objects). It consists of an array of data values coupled with an associated array of index labels. The DataFrame is a two-dimensional, size-mutable tabular data structure with labeled axes (rows and columns), conceptualized as an ordered dictionary of Series that share a unified Index.

Structurally, each column in a DataFrame is an individual Series object. Extracting a single column using single bracket notation (df["marks"]) returns a Series, whereas using double bracket notation (df[["marks"]]) returns a single-column DataFrame. A Series contains only one dimension (axis=0 representing index rows), while a DataFrame operates across two dimensions (axis=0 for index rows and axis=1 for columns).

👨‍🏫 Teacher's Board Note: Excel sheet सँग दाँजेर सम्झिनुहोस्: एउटा single column भनेको Series हो, र धेरै columns मिलेर बनेको complete spreadsheet भनेको DataFrame हो। df['col'] ले Series दिन्छ भने df[['col']] ले DataFrame दिन्छ भन्ने nuance खुलाउनुहोला।
Q6TU Model • Creating DataFrames from Scratch (6 Marks)

Demonstrate four different methods to create a Pandas DataFrame from scratch with Python code snippets.

5-POINT QUICK-GRAB MEMORY ANCHORS:
  • Method 1: Dict of Lists: Keys become column names; lists become column data (pd.DataFrame({"A": [1, 2]})).
  • Method 2: List of Dicts: Each dictionary represents a distinct row; keys are matched to column names.
  • Method 3: 2D NumPy Array: Raw numerical matrix with explicit `columns=[...]` and `index=[...]` parameters.
  • Method 4: Dict of Series: Combines individual Series objects, automatically aligning rows by their index.
  • Default Indexing: In all methods, if `index` is omitted, Pandas generates a zero-indexed integer RangeIndex (`0, 1, 2...`).

📝 Examination Paragraph Blueprint: Pandas offers versatile constructors for instantiating DataFrames from Python primitives and NumPy arrays. In Method 1 (Dictionary of Lists), dictionary keys serve as column headers and list items form sequential column entries; all lists must possess identical lengths to avoid a ValueError. In Method 2 (List of Dictionaries), each dictionary models a discrete record or row; missing keys across dictionaries are automatically populated with NaN.

In Method 3 (2D NumPy ndarray), raw matrix values are wrapped into a DataFrame by specifying explicit columns=['A', 'B'] and optional row index arrays. In Method 4 (Dictionary of Series), distinct Series objects with custom indexes are combined; Pandas performs union index alignment, filling mismatched index entries with NaN automatically.

👨‍🏫 Teacher's Board Note: परीक्षामा यी चारवटै construction approaches (Dict of Lists, List of Dicts, 2D NumPy array, र Dict of Series) को ३-३ लाइनको code लेखिदिनुहोस्। Dict of Lists मा सबै list को length बराबर हुनुपर्छ भन्ने edge case खुलाउँदा examiner प्रभावित हुन्छन्।
Q7TU Model • Handling Missing Values: Dropping vs Imputation (8 Marks)

How are missing values represented and handled in Pandas? Differentiate between dropping and imputation strategies with code.

5-POINT QUICK-GRAB MEMORY ANCHORS:
  • Missing Representation: Marked by `np.nan` (IEEE floating point NaN) or `pd.NA` in newer Pandas nullable types.
  • Detection: `df.isna()` or `df.isnull()` returns boolean matrix; `df.isna().sum()` aggregates count per column.
  • Dropping Strategy: `df.dropna(how='any')` deletes rows with missing values (risk of losing valuable sample data).
  • Imputation Strategy: `df.fillna(value)` fills missing values with statistics like mean, median, mode, or constant.
  • Propagation Techniques: Forward fill (`ffill()`) and Backward fill (`bfill()`) carry adjacent historical records forward.

📝 Examination Paragraph Blueprint: In Pandas, missing data is primarily represented by NumPy's np.nan (an IEEE 754 floating-point Not-a-Number value) or pd.NA for nullable integer/boolean types. Because np.nan != np.nan evaluates to True, identity checks must be performed using df.isna() or df.isnull(). Handling missing data is divided into two primary paradigms: Deletion (Dropping) and Imputation.

Dropping using df.dropna(how="any") removes entire rows containing even a single missing value. While computationally straightforward, it risks discarding significant proportions of the dataset, inducing statistical bias. Imputation using df.fillna() replaces missing entries with central tendency measures—such as column mean for normal distributions or median for skewed data (df["Salary"].fillna(df["Salary"].median(), inplace=True))—or forward-fills historical observations in time-series data using df.ffill().

👨‍🏫 Teacher's Board Note: fillna() भनेको Doctor हो (data लाई survive गराउने) र dropna() भनेको Undertaker हो (row delete गर्ने)। Dataset सानो छ भने सधैं Mean/Median imputation गर्नुपर्छ। inplace=True वा re-assignment (df = df.fillna(...)) गर्न कहिल्यै नबिर्सिनुहोस्!
Q8TU Model • .loc vs .iloc Deep Dive (8 Marks)

Explain the crucial differences between `.loc` and `.iloc` indexers in Pandas with attention to endpoint inclusivity.

5-POINT QUICK-GRAB MEMORY ANCHORS:
  • Targeting Basis: `.loc` queries by explicit string/custom labels; `.iloc` queries strictly by zero-based integer index offsets.
  • Endpoint Inclusivity: `.loc["a":"c"]` INCLUDES both "a" and "c"; `.iloc[0:3]` EXCLUDES index 3 (standard Python slice).
  • Boolean Masking: `.loc` accepts boolean arrays matching row index labels; `.iloc` accepts boolean lists/arrays directly.
  • Column Specification: `.loc[rows, ["colA", "colB"]]` uses column names; `.iloc[rows, [0, 1]]` uses numeric column indices.
  • TypeError Trigger: Supplying a non-integer position to `.iloc` triggers `TypeError: cannot do positional indexing with these indexers`.

📝 Examination Paragraph Blueprint: Data selection in Pandas relies primarily on two indexing accessors: .loc (label-based) and .iloc (integer position-based). The primary functional difference lies in how indices are resolved: .loc queries rows and columns using their explicit index labels and string column identifiers, whereas .iloc queries purely by zero-based integer offsets (positions 0, 1, 2, ... N-1).

The most vital distinction that examiners test is endpoint inclusivity during slicing: in .loc["R1":"R3"], the slice is inclusive of both the start label "R1" and the stop label "R3". In contrast, .iloc[0:3] adheres to standard Python slice semantics, where the stop position 3 is strictly exclusive (retrieving positions 0, 1, and 2 only). Attempting to pass string labels to .iloc raises a TypeError.

👨‍🏫 Teacher's Board Note: .loc ले नाम हेर्छ र अन्तिमको Label पनि include गर्छ (Inclusive)। .iloc ले integer position हेर्छ र Python slice जस्तै अन्तिमको index छाड्छ (Exclusive)। यो nuance प्रस्ट नलेख्दा विद्यार्थीहरूको २-३ मार्क्स सजिलै काटिन्छ।
Q9TU Model • Relational Operations: Merge vs Concat vs Join (8 Marks)

Differentiate between `pd.concat()`, `pd.merge()`, and `DataFrame.join()`. When should you use which?

5-POINT QUICK-GRAB MEMORY ANCHORS:
  • concat() Purpose: Stacks or glues DataFrames along an axis (vertically `axis=0` or horizontally `axis=1`) without checking keys.
  • merge() Purpose: Performs relational database joins on one or more specified common column keys (`on='key'`).
  • join() Purpose: Convenience wrapper around `merge` that defaults to joining on the DataFrame row Index rather than columns.
  • Join Modes: Supported `how` parameters: `inner` (intersection), `left` (all left), `right` (all right), `outer` (union).
  • Index Reset: `pd.concat(..., ignore_index=True)` prevents duplicate row index numbers when stacking vertically.

📝 Examination Paragraph Blueprint: Pandas provides three distinct relational operators for combining datasets. pd.concat() performs concatenation along an axis: stacking DataFrames vertically (axis=0) to append rows, or horizontally (axis=1) to append columns without evaluating relational key constraints. Setting ignore_index=True prevents overlapping duplicate row index labels.

In contrast, pd.merge() executes relational SQL-style database joins on explicit key columns (pd.merge(df1, df2, on="student_id", how="inner")). The how argument governs join behavior: inner retains key intersections; left preserves all records from the left DataFrame; right preserves all records from the right; and outer computes the union. Finally, df1.join(df2) is a specialized wrapper around merge designed specifically for joining on index labels rather than column values.

👨‍🏫 Teacher's Board Note: एउटा टेबल मुनि अर्को टेबल टाँस्न concat, SQL जस्तै साझा ID Key को आधारमा जोड्न merge, र Row Index को आधारमा जोड्न join प्रयोग गरिन्छ। Exam मा SQL comparison table (Figure 5.5) बनाउनुहोला।
Q10TU Model • Boolean Subsetting & Bitwise Operators (6 Marks)

How does Boolean Masking and the `.query()` method filter rows in Pandas? Why must bitwise operators (`&`, `|`) be used instead of Python keywords (`and`, `or`)?

5-POINT QUICK-GRAB MEMORY ANCHORS:
  • Vectorized Truth Evaluation: Filtering evaluates conditions element-by-element across the Series, producing a boolean Series of True / False.
  • Why Bitwise Operators: Python's and / or evaluate truthiness of an entire object at once (ValueError: Truth value of a Series is ambiguous).
  • Bitwise Requirement: Bitwise operators & (AND), | (OR), ~ (NOT) evaluate element-wise truth tables across the array.
  • Parentheses Precedence: In Python, bitwise & has higher precedence than comparison >; conditions MUST be enclosed in parentheses: (df["a"] > 5) & (df["b"] < 10).
  • query() Alternative: df.query("a > 5 and b < 10") provides clean SQL-like string filtering without operator parenthesis boilerplate.

📝 Examination Paragraph Blueprint: Boolean masking in Pandas applies conditional expressions across entire columns vectorially, producing a boolean Series of identical length where each entry indicates satisfaction of the predicate. Supplying this boolean mask into the DataFrame indexer (df[mask]) selects only rows evaluating to True.

Standard Python logical keywords and and or cannot be used because they attempt to evaluate the single scalar truthiness of the entire Series object, resulting in ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all(). Instead, bitwise operators & (bitwise AND) and | (bitwise OR) must be employed to evaluate element-by-element truth vectors. Because bitwise operators hold higher operator precedence than comparison operators, every conditional clause must be encapsulated in parentheses: df[(df['age'] > 20) & (df['gpa'] >= 3.5)].

👨‍🏫 Teacher's Board Note: Pandas मा row filter गर्दा and होइन &or होइन | चलाउनुपर्छ। साथै Python को operator precedence को कारण (condition1) & (condition2) गरि हरेक condition लाई bracket भित्र हाल्नै पर्छ, नत्र syntax error आउँछ!