Danh sách bài viết

Bài 36: Quan sát nhanh dữ liệu: head, info, describe, dtypes

EDA bước đầu với Pandas: head/tail, shape, columns, index, dtypes, info(), describe(), value_counts(), nunique(), memory_usage(), isna().sum(), corr(). Quy trình quan sát chuẩn sau khi đọc file, cảnh báo dtype object, tối ưu RAM bằng category. Code Python và bài tập.

24/05/2026
12 phút đọc
0 lượt xem
1

Mục tiêu bài học

  • Dùng head, tail, shape, columns, index, dtypes để xem nhanh cấu trúc DataFrame.
  • Dùng info(), describe(), value_counts(), nunique(), memory_usage() cho bước EDA (Exploratory Data Analysis).
  • Phát hiện missing data bằng isna().sum(), xem tương quan cột số bằng corr().
  • Ghi nhớ quy trình EDA 6 bước áp dụng cho mọi dataset mới.
2

Vì sao phải quan sát dữ liệu trước

EDA (Exploratory Data Analysis) là bước nằm giữa đọc file và train model. Bỏ qua bước này, bạn sẽ gặp các tình huống điển hình: feature lẽ ra là số nhưng đang ở kiểu object nên model train xong vẫn lỗi; cột target lệch lớp nghiêm trọng (95% lớp 0 - 5% lớp 1) nhưng model vẫn báo accuracy 95%; outlier ở max kéo lệch chuẩn hoá; missing 30% mà code vẫn chạy do silent fillna.

Pandas cung cấp một nhóm method "quan sát nhanh" — gọi xong là biết ngay cấu trúc, missing, outlier sơ bộ. Đây là 10 phút phải bỏ ra với mọi dataset trước khi viết bất kỳ dòng feature engineering nào.

3

head()tail()

Hai method đơn giản nhất, gọi đầu tiên sau khi đọc file để xác nhận pandas parse đúng:

import pandas as pd

df = pd.read_csv("students.csv")

df.head()       # 5 dòng đầu (mặc định)
df.head(10)     # 10 dòng đầu
df.tail()       # 5 dòng cuối
df.tail(3)      # 3 dòng cuối

Use case chính:

  • Kiểm tra header có đúng tên cột hay đang bị shift sang dòng dữ liệu.
  • Xem ngay format số / chuỗi / ngày có đọc đúng không.
  • tail() hữu ích khi file có dòng tổng (footer) mà read_csv không tự bỏ.
4

shape, columns, index

df.shape       # (rows, cols) — tuple
df.columns     # Index(['id', 'name', 'score', ...])
df.index       # RangeIndex(start=0, stop=N, step=1)

len(df)              # số dòng — tương đương df.shape[0]
df.columns.tolist()  # đổi sang list Python thuần

Ba thuộc tính này là attribute (không có dấu ngoặc), không phải method. Nắm rõ để không gõ nhầm df.shape() rồi gặp TypeError.

5

dtypes — kiểu dữ liệu từng cột

df.dtypes
# id            int64
# name          object
# score       float64
# is_active      bool
# joined       datetime64[ns]
# dtype: object

Các dtype hay gặp:

  • int64, int32, int16, int8 — số nguyên.
  • float64, float32 — số thực.
  • object — gần như luôn là chuỗi Python (Pandas chưa convert sang dtype riêng); cũng có thể là cột mix nhiều kiểu.
  • bool — true/false.
  • datetime64[ns] — thời gian (sau parse_dates hoặc pd.to_datetime).
  • category — chuỗi có ít giá trị unique (gender, country, status). Tốn ít RAM hơn object 5–20 lần.

Cảnh báo: nếu một cột số xuất hiện ở object là dấu hiệu có ký tự lạ trong file (khoảng trắng, dấu phẩy phân nhóm, ký hiệu tiền tệ). Cần clean rồi astype lại, nếu không các thống kê số sau đó sẽ bỏ qua cột này.

# Convert sang category để tiết kiệm RAM
df["country"] = df["country"].astype("category")
df["country"].dtype     # CategoricalDtype(...)
6

info() — tổng quan DataFrame

df.info() in cùng lúc: số dòng, số cột, tên cột, non-null count, dtype, memory usage. Đây là method "1 dòng nắm cả DataFrame".

df.info()
# <class 'pandas.core.frame.DataFrame'>
# RangeIndex: 1000 entries, 0 to 999
# Data columns (total 4 columns):
#  #   Column   Non-Null Count  Dtype
# ---  ------   --------------  -----
#  0   id       1000 non-null   int64
#  1   name     1000 non-null   object
#  2   score    987 non-null    float64    ← 13 missing
#  3   country  1000 non-null   object
# dtypes: float64(1), int64(1), object(2)
# memory usage: 31.4+ KB

Mẹo đọc nhanh: nhìn cột Non-Null Count. Cột nào nhỏ hơn tổng số rows tức là có missing — biết ngay phải xử lý cột đó. Cột memory usage có dấu + nghĩa là chưa tính sâu phần object/string; gọi df.info(memory_usage="deep") để có con số chính xác.

7

describe() — thống kê mô tả

Mặc định describe() chỉ tính cho cột số: count, mean, std, min, percentile 25/50/75, max.

df.describe()
#               score
# count    987.000000
# mean       7.412345
# std        1.205000
# min        2.000000
# 25%        6.500000
# 50%        7.500000
# 75%        8.400000
# max       10.000000

Với cột categorical, truyền tham số include:

df.describe(include="object")
#         name country
# count   1000    1000
# unique  812      5
# top     An       VN
# freq    7        612

df.describe(include="all")   # cả số và categorical chung 1 bảng

Đọc nhanh: min/max giúp phát hiện outlier (score = 250 trong khi thang điểm 10), std rất lớn so với mean báo hiệu phân tán rộng, freq sát count báo hiệu cột categorical gần như chỉ có 1 giá trị.

8

value_counts() cho cột categorical

Method bắt buộc thuộc cho mọi cột phân loại. Trả về Series sắp xếp giảm dần theo count:

df["country"].value_counts()
# VN    612
# US    180
# JP    120
# KR     50
# CN     38
# Name: count, dtype: int64

df["country"].value_counts(normalize=True)    # tỉ lệ thay vì count
# VN    0.612
# US    0.180
# ...

df["country"].value_counts(dropna=False)      # đếm cả NaN

Use case AI quan trọng nhất: kiểm tra cột target trong bài toán phân loại. Nếu lớp 0 chiếm 95%, mọi model "đoán 0" cũng đạt accuracy 95% — đó là class imbalance và sẽ ảnh hưởng tới chọn metric, kỹ thuật resampling.

y = df["churn"]
y.value_counts(normalize=True)
# 0    0.952
# 1    0.048   ← imbalance nặng, cần SMOTE / class_weight
9

nunique()unique()

df["country"].nunique()    # 5 — số giá trị khác nhau
df["country"].unique()     # array(['VN', 'US', 'JP', 'KR', 'CN'])

df.nunique()               # áp dụng cho mọi cột
# id         1000
# name        812
# score        47
# country       5

Quan sát nhanh:

  • nunique == len(df) với cột id → cột này là khoá unique, dùng làm index được.
  • nunique nhỏ (vài chục) trong khi dtype là object → chuyển sang category để tiết kiệm RAM.
  • nunique == 1 → cột constant, vứt được, không mang thông tin cho model.
10

memory_usage() — đo RAM

df.memory_usage()              # bytes mỗi cột (không sâu)
df.memory_usage(deep=True)     # bao gồm cả bytes thật của string

# Tổng RAM tính bằng MB
total_mb = df.memory_usage(deep=True).sum() / 1e6
print(f"DataFrame RAM = {total_mb:.2f} MB")

Với cột object chứa chuỗi, deep=False chỉ đếm pointer (8 byte/dòng) — sai số rất lớn. Luôn dùng deep=True khi muốn biết RAM thực.

Use case: trước khi train model trên VRAM hạn chế, ép dtype hẹp (int32, float32, category) rồi gọi lại memory_usage(deep=True).sum() để xác nhận mức giảm. Việc này gắn liền với phần dtype optimization ở Bài 35.

11

isna().sum() — đếm missing

df.isna()                   # DataFrame bool cùng shape
df.isna().sum()             # số missing mỗi cột
df.isna().sum().sum()       # tổng missing toàn DataFrame
df.isna().mean()            # tỉ lệ missing mỗi cột (0..1)

# Top 5 cột có nhiều missing nhất
df.isna().sum().sort_values(ascending=False).head(5)

Đây là preview. Chiến lược xử lý missing (dropna, fillna, mean/median/mode imputation, KNN imputer) sẽ deep ở Bài 40 trong nhóm này.

12

corr() — correlation matrix

df.corr(numeric_only=True)
#           score   age   gpa
# score      1.00  0.12  0.78
# age        0.12  1.00  0.05
# gpa        0.78  0.05  1.00

Cho biết hệ số tương quan Pearson (mặc định) giữa các cột số. Giá trị trong khoảng [-1, 1]: gần 1/-1 là tương quan tuyến tính mạnh, gần 0 là không có tương quan tuyến tính.

Use case: feature selection sơ bộ — nếu 2 cột có |corr| > 0.95 thì gần như trùng lặp thông tin, có thể bỏ 1. Phân tích sâu (Pearson vs Spearman, partial correlation, multicollinearity) sẽ ở Series 2 (Machine Learning).

13

Quy trình EDA chuẩn 6 bước

Mỗi lần load 1 dataset mới, chạy y nguyên 6 bước này:

  1. df.shape — biết kích thước (cảnh báo nếu file đọc thiếu / dư dòng).
  2. df.head() — xem 5 dòng đầu, check parse header đúng.
  3. df.info() — dtype, non-null, memory.
  4. df.describe(include="all") — stats số + categorical.
  5. df.isna().sum() — cột nào missing, mức độ bao nhiêu.
  6. df["target"].value_counts(normalize=True) (với bài toán classification) — phát hiện imbalance.

6 dòng code, < 30 giây chạy, nhưng tránh được rất nhiều bug phía sau pipeline.

14

Use case AI/ML

  • Phát hiện missing bằng info() + isna().sum() → chọn strategy dropna (nếu < 5% và random) hoặc fillna bằng mean/median/mode/KNN.
  • Phát hiện dtype sai bằng dtypes → cột số đang ở object phải clean ký tự và astype lại.
  • Phát hiện outlier sơ bộ qua min/max của describe() → quyết định clip, winsorize, hay điều tra ghi nhận lỗi.
  • Phát hiện class imbalance qua value_counts(normalize=True) cột target → chọn metric (F1, ROC-AUC thay vì accuracy), kỹ thuật resampling (SMOTE), class_weight.
  • Tối ưu RAM trước khi train trên GPU/máy yếu: xem memory_usage(deep=True), ép category/int32/float32.
  • Sanity check sau preprocessing: chạy lại 6 bước EDA sau khi clean để chắc chắn không vô tình tạo missing mới hoặc đổi nhầm dtype.
15

Code Python tổng hợp

Tạo DataFrame sinh viên có chủ đích lẫn missing + imbalance, chạy đầy đủ pipeline EDA:

import numpy as np
import pandas as pd

# 1) Tạo dữ liệu mẫu — có NaN, mixed dtype
rng = np.random.default_rng(42)

df = pd.DataFrame({
    "id":      range(1, 21),
    "name":    [f"SV{i:02d}" for i in range(1, 21)],
    "score":   rng.normal(7, 1.2, size=20).round(2),
    "country": rng.choice(["VN", "US", "JP"], size=20, p=[0.7, 0.2, 0.1]),
    "passed":  rng.choice([0, 1], size=20, p=[0.15, 0.85]),  # imbalance
    "dob":     pd.to_datetime("2003-01-01") + pd.to_timedelta(
                   rng.integers(0, 365, size=20), unit="D"),
})

# Cố tình thêm vài NaN ở score
df.loc[[3, 7, 15], "score"] = np.nan

# 2) Quy trình EDA 6 bước
print("shape:", df.shape)
print(df.head())
df.info()
print(df.describe(include="all"))
print("missing:")
print(df.isna().sum())
print("target distribution:")
print(df["passed"].value_counts(normalize=True))

# 3) Quan sát thêm
print("dtypes:\n", df.dtypes)
print("nunique:\n", df.nunique())
print("memory (MB):", df.memory_usage(deep=True).sum() / 1e6)

# 4) Convert country -> category, đo lại memory
df["country"] = df["country"].astype("category")
print("memory sau category (MB):",
      df.memory_usage(deep=True).sum() / 1e6)

# 5) Correlation giữa các cột số
print(df.corr(numeric_only=True))

Chạy đoạn này trên notebook một lần là bạn đã có toàn bộ thông tin cần để quyết định bước preprocessing tiếp theo: xử lý 3 NaN ở score, encode country, chọn metric cho cột passed bị imbalance.

16

Bài tập

  1. Tạo 1 DataFrame 10 dòng với 4 cột mixed dtype (int, float, string, datetime). Chạy df.info()df.describe(include="all"), xác nhận các cột categorical xuất hiện ở output.
  2. Cho 1 dataset bất kỳ (ví dụ Titanic tải từ URL ở Bài 35), in ra 5 cột có nhiều missing nhất kèm số missing và tỉ lệ missing.
    na_summary = pd.concat([
        df.isna().sum().rename("count"),
        df.isna().mean().rename("ratio"),
    ], axis=1).sort_values("count", ascending=False).head(5)
    print(na_summary)
    
  3. Tính tổng memory (MB) của DataFrame Titanic bằng memory_usage(deep=True). Convert cột SexEmbarked sang category, đo lại memory và in ra phần trăm giảm.
  4. (Mở rộng) Viết hàm quick_eda(df) nhận DataFrame, in lần lượt 6 bước EDA chuẩn. Tham số tuỳ chọn target=None; nếu có target thì in thêm value_counts(normalize=True).
17

Tóm tắt

  • head() / tail() — xem nhanh dòng đầu/cuối, dùng để check parse header đúng.
  • shape, columns, index — attribute, không phải method.
  • dtypes — phát hiện cột số đang ở object (lỗi parse) và cột nên convert sang category.
  • info() — 1 lệnh nắm shape, dtype, non-null, memory.
  • describe() — stats số mặc định; thêm include="object" hoặc include="all" cho categorical.
  • value_counts(normalize=True, dropna=False) — đếm tần suất, phát hiện class imbalance.
  • nunique() / unique() — đánh giá cardinality, chọn cột cho category, vứt cột constant.
  • memory_usage(deep=True) — đo RAM thực, dẫn đường cho dtype optimization.
  • isna().sum() — preview missing; chiến lược xử lý ở Bài 40.
  • corr() — tương quan tuyến tính giữa cột số, dùng feature selection sơ bộ.
  • Quy trình EDA chuẩn 6 bước: shapeheadinfodescribeisnavalue_counts(target).