Overview
Introduction: This chapter, "Data Handling using Pandas – II," continues the study of the pandas library introduced earlier and focuses on practical and advanced techniques for preparing, transforming and summarizing tabular data. It builds on basics (Series, DataFrame, indexing and selection) and introduces tools you need to clean real-world datasets and derive meaningful summaries. Importance: Real data is often messy, incomplete and stored in many formats. Mastering the techniques in this chapter is essential for extracting reliable information, performing analysis, and preparing data for visualization or further modeling. These skills are widely used in science, commerce and computer applications. Key themes (brief): - Handling missing and inconsistent data (drop, fill, interpolation) - Combining datasets (concat, join, merge) - Transforming data (apply, map, lambda, replace) - Reshaping and summarizing (groupby, aggregate, pivot_table, melt, crosstab) - Working with dates and times (datetime conversion, resampling, time-based indexing) - Input/output with common file formats (CSV, Excel, JSON) What the student will learn: By the end of this chapter students will be able to…
Learning Objectives
- Define Pandas Series and DataFrame and differentiate between their structure and typical uses
- Explain the difference between label-based (loc) and integer-based (iloc) indexing and selection
- Apply boolean indexing and conditional filters to select rows that meet given criteria
- Use read_csv and to_csv to import data from and export data to CSV files, and interpret common parameters
- Demonstrate sorting and reindexing using sort_values and sort_index and predict the resulting order
- Perform aggregation with groupby and aggregate functions (sum, mean, count, agg) to summarize grouped data
- Create and interpret pivot tables using pivot_table to present multi-dimensional summaries
- Handle missing data by identifying NaN values and using dropna, fillna and interpolation appropriately
Topics in this chapter
18 topics · tap a topic title to jump straight to it.
Introduction and Recap
Introduction and Recap
Key Point: Import data: pd.read_csv('file.csv')
What this topic is about
Pandas is a Python library for fast, flexible and expressive data manipulation. "Introduction and Recap" reviews core concepts and common operations you need before moving to advanced data handling in Class 11 Informatics Practices: creating Series and DataFrame objects, importing/exporting data, inspecting and summarizing data, indexing and selection, handling missing values, basic transformations, grouping/aggregation, merging/concatenating, reshaping and simple plotting.
Key building blocks
- Series — one-dimensional labeled array.
- DataFrame — two-dimensional labeled table (rows and columns).
- Import/export — read and write CSV/Excel/JSON (e.g. pd.read_csv, df.to_csv).
- Inspection — df.head(), df.tail(), df.info(), df.describe(), df.shape.
- Indexing & selection — df['col'], df.loc[row_label, col_label], df.iloc[row_idx, col_idx], boolean masks.
- Missing values — detect with df.isnull(), count with .sum(), remove with df.dropna(), replace with df.fillna().
- Type conversion — df.dtypes, df.astype('int'), pd.to_datetime() for dates.
- Aggregation & grouping — df.groupby('col').agg({'val':'mean'}), useful for summaries by category.
- Merging & concatenation — pd.concat([...]), pd.merge(df1, df2, on='key', how='inner').
- Reshaping — df.pivot_table(...), pd.melt(...).
- Apply/map — df['col'].apply(func), df.apply(func, axis=1) for elementwise or rowwise transforms.
- Plotting — basic plots via df.plot() or seaborn for quick visual checks.
Why recap matters
Before applying advanced functions (multi-indexing, time-series resampling, advanced joins or groupby complex aggregations) you must be comfortable with basic data ingestion, cleaning, indexing and simple summaries. Most data-analysis workflows are iterative: load → inspect → clean → transform → analyze → visualize.
Small code examples (recap)
# import and read
import pandas as pd
df = pd.read_csv('data.csv')
# inspect
df.head()
df.info()
df.describe()
# select rows where score > 50
passed = df[df['score'] > 50]
# handle missing
df['age'].fillna(df['age'].mean(), inplace=True)
# group and aggregate
summary = df.groupby('class')['score'].mean()
# merge datasets
merged = pd.merge(df1, df2, on='student_id', how='left')
# simple plot
df['score'].hist()
Study tips
Practice on small real datasets (student marks, sales records). After each operation inspect the result (shape, head, unique values). Use chaining where it improves readability but break complicated steps into named intermediate variables while learning.
- Student marks analysis: Read a CSV of students with columns (student_id, name, class, maths, science). Use df.describe() to see distribution, df['maths'].mean() for class average, df.groupby('class')['maths'].mean() to compare classes, fill missing marks with column mean.
- Sales dataset: Analyze daily sales (date, store, product, quantity, revenue). Convert date with pd.to_datetime, set it as index, resample monthly (df.resample('M').sum()) to see monthly totals, and merge with product master data to add categories.
- Hospital records: Clean patient records with missing age or diagnosis. Use df.dropna(subset=['patient_id']) to remove incomplete rows, df['age'].fillna(df['age'].median()) to impute ages, and groupby diagnosis to find average length of stay.
- Survey responses: Use value_counts() to get frequency of categorical answers, pivot_table to cross-tabulate responses by demographic groups, and melt to convert wide-format survey data to long format for analysis.
- Stock prices: Read time series, convert date column, set index, calculate daily returns (df['close'].pct_change()), compute rolling mean (df['close'].rolling(20).mean()) to study trends.
- \[Import data: pd.read_csv('file.csv')\]
- \[Export data: df.to_csv('out.csv'\]\[index=False)\]
- \[Inspect: df.head(n)\]\[df.tail(n)\]\[df.info()\]\[df.describe()\]\[df.shape\]
- \[Select columns/rows: df['col']\]\[df[['c1','c2']]\]\[df.loc[row_label\]\[col_label]\]\[df.iloc[row_index\]\[col_index]\]
- \[Boolean selection: df[df['col'] > value] or df[(df['a'] > 10) & (df['b'] == 'X')]\]
- \[Missing values: df.isnull().sum()\]\[df.dropna(subset=['col'])\]\[df.fillna(value\]\[inplace=True)\]
Reading and Writing Data
Reading and Writing Data
Key Point: pd.read_csv(filepath_or_buffer, sep=',', header='infer', index_col=None, usecols=None, dtype=None, parse_dates=None, na_values=None, skiprows=None, nrows=None, chunksize=None, encoding=None, compression=None)
What it is: Reading and writing data in Pandas means loading data from external sources (CSV, Excel, JSON, SQL, HTML, Parquet, URLs, compressed files) into DataFrame objects and saving DataFrames back to those formats. Pandas provides pd.read_... functions to read and DataFrame.to_... methods to write.
Key concepts:
- Readers: pd.read_csv, pd.read_excel, pd.read_json, pd.read_html, pd.read_sql, pd.read_parquet, etc.
- Writers: df.to_csv, df.to_excel, df.to_json, df.to_sql, df.to_parquet, etc.
- Important parameters: filepath_or_buffer, sep, header, index_col, usecols, dtype, parse_dates, na_values, skiprows, nrows, chunksize, encoding, compression, sheet_name, if_exists.
- Large files / streaming: Use chunksize to iterate over file pieces; use usecols and explicit dtype to reduce memory; consider reading compressed or parquet for space and speed.
- Data cleaning on read: Handle missing values (na_values), parse dates (parse_dates), convert dtypes, skip bad rows (error_bad_lines / on_bad_lines), and specify encodings.
Best practices:
- Pass explicit dtypes for large datasets to reduce memory and parsing time.
- Use parse_dates for time columns so you get datetime dtype.
- Use index_col when a column represents a natural index (e.g., id or timestamp).
- For multiple sheets in Excel use sheet_name=None to read all sheets into a dict of DataFrames.
- When writing, choose index=False unless the index is meaningful; pick appropriate compression to save disk space.
How it works (short workflow):
- Choose the reader matching the source format.
- Tune parameters (columns to read, dtypes, date parsing, encoding).
- Optionally read in chunks and process each chunk to avoid high memory use.
- After transformations, write out using the writer with desired options (index, header, compression).
Simple code pattern for chunked processing:
for chunk in pd.read_csv('bigfile.csv', chunksize=10000, usecols=['A','B'], parse_dates=['date']):
# process chunk (clean, aggregate, write or append to DB/file)
- Basic read CSV: import pandas as pd df = pd.read_csv('data.csv') # default comma separator
- Read CSV with selected columns, dtypes & parse dates: df = pd.read_csv('sales.csv', usecols=['date','region','amount'], parse_dates=['date'], dtype={'region': 'category', 'amount': 'float'})
- Read in chunks (process large files): chunks = pd.read_csv('big.csv', chunksize=50000) for chunk in chunks: chunk = chunk.dropna(subset=['id']) # aggregate or write processed chunk to disk/database
- Read Excel with multiple sheets: xls = pd.read_excel('workbook.xlsx', sheet_name=None) # returns dict: {'Sheet1': df1, 'Sheet2': df2}
- Read JSON from file or URL: df = pd.read_json('data.json', orient='records') # or from a web API: df = pd.read_json('https://api.example.com/data')
- Write DataFrame to CSV / Excel / SQL: df.to_csv('out.csv', index=False) df.to_excel('out.xlsx', sheet_name='Report', index=False) # write to sqlite import sqlite3 conn = sqlite3.connect('mydb.sqlite') df.to_sql('table_name', conn, if_exists='replace', index=False)
- \[pd.read_csv(filepath_or_buffer\]\[sep=','\]\[header='infer'\]\[index_col=None\]\[usecols=None\]\[dtype=None\]\[parse_dates=None\]\[na_values=None\]\[skiprows=None\]\[nrows=None\]\[chunksize=None\]\[encoding=None\]\[compression=None)\]
- \[pd.read_excel(io\]\[sheet_name=0\]\[header=0\]\[names=None\]\[index_col=None\]\[usecols=None\]\[dtype=None\]\[parse_dates=None\]\[engine=None)\]
- \[pd.read_json(path_or_buf\]\[orient='records'|'split'|'index'|'columns'|'values'\]\[lines=False)\]
- \[pd.read_sql(sql\]\[con\]\[index_col=None\]\[coerce_float=True\]\[parse_dates=None\]\[params=None)\]
- \[df.to_csv(path_or_buf\]\[sep=','\]\[index=True|False\]\[header=True|False\]\[mode='w'|'a'\]\[encoding=None\]\[compression=None\]\[date_format=None)\]
- \[df.to_excel(excel_writer\]\[sheet_name='Sheet1'\]\[index=True|False\]\[engine=None)\]
Selecting, Slicing and Indexing
Selecting, Slicing and Indexing
Key Point: Select column: df['col']
Overview: Selecting, slicing and indexing in pandas are the basic operations used to access subsets of data from a DataFrame or Series. Indexing identifies rows and columns (labels or integer positions). Selecting extracts specific columns or rows. Slicing returns a range of rows/columns.
Indexing types:
Label-based(use.loc): select using row/column labels. Note: label-based.locslice endpoints are inclusive.Position-based(use.iloc): select using integer positions..ilocslicing follows Python convention: end is exclusive.Scalar fast-access:.at(label) and.iat(integer position) for single value access, faster than.loc/.ilocfor single elements.
Selecting columns and rows:
- Select a column:
df['ColumnName']ordf.ColumnName(first is preferred). - Select multiple columns:
df[['A','B']]. - Select rows by label:
df.loc['row_label']or many rowsdf.loc['r1':'r5']. - Select rows by position:
df.iloc[0]ordf.iloc[0:5].
Slicing with both axes:
Use df.loc[row_labels, col_labels] or df.iloc[row_positions, col_positions]. Examples: df.loc[2:6, 'Name':'Marks'] (inclusive labels) or df.iloc[0:4, 1:3] (positions 0–3 rows and columns 1–2).
Boolean (conditional) indexing: Filter rows by condition(s): df[df['Marks'] >= 50]. Combine conditions with & (and), | (or) and wrap each condition in parentheses: df[(df['Age'] >= 18) & (df['City'] == 'Delhi')].
Advanced points & best practices:
- Avoid chained indexing like
df[df['A'] > 0]['B'] = valuebecause it can produce SettingWithCopyWarning. Uselocfor assignment:df.loc[df['A'] > 0, 'B'] = value. - Use
set_index('col')to make a column the DataFrame index (useful for time-series or unique IDs). Usereset_index()to revert. - Use
head(), tail(), sample()for quick checks.
Why it matters (real-life context): Efficient selecting and slicing lets you extract the exact subset of records you need for analysis, plotting or reporting — e.g., retrieving last month’s sales, selecting students who failed, or sampling rows for a preview.
- Select a single column: df['Name'] # returns a Series of student names
- Select multiple columns: df[['Name','Marks']] # returns a DataFrame with Name and Marks
- Label-based slice (inclusive): df.loc['2023-01-01':'2023-01-31'] # all rows for January (time-series index)
- Position-based slice (exclusive end): df.iloc[0:10] # first 10 rows (positions 0–9)
- Select rows by condition: df[df['Marks'] >= 75] # students scoring 75 or more
- Select rows and columns: df.loc[df['City']=='Mumbai', ['Name','Phone']] # contacts of Mumbai students
- \[Select column: df['col']\]
- \[Select multiple columns: df[['col1','col2']]\]
- \[Label-based selection: df.loc[row_label_or_slice\]\[col_label_or_slice]\]
- \[Position-based selection: df.iloc[row_pos_or_slice\]\[col_pos_or_slice]\]
- \[Single value access (label): df.at[row_label\]\[col_label]\]
- \[Single value access (pos): df.iat[row_pos\]\[col_pos]\]
Handling Missing Data
Handling Missing Data
Key Point: Percent missing in a column = (count_missing / total_rows) * 100
What is missing data? Missing data (or null values) appear when entries are absent in a dataset. Pandas represents them as NaN (Not a Number) or None. Handling missing data is essential because many operations and machine learning models require complete data and because improper handling can bias results.
Types of missingness:
- MCAR (Missing Completely At Random): missingness unrelated to any data values.
- MAR (Missing At Random): missingness related to observed data (e.g., older respondents skip an item).
- MNAR (Missing Not At Random): missingness related to the missing value itself (e.g., people with very high income not reporting it).
Common steps in Pandas:
- Detect: find where data are missing using
df.isnull(),df.isnull().sum(), ordf.info(). - Assess: compute percentage missing per column and decide whether to drop or impute (based on domain knowledge and amount missing).
- Handle: choose a strategy — remove rows/columns, impute values, or use models that can handle missingness.
Pandas techniques:
dropna(axis=0|1, how='any'|'all', thresh=... , subset=[...])— remove rows or columns with missing values. Usethreshto require a minimum number of non-null values.fillna(value=..., method='ffill'|'bfill', inplace=False)— replace nulls with a value or forward/backward fill.interpolate(method='linear'|'time')— numeric interpolation (useful for time-series).- Model-based imputers (scikit-learn):
KNNImputer,IterativeImputerfor more advanced filling. - Create a missing indicator column (e.g.,
df['age_missing'] = df['age'].isnull().astype(int)) to preserve information about missingness.
Trade-offs and guidance:
- If a column has a very high missing rate (e.g., >50–80%) consider dropping it unless it's critical.
- Mean/median imputation is simple but reduces variance and can bias relationships. Use median for skewed distributions and mean for symmetric numeric data.
- Forward/backward fill suits time series where previous/next value is a reasonable estimate.
- Model-based imputation or multiple imputation is better when preserving correlations between variables is important.
Short pandas example:
import pandas as pd # detect missing_counts = df.isnull().sum() # drop rows with any missing value df_drop = df.dropna() # fill numeric column with median df['marks'] = df['marks'].fillna(df['marks'].median()) # forward fill time series df['temperature'] = df['temperature'].ffill() # interpolate numeric values df['sensor'] = df['sensor'].interpolate(method='linear') # flag missing df['marks_missing'] = df['marks'].isnull().astype(int)
Always compare distributions before and after imputation and consider domain knowledge when choosing a method.
- School survey: Students skip the question about family income. If missingness is small, impute income using median by grade; if many skip, create a 'missing_income' flag and treat separately.
- E‑commerce orders: Delivery date missing for a few orders. Use business rules (e.g., estimated delivery = order_date + typical_delivery_days) or remove rows if not needed for analysis.
- Weather station time series: Hourly temperature has occasional gaps. Use linear interpolation or forward-fill for short gaps; for long gaps prefer model-based imputation or discard those intervals.
- Medical records: Lab test values missing not at random (sicker patients tested more). Do not use simple mean imputation — consider multiple imputation or include a missingness indicator in predictive models.
- \[Percent missing in a column = (count_missing / total_rows) * 100\]
- \[Mean (for mean imputation) = (sum of observed values) / (number of observed values)\]
- \[Threshold rule for dropna: keep row if non-null count >= thresh (e.g.\]\[thresh = required_non_nulls)\]
- \[Imputed value (forward fill): value_t = value_{t-1} if value_t is missing\]
- \[Linear interpolation between known points (t0,x0) and (t1,x1): x_t = x0 + (x1 - x0) * ((t - t0)/(t1 - t0))\]
Data Cleaning and Preparation
Data Cleaning and Preparation
Key Point: Mean (average): μ = (1/n) * Σ x_i
What is Data Cleaning and Preparation?
Data cleaning and preparation is the process of detecting, correcting, or removing errors and inconsistencies in data so it is accurate, complete, and ready for analysis or machine learning. In Pandas (Python), this typically means using functions to handle missing values, convert types, standardize formats, remove duplicates, detect outliers, and encode categorical data.
Why it matters
- Bad data gives misleading statistics and poor model performance.
- Preparation ensures reproducibility and correct downstream analysis.
Common steps in Pandas
- Inspect data: use
df.head(),df.info(),df.describe(),df.isna().sum()to find missing values, wrong types and summary stats. - Handle missing values:
df.dropna(),df.fillna(), forward/backward fill (ffill/bfill), or impute with mean/median/mode depending on context. - Remove duplicates:
df.drop_duplicates()and find duplicates withdf.duplicated(). - Fix data types: convert with
df['col'].astype(), parse dates withpd.to_datetime(). - Clean text and categories: standardize strings (
str.lower(),str.strip()), replace inconsistent labels (df.replace()), convert to categoricalpd.Categoricalor usepd.get_dummies()for one-hot encoding. - Detect and treat outliers: visualize with boxplots, compute IQR or z-scores and decide to cap, remove, or transform outliers.
- Scale/Normalize: apply min-max normalization or standardization before many machine learning algorithms.
- Validate and save: check ranges, constraints and export cleaned data with
df.to_csv()ordf.to_pickle().
Practical tips
- Always make a copy before destructive operations:
df_clean = df.copy(). - Decide handling strategy by column type: numeric, categorical, text, datetime.
- Keep a log of cleaning steps (or a notebook) so results are reproducible.
- Use visualization to guide decisions: histograms, boxplots and missingness heatmaps reveal issues quickly.
Example Pandas methods (quick reference)
- Missing values:
df.isna(),df.dropna(),df.fillna(value) - Duplicates:
df.duplicated(),df.drop_duplicates() - Type conversions:
df['col'].astype('int'),pd.to_datetime(df['date']) - String ops:
df['name'].str.lower(),df['col'].str.replace() - Categorical & encoding:
df['cat'] = df['cat'].astype('category'),pd.get_dummies(df, columns=['cat']) - Outliers: compute quartiles with
df['x'].quantile(), z-scores with(x - x.mean())/x.std()
When to remove vs impute
- Remove rows if missingness is rare and random and dropping won’t bias results.
- Impute (mean/median/mode or model-based) when dropping would lose important data or when patterns exist.
Final checks
- Check summary statistics (
df.describe()) make sense after cleaning. - Confirm data types, unique values for categorical fields, and no unintended NaNs remain.
- Survey data: Several respondents omitted age or answered 'N/A' for income. Use df['age'].fillna(df['age'].median()) for numeric imputation and df['income'].replace('N/A', np.nan).fillna('Unknown') for categorical handling.
- E-commerce orders: Duplicate order rows created by repeated submissions. Remove duplicates with df.drop_duplicates(subset=['order_id']). Standardize product names using df['product'] = df['product'].str.lower().str.strip().replace({'t-shirt':'tee','t shirt':'tee'}).
- Time-series sensors: Timestamps in multiple formats and occasional missing readings. Parse dates with pd.to_datetime(df['time'], errors='coerce'), sort by time, and fill small gaps with df['value'].interpolate(method='time').
- School marks: Scores range differently across subjects. Detect outliers with IQR and scale scores using min-max normalization before comparison or clustering.
- Categorical data for ML: Convert city names to one-hot vectors via pd.get_dummies(df['city']) or map labels to integers with df['city'].astype('category').cat.codes.
- \[Mean (average): μ = (1/n) * Σ x_i\]
- \[Median: middle value when observations are sorted (or average of two middle values if n is even)\]
- \[Mode: most frequent value in a dataset (useful for categorical imputation)\]
- \[Interquartile Range (IQR): IQR = Q3 - Q1 (outliers often defined as < Q1 - 1.5*IQR or > Q3 + 1.5*IQR)\]
- \[Z-score (standard score): z = (x - μ) / σ (values with |z| > 3 are potential outliers)\]
- \[Min-max normalization: x' = (x - min) / (max - min) (scales values to range [0,1])\]
Grouping and Aggregation
Grouping and Aggregation
Key Point: sum: df.groupby('col')['val'].sum() — total of val in each group
Overview
Grouping and aggregation in pandas is the process of splitting data into groups (based on values of one or more columns), applying summary computations to each group, and combining the results. This follows the "split-apply-combine" pattern:
- Split: separate rows into groups using df.groupby(...)
- Apply: compute an aggregation or transformation for each group (sum, mean, count, etc.)
- Combine: return the aggregated results as a new DataFrame or Series
Key object: df.groupby('col') returns a GroupBy object. You then call aggregation methods like sum(), mean(), agg(), or use transform()/filter()/apply().
Common operations
- Single-column grouping: df.groupby('Class')['Marks'].mean() — average marks by class.
- Multi-column grouping: df.groupby(['Store','Product'])['Sales'].sum() — sales summed per store-product pair.
- Multiple aggregations: df.groupby('Store')['Sales'].agg(['sum','mean','count']) — several statistics at once.
- Different aggregations per column: df.groupby('Class').agg({'Marks':'mean','Age':'max'})
- as_index: df.groupby('col', as_index=False).sum() keeps group labels as columns instead of index.
- reset_index: after grouping results often have group labels as index; use .reset_index() to convert them back to columns.
Transform vs Aggregate vs Filter
agg/aggregate: returns reduced output per group (one row per group or per-group statistics).transform: returns an object indexed like the original; used to produce a column of group-wise values (e.g., group mean for each row). Example: df['dev'] = df['Marks'] - df.groupby('Class')['Marks'].transform('mean').filter: keeps or drops entire groups based on a group-wise test. Example: keep groups with size > 10: df.groupby('Dept').filter(lambda g: len(g) > 10).
Pivot tables and cross-tabulations
pd.pivot_table(df, index='Date', columns='Product', values='Sales', aggfunc='sum')aggregates values into a matrix indexed by one column and with columns from another.pd.crosstab(df['A'], df['B'])counts occurrences (useful for frequency tables).value_counts()gives counts for a single column (like a quick group-by count).
Handling missing values and sorting
- Many aggregation functions ignore NaN by default (e.g., mean, sum). Use
dropnaorfillnaon original data as needed. - Sort grouped results with
.sort_values(). For groupby order, usesort=Falseingroupbyif you want to preserve original order.
Simple code examples
import pandas as pd
# example DataFrame
df = pd.DataFrame({'Student':['A','B','C','D','E'],
'Class':['X','X','Y','Y','X'],
'Marks':[78,85,90,62,70]})
# mean marks by class
df.groupby('Class')['Marks'].mean()
# multiple aggregations
df.groupby('Class')['Marks'].agg(['mean','max','min','count'])
# add column with deviation from class mean
df['Deviation'] = df['Marks'] - df.groupby('Class')['Marks'].transform('mean')
Tips for students
- Remember the difference between operations that reduce groups (agg) and those that return row-aligned outputs (transform).
- Use
as_index=Falseorreset_index()to get group keys back as columns for easier viewing or exporting. - Practice common aggregations (sum, mean, count, min, max, median, std) on small datasets to build intuition.
- Student marks by class: Given a DataFrame of students with columns ['Student','Class','Subject','Marks'], compute average marks per class and per subject. Code: df.groupby(['Class','Subject'])['Marks'].mean().reset_index(). This gives average marks for each (Class, Subject) pair.
- Retail sales summary: For a sales DataFrame with ['Date','Store','Product','Quantity','Sales'], get total sales per store and the top-selling product per store. Code: totals = df.groupby('Store')['Sales'].sum(); top = df.groupby('Store').apply(lambda g: g.groupby('Product')['Sales'].sum().nlargest(1)).
- Hospital patients: For records with ['PatientID','Department','VisitDate'], count monthly visits per department. Convert VisitDate to datetime, add df['Month']=df['VisitDate'].dt.to_period('M'), then df.groupby(['Department','Month']).size().unstack(fill_value=0) to get a table of counts.
- Sensor data: With time-series sensor readings ['Timestamp','SensorID','Value'], compute daily average and standard deviation per sensor. Code: df['Date']=df['Timestamp'].dt.date; df.groupby(['SensorID','Date'])['Value'].agg(['mean','std']).
- \[sum: df.groupby('col')['val'].sum() — total of val in each group\]
- \[mean (average): df.groupby('col')['val'].mean() — average of val per group\]
- \[count: df.groupby('col')['val'].count() — number of non-NaN entries per group\]
- \[size: df.groupby('col').size() — number of rows per group (including NaN values in columns)\]
- \[min / max: df.groupby('col')['val'].min() / .max() — smallest / largest value in each group\]
- \[median: df.groupby('col')['val'].median() — median per group\]
Pivot Tables and Crosstab
Pivot Tables and Crosstab
Key Point: pivot_table syntax: df.pivot_table(index=[rows], columns=[cols], values='value_column', aggfunc='sum'|'mean'|'count'|func, margins=True|False, fill_value=0)
What are Pivot Tables and Crosstab?
Pivot tables and crosstab are tools to summarize and reshape tabular data by aggregating values along two or more categorical dimensions. In pandas, pivot_table and pd.crosstab produce compact summary tables that help answer questions like "total sales by region and product" or "count of students by class and grade".
pivot_table (pandas.DataFrame.pivot_table)
- Purpose: Aggregate numeric data using one or more aggregation functions (sum, mean, count etc.) across specified index and column categories.
- Key parameters:
index(rows),columns(columns),values(numeric column(s) to aggregate),aggfunc(e.g., 'sum', 'mean', 'count', numpy functions),margins(include totals),fill_value(replace NaN),dropna. - When to use: When you want aggregated numeric summaries (sum, average, count, etc.) laid out in a matrix form.
pd.crosstab
- Purpose: Build a contingency table (frequency table) that counts occurrences of combinations of categorical variables. It can also aggregate values using
valuesandaggfunc. - Key parameters: first and second arrays/series (rows and columns),
normalize(proportions by 'index', 'columns', or 'all'),margins,valuesandaggfuncfor aggregated metrics rather than plain counts. - When to use: When you need counts or proportions of categorical combinations (e.g., survey responses by gender and age group).
Differences and relations
pivotvspivot_table:pivotrequires a unique index/column combination (no aggregation).pivot_tableallows aggregation when combinations are not unique.pivot_tableis similar togroupby(...).unstack(): groupby produces grouped aggregates and unstack converts one grouping level into columns—same result as many pivot tables.crosstabis a convenient wrapper for frequency tables; you can extend it withvaluesandaggfuncto compute sums or means instead of counts.
Handling missing values & totals
- Use
fill_valueto replace missing cells (NaN) after aggregation. - Use
margins=Trueto get row/column totals (added as 'All'). - Use
dropna=Falseif you want categories with all-NaN columns preserved.
Multi-index and multiple aggregations
- You can pass lists to
indexandcolumnsfor multi-level pivot tables. - Pass a list of functions to
aggfuncor a dict mapping columns to aggfuncs to compute multiple summaries (e.g., sum and mean).
Small code templates (pandas)
import pandas as pd
import numpy as np
# pivot_table template
pd.pivot_table(df, index=['row_cat'], columns=['col_cat'], values='num_col', aggfunc='sum', margins=True, fill_value=0)
# crosstab template (counts)
pd.crosstab(df['cat1'], df['cat2'], margins=True)
# crosstab with aggregation
pd.crosstab(df['cat1'], df['cat2'], values=df['num_col'], aggfunc=np.mean, margins=True)
Practical tips for students
- Start with a subset of columns to avoid huge pivot tables.
- Sort or filter the dataframe before pivoting if you want specific order or categories.
- Use
reset_index()on a pivot result to convert MultiIndex rows into columns for easier plotting or export.
- Sales summary: Given a sales table with columns ['Date','Region','Product','Sales','Quantity'], create a pivot table of total sales by Region (rows) and Product (columns): pd.pivot_table(df, index=['Region'], columns=['Product'], values='Sales', aggfunc='sum', margins=True, fill_value=0)
- Students' marks: For a marks table with ['Student','Class','Subject','Marks'], find average marks by Class and Subject: pd.pivot_table(df, index=['Class'], columns=['Subject'], values='Marks', aggfunc='mean', margins=True)
- Survey responses: For a survey with ['RespondentID','Gender','AgeGroup','Satisfaction'], create a contingency table of counts by Gender and AgeGroup: pd.crosstab(df['Gender'], df['AgeGroup'], margins=True) Or proportions by row: pd.crosstab(df['Gender'], df['AgeGroup'], normalize='index')
- Product demand: To see number of orders (count) by Region and Month from an orders table: pd.crosstab(df['Region'], df['Month'], values=df['OrderID'], aggfunc='count', margins=True)
- \[pivot_table syntax: df.pivot_table(index=[rows]\]\[columns=[cols]\]\[values='value_column'\]\[aggfunc='sum'|'mean'|'count'|func\]\[margins=True|False\]\[fill_value=0)\]
- \[crosstab syntax: pd.crosstab(index_series\]\[columns_series\]\[values=optional_series\]\[aggfunc='count'|func\]\[normalize=None|'index'|'columns'|'all'\]\[margins=True)\]
- \[Equivalent groupby-unstack: df.groupby(['row_cat','col_cat'])['value_col'].agg('sum').unstack(fill_value=0)\]
- \[Multiple aggregations: pd.pivot_table(df\]\[index=['A']\]\[columns=['B']\]\[values=['X','Y']\]\[aggfunc={'X':'sum','Y':'mean'})\]
- \[Normalize crosstab to proportions: pd.crosstab(a\]\[b\]\[normalize='index') # row-wise proportions\]
Reshaping: Melt, Stack, Unstack
Reshaping: Melt, Stack, Unstack
Key Point: pd.melt(df, id_vars=[id_cols], value_vars=[value_cols], var_name='variable', value_name='value')
Overview
Reshaping transforms a DataFrame between wide and long formats. Pandas provides melt, stack and unstack to perform these transformations. Understanding them is essential for cleaning data and preparing it for analysis or visualization.
1. pd.melt (wide -> long)
melt collapses multiple columns into two columns: one for variable names and one for values. Use it when you want to convert a wide table (many value-columns) into a tidy long table where each row is one observation.
# pattern
pd.melt(df, id_vars=[...], value_vars=[...], var_name='variable', value_name='value')
Parameters:
id_vars: columns to keep as identifier columns (not melted)value_vars: columns to melt (if omitted, all non-id_vars are used)var_nameandvalue_name: names for the new columns
Example explanation (melt)
Wide table: student exam scores by term (columns Term1, Term2). Use melt to produce one row per student-term-score.
df =
Student Term1 Term2
0 A 80 85
1 B 75 82
pd.melt(df, id_vars=['Student'], var_name='Term', value_name='Score')
# Result:
# Student Term Score
# A Term1 80
# A Term2 85
# B Term1 75
# B Term2 82
2. stack and unstack (index <-> column level pivoting)
stack and unstack operate on MultiIndex objects (index or columns). They pivot a level of the columns into the row index (stack) or a level of the row index into columns (unstack).
df.stack(level=-1): moves the specified column level to the innermost row index, returning a Series (or DataFrame if multiple levels stacked).df.unstack(level=-1): pivots the specified index level into columns, turning a Series with MultiIndex into a DataFrame.
Example explanation (stack/unstack)
# Suppose df has MultiIndex columns: ('Sales','Jan'), ('Sales','Feb'), ('Cost','Jan'), ('Cost','Feb')
# stack the outer column level -> inner index level
stacked = df.stack(level=0)
# unstack the last index level to columns
unstacked = stacked.unstack(level='Month')
Common workflow: set_index to create a MultiIndex (e.g., by ['Region','Product']), then unstack a level (e.g., 'Product') to get products as columns and regions as rows, or stack to revert to long form.
Key differences and when to use
- Use
meltto convert wide column-based values into long rows when working with simple column names. - Use
stack/unstackwhen your DataFrame uses MultiIndex on rows or columns and you want to shift an index level between rows and columns. meltis column-oriented;stackis index/column-level oriented and preserves MultiIndex structure.
Practical tips
- After
melt, you may want todropna()or convert types (e.g., to numeric) withpd.to_numeric. - Use
reset_index()orset_index()to move columns into or out of the index when preparing forstack/unstack. unstackcan create NaNs if some combinations are missing; usefill_valuewithpivot_tableif needed.
Inverse operations
melt is often inverted by pivot or pivot_table. Similarly, stack is inverted by unstack (and vice versa) when levels line up.
- Student scores across terms (wide -> long): Code: pd.melt(df, id_vars=['Student'], value_vars=['Term1','Term2'], var_name='Term', value_name='Score') Use: easier plotting of scores across terms (lineplot / boxplot).
- Sales by region and month (use MultiIndex columns): Start: df with columns like ('North','Jan'), ('North','Feb'), ('South','Jan') stack: df.columns = pd.MultiIndex.from_tuples([...]); stacked = df.stack(level=0) Use: stack to analyze per-region time series or unstack to create a region-by-month matrix for heatmap.
- Sensor readings from multiple devices (wide -> long): If columns are device IDs, melt into long format with columns ['Timestamp','Device','Reading'] for time-series analysis and grouping.
- Pivoting after groupby: groupby(['Region','Product'])['Sales'].sum() returns Series with MultiIndex — use unstack('Product') to show products as columns and regions as rows for comparison.
- \[pd.melt(df\]\[id_vars=[id_cols]\]\[value_vars=[value_cols]\]\[var_name='variable'\]\[value_name='value')\]
- \[df.stack(level=-1\]\[dropna=True) # move column level to row index\]\[returns Series (or DataFrame)\]
- \[df.unstack(level=-1) # move index level to columns\]\[inverse of stack for compatible levels\]
- \[df.pivot(index='id_col'\]\[columns='variable'\]\[values='value') # pivot long->wide (often inverse of melt)\]
- \[df.set_index(['A','B']).unstack('B') # create wide table with B level in columns\]
- \[df.reset_index() # often used after stack/unstack to flatten MultiIndex into columns\]
Sorting and Ranking
Sorting and Ranking
Key Point: Pandas percentage rank (pct=True): pct_rank = rank / n_non_na (pandas returns rank divided by number of non-NA values)
Overview: Sorting arranges data in a specified order (ascending/descending) based on one or more columns or the index. Ranking assigns ordinal positions to values (1st, 2nd, ...) and handles ties according to a chosen method. In pandas these operations are essential for data cleaning, analysis and reporting (leaderboards, top-k selection, percentile calculations).
Sorting in pandas
Common methods:
df.sort_values(by='col', ascending=True, inplace=False, na_position='last')
df.sort_values(by=['col1','col2'], ascending=[False, True]) # multi-column sort
df.sort_index(ascending=True) # sort by index
Options:
- by: column name or list of column names.
- ascending: bool or list of bools (one per column).
- inplace: modify original DataFrame if True.
- na_position: 'first' or 'last' for NaN placement.
Quick selection of top/bottom elements:
df.nlargest(n, 'sales')
df.nsmallest(n, 'price')
Ranking in pandas
Use Series.rank (also DataFrame.rank). It assigns ranks to values; smaller values get smaller ranks by default.
series.rank(ascending=True, method='average', pct=False)
Important parameters:
- method: how to handle ties: 'average', 'min', 'max', 'first', 'dense'.
- 'average': assign average of tied positions (e.g., tied at positions 2 and 3 => rank 2.5).
- 'min': all tied values get lowest position (2 in example).
- 'max': all tied values get highest position (3 in example).
- 'first': ranks assigned in order they appear in the data (no averaging).
- 'dense': like 'min' but next distinct value gets next integer (no gaps).
- ascending: False to rank high values as rank 1 (useful for leaderboards).
- pct: if True returns fractional rank = rank / count_non_na (percentage-based rank).
Example: compute descending ranks of scores and convert to percentile:
df['rank'] = df['score'].rank(ascending=False, method='min')
df['pct_rank'] = df['score'].rank(ascending=False, pct=True) # rank / n
Tie handling & choices: choose method based on context. For competitions where ties share places, use 'min' or 'max'. For continuous scoring where you want fractional positions, use 'average'. For strict ordering by appearance, use 'first'.
Practical tips:
- Chain sorting and selecting top-n: df.sort_values('sales', ascending=False).head(10) or df.nlargest(10,'sales').
- For grouped ranking (rank within groups): use groupby + rank: df['grp_rank'] = df.groupby('group')['value'].rank(ascending=False, method='dense').
- Rank across columns (rows): df.rank(axis=1) ranks values across columns for each row.
When to use sort vs rank: sort when you want ordered rows (presentation or slicing), rank when you need ordinal labels, percentiles, or positional metrics to feed into further calculation.
- Student marks: Rank students by total marks (descending), handle ties with 'min' so equal marks share the same position. Code: df['total_rank'] = df['total'].rank(ascending=False, method='min')
- Top-selling products: Get top 5 products by monthly sales. Code: top5 = df.nlargest(5, 'monthly_sales') or df.sort_values('monthly_sales', ascending=False).head(5)
- Leaderboard with ties: For a gaming leaderboard where earlier wins break ties, use method='first' on a time-sorted DataFrame: df.sort_values(['score','time'], ascending=[False,True]).rank(method='first')
- Group-wise ranking: Rank employees by salary within each department. Code: df['dept_rank'] = df.groupby('department')['salary'].rank(ascending=False, method='dense')
- Percentile conversion: Convert scores to percentiles using pct=True. Code: df['percentile'] = df['score'].rank(pct=True) # returns rank/n
- \[Pandas percentage rank (pct=True): pct_rank = rank / n_non_na (pandas returns rank divided by number of non-NA values)\]
- \[Alternative percentile normalization (0–1): normalized = (rank - 1) / (n - 1) (useful when you want 0 for smallest and 1 for largest)\]
- \[Average tie rank (example ties occupy positions i..j): average_rank = (i + j) / 2\]
- \[Dense rank: successive distinct values get consecutive integers (no gaps)\]\[If values tie at rank k\]\[next distinct value gets k+1.\]
- \[Typical sorting computational complexity: O(n log n) for comparison-based sorts (pandas uses fast underlying C/NumPy methods).\]
Hierarchical (Multi-)Indexing
Hierarchical (Multi-)Indexing
Key Point: Create MultiIndex from columns: df = df.set_index(['Level1','Level2'])
What is Hierarchical (Multi-)Indexing?
Hierarchical or multi-indexing in pandas allows a DataFrame or Series to have more than one index level on rows and/or columns. Instead of a single label per row, each row can be identified by a tuple of labels (level1, level2, ...). This lets you represent and operate on higher-dimensional data in a 2D table efficiently.
Why use it?
- Simplifies representation of grouped or panel data (e.g., city → store → product).
- Makes aggregation, slicing and reshaping across multiple keys easy and expressive.
- Reduces need for repeated columns and helps produce tidy, compact tables.
How to create a MultiIndex
- From columns: df.set_index(['Level1', 'Level2'])
- From arrays or tuples: pd.MultiIndex.from_tuples([('A',1),('A',2),('B',1)])
- During pivoting: df.pivot_table(...)
Basic operations and concepts
- Levels: each position in the tuple is a level (named or unnamed).
- Labels: member values in each level (e.g., city names).
- Accessing rows: use a tuple in .loc or chained .loc if levels are named.
- Slicing: IndexSlice helps with partial selection across levels.
- Stack / unstack: transform between row-level and column-level hierarchies.
- xs (cross-section): extract data at a particular level value quickly.
- swaplevel / sort_index: reorder levels to suit operations or display.
Short code examples
# create a DataFrame with a multi-index from columns
sales = pd.DataFrame({
'City': ['Mumbai','Mumbai','Delhi','Delhi'],
'Store': ['S1','S2','S1','S2'],
'Sales': [100, 150, 80, 120]
})
sales = sales.set_index(['City','Store'])
# now sales.index is a MultiIndex with two levels
# selection examples
sales.loc[('Mumbai','S2')] # select single row by full tuple
sales.loc['Mumbai'] # selects all stores in Mumbai
sales.xs('S1', level='Store') # cross-section by store across all cities
# reshape
sales_unstacked = sales.unstack(level='Store') # stores become columns
Tips for reading and writing
- When printing, pandas shows levels stacked vertically for readability.
- Name your levels (df.index.names = ['City','Store']) for clearer code.
- Keep indexes sorted (df.sort_index()) for fast, reliable slicing.
Example use-cases in real life
- Retail: sales by (Region, Store, Product) to analyze sales across hierarchies.
- Education: marks by (Class, Student, Subject) to compute averages per class and subject.
- Sensor networks: readings by (Date, SensorID) for time-series per sensor.
- Finance: stock prices with (Ticker, Date) or (Sector, Company, Quarter).
Hierarchical indexing is a powerful feature in pandas that helps Class 11 students organize, slice, aggregate and visualize grouped data with clarity and efficiency.
- Student marks: DataFrame indexed by ['Class','Student'] with columns for subjects. Use df.loc['Class10'] to get all students in Class 10, df.xs('Alice', level='Student') to get Alice across classes.
- Retail sales: sales.set_index(['City','Product']) so you can do sales.loc['Delhi','Mobile'] for Delhi mobile sales or sales.xs('Mobile', level='Product') to compare mobile sales across cities.
- Sensor readings: readings.set_index(['Date','SensorID']) then readings.unstack('SensorID') produces columns per sensor so you can plot sensors on the same time axis.
- Pivot table example: df.pivot_table(values='Sales', index=['Month','Region'], columns='Product', aggfunc='sum') — result is a MultiIndex on rows (Month, Region) and product columns.
- \[Create MultiIndex from columns: df = df.set_index(['Level1','Level2'])\]
- \[Create MultiIndex from tuples: idx = pd.MultiIndex.from_tuples([('A',1),('A',2),('B',1)])\]
- \[Select full tuple: df.loc[('level1_value','level2_value')]\]
- \[Select by one level: df.xs('level_value'\]\[level='LevelName')\]
- \[Swap levels: df.swaplevel(i=0\]\[j=1) # swap level positions\]
- \[Convert row level to columns: df.unstack(level='LevelName') # inverse: df.stack()\]
String Handling with .str
String Handling with .str
Key Point: str.strip() — remove leading/trailing whitespace
What is .str?
In pandas, .str is an accessor that provides vectorized string methods for Series (and Index). It allows you to perform string operations on all elements of a column efficiently (without explicit Python loops).
Why use .str?
It is fast, concise, and handles missing values (NaN) safely. Many methods accept regular expressions (regex) to match patterns and extract parts of strings.
Common categories of operations
- Case and trimming:
str.lower(),str.upper(),str.title(),str.strip()(remove whitespace or specific chars). - Searching and matching:
str.contains(),str.startswith(),str.endswith(),str.match(),str.find(). - Splitting and extracting:
str.split(),str.rsplit(),str.get(),str.extract(),str.findall(). - Replacing and cleaning:
str.replace(), often with regex to remove punctuation or standardize formats. - Length and slicing:
str.len(),str.slice(),str[:](slice usingstr.sliceorstr.get). - Concatenation and joining:
str.cat()to join strings from multiple columns or a list.
Notes on regex: Many .str methods have a regex parameter or accept regular expressions (e.g., extract, replace, contains). Use raw Python strings for patterns: r'\d{3}'.
Worked example (small DataFrame)
import pandas as pd
df = pd.DataFrame({
'full_name': [' Alice Brown', 'bob smith ', 'CHARLIE DAVIS', None],
'email': ['alice@school.edu', 'bob99@gmail.com', 'charlie@org.gov', 'na']
})
# Trim and standardize case
df['name_clean'] = df['full_name'].str.strip().str.title()
# Extract username and domain from email
df[['username','domain']] = df['email'].str.split('@', expand=True)
# Check rows with gmail
df['is_gmail'] = df['email'].str.contains('gmail', na=False)
# Get length of username
df['user_len'] = df['username'].str.len()
Handling missing values: By default .str methods return NaN if the element is missing. Use na='' or fillna to substitute if needed (some methods accept na= argument, e.g. str.contains('x', na=False)).
Performance tip: .str is vectorized and much faster than iterating row-by-row. For very large text processing (NLP) consider specialized libraries, but for typical table cleaning .str is ideal.
Class 11 learning points (what students must practice): trimming and case normalization, splitting full name into first/last, extracting domain from email, validating phone formats (using regex), counting words/characters, filtering rows by substring.
- Split full name into first and last: df[['first','last']] = df['name'].str.strip().str.split(' ', n=1, expand=True)
- Extract domain from email: df['domain'] = df['email'].str.extract(r'@(.+)$')
- Find rows that contain 'street' in address: df[df['address'].str.contains('street', case=False, na=False)]
- Replace punctuation and lowercase: df['text_clean'] = df['text'].str.replace(r"[^\w\s]", '', regex=True).str.lower()
- Get username length: df['user_len'] = df['email'].str.split('@').str.get(0).str.len()
- Pad codes with zeros: df['code_z'] = df['code'].str.zfill(5)
- \[str.strip() — remove leading/trailing whitespace\]
- \[str.lower()\]\[str.upper()\]\[str.title() — change case\]
- \[str.contains(pattern\]\[case=True/False\]\[na=False) — boolean mask for substring or regex\]
- \[str.split(sep\]\[n=-1\]\[expand=False) — split strings\]\[expand=True returns DataFrame\]
- \[str.extract(pattern\]\[expand=True/False) — extract capture groups using regex\]
- \[str.replace(pat\]\[repl\]\[regex=True) — replace substrings or regex matches\]
Date and Time Handling
Date and Time Handling
Key Point: Convert strings to datetime: df['date'] = pd.to_datetime(df['date_str'], format='%d-%m-%Y', errors='coerce')
Overview
Date and time handling is essential when working with time-stamped data (logs, sales by day, sensor readings, attendance). In Pandas, date/time values are represented using the datetime64[ns] dtype and Pandas/Timestamp/Timedelta/Period objects which allow fast vectorized operations, indexing, resampling and time-based grouping.
Parsing and converting
Use pd.to_datetime() to convert strings or numeric timestamps into datetimes. Important parameters: format (specify input format), errors='coerce' (invalid -> NaT), unit (e.g. 's' for seconds), and infer_datetime_format=True (faster when formats are consistent).
# example
df['date'] = pd.to_datetime(df['date_str'], format='%d-%m-%Y', errors='coerce')
DatetimeIndex and indexing
Setting a datetime column as the index (df.set_index('date')) creates a DatetimeIndex. This enables fast slicing by date ranges, e.g. df['2021-01'] or df['2021-01-01':'2021-01-31'].
Accessing components
Use the dt accessor to get parts: df['date'].dt.year, .dt.month, .dt.day, .dt.hour, .dt.weekday, .dt.day_name(), .dt.time. Also methods like .dt.normalize() (set time to midnight), .dt.floor(), .dt.ceil(), .dt.round().
Timedelta and arithmetic
Differences between datetimes produce Timedelta objects. You can add/subtract timedeltas, compute durations, and convert to units: td.days, td.total_seconds(). Create timedeltas with pd.to_timedelta() or pd.Timedelta().
Resampling and frequency conversion
Use resample() (requires DatetimeIndex) to change frequency and aggregate: df.resample('M').sum() for monthly totals. For grouping without index, use pd.Grouper(key='date', freq='M') in groupby. Common freq aliases: 'D' (daily), 'W' (weekly), 'M' (month-end), 'MS' (month-start), 'H' (hourly).
Periods and time spans
Use Period for closed intervals (e.g., month periods) and period_range() to build period indexes. Useful for handling business quarters, months, or custom intervals.
Time zones
Localize naive timestamps with tz_localize() and convert between time zones with tz_convert(). Be careful when localizing: df['date'] = df['date'].dt.tz_localize('UTC').dt.tz_convert('Asia/Kolkata').
Missing values and parsing errors
Invalid date strings become NaT. Handle with fillna(), interpolate() (for numeric series indexed by time), or drop with dropna(). Use errors='coerce' to avoid exceptions when parsing.
Performance tips
Keep date columns as datetime64[ns], avoid repeated string parsing, and prefer DatetimeIndex for time-series ops. Use vectorized dt methods instead of Python loops.
Common workflow summary
1) Parse strings to datetimes (pd.to_datetime). 2) Set as index for time series ops (set_index). 3) Extract components with .dt. 4) Resample/group/roll as needed. 5) Visualize trends and seasonality.
- Sales over time: Convert order date strings to datetime, set as index, resample monthly and plot month-wise revenue. Code: df['date']=pd.to_datetime(df['order_date'], format='%Y-%m-%d'); df.set_index('date').resample('M')['revenue'].sum()
- Sensor logs: Parse timestamps with milliseconds: df['ts']=pd.to_datetime(df['timestamp_ms'], unit='ms'); then compute time gaps: df['gap']=df['ts'].diff()
- Attendance register: Extract weekday to analyze absenteeism: df['weekday']=df['date'].dt.day_name(); df.groupby('weekday')['present'].mean()
- Web server logs: Parse combined date/time strings with timezone; localize and convert: df['time']=pd.to_datetime(df['time_str']); df['time']=df['time'].dt.tz_localize('UTC').dt.tz_convert('Asia/Kolkata')
- Duration calculation: Compute time spent: df['duration']=pd.to_datetime(df['end']) - pd.to_datetime(df['start']); df['mins']=df['duration'].dt.total_seconds()/60
- \[Convert strings to datetime: df['date'] = pd.to_datetime(df['date_str']\]\[format='%d-%m-%Y'\]\[errors='coerce')\]
- \[Set DatetimeIndex: df = df.set_index('date') # enables resample and time slicing\]
- \[Resample monthly sum: monthly = df.resample('M')['value'].sum()\]
- \[Group by month without index: df.groupby(pd.Grouper(key='date'\]\[freq='M'))['value'].sum()\]
- \[Extract components: df['year']=df['date'].dt.year\]\[df['month']=df['date'].dt.month\]\[df['weekday']=df['date'].dt.day_name()\]
- \[Timedelta (duration): df['duration'] = pd.to_datetime(df['end']) - pd.to_datetime(df['start'])\]\[minutes = df['duration'].dt.total_seconds()/60\]
Categorical Data and Factorization
Categorical Data and Factorization
Key Point: Frequency of a category c: freq(c) = count(c). (count(c) is number of rows where value == c)
What are categorical data? Categorical data are variables that take values from a limited set of discrete labels (categories). Examples: gender ('Male', 'Female'), blood group ('A', 'B', 'AB', 'O'), sizes ('S', 'M', 'L'), grades ('A', 'B', 'C'). They are either nominal (no natural order) or ordinal (have a meaningful order).
Why treat them specially? In memory and computation, repeating strings are costly. Many operations (grouping, counting, plotting) are faster and more meaningful if categories are stored as integer codes with a small lookup of category names. Also ordinality matters for comparisons and sorting.
Pandas support: Pandas provides a categorical dtype and helper functions to convert and factorize categorical data. Main tools:
Series.astype('category')— converts a column to categorical dtype.pd.Categorical(values)— constructs a categorical object (can specify order and categories).Series.cat.codes— shows the integer codes that represent categories.pd.factorize(series)— returns a tuple (codes, uniques) to map each value to an integer code and the unique category labels.Series.cat.reorder_categories([...], ordered=True)orCategoricalDtype(categories=[...], ordered=True)— to set an order for ordinal categories.
How factorization works (conceptually): choose a list of unique categories (the vocabulary) and replace each value by the index of that category. For example, if uniques = ['Red','Green','Blue'] then codes = [0,1,2,...]. This reduces memory and speeds up group operations.
Advantages:
- Lower memory usage: repeated strings stored once (metadata) + small integer array for values.
- Faster grouping, sorting, comparisons and joins.
- Explicit ordering for ordinal data allowing correct comparisons and plotting.
Typical workflow (pandas):
# convert column to category
df['grade'] = df['grade'].astype('category')
# view category labels and codes
df['grade'].cat.categories
df['grade'].cat.codes
# factorize a series directly
codes, uniques = pd.factorize(df['city'])
Notes on missing values: Missing values (NaN) are allowed; codes for NaN are often set to -1 when using Series.cat.codes or pd.factorize, so handle them appropriately before modelling or plotting.
- Survey of favourite subject: ['Math', 'Science', 'English', 'Math', 'English'] → pd.factorize gives codes [0,1,2,0,2] and uniques ['Math','Science','English']; convert to category for memory savings and fast counts.
- Sizes in clothing (ordinal): ['S','M','L','M','S'] → use CategoricalDtype(categories=['XS','S','M','L','XL'], ordered=True) so comparisons (e.g. 'M' > 'S') work as expected.
- Blood groups (nominal): convert to category to speed up grouping and get frequency table: df['blood'].value_counts() (on categorical series this is efficient).
- City column with many repeats: use df['city'] = df['city'].astype('category') to store unique city names once and codes for each row — saves memory for large datasets.
- \[Frequency of a category c: freq(c) = count(c). (count(c) is number of rows where value == c)\]
- \[Relative frequency (proportion): rel_freq(c) = count(c) / N\]\[where N is total non-missing observations.\]
- \[Percent: percent(c) = rel_freq(c) * 100.\]
- \[Factorization mapping (conceptual): Let uniques = [u0\]\[u1, ...\]\[u(k-1)]\]\[For each value v\]\[code(v) = i such that v == ui\]\[Missing → code = -1 (convention).\]
Applying Functions: apply, map, applymap
Applying Functions: apply, map, applymap
Key Point: Series.map(arg) -- arg can be a function, dict, or Series. Returns a Series.
Overview: In pandas, apply, map and applymap let you run a function over Series or DataFrame values to transform, clean or aggregate data. They let you use Python functions (including lambdas) to perform custom operations that vectorized methods don’t cover.
map: Works on a Series only. It maps each element to a new value using either a function, a dict/Series (for lookup), or a scalar replacement. Use map when you want elementwise substitution or lookup of categorical values.
# Example: map with a dict
s = pd.Series(['A','B','A','C'])
s.map({'A':'Apple','B':'Banana','C':'Cherry'})
apply (Series): Series.apply(func) applies a function to each element of the Series (elementwise), similar to map but more general (works with functions that return scalars or more complex objects).
# Example: apply on a Series
s = pd.Series([10,20,30])
s.apply(lambda x: x/10) # returns 1.0,2.0,3.0
apply (DataFrame): DataFrame.apply(func, axis=0 or 1) applies func along rows (axis=1) or columns (axis=0). The function receives a Series (a row or a column) and should return either a scalar or a Sequence. Useful for row/column aggregates or deriving new columns from multiple columns.
# Example: apply along rows
df = pd.DataFrame({'eng':[80,70],'math':[90,85]})
df['total'] = df.apply(lambda row: row['eng'] + row['math'], axis=1)
applymap: DataFrame.applymap(func) applies func elementwise to every single cell of the DataFrame. Use it when you need to transform every value independently (e.g., format numbers, convert units).
# Example: applymap to convert units
df.applymap(lambda x: x*0.001) # if all numeric and you want to scale
Key differences (quick):
- map: Series only, elementwise, accepts dict/Series/function.
- Series.apply: Series only, elementwise, more flexible than map.
- DataFrame.apply: Works on rows or columns (aggregations or row-wise computations).
- DataFrame.applymap: Elementwise on entire DataFrame (every cell).
Performance note: Vectorized pandas/numpy operations are fastest. apply/applymap/map are slower because they loop in Python. Use them when vectorized alternatives are not available.
Common patterns:
- Cleaning strings: df['name'] = df['name'].str.strip().map(lambda s: s.title())
- Category lookup: df['state_name'] = df['state_code'].map(code_to_name_dict)
- Row calculations: df['grade'] = df.apply(lambda r: compute_grade(r['marks1'], r['marks2']), axis=1)
- Elementwise numeric transform: df = df.applymap(lambda x: round(x,2))
- Convert codes to names (map): import pandas as pd s = pd.Series(['NY','CA','TX']) lookup = {'NY':'New York','CA':'California','TX':'Texas'} print(s.map(lookup))
- Add bonus marks to a marks column (Series.apply): import pandas as pd df = pd.DataFrame({'name':['A','B'],'marks':[70,85]}) df['marks_plus_5'] = df['marks'].apply(lambda x: x + 5) print(df)
- Compute total and grade from multiple columns (DataFrame.apply): import pandas as pd df = pd.DataFrame({'eng':[80,60],'math':[90,70]}) df['total'] = df.apply(lambda r: r['eng'] + r['math'], axis=1) df['grade'] = df['total'].apply(lambda t: 'A' if t>=160 else ('B' if t>=120 else 'C'))
- Scale all numeric values to thousands (applymap): import pandas as pd df = pd.DataFrame({'a':[1000,2000],'b':[3000,4000]}) df_scaled = df.applymap(lambda x: x/1000) print(df_scaled)
- \[Series.map(arg) -- arg can be a function\]\[dict\]\[or Series\]\[Returns a Series.\]
- \[Series.apply(func\]\[convert_dtype=True\]\[args=(), **kwargs) -- elementwise Series operation.\]
- \[DataFrame.apply(func\]\[axis=0\]\[raw=False\]\[result_type=None\]\[args=(), **kwargs) -- apply func across columns (axis=0) or rows (axis=1).\]
- \[DataFrame.applymap(func) -- apply elementwise to every cell in the DataFrame.\]
Window and Rolling Operations
Window and Rolling Operations
Key Point: Simple Moving Average (SMA) for window size n at time t: SMA_t = (1/n) * sum_{i=0}^{n-1} x_{t-i}
What are window / rolling operations?
Window (or rolling) operations compute summary statistics over a moving window of consecutive rows in a series or DataFrame. Instead of a single global aggregation (like mean of entire column), a rolling operation returns a result for each position using values from a local neighborhood (the window).
Key concepts
- Window size (n): number of consecutive observations used for each calculation.
- Centered vs right-aligned: a centered window takes values before and after the current index; right-aligned (default) uses current and previous values.
- min_periods: minimum non-NA observations in window required to produce a value (prevents NaN outputs at start).
- Types: rolling (fixed-size), expanding (grows from start to current index), ewm/exponentially-weighted (gives more weight to recent values).
Pandas usage (simple examples)
import pandas as pd
# rolling mean with window 3
df['SMA_3'] = df['value'].rolling(window=3, min_periods=1).mean()
# rolling sum
df['roll_sum_7'] = df['rainfall'].rolling(window=7).sum()
# expanding (cumulative) mean
df['cum_mean'] = df['sales'].expanding(min_periods=1).mean()
# exponential moving average (EMA)
df['EMA_10'] = df['price'].ewm(span=10, adjust=False).mean()
Why useful (intuition)
Rolling windows smooth noisy data, reveal short-term trends, detect local peaks/valleys, compute local variability (std), and find correlations that vary over time. Expanding windows provide cumulative summary (like running total). Exponentially-weighted methods adapt faster to recent changes by giving higher weights to recent observations.
Practical notes
- Rolling results often produce NaNs at the start if min_periods < window size is not set.
- For time-series with irregular timestamps use rolling on a time window: df.rolling('7D', on='date').mean().
- Use .shift() together with rolling to avoid look-ahead bias (e.g., when computing features for modeling).
- Stock prices — 20-day simple moving average (SMA): df['SMA_20'] = df['Close'].rolling(window=20).mean(). Helps identify trend changes and smooth daily volatility.
- Weather — 7-day rolling sum of rainfall to detect wet spells: df['weekly_rain'] = df['rain'].rolling(window=7).sum().
- Sales — cumulative revenue (expanding): df['cum_revenue'] = df['revenue'].expanding().sum(). Useful to track progress toward targets.
- Sensor monitoring — rolling standard deviation to detect increase in variability (possible fault): df['roll_std_30'] = df['sensor'].rolling(30).std().
- Rolling correlation — measure local correlation between two series (e.g., temperature and ice-cream sales): df['corr_30'] = df['temp'].rolling(30).corr(df['sales']).
- \[Simple Moving Average (SMA) for window size n at time t: SMA_t = (1/n) * sum_{i=0}^{n-1} x_{t-i}\]
- \[Exponential Moving Average (EMA) recurrence: EMA_t = α * x_t + (1 - α) * EMA_{t-1}\]\[where α (alpha) = 2 / (N + 1) for span N (common choice).\]
- \[Rolling variance (population form) over window of n: var_t = (1/n) * sum_{i=0}^{n-1} (x_{t-i} - mean_t)^2 (Pandas default uses sample std with ddof=1 for .std()).\]
- \[Rolling covariance of X and Y: cov_t = (1/(n-1)) * sum_{i=0}^{n-1} (X_{t-i} - meanX_t)*(Y_{t-i} - meanY_t).\]
- \[Rolling correlation: corr_t = cov_t / (stdX_t * stdY_t)\]
Basic Visualization with pandas
Basic Visualization with pandas
Key Point: Mean (average): mean = sum(x_i) / n. In pandas: df['col'].mean()
Overview: Basic visualization with pandas means using pandas' built-in plotting methods (which use matplotlib under the hood) to quickly turn Series and DataFrame data into charts. Visuals help summarize patterns, trends, distributions and relationships in tabular data.
Requirements
- import pandas as pd
- import matplotlib.pyplot as plt
- Data in a Series or DataFrame (columns numeric or categorical as appropriate)
Common plotting methods
Series.plot(kind='line')orDataFrame.plot()— default: line plot for trends over index or time.plot(kind='bar')/plot(kind='barh')— bar charts for categorical comparisons.plot(kind='hist')ordf.hist()— histograms for distribution of a numeric column.plot(kind='box')ordf.boxplot()— boxplots for spread and outliers.plot(kind='scatter', x='col1', y='col2')— scatter for relationships between two numeric variables.plot(kind='pie')— pie chart for parts of a whole (single Series).plot(kind='kde')— kernel density estimate for smoothed distributions.
Typical arguments and customization
kind: type of plot.x,y: column names for axes.title='...' , xlabel='...' , ylabel='...'color,style,legend,figsize=(w,h),grid=Truesubplots=Trueto draw each column in separate axes;layout=(r,c)to arrange them.- Use
plt.savefig('file.png')to save the figure.
How to build a simple plot (example)
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv('sales.csv')
df.groupby('month')['revenue'].sum().plot(kind='line', figsize=(8,4), title='Monthly Revenue')
plt.xlabel('Month')
plt.ylabel('Revenue')
plt.grid(True)
plt.show()
Working with grouped data and counts
Use df.groupby(...).sum(), .mean() or value_counts() as input to plotting. Example: df['region'].value_counts().plot(kind='bar').
Handling missing values
- Either drop (
dropna()) or fill (fillna()) before plotting to avoid misleading charts.
Interpretation tips
- Choose chart type to match the question: trends (line), comparisons (bar), composition (pie), distribution (hist/box), relationships (scatter).
- Check summary statistics (mean, median, std) to complement visuals.
Basic visualization with pandas provides quick, readable charts directly from DataFrame and Series objects and is ideal for exploratory data analysis in Class 11 level projects.
- Temperature over time (Line): Track daily temperature. Code: df.plot(kind='line', x='date', y='temp', title='Daily Temperature')
- Sales by product (Bar): Compare total sales per product. Code: df.groupby('product')['sales'].sum().plot(kind='bar', color='skyblue')
- Exam score distribution (Histogram & Boxplot): See spread and outliers of marks. Code: df['marks'].plot(kind='hist', bins=10); df['marks'].plot(kind='box')
- Market share (Pie): Show percentage share per company. Code: df.groupby('company')['revenue'].sum().plot(kind='pie', autopct='%1.1f%%')
- Study hours vs Marks (Scatter): Check correlation. Code: df.plot(kind='scatter', x='hours', y='marks')
- \[Mean (average): mean = sum(x_i) / n\]\[In pandas: df['col'].mean()\]
- \[Median: middle value when sorted\]\[In pandas: df['col'].median()\]
- \[Variance: Var = (1/(n-1)) * Σ(x_i - mean)^2\]\[In pandas: df['col'].var()\]
- \[Standard deviation: SD = sqrt(Variance)\]\[In pandas: df['col'].std()\]
- \[Covariance: cov(X,Y) = (1/(n-1)) * Σ(x_i - mean_x)*(y_i - mean_y)\]\[In pandas: df['col1'].cov(df['col2'])\]
- \[Pearson correlation (r): r = cov(X,Y) / (sd_X * sd_Y)\]\[In pandas: df['col1'].corr(df['col2'])\]
Exporting and Reporting Results
Exporting and Reporting Results
Key Point: Export DataFrame to CSV: df.to_csv('cleaned_data.csv', index=False, encoding='utf-8')
Exporting and reporting are the final steps of a data-analysis workflow: after cleaning and analysing data with pandas, you save (export) results and create human-friendly reports so others can read, reuse or visualise the findings. Common targets are files (CSV, Excel, JSON, HTML), databases (SQL), binary serialisations (pickle, parquet) and visual files (PNG, SVG). Reporting includes summary tables, charts and formatted HTML/Excel reports.
Key pandas methods for exporting:
DataFrame.to_csv()— save to comma/other‑delimited files.DataFrame.to_excel()/pd.ExcelWriter— write one or multiple sheets to Excel workbooks.DataFrame.to_json()— export to JSON for APIs or web apps.DataFrame.to_html()— create HTML tables for web reports or emails.DataFrame.to_sql()— write to SQL databases (needs SQLAlchemy/DB connector).DataFrame.to_parquet(),to_pickle()— efficient binary formats for large datasets.- Save plots from matplotlib/seaborn using
plt.savefig().
Important options and considerations:
- index (True/False) — whether to write the DataFrame index as a column.
- columns — list to restrict exported columns.
- encoding (e.g.
'utf-8') — ensure correct character encoding for non-ASCII text. - sep — delimiter for CSV (comma, tab
'\t'for TSV). - compression (e.g.
'gzip') — reduce file size for large exports. - chunksize — write/read large files in pieces to avoid memory issues.
- Excel: use
ExcelWriterfor multiple sheets and formatting; consider engine (openpyxl,xlsxwriter). - Database export: use correct dtype mapping and transactions; avoid exporting sensitive data without anonymisation.
Reporting practices:
- Create summary tables with
df.describe(),groupby().agg()orpivot_table(), then export these summaries. - Generate charts (bar, line, histogram, boxplot, heatmap) to visualise main findings and save images with
savefig. - Combine tables and charts in Excel sheets or HTML dashboards; libraries like pandas styling, Jupyter notebooks or profiling tools (ydata‑profiling) can help create richer reports.
- Include metadata (generation date, filtration steps, source) so results are reproducible.
Best practices: set index=False for tidy CSVs, use UTF‑8 encoding, compress large files, export only required columns, anonymise sensitive fields, and include a README or header with context. Test exported files by reloading them to ensure correctness.
- School exam results: after cleaning marks and computing totals and grades, save the final table to CSV for the school and to Excel with separate sheets for each class using pd.ExcelWriter.
- Monthly sales summary: group sales by month and product, create a pivot table and a line chart for sales trend; export pivot to Excel and chart to PNG for inclusion in a management report.
- Hospital data: anonymise patient identifiers, export cleaned patient records to a secure SQL database using DataFrame.to_sql for downstream analytics and reporting dashboards.
- Sensor logs (large dataset): write sensor readings to compressed parquet (<code>to_parquet()</code>) for efficient storage; export daily summary CSV files with <code>chunksize</code> to avoid memory spikes.
- HTML dashboard: convert summary DataFrame to HTML (<code>to_html()</code>) and embed saved plot images to produce a self-contained report for emailing or hosting on an intranet.
- \[Export DataFrame to CSV: df.to_csv('cleaned_data.csv'\]\[index=False\]\[encoding='utf-8')\]
- \[Export to Excel (single sheet): df.to_excel('report.xlsx'\]\[sheet_name='Summary'\]\[index=False)\]
- \[Export multiple DataFrames to one Excel file: with pd.ExcelWriter('multi_sheet.xlsx') as writer:\n df_summary.to_excel(writer, 'Summary'\]\[index=False)\n df_details.to_excel(writer, 'Details'\]\[index=False)\]
- \[Save to JSON: df.to_json('data.json'\]\[orient='records'\]\[lines=True)\]
- \[Save to SQL (requires engine): df.to_sql('table_name'\]\[con=engine\]\[if_exists='replace'\]\[index=False)\]
- \[Save DataFrame to parquet for speed/space: df.to_parquet('data.parquet'\]\[compression='snappy')\]
Practical Examples and Exercises
Practical Examples and Exercises
Key Point: Mean (arithmetic mean): μ = Σx / n
Overview: This topic shows how to apply pandas to real datasets: load and inspect data, clean and transform it, compute summary statistics, aggregate/group data, join datasets, and create visualisations to draw insights. Typical workflow: load & inspect → clean → transform → analyse (aggregate/summary/stats) → visualise → save results.
Common steps with short code examples:
- Load and inspect: Use
pd.read_csvto read files, thendf.head(),df.info(),df.describe()to inspect.import pandas as pd df = pd.read_csv('data.csv') print(df.head()) print(df.info()) print(df.describe()) - Clean missing or wrong data: identify with
df.isna().sum(), remove or impute withdf.dropna()ordf.fillna(value).# drop rows with any missing values df_clean = df.dropna() # fill numerical missing values with column mean df['score'] = df['score'].fillna(df['score'].mean()) - Filter & transform: use boolean masks,
loc,applyor vectorised operations.# select students with marks >= 60 high = df.loc[df['marks'] >= 60] # create grade column df['grade'] = df['marks'].apply(lambda x: 'A' if x >= 75 else ('B' if x >= 60 else 'C')) - Aggregate & group: group by categories and apply aggregations.
grouped = df.groupby('class').agg({'marks': ['mean', 'median', 'max']}) pivot = df.pivot_table(index='month', columns='product', values='sales', aggfunc='sum') - Merge & reshape: combine datasets with
mergeorconcat.df_all = pd.merge(transactions, customers, on='customer_id', how='left')
- Visualise: use pandas plotting (matplotlib) or seaborn to create histograms, boxplots, scatter plots, bar charts and heatmaps to explore distributions and relationships.
- Save results:
df.to_csv('cleaned.csv', index=False).
Pedagogical tips for exercises: use small real datasets (students marks, sales by month, weather readings). Ask students to (a) state objective; (b) write code to load & inspect; (c) clean; (d) perform at least two aggregations; (e) plot results and interpret; (f) export summary.
- Student marks analysis: Load 'students.csv' (columns: student_id, name, class, subject, marks). Clean missing marks, compute class-wise mean, median and pass percentage, create grade column, and plot a bar chart of average marks per class.
- Retail sales: Given 'sales.csv' (date, product, region, quantity, price), compute daily and monthly total sales, top 5 products by revenue, pivot table of revenue per region per product, and plot a line chart of monthly revenue.
- Weather data: Use 'weather.csv' (date, station, temp_max, temp_min, precipitation). Fill missing temperatures using forward fill or station mean, compute monthly average temperature, and show a heatmap of correlation between variables.
- Bank transactions: From 'transactions.csv' (txn_id, account_id, date, amount, type), remove fraudulent/negative entries, compute account-wise balance changes, identify top spenders, and visualise distribution of transaction amounts with a boxplot.
- Survey analysis: For a survey dataset (respondent_id, age, gender, satisfaction_rating), group by gender and age-group to compute average satisfaction, use chi-square or cross-tab to compare categories, and plot stacked bar charts of responses.
- Merging datasets: Merge 'students.csv' and 'attendance.csv' on student_id to compute relationship between attendance percentage and marks; plot scatter with a regression line to show correlation.
- \[Mean (arithmetic mean): μ = Σx / n\]
- \[Median: middle value when data sorted (or average of two middle values for even n)\]
- \[Mode: most frequently occurring value\]
- \[Sample variance: s² = Σ(x - x̄)² / (n - 1)\]
- \[Sample standard deviation: s = sqrt(s²)\]
- \[Covariance: cov(X,Y) = Σ(x - x̄)(y - ȳ) / (n - 1)\]
Key Concepts
- DataFrame
- Two-dimensional labeled data structure in pandas with rows and columns, like a table or spreadsheet.
- Series
- One-dimensional labeled array capable of holding any data type; a single column of a DataFrame is a Series.
- Index
- Labels that identify rows (and columns) in a DataFrame or Series; can be numeric or non-numeric and supports alignment operations.
- dtype
- Data type of a Series or DataFrame column (e.g., int64, float64, object, bool); use to check or convert types.
- NaN
- Special marker for missing or Not-a-Number values (often numpy.nan or pd.NA) used to represent missing data.
- isnull / isna
- Functions to detect missing values; return boolean Series/DataFrame indicating True where values are NaN.
- dropna
- Remove rows or columns with missing values; parameters control threshold, axis, and subset of columns to consider.
- fillna
- Fill missing values with a specified value or an imputed value (mean, median, forward-fill, etc.).
- drop_duplicates
- Remove duplicate rows from a DataFrame; can consider a subset of columns and keep first/last occurrence.
- groupby
- Split DataFrame into groups based on column(s) and apply aggregate or transformation functions to each group.
- aggregate / agg
- Apply one or more aggregation functions (like sum, mean, min, max) to grouped data or DataFrame columns.
- pivot_table
- Create a spreadsheet-style pivot table that aggregates values using specified index, columns, values, and aggfunc.
- merge
- Combine two DataFrames by columns or indices similar to SQL joins (inner, left, right, outer) based on key(s).
- concat
- Concatenate pandas objects along a particular axis (stack rows or join columns); useful for combining similar data.
- loc
- Label-based indexer to select rows and columns by labels or boolean masks; includes both endpoints for slices.
- iloc
- Integer position-based indexer to select rows and columns by integer location (like NumPy indexing).
- apply
- Apply a function along an axis (rows or columns) of a DataFrame or to elements of a Series for custom transformations.
- map
- Map values of a Series according to an input mapping (dict or function); commonly used for value replacement.
- sort_values
- Sort a DataFrame by one or more column values, ascending or descending, optionally handling NaNs position.
- describe
- Generate descriptive statistics (count, mean, std, min, quartiles, max) for numeric (and optionally object) columns.
Practice Questions
-
Explain the difference between label-based indexing (.loc) and integer position-based indexing (.iloc) in Pandas, mentioning how their slice endpoints behave. / Pandas में लेबल-आधारित इंडेक्सिंग (.loc) और पूर्णांक स्थिति-आधारित इंडेक्सिंग (.iloc) के बीच अंतर समझाइए, साथ ही उनके स्लाइस छोर के व्यवहार का उल्लेख कीजिए।
Show answer
.loc selects rows and columns by their labels and its slice endpoint is inclusive, whereas .iloc selects by integer position and its slice endpoint is exclusive following Python convention. For example df.loc['a':'c'] includes 'c' but df.iloc[0:3] returns positions 0,1,2 only. / .loc पंक्तियों और स्तंभों को उनके लेबल द्वारा चुनता है और इसका स्लाइस छोर समावेशी होता है, जबकि .iloc पूर्णांक स्थिति द्वारा चुनता है और इसका स्लाइस छोर Python परंपरा के अनुसार अपवर्जी होता है। उदाहरण के लिए df.loc['a':'c'] में 'c' शामिल है पर df.iloc[0:3] केवल स्थिति 0,1,2 लौटाता है।
-
A DataFrame df has a numeric column 'marks' containing some NaN values. Write a statement to fill the missing marks with the median of that column, and state why median is preferred over mean for skewed data. / एक DataFrame df में संख्यात्मक स्तंभ 'marks' है जिसमें कुछ NaN मान हैं। अनुपस्थित अंकों को उस स्तंभ के माध्यिका (median) से भरने हेतु एक कथन लिखिए, और बताइए कि विषम (skewed) डेटा के लिए माध्य की तुलना में माध्यिका क्यों पसंद की जाती है।
Show answer
df['marks'] = df['marks'].fillna(df['marks'].median()) fills NaN with the median. Median is preferred for skewed data because it is not pulled by extreme outliers, while the mean can be distorted by very high or low values. / df['marks'] = df['marks'].fillna(df['marks'].median()) NaN को माध्यिका से भर देता है। विषम डेटा के लिए माध्यिका पसंद की जाती है क्योंकि यह चरम बाह्यमानों (outliers) से प्रभावित नहीं होती, जबकि माध्य बहुत ऊँचे या नीचे मानों से विकृत हो सकता है।
-
What is the 'split-apply-combine' pattern in groupby, and how does transform() differ from agg() in its output? / groupby में 'split-apply-combine' पैटर्न क्या है, और transform() अपने आउटपुट में agg() से कैसे भिन्न है?
Show answer
Split-apply-combine means rows are split into groups, an operation is applied to each group, and the results are combined. agg() returns a reduced result (one value per group), whereas transform() returns an object aligned to the original DataFrame, so each row gets its group-wise value. / Split-apply-combine का अर्थ है पंक्तियों को समूहों में बाँटना, प्रत्येक समूह पर एक संक्रिया लागू करना और परिणामों को संयोजित करना। agg() एक संक्षिप्त परिणाम (प्रति समूह एक मान) लौटाता है, जबकि transform() मूल DataFrame के अनुरूप संरेखित वस्तु लौटाता है, इसलिए प्रत्येक पंक्ति को उसका समूह-वार मान मिलता है।
-
Why does chained indexing like df[df['A'] > 0]['B'] = value produce a SettingWithCopyWarning, and what is the correct alternative? / df[df['A'] > 0]['B'] = value जैसी श्रृंखलित इंडेक्सिंग SettingWithCopyWarning क्यों उत्पन्न करती है, और सही विकल्प क्या है?
Show answer
Chained indexing may operate on a temporary copy rather than the original DataFrame, so the assignment might not affect the original data, triggering the warning. The correct way is to use .loc in a single step: df.loc[df['A'] > 0, 'B'] = value. / श्रृंखलित इंडेक्सिंग मूल DataFrame के बजाय एक अस्थायी प्रति पर कार्य कर सकती है, इसलिए असाइनमेंट मूल डेटा को प्रभावित नहीं कर सकता, जिससे चेतावनी उत्पन्न होती है। सही तरीका एकल चरण में .loc का उपयोग करना है: df.loc[df['A'] > 0, 'B'] = value।
-
State the difference between pivot_table() and crosstab(), and give one situation where each is preferred. / pivot_table() और crosstab() के बीच अंतर बताइए, और प्रत्येक के लिए एक स्थिति दीजिए जहाँ वह पसंद की जाती है।
Show answer
pivot_table() aggregates a numeric values column (e.g., sum or mean) across index and column categories, while crosstab() by default builds a frequency (count) table of categorical combinations. Use pivot_table for 'total sales by region and product', and crosstab for 'count of students by class and grade'. / pivot_table() सूचकांक और स्तंभ श्रेणियों में एक संख्यात्मक मान स्तंभ (जैसे योग या माध्य) को एकत्रित करता है, जबकि crosstab() डिफ़ॉल्ट रूप से श्रेणीगत संयोजनों की आवृत्ति (गणना) तालिका बनाता है। 'क्षेत्र और उत्पाद के अनुसार कुल बिक्री' के लिए pivot_table और 'कक्षा व ग्रेड के अनुसार छात्रों की गणना' के लिए crosstab उपयोग करें।
-
Using marks 78, 85, 90, 62, 70 with method='min' and ascending=False, rank these values and explain how ties would be handled. / अंक 78, 85, 90, 62, 70 के लिए method='min' और ascending=False का प्रयोग करते हुए इन मानों को रैंक कीजिए और समझाइए कि टाई (समान मान) कैसे संभाली जाती है।
Show answer
Ranking highest as 1: 90→1, 85→2, 78→3, 70→4, 62→5. With method='min', if two values are tied they both get the lowest available position (e.g., two values tied for 2nd/3rd both become rank 2). / सबसे ऊँचे को 1 रैंक देते हुए: 90→1, 85→2, 78→3, 70→4, 62→5। method='min' के साथ, यदि दो मान समान हों तो दोनों को सबसे निचली उपलब्ध स्थिति मिलती है (जैसे 2/3 स्थान पर समान दो मान दोनों रैंक 2 बन जाते हैं)।
-
When reading a large CSV file with pandas, name two parameters that reduce memory usage and explain the role of each. / pandas से एक बड़ी CSV फ़ाइल पढ़ते समय, दो ऐसे पैरामीटर बताइए जो मेमोरी उपयोग कम करते हैं और प्रत्येक की भूमिका समझाइए।
Show answer
usecols limits reading to only the needed columns, reducing memory, and dtype lets you specify smaller/explicit data types so columns are not stored in heavier default types. chunksize can additionally process the file in pieces. / usecols केवल आवश्यक स्तंभों तक पढ़ना सीमित करता है, मेमोरी घटाता है, और dtype आपको छोटे/स्पष्ट डेटा प्रकार निर्दिष्ट करने देता है ताकि स्तंभ भारी डिफ़ॉल्ट प्रकारों में संग्रहीत न हों। chunksize अतिरिक्त रूप से फ़ाइल को टुकड़ों में संसाधित कर सकता है।
-
Explain the purpose of pd.melt() and give an example of when you would convert a wide table to long format. / pd.melt() का उद्देश्य समझाइए और एक उदाहरण दीजिए कि आप कब एक चौड़ी (wide) तालिका को लंबे (long) प्रारूप में बदलेंगे।
Show answer
pd.melt() collapses multiple value-columns into two columns — one for variable names and one for values — producing a tidy long table with one observation per row. For example, a table of student scores in Term1 and Term2 columns can be melted into rows of (Student, Term, Score) for easier plotting across terms. / pd.melt() कई मान-स्तंभों को दो स्तंभों में समेटता है — एक चर नामों के लिए और एक मानों के लिए — जिससे प्रति पंक्ति एक प्रेक्षण वाली व्यवस्थित लंबी तालिका बनती है। उदाहरण के लिए, Term1 और Term2 स्तंभों में छात्र अंकों की तालिका को (Student, Term, Score) पंक्तियों में melt किया जा सकता है ताकि टर्म-वार प्लॉटिंग आसान हो।
Related Laws & Principles
Explore allFoundational laws & principles connected to this chapter — tap to open in the Laws Explorer.