Programming for Data Processing

Lectures: Nguyễn Tuấn Phong, Đào Việt Anh

Labs: Trịnh Ngọc Huỳnh, Phạm Tiến Du

Semester: 2025-2026-1

Institute for AI, VNU-UET

Cute panda illustration

Lecture 7
Data Loading & Cleaning

Today

  1. Data Loading and Storage →
  2. Data Cleaning and Preparation →
Cute panda loading illustration

1.
Data Loading and Storage

Overview

  • Loading data from various file formats
  • Storing data in different formats
  • Best practices for data loading and storage

Outline

  1. CSV/TSV — delimited text tables ⭐️ →
  2. JSON — web/APIs; can be nested ⭐️ →
  3. Excel (.xlsx) — spreadsheets →
  4. Some notes on data loading →

1.1.
CSV:
Comma-Separated Values ⭐️

CSV — The Workhorse

Read delimited text into a DataFrame

CSV — Common Options

  • names=/header= for column labels
  • index_col= sets the row index
  • usecols= selects columns early
  • dtype=, parse_dates=, na_values=

CSV — Write to File

Use DataFrame.to_csv(..., index=False, na_rep="NULL")

TSV — Tab Separated Values

  • Set sep="\t"
  • Writing: to_csv(..., sep="\t")

CSV — Scale Tips

  • Quick sample with nrows=
  • Thin data with skiprows=

CSV — Scale Tips

  • Use chunksize= to iterate
  • Stream large files in pieces; constant memory
  • Aggregate per chunk, then combine

1.2.
JSON:
JavaScript Object Notation ⭐️

JSON — Records

Arrays of objects map cleanly to rows

JSON — Nested → Flat

Normalize nested structures with pd.json_normalize

1.3.
Excel:
Spreadsheets

Excel: Reading

  • Use ExcelFile to inspect sheet names before reading
  • Install dependencies: conda install openpyxl xlrd

Excel: Reading

  • Use pd.read_excel for convenience

Excel: Writing

1.4.
Notes on Data Loading

Common Arguments

Argument Used In Description
header / names CSV, Excel Row to use as header or explicit list of names.
index_col CSV, Excel Column(s) to set as row index.
usecols CSV, Excel Select a subset of columns at read time.
dtype CSV, Excel Force or preserve data types.
parse_dates CSV Parse date-like strings into datetime.
na_values CSV Custom strings to treat as NaN.
chunksize / nrows CSV Iterate or sample large inputs.

Pick the Right Format

Format Pros Cons Typical Use
CSV/TSV Human-readable, ubiquitous No types; big files are slow Interchange, quick inspection
JSON Flexible, web-native Nesting; need normalize APIs, logs, config
Excel Multiple sheets; stakeholders Heavier, slower; binary Business reporting

Other Formats

There are many more formats Pandas can read and write. Please refer to the IO documentation for details.

Cute panda cleaning illustration

2.
Data Cleaning and Preparation

Overview

Why Cleaning Matters

  • Most time goes to preparation: load → clean → transform → rearrange
  • Raw formats rarely fit the task → need reshaping & type fixes
  • pandas + Python make data analysis-ready

Outline

  1. Handling Missing Data ⭐️ →
  2. Data Transformation ⭐️ →
  3. Extension Data Types ⭐️ →
  4. String Manipulation ⭐️ →
  5. Categorical Data ⭐️ →

2.1.
Handling Missing Data

Overview

  • Missing values are common
  • We call missing values NA (not available)
  • Many stats exclude NA by default
  • float64 uses NaN as a sentinel
  • Detect with isna / notna (or pandas.isna)

Outline

  1. Filtering Out Missing Data ⭐️ →
  2. Filling In Missing Data ⭐️ →

NaN as Sentinel

NaN marks missing values in float64

None is Also NA

None is treated as NA (Not Available) in pandas

None is Also NA

None is treated as NA (Not Available) in pandas

2.1.1.
Filtering Out Missing Data ⭐️

Series

Use dropna() or Boolean index with notna()

DataFrame

dropna() drops rows with any NA by default

All-NA Rows

Only drop rows of all NA: dropna(how="all")

All-NA Columns

Use axis="columns"

Keep Rows with ≥ N Non-NA

2.1.2.
Filling In Missing Data ⭐️

Fill Missing Values

Constant fill: fillna(0)

Fill Missing Values

Per-column fill: fillna({col: value})

Fill by Propagation

Forward/backward fill: method="ffill" / "bfill"

Fill by Propagation

Limit runs with limit=

Simple Imputation

Can use statistics like mean / median for filling

2.2.
Data Transformation

Overview

  • Beyond missing values → clean & reshape data
  • Topics:
    • Remove duplicates
    • Map / transform values
    • Replace codes
    • Rename labels
    • Bin continuous data
    • Handle outliers & sample randomly
    • Create dummy variables

Outline

  1. Removing Duplicates ⭐️ →
  2. Transforming Data Using a Func or Mapping ⭐️ →
  3. Replacing Values ⭐️ →
  4. Renaming Axis Indexes 💨 →
  5. Discretization and Binning 💨 →
  6. Detecting and Filtering Outliers 💨 →
  7. Permutation and Random Sampling 💨 →
  8. Computing Indicator / Dummy Variables ⭐️ →

2.2.1.
Removing Duplicates ⭐️

Overview

  • Detect dupes: duplicated()
  • Drop dupes: drop_duplicates()
  • Default uses all columns and keeps the first occurrence

Creating dummy dataframe

Detecting Duplicates

  • Detect dup rows with .duplicated()
  • Default uses all columns

Removing Duplicates

Basics

  • Drop dup rows with .drop_duplicates()
  • Default keeps the first occurrence

Removing Duplicates

Subset

Use subset=[...] to check only specific columns

Removing Duplicates

Keep

Control which duplicate to keep via keep="first" (default) or keep="last"

2.2.2.
Transforming Data Using a Function or Mapping ⭐️

Overview

  • Series.map() applies a function or mapping to elements
  • Great for feature engineering / adding derived columns

Transforming Values

with dict

Transforming Values

with function

  • Pass a custom callable for flexible logic
  • Equivalent result to dict mapping above

2.2.3.
Replacing Values ⭐️

Replacing Values

  • General-purpose replacement with .replace
  • Use it for sentinel values → NaN or other codes

Replacing Multiple Values

Replace many-to-one: pass a list of targets

Replacing Multiple Values

Replace many-to-many: pass lists or a dict

Note: Series.str.replace is for string substitution (different API)

2.2.4.
Renaming Axis Indexes 💨

Transform labels

Use .map() with index / columns to modify labels

Modify in place

Assign back to .index or .columns

Copy data then modify

Use .rename() to create a modified copy of the dataframe

2.2.5.
Discretization & Binning 💨

Overview

  • Goal: Bin continuous data.
  • Why: Simpler analysis (histograms, features).
  • Tools:
    • pd.cut = fixed edges
    • pd.qcut = quantiles (equal counts)

Example: Bin Ages

  • Goal: group ages into buckets (18–25, 26–35, 36–60, 61+)
  • pd.cut assigns each value to an interval

What pd.cut Returns

  • A Categorical array with Interval labels
  • Use .codes for bin indices and .categories for interval metadata

Bin Counts

Categorical.value_counts gives bin frequencies

Open vs Closed Intervals

  • (a, b]: left open (exclusive), right closed (inclusive)
  • Set right=False for [a, b) (left-closed, right-open)

Custom Bin Labels

Use labels=... to name intervals

Equal-Width Bins by Count

  • Pass an int to pd.cut(data, k) for k equal-width bins
  • Use precision to limit decimal digits in labels

Quantile Bins

  • Splits by sample quantiles → roughly equal counts per bin
  • Great for balancing groups (e.g., quartiles, deciles)

Custom Quantiles

  • Provide explicit probabilities in [0, 1]
  • Example: 10%, 50%, 90% breakpoints

When to Use cut vs qcut

  • cut: fixed-width or domain-specific edges (e.g., age groups)
  • qcut: balanced group sizes for fair comparisons
  • Always inspect: .categories, .codes, and value_counts
  • Be explicit about interval closure with right=...

2.2.6.
Detecting & Filtering Outliers 💨

Overview

  • Goal: Identify and handle extreme values.
  • Why: Robust analysis & stable models.
  • How:
    • Boolean masks with .abs(), comparisons
    • Row filtering via .any(axis="columns")
    • Capping (winsorizing) with np.sign

Setup: Simulated Data

Find Outliers in One Column

  • Example: values in column 2 with |x| > 3
  • Use .abs() and Boolean indexing

Filter Rows with Any Outlier

  • Create a Boolean mask: data.abs() > 3
  • Reduce across columns with .any(axis="columns")

Cap Extreme Values

  • Replace |x| > 3 with 3 * np.sign(x)
  • Keeps direction; limits magnitude to threshold

What np.sign Does

  • Returns -1, 0, or 1 elementwise
  • Used to preserve the sign when capping

Tips & Variations

  • Try other thresholds (e.g., z-score > 3)
  • Alternative to capping: drop rows with outliers
  • Document decisions; outlier handling affects downstream analysis

2.2.7.
Permutation & Random Sampling 💨

Overview

  • Goal: Randomly reorder or select data.
  • Key tools:
    • np.random.permutation → shuffle order
    • take / iloc → apply permutation
    • .sample() → draw random subset
  • Sampling: without replacement (default); set replace=True to allow repeats

Why Random Sampling?

  • Shuffle for unbiased train/test splits
  • Random folds in cross-validation
  • Bootstrapping with replace=True
  • Quick Exploratory Data Analysis (EDA) with a random subset
  • Downsample large data with sample(n | frac)

Setup: Simple DataFrame

A 5×7 grid of integers 0…34

Row Permutation

  • Generate random row order with np.random.permutation
  • Apply to dataframe with take or iloc

Column Permutation

Use take(axis="columns") to permute columns

Random Subset without Replacement: sample

Select n random rows; no repeats by default

Sampling With Replacement

  • Allow repeated draws using replace=True
  • Works on Series and DataFrame

Tips & Notes

  • Use random_state=... in .sample for reproducibility
  • Weighted sampling: .sample(weights=...) (probability-proportional)
  • Permute columns: take(..., axis="columns"); permute rows: take(...) or iloc

2.2.8.
Computing Indicator / Dummy Variables ⭐️

Overview

  • Goal: Convert a categorical field into an indicator matrix of 0/1 columns.
  • Why: Required by many ML/statistical models; enables grouping & aggregation.
  • Key tool: pd.get_dummies (or Series.str.get_dummies for multi-membership)
  • Output type: recent pandas → boolean by default; set dtype=float if needed.

Example DataFrame

Make a small dataset with a categorical key

Basic One-Hot Encoding

Turn categories into indicator columns

Add Prefix & Join

  • Name columns with prefix=...
  • Combine with original data using DataFrame.join

Multi-Membership Categories

When rows belong to multiple categories in a delimited string, use Series.str.get_dummies

Multi-Membership Categories

When rows belong to multiple categories in a delimited string, use Series.str.get_dummies

Join with Prefixed Columns

Add indicators to the original table with a clear prefix

Recipe: Bin then One-Hot

Discretize a numeric variable with pd.cut, then encode with pd.get_dummies

2.3.
Extension Data Types

Overview

  • Goal: Support richer, nullable, and non-NumPy types in pandas
  • Motivation:
    • NumPy lacked full support for missing integers & booleans
    • String & object arrays used excessive memory
    • Time zones, intervals, etc. needed specialized storage
  • Solution: pandas ExtensionDtypes — fully integrated custom array types

Legacy vs Extension Behavior

Missing integers default to float64 with NaN:

→ Use Int64Dtype for a proper nullable integer type:

Handling Missing Values

Extension arrays use <NA> (the pd.NA sentinel)

String Extension Type

Use pd.StringDtype() for efficient, nullable string arrays (requires pyarrow)

Converting Columns with astype

Extension dtypes integrate with .astype() for consistent null handling

Common Extension Types

  • Int/UInt* – nullable integers ("Int64", "UInt32", etc.)
  • Float* – nullable floats ("Float32", "Float64")
  • boolean – nullable booleans
  • string – efficient string arrays
  • category – categorical data
  • datetime[tz] – datetimes with timezone

Key Takeaways

  • Extension types fix NumPy’s null handling and memory limits
  • Use pd.NA and new dtypes (Int64, boolean, string) for clean pipelines
  • Convert easily with .astype()
  • Widely used in modern pandas versions for robust data cleaning

2.4.
String Manipulation

Overview

  • Goal: Clean and transform text data.
  • Python: Built-ins (split, strip, join, find, replace).
  • Regex: Flexible patterns via re (findall, search, sub).
  • pandas: Vectorized Series.str, NA-safe, regex-aware.

Outline

  1. Python Built-In String Object Methods 💨 →
  2. Regular Expressions 💨 →
  3. String Functions in pandas ⭐️ →

2.4.1.
Python Built-In String Methods 💨

Overview

  • Use core split / strip / join to normalize tokens.
  • in / find / index / count for lookups & positions.
  • replace to swap or remove characters.

Split → Clean → Join

Split on commas, trim whitespace, then rejoin with "::" — a common normalize step.

Find vs Index vs Count

Choose the right lookup: in (boolean), find (pos or −1), index (raises), count (frequency).

Replace

Substitute delimiters or delete characters entirely during cleanup.

2.4.2.
Regular Expressions 💨

Overview

  • Regex = flexible patterns for split, search, extract, replace.
  • Use raw strings r"...\" to avoid escapes.
  • re.compile for repeated use (faster).

Whitespace Split

Split on any run of spaces/tabs/newlines with \s+ — robust tokenization.

Email Pattern: Find & Redact

findall extracts all emails; sub safely redacts them in text.

Capture Groups

Parentheses segment matches (user, domain, suffix) for structured reuse in code or replacements.

2.4.3.
String Functions in pandas ⭐️

Overview

  • Use Series.str for vectorized, NA-safe string ops.
  • Prefer extension dtypes (string, boolean) for nullable results.
  • Regex integrates via contains, findall, extract.

NA-Safe Contains

Series.str.contains works across the column and preserves NA with nullable booleans.

Regex: vectorized findall

Match emails per row; each cell holds a list of (user, domain, suffix) tuples.

Pick & transform to domains

To get the domain of each email: Pull group 2 with .str.get(1)—no loops.

Regex → DataFrame

str.extract returns one column per capture group — great for parsing fields.

💡 Combine with DataFrame.columns= to name the extracted fields.

Quick Slicing

Vectorized preview/truncation without Python loops.

Tips & Notes

  • Prefer re.compile(...) for repeated use
  • Use raw strings r"...\" for regex patterns
  • Adopt string/boolean dtypes for cleaner NA handling
  • Leverage str.extract for structured parsing

2.5.
Categorical Data

Overview

  • Goal: Introduce pandas Categorical type for efficient label storage.
  • Why: Boost performance and reduce memory use in repeated-value columns.
  • Use: Simplify handling of categorical data in stats and ML workflows.

Outline

  1. Background & Motivation ⭐️ →
  2. Categorical Extension Type in pandas ⭐️ →
  3. Computations with Categoricals 💨 →
  4. Categorical Methods ⭐️ →

2.5.1.
Background & Motivation ⭐️

Overview

Why Categorical Data?

  • Many columns repeat a small set of labels (low cardinality).
  • Idea: store labels once; reference by integer codes.
  • Benefits: smaller memory, faster groupby/joins, clear semantics.

Distinct Values & Counts

Find unique labels and their frequencies.

Dictionary Encoding

Store labels once (dimension); store integer codes in main column.

→ Less memory: store integers (codes) instead of full strings (labels).

Restore Labels with take

Map codes back to labels via the dimension table.

Terminology & Why It Matters

  • Categories: the distinct labels (e.g., ['apple','orange']).
  • Codes: integer indices into categories.
  • Perks: rename categories or add new ones without rewriting codes; speed & memory wins.

2.5.2.
Categorical Extension Type in pandas ⭐️

Overview

  • What: pandas Categorical stores integer codes + shared categories.
  • Why: lower memory, faster groupby/joins, clearer semantics.
  • How: astype('category'), pd.Categorical(...), from_codes.

Example DataFrame

Small fruit basket with repeated labels.

Convert to Categorical

Swaps Python strings for compact category codes.

What's Inside: Categorical

Access the underlying categorical array and its pieces.

Codes ↔ Labels Mapping

Quick lookup table for interpreting codes.

In-Place on a DataFrame

Assign the converted column back to df.

Create a Categorical Directly

From any sequence of values.

Construct from Codes

When you already have integer codes and a category list.

Ordered Categoricals

Specify an order for comparisons, sorting, and bins.

Notes & Takeaways

  • Not just strings: categories can be any immutable values.
  • Performance: fewer bytes → faster ops on repeated labels.
  • Accessors: use .cat for category-aware methods (next).

2.5.3.
Computations with Categoricals 💨

Overview

  • Same APIs: Categoricals behave like strings/objects in most ops.
  • Faster ops: groupby, value_counts leverage integer codes.
  • Ordered use: Some functions respect categorical ordering.

Make Quartile Bins with qcut

Vectorized binning returns an ordered Categorical.

Label Bins

Human-friendly quartile names keep the categorical ordering.

GroupBy with Categoricals

Use labels to aggregate numeric data by quartile.

Performance & Memory

Large repeated-label Series benefit from categorical encoding.

→ Huge memory savings: 520 MB → 10 MB

Performance & Memory

Conversion is not free. But it is a one-time cost.

value_counts Speedup

Counts use integer codes under the hood → big win.

Takeaways

  • qcut/cut: produce ordered Categorical bins.
  • GroupBy: faster on categoricals; ordered labels keep sort semantics.
  • Scale: huge memory savings; big speedups for counting/aggregation.

2.5.4.
Categorical Methods ⭐️

Overview

  • Accessor: Use .cat for codes & categories.
  • Manage: Set/rename/reorder/add/remove categories.
  • Modeling: Create dummy (one-hot) variables.

Create & Inspect

Convert labels to category

Create & Inspect

Explore codes and category names via attribute .cat

Set Categories

Declare the full universe of categories (even if absent in data).

value_counts Respects Categories

Unseen categories appear with zero count when defined.

Remove Unused Categories

After filtering, drop categories not present in the data.

Other Handy Methods

Method What it does
add_categories Append new categories.
rename_categories Relabel without changing count.
reorder_categories Change order (can set ordered).
as_ordered / as_unordered Toggle the ordering flag.
remove_categories Drop categories (values → NA).

Dummy Variables (One-Hot)

  • One-hot encoding is common in stats and machine learning.
  • pd.get_dummies creates binary indicator columns for each category.
  • Each row is now a one-hot vector.

Takeaways

  • .cat gives direct access to codes & categories.
  • Define category universe early to control downstream behavior.
  • Trim unused categories post-filter for clarity & memory wins.
  • get_dummies bridges to stats/ML pipelines.

Wrap-up

  • Loading: Efficiently load data from various sources.
  • Cleaning: Handle missing data, duplicates, and outliers.
  • Transformation: Map, replace, bin, and sample efficiently.
  • Strings: Use Series.str for fast text ops.
  • Extension dtypes: Nullable Int64, boolean, string.
  • Categoricals: Encode labels for speed, memory, and modeling.
Next: Time Series Data Processing