Informatics Practices: Class 12 — Chapter 3: Data Handling using Pandas - II

3.1 Introduction & Case Study Setup

Pandas is an established Python library used for data manipulation, processing, and analysis. This chapter covers advanced features of DataFrame such as sorting, descriptive statistics, grouping, reshaping, handling missing values, and database integration.

Technical Profile: Case Study Dataset (marksUT)

  • Architecture Layer / Tier: In-memory Pandas DataFrame representation of student unit test marks.
  • Operational Mechanics: Dictionary containing student names, unit test numbers, and marks across five subjects (Maths, Science, S.St, Hindi, Eng).
  • System Impact & Execution Outputs: Serves as the baseline dataset for all statistical, sorting, grouping, and reshaping operations in this chapter.

The baseline dataset contains marks scored by 4 students (Raman, Zuhaire, Aashravy, Mishti) in Unit Tests 1, 2, and 3. Maximum marks are 25 per subject.

Name UT Maths Science S.St Hindi Eng
Raman 1 22 21 18 20 21
Raman 2 21 20 17 22 24
Raman 3 14 19 15 24 23
Zuhaire 1 20 17 22 24 19
Zuhaire 2 23 15 21 25 15
Zuhaire 3 22 18 19 23 13
Aashravy 1 23 19 20 15 22
Aashravy 2 24 22 24 17 21
Aashravy 3 12 25 19 21 23
Mishti 1 15 22 25 22 22
Mishti 2 18 21 25 24 23
Mishti 3 17 18 20 25 20
  
    >>> import pandas as pd
    >>> marksUT = {
    'Name': ['Raman','Raman','Raman','Zuhaire','Zuhaire','Zuhaire','Ashravy','Ashravy','Ashravy','Mishti','Mishti','Mishti'],
    'UT': [1,2,3,1,2,3,1,2,3,1,2,3],
    'Maths': [22,21,14,20,23,22,23,24,12,15,18,17],
    'Science': [21,20,19,17,15,18,19,22,25,22,21,18],
    'S.St': [18,17,15,22,21,19,20,24,19,25,25,20],
    'Hindi': [20,22,24,24,25,23,15,17,21,22,24,25],
    'Eng': [21,24,23,19,15,13,22,21,23,22,23,20]
    }
    >>> df = pd.DataFrame(marksUT)
  
3.2 Descriptive Statistics

Descriptive statistics summarize the given data to provide a basic understanding of its distribution and characteristics.

Computing Method Exact Syntactic Execution Signature Supported Parameters & Arguments Algorithmic Purpose & Output Representation
max() DataFrame.max(axis=0, numeric_only=False) axis (0 for column, 1 for row), numeric_only (bool) Returns the maximum value across the specified axis. For non-numeric, evaluates alphabetically.
min() DataFrame.min(axis=0, numeric_only=False) axis , numeric_only Returns the minimum value across the specified axis.
sum() DataFrame.sum(axis=0, numeric_only=False) axis , numeric_only Returns the sum of values across the specified axis. Concatenates strings if non-numeric.
count() DataFrame.count(axis=0, numeric_only=False) axis , numeric_only Returns the count of non-NA/null observations across the specified axis.
mean() DataFrame.mean(axis=0, numeric_only=True) axis , numeric_only Returns the mathematical mean (average) of values. Only applicable to numeric columns.
median() DataFrame.median(axis=0, numeric_only=True) axis , numeric_only Returns the median (middle value) of the data. If even count, averages the two middle values.
mode() DataFrame.mode(axis=0, numeric_only=False) axis , numeric_only Returns the mode (most frequent value) of the dataset. Can return multiple modes.
quantile() DataFrame.quantile(q=0.5, axis=0, numeric_only=True) q (float or array-like, default 0.5), axis , numeric_only Returns values at the given quantile. 0.25 is 1st quartile, 0.50 is median, 0.75 is 3rd quartile.
var() DataFrame.var(axis=0, numeric_only=True) axis , numeric_only Returns unbiased variance (average of squared differences from the mean) of numeric columns.
std() DataFrame.std(axis=0, numeric_only=True) axis , numeric_only Returns sample standard deviation (square root of variance) of numeric columns.
describe() DataFrame.describe(percentiles=None, include=None, exclude=None) percentiles , include , exclude Generates descriptive statistics summarizing central tendency, dispersion, and shape of dataset.
3.2.1 - 3.2.4 Max, Min, Sum, and Count Operations

Demonstration of basic aggregation methods on the DataFrame:

  
    >>> # Calculate maximum values (including alphabetical for Name)
    >>> print(df.max())
    Name       Zuhaire
    UT               3
    Maths           24
    Science         25
    S.St            25
    Hindi           25
    Eng             24
    dtype: object
    >>> # Restrict to numeric columns only
    >>> print(df.max(numeric_only=True))
    UT          3
    Maths      24
    Science    25
    S.St       25
    Hindi      25
    Eng        24
    dtype: int64
    >>> # Row-wise maximum calculation (axis=1)
    >>> print(df.max(axis=1).head(3))
    0    22
    1    24
    2    24
    dtype: int64
    >>> # Minimum values
    >>> print(df.min())
    Name       Ashravy
    UT               1
    Maths           12
    Science         15
    S.St            15
    Hindi           15
    Eng             13
    dtype: object
    >>> # Sum of values (concatenates strings in 'Name')
    >>> print(df['Maths'].sum())
    231
    >>> # Count of non-null values
    >>> print(df.count())
    Name       12
    UT         12
    Maths      12
    Science    12
    S.St       12
    Hindi      12
    Eng        12
    dtype: object
  
3.2.5 - 3.2.11 Mean, Median, Mode, Quartile, Variance, and Std Dev

Advanced statistical metrics applied to the dataset:

  
    >>> # Mean calculation
    >>> print(df.mean(numeric_only=True))
    UT          2.5000
    Maths      18.6000
    Science    19.8000
    S.St       20.0000
    Hindi      21.3125
    Eng        19.8000
    dtype: float64
    >>> # Median calculation
    >>> print(df.median(numeric_only=True))
    UT          2.5
    Maths      19.0
    Science    20.0
    S.St       19.5
    Hindi      21.5
    Eng        21.0
    dtype: float64
    >>> # Mode calculation for Hindi marks
    >>> print(df['Hindi'].mode())
    0    24
    dtype: int64
    >>> # Quartile calculation (25%, 50%, 75%)
    >>> print(df.quantile(q=[0.25, 0.75], numeric_only=True))
    UT  Maths  Science   S.St  Hindi    Eng
    0.25 1.0  16.50    18.00  18.75  20.75  19.75
    0.75 3.0  22.25    21.25  22.50  24.00  23.00
    >>> # Variance and Standard Deviation
    >>> print(df[['Maths', 'Science']].var())
    Maths      15.840909
    Science     7.113636
    dtype: float64
    >>> print(df[['Maths', 'Science']].std())
    Maths      3.980064
    Science    2.667140
    dtype: float64
    >>> # Comprehensive summary using describe()
    >>> print(df.describe())
  
3.3 Data Aggregations

Aggregation transforms a dataset by applying mathematical operations to produce single or multiple summarized numeric values from arrays of data.

  
    >>> # Apply a single aggregation function to the entire DataFrame
    >>> print(df.aggregate('max'))
    Name       Zuhaire
    UT               3
    Maths           24
    dtype: object
    >>> # Apply multiple aggregation functions simultaneously
    >>> print(df.aggregate(['max', 'count']))
    Name  UT  Maths  Science  S.St  Hindi  Eng
    max   Zuhaire   3     24       25    25     25   24
    count      12  12     12       12    12     12   12
    >>> # Apply specific aggregations to specific columns with row-wise summation
    >>> print(df[['Maths', 'Science']].aggregate('sum', axis=1).head(3))
    0    43
    1    41
    2    33
    dtype: int64
  
3.4 Sorting a DataFrame

Sorting arranges data elements in a specified order (ascending or descending) based on one or more columns.

Technical Profile: sort_values() Function

  • Architecture Layer / Tier: Core Pandas DataFrame manipulation method.
  • Operational Mechanics: DataFrame.sort_values(by, axis=0, ascending=True, inplace=False)
  • System Impact & Execution Outputs: Returns a new sorted DataFrame (or modifies in-place). Row indices are preserved but reordered.
  
    >>> # Sort by student Name in ascending order
    >>> print(df.sort_values(by=['Name']).head(4))
    Name  UT  Maths  Science  S.St  Hindi  Eng
    6   Ashravy   1     23       19    20     15   22
    7   Ashravy   2     24       22    24     17   21
    8   Ashravy   3     12       25    19     21   23
    9    Mishti   1     15       22    25     22   22
    >>> # Sort by English marks in descending order for Unit Test 3
    >>> dfUT3 = df[df.UT == 3]
    >>> print(dfUT3.sort_values(by=['Eng'], ascending=False))
    Name  UT  Maths  Science  S.St  Hindi  Eng
    2      Raman   3     14       19    15     24   23
    8    Ashravy   3     12       25    19     21   23
    11    Mishti   3     17       18    20     25   20
    5    Zuhaire   3     22       18    19     23   13
    >>> # Sorting by multiple columns (Science ascending, then Hindi ascending)
    >>> print(dfUT3.sort_values(by=['Science', 'Hindi']))
    Name  UT  Maths  Science  S.St  Hindi  Eng
    5    Zuhaire   3     22       18    19     23   13
    11    Mishti   3     17       18    20     25   20
    2      Raman   3     14       19    15     24   23
    8    Ashravy   3     12       25    19     21   23
  
3.5 GROUP BY Functions

The groupby() function splits the dataset into groups based on specified criteria, applies a function, and combines the results (Split-Apply-Combine strategy).

"The GROUP BY function works based on a split-apply-combine strategy: Step 1: Split the data into groups by creating a GROUP BY object. Step 2: Apply the required function. Step 3: Combine the results to form a new DataFrame."


Socio-Technical Analysis & Core Systemic Impact: Grouping allows educators and analysts to aggregate individual performance metrics (like unit test scores) into institutional insights (like student-wise averages or test-wise trends), shifting focus from raw transactional data to systemic performance evaluation.

  
    >>> # Group by Name
    >>> g1 = df.groupby('Name')
    >>> # Display first entry of each group
    >>> print(g1.first())
    UT  Maths  Science  S.St  Hindi  Eng
    Name
    Ashravy   1     23       19    20     15   22
    Mishti    1     15       22    25     22   22
    Raman     1     22       21    18     20   21
    Zuhaire   1     20       17    22     24   19
    >>> # Display size of each group
    >>> print(g1.size())
    Name
    Ashravy    3
    Mishti     3
    Raman      3
    Zuhaire    3
    dtype: int64
    >>> # Retrieve a single group
    >>> print(g1.get_group('Raman'))
    Name  UT  Maths  Science  S.St  Hindi  Eng
    0  Raman   1     22       21    18     20   21
    1  Raman   2     21       20    17     22   24
    2  Raman   3     14       19    15     24   23
    >>> # Grouping by multiple attributes (Name and UT)
    >>> g2 = df.groupby(['Name', 'UT'])
    >>> print(g2.first().head(3))
    >>> # Aggregation on groups: average marks per UT
    >>> print(df.groupby(['UT']).aggregate('mean'))
    Maths  Science   S.St  Hindi    Eng
    UT
    1   20.00    19.75  21.25  20.25  21.00
    2   21.50    19.50  21.75  22.00  20.75
    3   16.25    20.00  18.25  23.25  19.75
  
3.6 Altering the Index

Indexes are used for fast retrieval of data. Pandas allows resetting, setting, or dropping indexes to align with analytical requirements.

  
    >>> # Slicing data creates non-continuous index
    >>> dfUT1 = df[df.UT == 1]
    >>> print(dfUT1)
    Name  UT  Maths  Science  S.St  Hindi  Eng
    0     Raman   1     22       21    18     20   21
    3   Zuhaire   1     20       17    22     24   19
    6   Ashravy   1     23       19    20     15   22
    9    Mishti   1     15       22    25     22   22
    >>> # Reset index to create a continuous sequence (keeps old index as a column)
    >>> dfUT1.reset_index(inplace=True)
    >>> print(dfUT1)
    index     Name  UT  Maths  Science  S.St  Hindi  Eng
    0      0    Raman   1     22       21    18     20   21
    1      3  Zuhaire   1     20       17    22     24   19
    >>> # Drop the old index column
    >>> dfUT1.drop(columns=['index'], inplace=True)
    >>> # Set a specific column as the index
    >>> dfUT1.set_index('Name', inplace=True)
    >>> print(dfUT1)
    UT  Maths  Science  S.St  Hindi  Eng
    Name
    Raman     1     22       21    18     20   21
    Zuhaire   1     20       17    22     24   19
    >>> # Revert back to default integer index
    >>> dfUT1.reset_index(inplace=True)
  
3.7 Reshaping Data (Pivot & Pivot Table)

Reshaping changes the layout of a dataset to make it more readable and suitable for analysis. Pandas provides pivot() and pivot_table() for this purpose.

3.7.1 (A) Pivot Function

The pivot() function reshapes data based on column values. It requires unique index-column combinations; duplicate entries will raise a ValueError .

  
    >>> # Reshaping store sales data
    >>> pivot1 = df.pivot(index='Store', columns='Year', values='Total_sales(Rs)')
    >>> print(pivot1)
    Year      2016      2017     2018
    Store
    S1     12000.0   20000.0  30000.0
    S2         NaN   10000.0  11000.0
    S3    420000.0  450000.0  89000.0
    S4    330000.0       NaN      NaN
  
3.7.2 (B) Pivot Table Function

The pivot_table() function aggregates values from rows with duplicate entries for specified columns. The default aggregation function is mean .

  
    >>> # Pivot table with duplicate entries, aggregating by mean
    >>> df1 = df.pivot_table(index=['Item', 'Color'])
    >>> print(df1)
    Price(Rs)  Units_in_stock
    Item   Color
    Pen    Blue            35.0            34.5
    Red             17.5            30.0
    Pencil Black            6.0            40.5
    >>> # Using multiple aggregate functions (sum, max, mean)
    >>> pivot_table1 = df.pivot_table(index='Item', columns='Color', values='Units_in_stock', aggfunc=[sum, max, 'mean'])
  
3.8 Handling Missing Values

Real-world datasets often contain missing values, represented as NaN (Not a Number) in Pandas. These must be identified and resolved to prevent analytical errors.

"Missing values can be handled by either dropping the entire row having missing value or replacing it with an appropriate value."


Socio-Technical Analysis & Core Systemic Impact: Dropping rows with missing values reduces dataset size and can introduce selection bias if the missingness is non-random. Conversely, estimating/imputing values (e.g., using zero, mean, or forward-fill) preserves sample size but introduces artificial variance, potentially skewing downstream statistical models.

3.8.1 Checking for Missing Values

Functions used to detect missing values:

  • isnull() : Returns a boolean DataFrame indicating missing values ( True ).
  • any() : Checks if any value in a column is missing.
  • sum() : Counts the number of missing values per column.
  
    >>> # Check for nulls in the entire DataFrame
    >>> print(df.isnull().any())
    Name       False
    UT         False
    Maths       True
    Science     True
    S.St       False
    Hindi      False
    Eng         True
    dtype: bool
    >>> # Count missing values per column
    >>> print(df.isnull().sum())
    Maths      1
    Science    1
    Eng        1
    dtype: int64
  
3.8.2 Dropping Missing Values

The dropna() function removes rows or columns containing missing values.

  
    >>> # Drop any row containing at least one NaN value
    >>> df_clean = df.dropna()
    >>> # Drop in-place for a specific subset
    >>> dfRaman.dropna(inplace=True, how='any')
  
3.8.3 Estimating & Filling Missing Values

The fillna() function replaces missing values with specified constants or calculated estimates.

  • fillna(0) : Replaces all NaN values with 0.
  • fillna(method='pad') / ffill : Replaces missing values with the preceding row's value.
  • fillna(method='bfill') : Replaces missing values with the succeeding row's value.
  
    >>> # Replace NaN with 0 in Science marks
    >>> df['Science'].fillna(0, inplace=True)
    >>> # Forward fill (pad) English marks
    >>> df['Eng'].fillna(method='pad', inplace=True)
  
3.9 Import and Export of Data between Pandas and MySQL

Integrating Pandas with relational databases like MySQL allows seamless data pipelines for loading raw transactional records and saving processed analytical results.

Technical Profile: Database Connectors (pymysql & sqlalchemy)

  • Architecture Layer / Tier: Database Driver Layer ( pymysql ) and Object-Relational Mapping/Engine Layer ( sqlalchemy ).
  • Operational Mechanics: Connection string format: mysql+pymysql://username:password@host:port/database_name . Established via create_engine() .
  • System Impact & Execution Outputs: Enables high-throughput read/write operations directly between Pandas DataFrames and MySQL tables.
3.9.1 Importing Data from MySQL to Pandas

Three primary functions are available for reading database records:

Function Exact Syntactic Execution Signature Supported Parameters & Arguments Algorithmic Purpose & Output Representation
read_sql_query() pandas.read_sql_query(sql, con) sql (SQL query string), con (Engine connection object) Executes a SQL query and returns the result set as a DataFrame.
read_sql_table() pandas.read_sql_table(table_name, con) table_name (string), con (Engine connection object) Reads an entire database table directly into a DataFrame.
read_sql() pandas.read_sql(sql, con) sql (Query or Table name), con (Engine connection object) Generic wrapper that routes to either query or table reader based on input.
  
    >>> import pandas as pd
    >>> import sqlalchemy
    >>> # Create database engine connection
    >>> engine = sqlalchemy.create_engine('mysql+pymysql://root:smsmb@localhost:3306/CARSHOWROOM')
    >>> # Import table data using SQL query
    >>> df_inventory = pd.read_sql_query('SELECT * FROM INVENTORY', engine)
    >>> print(df_inventory.head(2))
  
3.9.2 Exporting Data from Pandas to MySQL

The to_sql() function writes DataFrame records to a MySQL database table.

  
    >>> # Export DataFrame to MySQL table
    >>> df.to_sql('showroom_info', engine, if_exists='replace', index=False)
  

Parameters for if_exists :

  • "fail" : Default. Raises a ValueError if the table already exists.
  • "replace" : Drops the existing table and recreates it with the new DataFrame structure and values.
  • "append" : Inserts new rows into the existing table. Requires matching column schemas.