Bộ dữ liệu này là một tập hợp thông tin đa chiều và quy mô lớn, bao gồm 200,000 quan sát trên 25 biến số, cung cấp cái nhìn toàn diện về thị trường Bất động sản (Real Estate) và các yếu tố liên quan đến Tài chính/Tín dụng Khách hàng trong các giao dịch nhà ở. Dữ liệu bao quát các yếu tố Cung (như property_size_sqft, property_type, price), yếu tố Cầu và Khả năng Chi trả (customer_salary, loan_amount, emi_to_income_ratio), cùng với các yếu tố Ngoại cảnh/Đánh giá (neighbourhood_rating, satisfaction_score) và Kết quả Quyết định (decision). Quy mô lớn này là nền tảng vững chắc để thực hiện các phân tích.từ đó đưa ra những nhận xét có ý nghĩa thực tiễn cho các bên liên quan trong lĩnh vực bất động sản và tài chính ngân hàng.
data <- read.csv("C:/Users/ACER/OneDrive/Documents/tmt/filehoc.csv",header = TRUE)
Hàm được sử dụng là read.csv(), đây là một hàm bao (wrapper function) của read.table(), chuyên dùng để đọc các tệp dữ liệu được phân cách bằng dấu phẩy (CSV) vào môi trường R dưới dạng một Data Frame.
Cấu trúc hàm gốc bao gồm các tham số cốt lõi sau:
read.csv(file, header = TRUE, sep = “,”, dec = “.”, stringsAsFactors = TRUE, …) nạp gói
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(moments) # để tính skewness, kurtosis
library(broom)
library(ggplot2)
library(tidyr)
library(stringr)
library(corrplot)
## corrplot 0.95 loaded
Lệnh install.packages() được sử dụng để tải xuống và cài đặt một gói (package) từ kho lưu trữ CRAN (Comprehensive R Archive Network) hoặc các nguồn khác vào hệ thống máy tính
Cấu trúc hàm gốc:
install.packages(pkgs, lib = NULL, repos = getOption(“repos”), …)
Trong đó:
pkgs: tham số BẮT BUỘC là tên của một hoặc nhiều gói cần cài đặt. Tên gói phải được đặt trong dấu ngoặc kép (ví dụ: “ggplot2”) vì nó được truyền vào dưới dạng chuỗi ký tự
lib = NULL: tham số không bắt buộc và mặc định là: NULL. R sẽ tự động cài đặt vào thư mục thư viện mặc định của người dùng.
repos = getOption(“repos”): tham số không bắt buộc và mặc định : getOption(“repos”). R sử dụng kho CRAN đã được cấu hình mặc định. Tham số này hiếm khi cần thay đổi trừ khi cài đặt từ các kho lưu trữ gói tùy chỉnh khác.
dim(data)
## [1] 200000 25
Trong đó:
x: Tham số bắt buộc. Đây là đối tượng mà bạn muốn xác định kích thước. Trong R, x thường là một matrix, array, hoặc data frame*.
dim() không có tham số bổ sung nào khác ngoài đối tượng đầu vào (x) khi được sử dụng để truy xuất kích thước.
Dòng code được sử dụng: dim(data)
Lệnh này yêu cầu R trả về kích thước của đối tượng Data Frame data đã được nạp ở bước trước.
Kết quả trả về là một vector số: (200000, 25).
200000 (Hàng): Đây là số lượng quan sát (observations) hay bản ghi (records) của dữ liệu. Mỗi hàng đại diện cho một giao dịch bất động sản hoàn chỉnh (bao gồm thuộc tính tài sản, thông tin khách hàng, và quyết định mua,…).
25 (Cột): Đây là số lượng biến số (variables) được thu thập cho mỗi quan sát. Các biến này bao gồm các yếu tố về bất động sản, tài chính khách hàng, và các chỉ số đánh giá khác.
Tầm quan trọng: Việc xác nhận kích thước này cho thấy dữ liệu đã được nạp thành công, không bị mất mát quan sát hay cột nào trong quá trình read.csv().
head(data, 5)
## property_id country city property_type furnishing_status
## 1 1 France Marseille Farmhouse Semi-Furnished
## 2 2 South Africa Cape Town Apartment Semi-Furnished
## 3 3 South Africa Johannesburg Farmhouse Semi-Furnished
## 4 4 Germany Frankfurt Farmhouse Semi-Furnished
## 5 5 South Africa Johannesburg Townhouse Fully-Furnished
## property_size_sqft price constructed_year previous_owners rooms bathrooms
## 1 991 412935 1989 6 6 2
## 2 1244 224538 1990 4 8 8
## 3 4152 745104 2019 5 2 1
## 4 3714 1110959 2008 1 3 3
## 5 531 99041 2007 6 3 3
## garage garden crime_cases_reported legal_cases_on_property customer_salary
## 1 1 1 1 0 10745
## 2 1 1 1 1 16970
## 3 1 1 0 0 21914
## 4 0 1 0 0 17980
## 5 1 1 3 1 17676
## loan_amount loan_tenure_years monthly_expenses down_payment
## 1 193949 15 6545 218986
## 2 181465 20 8605 43073
## 3 307953 30 2510 437151
## 4 674720 15 8805 436239
## 5 65833 25 8965 33208
## emi_to_income_ratio satisfaction_score neighbourhood_rating
## 1 0.16 1 5
## 2 0.08 9 1
## 3 0.09 6 8
## 4 0.33 2 6
## 5 0.03 3 3
## connectivity_score decision
## 1 6 0
## 2 2 0
## 3 1 0
## 4 6 0
## 5 4 0
Hàm được sử dụng là head(), một hàm cơ bản (base R) dùng để trả về các phần tử đầu tiên (hàng) của một đối tượng.
Cấu trúc hàm gốc: head(x, n = 6L, …)
x: Tham số BẮT BUỘC. Là đối tượng (Data Frame data) mà bạn muốn xem các hàng đầu tiên.
n: Tham số không bắt buộc. Chỉ định số lượng hàng đầu tiên muốn hiển thị .Giá trị mặc định: n = 6L (6 hàng).
Dòng code được sử dụng: head(data, 5)
Lệnh này có mục đích là thực hiện kiểm tra chất lượng dữ liệu sơ bộ để xem giá trị của 5 hàng đầu tiên có được đọc chính xác không, đặc biệt là các biến định tính và định lượng chính.
Kết quả hiển thị 5 quan sát đầu tiên và tất cả 25 biến số.
Cho thấy các biến Định tính (country, property_type, furnishing_status) đã được đọc dưới dạng chuỗi ký tự (như ‘France’, ‘Marseille’, ‘Farmhouse’). Các biến định lượng (price, property_size_sqft, customer_salary,…) đã được đọc dưới dạng số. Điều này xác nhận rằng quá trình nạp dữ liệu ở bước read.csv() đã thành công.
names(data)
## [1] "property_id" "country"
## [3] "city" "property_type"
## [5] "furnishing_status" "property_size_sqft"
## [7] "price" "constructed_year"
## [9] "previous_owners" "rooms"
## [11] "bathrooms" "garage"
## [13] "garden" "crime_cases_reported"
## [15] "legal_cases_on_property" "customer_salary"
## [17] "loan_amount" "loan_tenure_years"
## [19] "monthly_expenses" "down_payment"
## [21] "emi_to_income_ratio" "satisfaction_score"
## [23] "neighbourhood_rating" "connectivity_score"
## [25] "decision"
Hàm được sử dụng là names(), một hàm cơ bản (base R) dùng để truy xuất hoặc thiết lập tên của các phần tử (thường là tên cột) trong một đối tượng, chẳng hạn như Data Frame.
Cấu trúc hàm gốc: names(x)
Trong đó:
x: Tham số BẮT BUỘC. Là đối tượng (Data Frame data) mà bạn muốn truy xuất tên các cột.
names() không có tham số bổ sung nào khác ngoài đối tượng đầu vào (x) khi được sử dụng để truy xuất tên.
Dòng code được sử dụng: names(data)
Mục đích của lệnh này là kiểm tra danh sách đầy đủ và chính xác của các biến số đã được nạp từ tệp CSV, đảm bảo rằng tên cột đã được đọc đúng từ dòng tiêu đề (header = TRUE) và không bị lỗi mã hóa ký tự.
Kết quả khẳng định rằng tham số header = TRUE đã hoạt động chính xác, đảm bảo tên biến được gán đầy đủ.
(sapply(data, class))
## property_id country city
## "integer" "character" "character"
## property_type furnishing_status property_size_sqft
## "character" "character" "integer"
## price constructed_year previous_owners
## "integer" "integer" "integer"
## rooms bathrooms garage
## "integer" "integer" "integer"
## garden crime_cases_reported legal_cases_on_property
## "integer" "integer" "integer"
## customer_salary loan_amount loan_tenure_years
## "integer" "integer" "integer"
## monthly_expenses down_payment emi_to_income_ratio
## "integer" "integer" "numeric"
## satisfaction_score neighbourhood_rating connectivity_score
## "integer" "integer" "integer"
## decision
## "integer"
Nếu FALSE, kết quả sẽ không có tên.
Dòng code được sử dụng: sapply(data, class)
Lệnh này có mục đích là kiểm tra kiểu dữ liệu (Data Type Inspection) của tất cả 25 biến số trong Data Frame data.
Kết quả chạy lệnh sapply(data, class) cho thấy tập dữ liệu có sự phân bổ rõ ràng giữa các kiểu dữ liệu, xác nhận cấu trúc đa chiều của nó.
21 biến là Định lượng (Quantitative), bao gồm các biến tài chính cốt lõi như price, customer_salary, loan_amount,…(kiểu Numeric/Integer). Sự chính xác về kiểu dữ liệu này khẳng định các biến sẵn sàng cho các phép toán thống kê mô tả (Mean, SD) và mô hình hồi quy OLS. Chỉ có 4 biến là Định tính (Qualitative), bao gồm country, city, property_type, và furnishing_status (kiểu object/Character).
colSums(is.na(data))
## property_id country city
## 0 0 0
## property_type furnishing_status property_size_sqft
## 0 0 0
## price constructed_year previous_owners
## 0 0 0
## rooms bathrooms garage
## 0 0 0
## garden crime_cases_reported legal_cases_on_property
## 0 0 0
## customer_salary loan_amount loan_tenure_years
## 0 0 0
## monthly_expenses down_payment emi_to_income_ratio
## 0 0 0
## satisfaction_score neighbourhood_rating connectivity_score
## 0 0 0
## decision
## 0
(sum(duplicated(data)))
## [1] 0
(summary(data$price))
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 56288 565990 1023429 1215365 1725557 4202732
Hàm được sử dụng là summary(), một hàm cơ bản (base R) dùng để cung cấp bản tóm tắt thống kê của một đối tượng. Khi áp dụng cho một biến định lượng như data$price, nó sẽ trả về 6 chỉ số thống kê vị trí chính.
Cấu trúc hàm gốc: summary(object, …)
Trong đó:
object: Tham số BẮT BUỘC. Là đối tượng cần tóm tắt. Trong trường hợp này là data$price, tức là cột price (Giá nhà) từ Data Frame data.
…: Tham số không bắt buộc. Dùng để truyền các đối số bổ sung nếu cần.
Dòng code được sử dụng: summary(data$price)
Mục đích là cung cấp cái nhìn tổng quan đầu tiên về phân phối của giá nhà, là biến mục tiêu quan trọng nhất trong các mô hình định giá.
Kết quả tóm tắt thống kê của biến Giá nhà (price) cho thấy sự phân bổ giá trị không đồng đều trên thị trường.
Với giá trị Trung bình (Mean) là 1,215,365 cao hơn đáng kể so với Trung vị (Median) là 1,023,429, phân phối giá nhà xác nhận bị lệch *phải dương** (Positive Skewness). Điều này hàm ý rằng thị trường tồn tại một số lượng đáng kể các giao dịch có giá trị rất ca kéo mức giá trung bình lên.
Phạm vi giá trải dài từ mức thấp nhất Min 56,288 đến mức cao nhất Max. 4,202,732. Phân tích Tứ phân vị (Quartiles) mang lại cái nhìn sâu sắc về cấu trúc thị trường. Phân vị thứ nhất (Q1) là 565,990. Đây là ngưỡng giá mà 25% tổng số giao dịch nằm dưới mức này, xác định phân khúc thị trường giá thấp).
Khoảng Thanh khoản Cao: Khoảng giá từ Q1 565,990 đến Q3 1,725,557 bao gồm 50% các giao dịch, xác định đây là khu vực có tính thanh khoản (Liquidity) cao nhất trên thị trường.
Phân khúc Cao cấp: Giá trị Phân vị thứ 3 (Q3) 1,725,557 là ngưỡng quan trọng để định nghĩa phân khúc giá cao.
(names(select_if(data, is.numeric)))
## [1] "property_id" "property_size_sqft"
## [3] "price" "constructed_year"
## [5] "previous_owners" "rooms"
## [7] "bathrooms" "garage"
## [9] "garden" "crime_cases_reported"
## [11] "legal_cases_on_property" "customer_salary"
## [13] "loan_amount" "loan_tenure_years"
## [15] "monthly_expenses" "down_payment"
## [17] "emi_to_income_ratio" "satisfaction_score"
## [19] "neighbourhood_rating" "connectivity_score"
## [21] "decision"
lapply(select(data, country, property_type, furnishing_status), function(x) length(unique(x)))
## $country
## [1] 13
##
## $property_type
## [1] 6
##
## $furnishing_status
## [1] 3
tail(data,5)
## property_id country city property_type furnishing_status
## 199996 199996 Germany Berlin Villa Fully-Furnished
## 199997 199997 China Shenzhen Townhouse Unfurnished
## 199998 199998 Japan Kyoto Villa Semi-Furnished
## 199999 199999 South Africa Johannesburg Apartment Unfurnished
## 200000 200000 Brazil Rio de Janeiro Apartment Semi-Furnished
## property_size_sqft price constructed_year previous_owners rooms
## 199996 685 203328 1968 1 3
## 199997 3818 1454627 1977 5 7
## 199998 3603 1619147 1990 2 4
## 199999 1706 306165 2010 0 4
## 200000 3652 732698 1986 0 1
## bathrooms garage garden crime_cases_reported legal_cases_on_property
## 199996 2 0 0 1 0
## 199997 5 1 1 1 0
## 199998 4 1 1 1 0
## 199999 1 1 0 0 1
## 200000 1 1 0 3 0
## customer_salary loan_amount loan_tenure_years monthly_expenses
## 199996 78330 104050 15 17670
## 199997 25400 1175297 20 2865
## 199998 28220 743049 30 5595
## 199999 12240 150774 15 16300
## 200000 22644 548714 30 5165
## down_payment emi_to_income_ratio satisfaction_score neighbourhood_rating
## 199996 99278 0.01 8 4
## 199997 279330 0.34 7 10
## 199998 876098 0.17 5 3
## 199999 155391 0.11 6 10
## 200000 183984 0.15 6 4
## connectivity_score decision
## 199996 5 1
## 199997 9 1
## 199998 9 0
## 199999 6 0
## 200000 9 0
Hàm được sử dụng là tail(), một hàm cơ bản (base R) dùng để trả về các phần tử cuối cùng (hàng) của một đối tượng
Cấu trúc hàm gốc: tail(x, n = 6L, …)
Trong đó:
x: Tham số BẮT BUỘC. Là đối tượng (Data Frame data) mà bạn muốn xem các hàng cuối cùng.
n: Tham số không bắt buộc. Chỉ định số lượng hàng cuối cùng muốn hiển thị. Giá trị mặc định: n = 6L (6 hàng).
Dòng code được sử dụng: tail(data, 5)
Mục đích của lệnh này là kiểm tra tính toàn vẹn của dữ liệu đến cuối tệp (dòng \(200,000\)) và đảm bảo rằng 5 hàng cuối cùng của tập dữ liệu không có sự sai lệch hoặc mất mát.
Kết quả hiển thị 5* quan sát cuối cùng (từ quan sát thứ \(199,996\) đến \(200,000\)) và tất cả 25 biến số.
Các quan sát cuối cùng tiếp tục thể hiện tính đa dạng cao về biến country (Germany, China, Japan, South Africa, Brazil) và loại hình tài sản (Villa, Townhouse, Apartment).
Giá trị property_id kết thúc ở \(200,000\), khớp với kích thước tổng thể của Data Frame (dim(data)), xác nhận dữ liệu đã được nạp đủ đến quan sát cuối cùng.
Các biến số lượng và định tính ở cuối tệp vẫn được đọc chính xác như các quan sát đầu tiên, xác nhận tính nhất quán về kiểu dữ liệu trên toàn bộ \(200,000\) quan sát. #### 1.1.12. Hiển thị các giá trị duy nhất(biến country)
unique(data$country)
## [1] "France" "South Africa" "Germany" "Canada" "Brazil"
## [6] "UAE" "Australia" "UK" "USA" "China"
## [11] "Singapore" "India" "Japan"
Hàm được sử dụng là unique(), một hàm cơ bản (base R) dùng để trả về các giá trị không bị lặp lại (duy nhất) của một vector.
Cấu trúc hàm gốc: unique(x)
x: Tham số BẮT BUỘC. Là vector đầu vào mà bạn muốn trích xuất các giá trị duy nhất. Trong trường hợp này là data$country.
Dòng code được sử dụng: unique(data$country)
Lệnh này có mục đích là xác định toàn bộ các cấp độ (levels) của biến định tính country.
Kết quả là lệnh sẽ trả về một vector chứa 13 tên quốc gia duy nhất mà không có sự lặp lại. #### 1.1.13. Hiển thị bảng tần suất
table(data$country)
##
## Australia Brazil Canada China France Germany
## 15442 15397 15401 15536 15628 15408
## India Japan Singapore South Africa UAE UK
## 15357 15317 15278 15401 15141 15413
## USA
## 15281
Hàm được sử dụng là table(), một hàm cơ bản (base R) dùng để xây dựng bảng tần suất (frequency table) của một hoặc nhiều biến số định tính.
Cấu trúc hàm gốc:
table(…, exclude = if (useNA == “no”) c(NA, NaN), useNA = c(“no”, “ifany”, “always”), dnn = list.names(…),deparse.level = 1)
Trong đó:
…: Tham số BẮT BUỘC. Là vector (hoặc danh sách các vector) mà bạn muốn đếm tần suất. Trong trường hợp này là data$country.
useNA (mặc định là “no”) kiểm soát cách R xử lý các giá trị NA: nếu đặt là “ifany” hoặc “always”, hàm sẽ thêm một hàng/cột vào bảng để đếm tần suất của NA, giúp kiểm soát chất lượng dữ liệu.
exclude hoạt động bổ sung, cho phép người dùng chỉ định các giá trị (bao gồm NA hoặc NaN) cần loại trừ khỏi quá trình đếm.
Cuối cùng, tham số dnn (dimension names) cho phép đặt tên tường minh cho các chiều của bảng, rất hữu ích khi tạo bảng tương quan chéo, trong khi deparse.level (mặc định là 1) kiểm soát cách R tạo tên chiều mặc định khi dnn không được chỉ định.
Dòng code được sử dụng: table(data$country)
Mục đích của lệnh này là xác định thị phần của mỗi quốc gia trong tập dữ liệu, một bước thiết yếu trong phân tích dữ liệu định tính.
Bảng tần suất trả về số lượng giao dịch cho 13 quốc gia
Kết quả cho thấy số lượng giao dịch (tần suất) của các quốc gia dao động trong phạm vi từ 15,141 (UAE) đến 15,628 (France). Sự đồng đều này là lý tưởng. Nó khẳng định dữ liệu không bị thiên lệch mẫu nghiêm trọng về mặt địa lý, đảm bảo rằng không có quốc gia nào chi phối quá mức tập dữ liệu. #### 1.1.14. Giải thích ý nghĩa các biến
ten_bien_goc <- c(
"property_id", "country", "city", "property_type", "furnishing_status",
"property_size_sqft", "price", "constructed_year", "previous_owners",
"rooms", "bathrooms", "garage", "garden", "crime_cases_reported",
"legal_cases_on_property", "customer_salary", "loan_amount",
"loan_tenure_years", "monthly_expenses", "down_payment",
"emi_to_income_ratio", "satisfaction_score", "neighbourhood_rating",
"connectivity_score", "decision"
)
y_nghia_giai_thich <- c(
"ID Bất động sản", "Quốc gia", "Thành phố", "Loại hình Bất động sản", "Tình trạng Nội thất",
"Diện tích Bất động sản( m²)", "Giá bán Bất động sản", "Năm Xây dựng", "Số Chủ sở hữu Trước",
"Số phòng", "Số phòng tắm", "Có Garage (1=Có, 0=Không)", "Có Vườn (1=Có, 0=Không)",
"Số Vụ án Báo cáo (Khu vực)", "Có Vụ kiện Pháp lý (1=Có, 0=Không)", "Thu nhập Hàng tháng Khách hàng",
"Số tiền Vay", "Thời hạn Vay (năm)", "Chi phí Hàng tháng", "Khoản Đặt cọc (Down Payment)",
"Tỷ lệ EMI/Thu nhập (Rủi ro Tín dụng)", "Điểm Hài lòng (1-10)", "Điểm Khu vực Lân cận (1-10)",
"Điểm Kết nối Giao thông (1-10)", "Quyết định Mua (1=Có, 0=Không)")
bang_chu_giai <- data.frame(
Ten_Bien_Goc = ten_bien_goc,
Y_Nghia_Giai_Thich = y_nghia_giai_thich
)
View(bang_chu_giai)
Hàm c() (Combine) là một trong những hàm cơ bản nhất, dùng để kết hợp các đối tượng thành một vector duy nhất. Nó được sử dụng thường xuyên đến mức gần như vô hình trong code R.
Cấu trúc hàm gốc: c(…, recursive = FALSE)
… là tham số Bắt buộc.Đây là nơi truyền vào các đối tượng (thường là các vector hoặc các giá trị đơn) mà bạn muốn kết hợp lại với nhau. Hàm c() sẽ tạo một vector mới từ các đối tượng này.
recursive = FALSE là tham số không bắt buộc và mặc định sẽ là FALSE, đảm bảo vector được tạo ra là phẳng, không lồng nhau.
Hàm data.frame() là hàm dùng để tạo ra DataFrame, cấu trúc dữ liệu cơ bản và phổ biến nhất để lưu trữ dữ liệu bảng (giống như bảng tính Excel) trong R.
Cấu trúc hàm gốc:
data.frame(…, row.names = NULL, check.rows = FALSE, check.names = TRUE, stringsAsFactors = TRUE)
Trong đó:
Tham số BẮT BUỘC đầu tiên là … (vector đầu vào), yêu cầu các vector cung cấp phải có cùng độ dài để Data Frame được hình thành chính xác.
stringsAsFactors = TRUE là tham số cần được lưu ý nhất (mặc định trong R Base), nó tự động chuyển đổi các cột chuỗi ký tự (Character) thành kiểu Factor
Tham số row.names = NULL (mặc định) chỉ định R tự động gán chỉ mục hàng bằng số thứ tự. Bên cạnh đó, tham số check.names = TRUE (mặc định) đảm bảo tính hợp lệ cú pháp của tên cột trong R bằng cách thay thế các ký tự không hợp lệ (ví dụ: khoảng trắng) bằng dấu chấm (.).
Cuối cùng, check.rows = FALSE (mặc định) cho phép R hoạt động hiệu quả hơn nhưng yêu cầu người dùng phải tự đảm bảo các vector có cùng độ dài
Hàm View() trong R là một hàm rất hữu ích, đặc biệt khi làm việc với các DataFrame lớn.
Cấu trúc hàm gốc: View(x)
Trong đó:
x là tham số “Bắt buộc”. Đây là đối tượng muốn xem, thường là một data.frame, matrix, hoặc list.
Kết quả trả về là R đã thực thi thành công việc tạo đối tượng Data Frame bang_chu_giai với kích thước 25 hàng và 2 cột.
Bảng dữ liệu này đóng vai trò là tài liệu hóa cốt lõi. Nó thực hiện việc ánh xạ tường minh các tên biến kỹ thuật(cột Ten_Bien_Goc) sang ý nghĩa diễn giải bằng ngôn ngữ kinh tế học thuật (Y_Nghia_Giai_Thich), nhằm minh bạch hóa báo cáo và hỗ trợ giải thích các biến.
Kết quả này khẳng định tính minh bạch và logic của báo cáo, cho phép người đọc dễ dàng đối chiếu \(25\) biến số, từ các yếu tố cốt lõi như price (“Giá bán Bất động sản”) và customer_salary (“Thu nhập Hàng tháng Khách hàng”), đến các chỉ số rủi ro quan trọng như emi_to_income_ratio (“Tỷ lệ EMI/Thu nhập”) và legal_cases_on_property (“Có Vụ kiện Pháp lý”). Việc tổ chức Data Frame này là bước nền tảng để đảm bảo sự chính xác trong diễn giải các hệ số hồi quy sau này, giúp chuyển đổi kết quả thống kê thuần túy sang nhận định kinh tế thực tiễn một cách rõ ràng.
Lệnh colSums(is.na(x)) là sự kết hợp của hai hàm cơ bản (Base R) để thực hiện kiểm toán chất lượng dữ liệu.
colSums(is.na(data))
## property_id country city
## 0 0 0
## property_type furnishing_status property_size_sqft
## 0 0 0
## price constructed_year previous_owners
## 0 0 0
## rooms bathrooms garage
## 0 0 0
## garden crime_cases_reported legal_cases_on_property
## 0 0 0
## customer_salary loan_amount loan_tenure_years
## 0 0 0
## monthly_expenses down_payment emi_to_income_ratio
## 0 0 0
## satisfaction_score neighbourhood_rating connectivity_score
## 0 0 0
## decision
## 0
Cấu trúc hàm gốc: colSums(x, na.rm = FALSE, …) Trong đó:
Nếu na.rm=TRUE: R sẽ loại bỏ tất cả các giá trị NA trước khi tính tổng.
Ý nghĩa: Tính tổng các giá trị trong mỗi cột của đối tượng x.
Khi áp dụng cho ma trận logic (TRUE/FALSE) từ is.na(data), R tự động chuyển đổi TRUE thành 1 và FALSE thành 0. Do đó, colSums() tính tổng (count) số lần xuất hiện của TRUE (NA) trong mỗi cột. Dòng code được sử dụng:
colSums(is.na(data))
## property_id country city
## 0 0 0
## property_type furnishing_status property_size_sqft
## 0 0 0
## price constructed_year previous_owners
## 0 0 0
## rooms bathrooms garage
## 0 0 0
## garden crime_cases_reported legal_cases_on_property
## 0 0 0
## customer_salary loan_amount loan_tenure_years
## 0 0 0
## monthly_expenses down_payment emi_to_income_ratio
## 0 0 0
## satisfaction_score neighbourhood_rating connectivity_score
## 0 0 0
## decision
## 0
sum(duplicated(data))
## [1] 0
sum(duplicated(data))
## [1] 0
df <- data
Lệnh này không sử dụng một hàm phức tạp, mà sử dụng toán tử gán <- (Assignment Operator). Cấu trúc hàm gốc: target <- value Trong đó:
value: Tham số BẮT BUỘC. Là đối tượng nguồn cần sao chép. Trong trường hợp này là data (Data Frame gốc, 200,000 hàng, 25 cột).
target: Tham số BẮT BUỘC. Là tên của đối tượng mới được tạo ra và lưu trữ bản sao của value.
Trong trường hợp này là df. Toán tử <-: Toán tử gán chính thức trong R, dùng để lưu trữ value vào target. - Dòng code được sử dụng:
df <- data
Lệnh df <- data tạo ra một bản sao (copy) hoàn chỉnh của đối tượng Data Frame data và gán nó cho tên mới là df
Mục đích: Đảm bảo rằng mọi thao tác tiền xử lý, chuẩn hóa, lọc hoặc tạo biến mới sẽ chỉ được thực hiện trên df. Data Frame gốc data được giữ lại nguyên vẹn như một bản sao lưu (backup) hoặc nguồn đối chiếu. Điều này cho phép người nghiên cứu quay lại trạng thái dữ liệu ban đầu nếu có lỗi xảy ra trong quá trình tiền xử lý phức tạp.
Kết quả: Thực thi lệnh df <- data dẫn đến việc
tạo ra một đối tượng Data Frame mới là df, bản sao hoàn chỉnh của dữ
liệu gốc data gồm 200,000 quan sát và 25 biến số).
names(df) <- tolower(gsub(" ", "_", names(df)))
df$country <- as.factor(df$country)
df$property_type <- as.factor(df$property_type)
df$furnishing_status <- as.factor(df$furnishing_status)
df$city <- as.factor(df$city)
Hàm được sử dụng là as.factor(), một hàm cơ bản (Base R) dùng để
chuyển đổi một vector đầu vào thành kiểu Factor. Cấu trúc hàm gốc:
as.factor(x) - x: Tham số BẮT BUỘC. Là vector chuỗi ký tự hoặc số (ví
dụ: df\(country) cần được chuyển đổi.
Khối code đã dùng:
df\)country <- as.factor(df\(country)
df\)city <- as.factor(df\(city)
df\)property_type <- as.factor(df\(property_type)
df\)furnishing_status <- as.factor(df$furnishing_status) - Mục
đích là chuyển đổi bốn biến định tính quan trọng (ban đầu là kiểu
Character/Object) sang kiểu Factor, vì đây là những yếu tố phân loại cốt
lõi trong phân tích. Các cột này trong Data Frame df được thay thế bằng
phiên bản Factor của chúng. Ví dụ cột country sẽ được lưu trữ dưới dạng
các số nguyên 1 đến 13 kèm theo nhãn cấp độ (tên quốc gia).
Khối lệnh này sử dụng một chuỗi các hàm cốt lõi từ gói dplyr (Tidyverse) để thực hiện thao tác phân khúc hóa dữ liệu (data segmentation) và tổng hợp thống kê (aggregation)
Hàm mutate(…): Dùng để tạo cột mới (price_group và Percent). Tham số BẮT BUỘC là tên cột mới và biểu thức giá trị của nó.
Hàm case_when(…): Đây là hàm logic nâng cao, tương đương với chuỗi IF-ELSE IF. Tham số BẮT BUỘC là các cặp đối số condition ~ value (biểu thức logic và giá trị gán tương ứng).
Hàm group_by(…) và summarise(…): Hai hàm này hoạt động cùng nhau để tính tổng hợp. group_by() chuẩn bị Data Frame bằng cách nhóm các hàng theo biến price_group, trong khi summarise() tính toán giá trị tổng hợp cho mỗi nhóm (Count = n(), trong đó n() là hàm nội bộ để đếm số lượng hàng).
Hàm arrange(…): Dùng để sắp xếp các hàng của Data Frame kết quả, với hàm desc() bên trong, chỉ định sắp xếp theo thứ tự giảm dần của cột Count.
Dòng code được sử dụng:
df <- df %>%
mutate(price_group = case_when(
price < 200000 ~ "Thấp",
price >= 200000 & price < 500000 ~ "Trung bình",
price >= 500000 & price < 1000000 ~ "Cao",
price >= 1000000 ~ "Rất cao"
))
price_summary <- df %>%
group_by(price_group) %>%
summarise(
Count = n()
) %>%
mutate(Percent = round(Count / sum(Count) * 100, 2)) %>%
arrange(desc(Count))
View(price_summary)
Khối lệnh trên được chia thành hai giai đoạn chính:
Giai đoạn 1: Phân Khúc Hóa Thị Trường (Tạo price_group)
Giai đoạn 2: Tính Toán Cấu trúc Thị phần (Tạo price_summary)
Phân khúc Rất cao 1,000,000: 102,236 giao dịch, chiếm 51.12%
Phân khúc Cao 500,000 < 1,000,000: 55,274 giao dịch, chiếm \(mathbf{27.64%}\)
Phân khúc Trung bình ge 200,000$ đến < 500,000: 33,615 giao dịch, chiếm 16.81
Phân khúc Thấp (< 200,000): 8,875 giao dịch, chiếm 4.44% Kết quả cho thấy nhu cầu thị trường và tổng giá trị giao dịch tập trung mạnh mẽ vào các tài sản có giá trị lớn.
Phân khúc Rất cao 1,000,000 chi phối với 102,236 giao dịch , chiếm hơn một nửa (51.12%) tổng số giao dịch.
Phân khúc Cao 500,000 < 1,000,000 chiếm tỷ trọng lớn thứ hai với 27.64% thị phần, tương đương với \(55,274\) giao dịch. Phân khúc này đại diện cho khách hàng trung thượng lưu có khả năng chi trả tốt. Đây là phân khúc có tổng số giao dịch lớn thứ hai, cho thấy nó là thị trường cốt lõi quan trọng để duy trì doanh số và lợi nhuận.
Phân khúc Trung bình 200,00 < 500,000 chiếm 16.81% thị phần, tương đương 33,615 giao dịch. Phân khúc này đại diện cho thị trường đại chúng, mặc dù tỷ trọng không cao bằng hai nhóm trên, đây thường là khu vực có tính thanh khoản (Liquidity) tốt nhất do rào cản tài chính thấp hơn.
Và cuối cùng là phân khúc Thấp (< 200,000) chỉ chiếm 4.44% thị phần, tương đương 8,875 giao dịch. Tỷ trọng thấp cho thấy đây là thị trường ngách hoặc là thị trường có tính thanh khoản rất thấp.
df <- df %>%
mutate(
price_scaled = scale(price),
property_size_scaled = scale(property_size_sqft),
salary_scaled = scale(customer_salary),
loan_scaled = scale(loan_amount)
)
Kết luận: Điều này cho thấy khách hàng tại Pháp (Marseille) đang phải gánh chịu một Phụ phí tương đối (Relative Premium): mức giá và khoản vay họ phải trả có vị trí gần với mức trung bình hơn rất nhiều so với vị trí yếu của tài sản (Diện tích) và vị thế tài chính (Thu nhập) của họ. Đây là bằng chứng mạnh mẽ cho Hiệu ứng Vị trí (Location Effect) hoặc Phí tiện ích khu vực cao khiến giá trị tài sản vượt trội so với các thuộc tính cơ bản. - Với giao dịch thứ 9 tại Dubai này là một giao dịch siêu sang (Luxury Outlier), với các yếu tố Giá và Vay đạt mức bất thường. Giá nhà (\(mathbf{Z=+2.326}\)) và Khoản vay (\(mathbf{Z=+2.016}\)) vượt trội hơn 2 độ lệch chuẩn so với mức trung bình toàn mẫu. Điều này xác nhận đây là một giao dịch có giá trị cực đoan, không đại diện cho thị trường chung. Mặc dù Thu nhập (\(mathbf{Z=+0.347}\)) và Diện tích (\(mathbf{Z=+1.250}\)) đều cao hơn trung bình, nhưng mức độ cao của Giá và Khoản vay là mạnh hơn rất nhiều (gấp 6 lần Z-score của Thu nhập). Kết luận: Sự chênh lệch lớn này chỉ ra gánh nặng tài chính của khách hàng này là rất lớn so với mức thu nhập mà họ đang có. Điều này gợi ý rằng khách hàng ở thị trường cao cấp (Dubai) đang sử dụng nguồn vốn bổ sung (ví dụ: vốn đầu tư) để hỗ trợ cho Khoản vay và Giá nhà vượt xa khả năng chi trả thông thường. Vấn đề này làm tăng rủi ro thanh toán tiềm ẩn .
df <- df %>%
mutate(size_bin = case_when(
property_size_sqft < 2000 ~ "Small",
property_size_sqft >= 2000 & property_size_sqft < 4000 ~ "Medium",
property_size_sqft >= 4000 ~ "Large"
))
table(df$size_bin)
##
## Large Medium Small
## 71142 71742 57116
df <- df %>%
mutate(expense_ratio = monthly_expenses / customer_salary)
Dòng code trên thực hiện các hành động sau:
Lấy data frame df hiện có.
Sử dụng mutate() để tạo một cột mới tên là expense_ratio.
Giá trị cho mỗi hàng trong cột expense_ratio được tính bằng cách chia giá trị trong cột monthly_expenses cho giá trị trong cột customer_salary của cùng hàng đó.
Data frame đã được sửa đổi (giờ đã bao gồm cột expense_ratio) được gán lại cho đối tượng df, ghi đè lên phiên bản trước đó.
Kết quả: Data frame df giờ có thêm một biến định lượng (expense_ratio) đại diện cho tỷ lệ thu nhập hàng tháng của khách hàng được dùng cho các chi phí hàng tháng đã báo cáo.
Với đối tượng đầu tiên, giá trị expense_ratio xấp xỉ 0.609 là kết quả của phép chia chi phí hàng tháng (6545 USD) cho thu nhập hàng tháng (10745 USD). Con số này đại diện cho tỷ lệ của thu nhập được dùng để trang trải chi phí. Tỷ lệ cho thấy khách hàng này tại Pháp dành khoảng 60.9% tổng thu nhập hàng tháng của mình cho các chi phí sinh hoạt đã khai báo. Đây là một tỷ lệ chi tiêu rất cao, mang nhiều hàm ý kinh tế quan trọng:
Thu nhập khả dụng thấp: Phần thu nhập còn lại sau khi trừ đi chi phí (thu nhập khả dụng) của khách hàng này rất hạn chế. Điều này ảnh hưởng trực tiếp đến khả năng tiết kiệm, đầu tư, hoặc chi tiêu cho các nhu cầu không thiết yếu.
Áp lực tài chính cao: Một tỷ lệ chi tiêu trên 60% cho thấy một gánh nặng chi phí sinh hoạt đáng kể. Khách hàng này có thể dễ bị tổn thương trước các cú sốc tài chính bất ngờ (ví dụ: mất việc, chi phí y tế đột xuất) do không có nhiều “vùng đệm” tài chính.
Đánh giá rủi ro tín dụng: Từ góc độ của một tổ chức tài chính, khách hàng này có thể được xem là có rủi ro cao hơn. Khả năng trả thêm các khoản nợ mới (như vay mua nhà, vay tiêu dùng) của họ bị hạn chế do phần lớn thu nhập đã được phân bổ cho các chi phí hiện tại.
Với đối tượng thứ 2, giá trị expense_ratio là xấp xỉ 0.507, được tính bằng cách chia chi phí hàng tháng (8605 USD) cho thu nhập hàng tháng (16970 USD). Khách hàng này dành khoảng 50.7% thu nhập cho chi phí sinh hoạt. Mặc dù chi phí tuyệt đối (8,605 USD) cao hơn so với khách hàng ở Pháp, tỷ lệ expense_ratio lại thấp hơn đáng kể. Đây chính là sức mạnh của việc sử dụng một chỉ số tương đối:
Sự linh hoạt tài chính lớn hơn: Với tỷ lệ chi tiêu thấp hơn, khách hàng này có một phần thu nhập khả dụng lớn hơn. Điều này mang lại cho họ sự linh hoạt cao hơn trong việc quản lý tài chính, khả năng tiết kiệm tốt hơn và sức chống chịu tốt hơn trước các biến cố tài chính.
Gánh nặng chi phí tương đối nhẹ hơn: Mặc dù sống ở một nơi có thể có chi phí cao hơn (phản ánh qua monthly_expenses), nhưng mức lương cao hơn đáng kể đã giúp giảm bớt gánh nặng chi phí so với tổng thu nhập.
Hồ sơ rủi ro thấp hơn: Dựa trên chỉ số này, khách hàng tại Nam
Phi có hồ sơ rủi ro tín dụng tốt hơn so với khách hàng tại Pháp. Họ có
nhiều khả năng hơn trong việc gánh vác thêm các nghĩa vụ tài chính mới.
Tuy nhiên, để có một đánh giá toàn diện, cần phải kết hợp chỉ số này với
các tỷ lệ nợ khác (như emi_to_income_ratio) để hiểu rõ tổng nghĩa vụ tài
chính của họ.
df <- df %>%
mutate(garden_label = factor(garden,
levels = c(0, 1),
labels = c("Không có", "Có")))
df <- df %>%
mutate(satisfaction_level = factor( # Tạo biến Factor có thứ bậc
case_when(
satisfaction_score <= 4 ~ "Chưa hài lòng",
satisfaction_score >= 5 & satisfaction_score <= 8 ~ "Hài lòng",
satisfaction_score >= 9 ~ "Rất hài lòng" # Bao gồm 9 và 10
),
levels = c("Chưa hài lòng", "Hài lòng", "Rất hài lòng"), # Xác định thứ tự
ordered = TRUE # Chỉ định đây là Factor có thứ bậc
))
Khối lệnh này sử dụng các hàm thống kê mô tả cơ bản của Base R để tính toán các chỉ số chính cho cột price. Dòng code đã thực hiện:
mean_price <- mean(df$price)
median_price <- median(df$price)
sd_price <- sd(df$price)
iqr_price <- IQR(df$price)
c(mean = mean_price, median = median_price, sd = sd_price, IQR = iqr_price)
## mean median sd IQR
## 1215365.1 1023429.0 823663.3 1159567.0
Khối lệnh này sử dụng hai hàm chính từ gói moments (cần nạp bằng library(moments)) và hàm c() của Base R. skewness(x, na.rm = FALSE): Hàm từ gói moments dùng để tính hệ số bất đối xứng của vector số x. Nó đo lường mức độ không đối xứng của phân phối dữ liệu quanh giá trị trung bình.
Skewness = 0: Phân phối đối xứng (như phân phối chuẩn).
Skewness > 0: Phân phối lệch phải (đuôi dài về phía giá trị lớn).
Skewness < 0: Phân phối lệch trái (đuôi dài về phía giá trị nhỏ). kurtosis(x, na.rm = FALSE): Hàm từ gói moments dùng để tính hệ số độ nhọn vector số x. Nó đo lường mức độ tập trung của dữ liệu ở phần đuôi so với phân phối chuẩn. Gói moments thường tính kurtosis dư, tức là kurtosis trừ đi 3 (giá trị kurtosis của phân phối chuẩn).
Kurtosis dư = 0: Độ nhọn tương tự phân phối chuẩn.
Kurtosis dư > 0: Phân phối nhọn hơn chuẩn, đuôi nặng hơn, có nhiều giá trị ngoại lai hơn.
Kurtosis dư < 0: Phân phối bẹt hơn chuẩn, đuôi nhẹ hơn. Dòng code thực hiện:
skew_price <- skewness(df$price)
kurt_price <- kurtosis(df$price)
c(skewness = skew_price, kurtosis = kurt_price)
## skewness kurtosis
## 0.9448901 3.5509857
Khối code thực hiện các bước sau:
skew_price <- skewness(df$price): Tính hệ số bất đối xứng cho cột price (dữ liệu gốc) và gán vào biến skew_price.
kurt_price <- kurtosis(df$price): Tính hệ số độ nhọn (dư) cho cột price và gán vào biến kurt_price.
c(skewness = skew_price, kurtosis = kurt_price): Tạo và hiển thị một vector có tên, trình bày gọn gàng 2 chỉ số vừa tính được. Phân tích Kỹ thuật
Hệ số Bất đối xứng (Skewness):
Hệ số này đo lường mức độ bất đối xứng của một phân phối. Một phân phối đối xứng hoàn hảo (như phân phối chuẩn) có skewness = 0.
Kết quả 0.9449 là một giá trị dương, cho thấy phân phối của giá nhà bị lệch phải. Điều này có nghĩa là phần đuôi bên phải của biểu đồ phân phối dài hơn, và có một lượng đáng kể các giá trị cao tập trung ở đó. Kết quả này hoàn toàn nhất quán với quan sát trước đó là mean > median.
Hệ số Độ nhọn (Kurtosis): Hệ số này đo lường “độ nhọn” của đỉnh và “độ dày” của hai đuôi của phân phối so với phân phối chuẩn. Một phân phối chuẩn có kurtosis = 3.
Kết quả 3.5510 lớn hơn 0. Về mặt kỹ thuật, điều này có hai đặc điểm: (1) đỉnh của phân phối nhọn hơn và (2) hai đuôi của phân phối “dày” hơn so với phân phối chuẩn. Đuôi dày hơn có nghĩa là các giá trị ngoại lai (rất cao hoặc rất thấp) xuất hiện thường xuyên hơn dự kiến. Hai con số này cung cấp hai góc nhìn kinh tế bổ sung và rất quan trọng về thị trường.
Skewness = 0.9449
Nhận định: Thị trường bất động sản này rõ ràng bị chi phối bởi một phân khúc cao cấp. Sự tồn tại của các bất động sản siêu sang và đắt tiền đang “kéo” toàn bộ thị trường về phía chúng. Hàm ý: Đối với một người mua nhà thông thường, việc nhìn vào giá trung bình sẽ cho một cái nhìn quá lạc quan về giá trị nhà đất. Thị trường này không “bằng phẳng”, mà có một sự phân hóa rõ rệt, với một nhóm nhỏ các bất động sản có giá trị vượt trội so với phần còn lại. - Kurtosis = 3.5510
Nhận định: Kết quả này cho thấy hai điều: 1. Sự tập trung: Có một sự tập trung rất mạnh mẽ của các giao dịch mua bán xung quanh một mức giá “phổ biến” nhất định (gần với giá trị trung vị $1,023,429). Đây là “trái tim” của thị trường, nơi phần lớn hoạt động diễn ra. 2. Sự tồn tại của các thái cực: Đồng thời, thị trường này có nhiều bất động sản ở hai thái cực (cả rất đắt và cả tương đối rẻ) hơn so với một thị trường “bình thường”. - Hàm ý: Đây là một thị trường của “cả số đông và những trường hợp cá biệt”. Hầu hết mọi người sẽ giao dịch trong một khoảng giá khá dễ đoán, nhưng các nhà đầu tư sành sỏi có thể tìm thấy cả những cơ hội “săn lùng món hời” (ở đuôi bên trái) và những bất động sản đầu tư siêu lợi nhuận (ở đuôi bên phải).
quantile(df$price, probs = c(0, 0.01, 0.05, 0.25, 0.5, 0.75, 0.95, 0.99, 1))
## 0% 1% 5% 25% 50% 75% 95% 99%
## 56288.0 110735.9 210252.0 565989.5 1023429.0 1725556.5 2807338.9 3671005.4
## 100%
## 4202732.0
Hàm được sử dụng là quantile(), một hàm cơ bản (Base R) dùng để tính toán các phân vị mẫu tương ứng với các xác suất cho trước. quantile(x, probs = seq(0, 1, 0.25), na.rm = FALSE, names = TRUE, type = 7, …)
x: Tham số BẮT BUỘC. Là vector số mà bạn muốn tính phân vị (ở đây là df$price).
probs: Tham số không bắt buộc, MẶC ĐỊNH là seq(0, 1, 0.25) (tức là tính các Tứ phân vị: 0%, 25%, 50%, 75%, 100%). Tham số này nhận một vector các xác suất (giá trị từ 0 đến 1) mà bạn muốn tính phân vị tương ứng.
na.rm = FALSE: Tham số không bắt buộc, MẶC ĐỊNH là FALSE. Chỉ định liệu có nên loại bỏ các giá trị NA trước khi tính toán hay không. - names = TRUE: Tham số không bắt buộc, MẶC ĐỊNH là TRUE. Chỉ định liệu kết quả trả về có nên bao gồm tên (dưới dạng phần trăm) cho mỗi phân vị hay không.
type = 7: Tham số không bắt buộc, MẶC ĐỊNH là 7. Chỉ định thuật toán sử dụng để tính phân vị (có 9 loại khác nhau trong R). Loại 7 là mặc định và phổ biến. - Mục đích của lệnh này là tính toán một tập hợp phân vị chi tiết cho biến price, vượt ra ngoài các Tứ phân vị cơ bản, để hiểu rõ hơn về đuôi phân phối và các ngưỡng giá quan trọng. Tham số probs được tùy chỉnh: Vector c(0, 0.01, 0.05, 0.25, 0.5, 0.75, 0.95, 0.99, 1) yêu cầu R tính các phân vị sau: - 0% (Min)
1% (Phân vị thứ nhất)
5% (Phân vị thứ năm)
25% (Q1 - Tứ phân vị thứ nhất)
50% (Median - Trung vị)
75% (Q3 - Tứ phân vị thứ ba)
95% (Phân vị thứ 95) - 99% (Phân vị thứ 99)
100% (Max) Kết quả :
0% (Min): 56,288.0$ - Giá trị giao dịch thấp nhất.
1%: 110,735.9$
Chỉ 1% giao dịch có giá dưới mức này.
5%: 210,252.0$
Chỉ 5%$ giao dịch có giá dưới mức này.
25% (Q1):565,989.5$
25% giao dịch có giá dưới mức này (ngưỡng phân khúc thấp).
50% (Median): 1,023,429.0$ - Giá trị trung vị, chia dữ liệu thành hai nửa.
75% (Q3): 1,725,556.5
75% giao dịch có giá dưới mức này (ngưỡng phân khúc cao).
95%: 2,807,338.9$ - Chỉ 5% giao dịch có giá trên mức này. - 99%: 3,671,005.4 - Chỉ $1% giao dịch có giá trên mức này.
100% (Max): \(4,202,732.0\) - Giá trị giao dịch cao nhất. Bảng phân vị này cung cấp một bản đồ chi tiết về cấu trúc giá của thị trường bất động sản. Mỗi con số là một cột mốc quan trọng, giúp chúng ta định hình các phân khúc thị trường một cách rõ ràng. 1. Phân khúc “Giá cả phải chăng” : Dưới 566,000 USD - Điểm mốc (25% - Q1): 565,989.5 USD - Đây là ngưỡng giá dành cho 1/4 thị trường có giá thấp nhất. Các bất động sản trong phân khúc này có thể là nhà ở khởi điểm , căn hộ nhỏ, hoặc nhà ở các khu vực xa trung tâm. o 5% thấp nhất (dưới 210,252 USD): Đây là phân khúc siêu rẻ, có thể là các sản phẩm nhà ở xã hội, nhà cần sửa chữa lớn, hoặc ở các vị trí rất kém thuận lợi. o 1% thấp nhất (dưới 110,735.9 USD): Đây là các giao dịch cực kỳ cá biệt, gần như không đại diện cho thị trường chung. 2. Phân khúc “Thị trường Cốt lõi” : Từ 566,000 USD đến 1,726,000 USD - Điểm mốc (Median - 50%):
1,023,429 USD - Đây là “trái tim” của thị trường, nơi một nửa số giao dịch (50%) diễn ra. Giá trị trung vị $1,023,429 là con số phản ánh chính xác nhất giá của một bất động sản “điển hình”. Đây là mức giá mà phần lớn các gia đình trung lưu và cận cao cấp đang nhắm tới. Sự chênh lệch lớn giữa Q1 và Q3 (khoảng 1.16 triệu USD) cho thấy ngay cả trong phân khúc cốt lõi này, sự đa dạng về loại hình và vị trí cũng rất lớn.
Khối lệnh này sử dụng hàm mutate() từ gói dplyr, toán tử chia / của Base R, và hàm summary() của Base R. - mutate(…): Hàm dplyr này dùng để tạo cột mới (price_per_sqft) trong Data Frame df. Nó nhận tên cột mới và biểu thức tính toán giá trị (price / property_size_sqft). - Toán tử /: Toán tử chia cơ bản, thực hiện phép chia theo từng phần tử giữa hai vector (price và property_size_sqft). - summary(object, …): Hàm Base R này tính toán và hiển thị các thống kê mô tả chính (Min, Q1, Median, Mean, Q3, Max) cho một đối tượng, ở đây là vector số df$price_per_sqft mới được tạo. Dòng code thực hiện:
df <- df %>% mutate(price_per_sqft = price / property_size_sqft)
summary(df$price_per_sqft)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 138.7 299.2 380.0 380.3 450.7 712.0
Khối code thực hiện hai bước chính:
o Mỗi giá trị trong cột này là kết quả của phép chia giá trị price cho giá trị property_size_sqft tương ứng trên cùng một hàng. Nó đại diện cho mật độ giá trị trên mỗi đơn vị diện tích.
o Kết quả: Data Frame df được cập nhật với cột mới này.
o Mục đích: Cung cấp cái nhìn tổng quan nhanh về phân phối của chỉ số giá trên mỗi mét vuông. Kết quả 6 chỉ số thống kê cho biến Giá/m² là:
Min. (Tối thiểu): \(138.7\)
1st Qu. (Q1): \(299.2\)
Median (Trung vị): \(380.0\) -
Mean (Trung bình): \(380.3\)
3rd Qu. (Q3): \(450.7\)
Max. (Tối đa): \(712.0\) Giá trị Trung bình (Mean = 380.3) rất gần với Trung vị (Median = 380.0). Điều này cho thấy phân phối của biến price_per_sqft gần như đối xứng, không bị lệch quá nhiều về bên nào Khoảng Tứ phân vị (IQR): Q_3 - Q_1 = 450.7 - 299.2 = 151.5 USD. Điều này cho thấy 50% các giao dịch nằm ở trung tâm phân phối có sự chênh lệch Giá/m² khoảng 151.5 USD. Phạm vi (Range): Max - Min = 712.0 - 138.7 = 573.3 USD. Phạm vi này cho thấy có sự khác biệt đáng kể về mật độ giá trị giữa các giao dịch thấp nhất và cao nhất.
Khám phá quan trọng nhất là giá trị trung bình (380.3 USD) gần như bằng hệt giá trị trung vị (380.0 USD). Đây là một phát hiện cho thấy rằng sau khi loại bỏ ảnh hưởng của diện tích, thị trường này rất cân bằng và đối xứng về mặt giá trị trên mỗi mét vuông. Xác định “Ngưỡng Giá trị Chuẩn” của Thị trường: 50% số bất động sản cốt lõi của thị trường có đơn giá dao động trong khoảng từ 299.2 USD đến 450.7 USD mét vuông (tương ứng với Khoảng Tứ phân vị IQR = 151.5 USD).
Đây chính là “ngưỡng giá trị chuẩn” cho một bất động sản điển hình trong khu vực. Một bất động sản có đơn giá nằm ngoài khoảng này có thể được xem là có các đặc tính đặc biệt (rẻ hơn hoặc đắt hơn mức trung bình).
Khối code R này có mục đích tổng quan là tính toán và hiển thị ma trận tương quan Pearson giữa một tập hợp gồm 7 biến định lượng quan trọng được chọn lọc từ data frame df. Dòng code đã thực hiện:
num_for_corr <- df %>% select(price, property_size_sqft, customer_salary, loan_amount, down_payment, emi_to_income_ratio, price_per_sqft)
cor_mat <- cor(num_for_corr, use = "pairwise.complete.obs")
cor_mat
## price property_size_sqft customer_salary loan_amount
## price 1.0000000 7.450543e-01 2.408865e-01 0.9377558
## property_size_sqft 0.7450543 1.000000e+00 2.183926e-06 0.6994961
## customer_salary 0.2408865 2.183926e-06 1.000000e+00 0.2270463
## loan_amount 0.9377558 6.994961e-01 2.270463e-01 1.0000000
## down_payment 0.8509723 6.327849e-01 2.032432e-01 0.6156009
## emi_to_income_ratio 0.3800601 4.487251e-01 -4.737263e-01 0.4244579
## price_per_sqft 0.5957795 6.757026e-04 4.043301e-01 0.5578220
## down_payment emi_to_income_ratio price_per_sqft
## price 0.8509723 0.38006012 0.5957795035
## property_size_sqft 0.6327849 0.44872514 0.0006757026
## customer_salary 0.2032432 -0.47372628 0.4043301358
## loan_amount 0.6156009 0.42445786 0.5578219741
## down_payment 1.0000000 0.22050294 0.5083131259
## emi_to_income_ratio 0.2205029 1.00000000 0.0609301274
## price_per_sqft 0.5083131 0.06093013 1.0000000000
cor_test <- cor.test(df$price, df$property_size_sqft)
cor_test
##
## Pearson's product-moment correlation
##
## data: df$price and df$property_size_sqft
## t = 499.54, df = 199998, p-value < 2.2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
## 0.7430981 0.7469978
## sample estimates:
## cor
## 0.7450543
2.1. Ước lượng Tương quan Mẫu (cor): Hệ số tương quan Pearson mẫu là r ≈ 0.745. Con số này chỉ ra một mối quan hệ tuyến tính dương mạnh giữa giá và diện tích. Khi diện tích tăng, giá nhà có xu hướng tăng theo một đường gần như tuyến tính.
2.2. Giá trị Thống kê t (t): Giá trị t = 499.54 là cực kỳ lớn. Thống kê t này được sử dụng để kiểm định giả thuyết H0 rằng hệ số tương quan thực sự trong tổng thể (\(rho\)) bằng 0.
2.3. Bậc tự do (df): Bậc tự do là 199998 (thường là n-2, với n là số cặp quan sát). Con số này phản ánh kích thước mẫu rất lớn được sử dụng để tính toán.
2.4. Giá trị p (p-value): Giá trị p < 2.2e-16 (nhỏ hơn \(0.000...0022\) với 15 số 0 sau dấu phẩy). Đây là một giá trị p cực kỳ nhỏ, thực tế bằng 0 về mặt tính toán.
2.5. Giả thuyết Đối (alternative hypothesis): Kiểm định này sử dụng giả thuyết đối hai phía (“true correlation is not equal to 0”), tức là kiểm tra xem tương quan có khác 0 hay không (dương hoặc âm).
2.6.Khoảng Tin cậy 95% (95 percent confidence interval):
Khoảng tin cậyất hẹp và hoàn toàn nằm ở phía dương (không chứa số 0). Kết luận: Ý nghĩa Thống kê: Vì p-value cực kỳ nhỏ (nhỏ hơn bất kỳ mức ý nghĩa \(alpha\) nào, ví dụ: 0.05, 0.01), chúng ta có bằng chứng thống kê cực kỳ mạnh mẽ để bác bỏ giả thuyết H0 (\(rho = 0\)). Kết luận: Mối quan hệ tương quan tuyến tính dương giữa giá nhà và diện tích là có ý nghĩa thống kê cao. Hệ số tương quan mẫu r ≈ 0.745 cho thấy mối quan hệ tuyến tính mạnh. Khoảng tin cậy 95% rất hẹp (0.743 đến 0.747), do kích thước mẫu lớn, cho thấy ước lượng về độ mạnh của mối quan hệ này trong tổng thể là rất chính xác Kết quả kiểm định cung cấp hai bằng chứng không thể chối cãi để khẳng định một nguyên lý kinh tế cơ bản của thị trường này:
Khối lệnh này sử dụng một chuỗi các hàm của gói dplyr để tính toán các thống kê tóm tắt cho mỗi nhóm giá.
Dòng code đã thực hiện:
mean_by_group <- df %>% group_by(price_group) %>% summarise(n = n(), mean_price = mean(price), median_price = median(price))
mean_by_group
## # A tibble: 4 × 4
## price_group n mean_price median_price
## <chr> <int> <dbl> <dbl>
## 1 Cao 55274 743529. 741477
## 2 Rất cao 102236 1847464. 1709161
## 3 Thấp 8875 144937. 151578
## 4 Trung bình 33615 351378. 351586
Diễn giải Kinh tế:
Phân khúc “Rất cao” Với 102,236 giao dịch, đây không phải là một thị trường ngách mà chính là phân khúc cốt lõi và lớn nhất. Điều này cho thấy thị trường đang được định hướng bởi các bất động sản có giá trị cao. Sự chênh lệch giữa giá trung bình và trung vị (mean > median) cho thấy sự tồn tại của một nhóm bất động sản siêu sang, tạo ra một “đuôi phải” kéo dài ngay cả trong phân khúc cao cấp nhất này.
Phân khúc “Cao” và “Trung bình” Trong hai phân khúc này, giá trị trung bình và trung vị gần như bằng nhau. Về mặt kinh tế, điều này cho thấy đây là những phân khúc lành mạnh và dễ dự đoán. Giá cả được phân bổ đối xứng, không có các giao dịch cực đoan làm sai lệch giá trị trung tâm, phản ánh một thị trường có sự định giá rõ ràng và ổn định cho các loại tài sản này.
Phân khúc “Thấp” Đây là phân khúc có số lượng giao dịch ít nhất (8,875), cho thấy đây không phải là phân khúc chủ đạo của thị trường. Việc giá trung bình thấp hơn giá trung vị (mean < median) là một chi tiết thú vị, nó ngụ ý rằng hầu hết các bất động sản trong nhóm này có giá trị tiệm cận ngưỡng trên (gần 200,000 USD), trong khi một số ít giao dịch có giá trị rất thấp (có thể là tài sản cần sửa chữa lớn hoặc ở vị trí rất xa) đã kéo giá trung bình chung xuống.
###3.7.Kiểm định T-test So sánh Giá nhà giữa Phân khúc Small và Large
khối lệnh R này thực hiện kiểm định t (t-test) để so sánh giá nhà trung bình giữa hai nhóm kích thước bất động sản: “Small” và “Large”. Dòng code thực hiện:
t_res <- t.test(price ~ size_bin, data = df %>% filter(size_bin %in% c("Small","Large")))
t_res
##
## Welch Two Sample t-test
##
## data: price by size_bin
## t = 450.8, df = 89788, p-value < 2.2e-16
## alternative hypothesis: true difference in means between group Large and group Small is not equal to 0
## 95 percent confidence interval:
## 1438124 1450684
## sample estimates:
## mean in group Large mean in group Small
## 1901990.6 457586.8
Khối lệnh này sử dụng hai hàm cơ bản (Base R) là table() và prop.table(). 1. table(…): - Ý nghĩa: Như đã phân tích trước, hàm này tạo ra một bảng tần suất tuyệt đối, đếm số lần xuất hiện của mỗi cấp độ (loại hình bất động sản) trong vector df$property_type. - Tham số … (Bắt buộc): Vector đầu vào (df$property_type). 2. prop.table(x, margin = NULL): - Ý nghĩa: Hàm này lấy một đối tượng bảng (thường là kết quả từ table()) và chuyển đổi các tần suất tuyệt đối thành tần suất tương đối (proportions) hay tỷ lệ phần trăm (nếu nhân với 100). - Tham số x (Bắt buộc): Bảng tần suất đầu vào (ở đây là tab_type). - Tham số margin = NULL (Mặc định): Chỉ định cách tính tỷ lệ. Với NULL (mặc định) cho bảng một chiều, nó tính tỷ lệ dựa trên tổng tất cả các ô trong bảng. Dòng code thực hiện:
tab_type <- table(df$property_type)
tab_type
##
## Apartment Farmhouse Independent House Studio
## 33398 33518 33334 33008
## Townhouse Villa
## 33395 33347
prop.table(tab_type)
##
## Apartment Farmhouse Independent House Studio
## 0.166990 0.167590 0.166670 0.165040
## Townhouse Villa
## 0.166975 0.166735
Khối code thực hiện ba bước tuần tự: 1. tab_type <-
table(df$property_type): - Hành động: Tính toán tần suất tuyệt đối (số
lượng) cho mỗi loại hình bất động sản (Apartment, Farmhouse, Townhouse,
Villa, Independent House, Studio) có trong cột property_type. - Kết quả:
Kết quả (một đối tượng kiểu table) được lưu vào biến tab_type. 2.
tab_type: - Hành động: Hiển thị nội dung của đối tượng tab_type (bảng
tần suất tuyệt đối). 3. prop.table(tab_type): - Hành động: Lấy bảng tần
suất tuyệt đối tab_type và tính toán tần suất tương đối (tỷ lệ) cho mỗi
loại hình. - Kết quả: Hiển thị bảng tần suất tương đối (các giá trị tỷ
lệ cộng lại bằng 1). Kết quả cho thấy tần suất tuyệt đối (số lượng) và
tần suất tương đối (tỷ lệ) của 6 loại hình bất động sản trong tập dữ
liệu. 4. Tần suất Tuyệt đối (table()): - Số lượng quan sát cho mỗi loại
hình (Apartment, Farmhouse, Independent House, Studio, Townhouse, Villa)
dao động trong khoảng 33,008 (Studio) đến 33,518 (Farmhouse). - Về mặt
kỹ thuật, điều này cho thấy một sự phân bổ cực kỳ đồng đều về số lượng
giữa các loại hình bất động sản trong mẫu dữ liệu. Không có loại hình
nào chiếm ưu thế vượt trội hay quá hiếm gặp. 5. Tần suất Tương đối
(prop.table()): - Tỷ lệ (thị phần) của mỗi loại hình dao động rất hẹp,
khoảng 0.165 (16.50%) cho Studio đến 0.168 (16.76%) cho Farmhouse. - Về
mặt kỹ thuật, sự đồng đều về tỷ lệ này xác nhận rằng mỗi loại hình bất
động sản chiếm xấp xỉ 1/6 tổng số quan sát. Tổng các tỷ lệ này bằng 1
(hoặc 100%). Kết luận: Biến property_type là một biến
định tính (Factor) có 6 cấp độ với sự phân bổ số lượng quan sát rất cân
bằng. Đây là một đặc điểm kỹ thuật thuận lợi cho việc xây dựng mô hình
thống kê. Với loại hình Apartment có 33,398 quan sát, chiếm tỷ lệ 16.7%
(0.166990) trong tổng số. Con số này cho thấy căn hộ là một trong những
loại hình nhà ở phổ biến, chiếm một phần sáu thị trường trong mẫu dữ
liệu này. Đây thường là sản phẩm cốt lõi ở các khu vực đô thị, đáp ứng
nhu cầu của các gia đình trẻ, người độc thân, và các nhà đầu tư mua để
cho thuê. Phát hiện kinh tế quan trọng nhất không nằm ở một loại hình cụ
thể nào, mà ở sự cân bằng gần như hoàn hảo trên toàn bộ bảng kết quả.
Mỗi loại hình bất động sản, từ Apartment, Farmhouse (Trang trại),
Independent House (Nhà độc lập), Studio, Townhouse (Nhà phố) cho đến
Villa (Biệt thự), đều chiếm xấp xỉ 16.7% thị phần trong mẫu dữ liệu. Sự
hiện diện ngang bằng của Apartment, Studio (cho người độc thân, sinh
viên), Townhouse, Independent House (cho các gia đình), Villa (cho phân
khúc cao cấp), và cả Farmhouse (cho nhu cầu nghỉ dưỡng hoặc nông nghiệp)
cho thấy một nền kinh tế và cấu trúc xã hội đa dạng. Thị trường có khả
năng phục vụ đồng thời cho cả nhu cầu sống trong đô thị, ngoại ô và cả
khu vực nông thôn. Một thị trường cân bằng như vậy gợi ý về một chiến
lược quy hoạch tổng thể rất thành công, nơi các nhà hoạch định chính
sách đã tạo điều kiện để phát triển đồng đều nhiều loại hình nhà ở,
tránh được tình trạng quá tải hoặc thiếu hụt ở bất kỳ phân khúc
nào.
### 3.9.Phân tích tương quan chéo giữa biến Garage và Quyết định Mua
Khối lệnh R này thực hiện việc tạo bảng tần suất hai chiều (cross-tabulation) để kiểm tra mối liên hệ giữa việc có garage (garage) và quyết định mua (decision), sau đó tính toán tỷ lệ phần trăm theo hàng để dễ dàng so sánh.
Dòng code thực hiện:
tab_gar_dec <- table(df$garage, df$decision)
tab_gar_dec
##
## 0 1
## 0 77121 23009
## 1 76811 23059
prop.table(tab_gar_dec, margin = 1)
##
## 0 1
## 0 0.7702087 0.2297913
## 1 0.7691098 0.2308902
hối code thực hiện ba bước: 1. tab_gar_dec <- table(df\(garage, df\)decision): - Hành động: Tính bảng tần suất 2x2, đếm số lượng cho 4 trường hợp kết hợp giữa garage (0=Không, 1=Có) và decision (0=Không mua, 1=Có mua). - Kết quả: Bảng 2x2 chứa tần suất tuyệt đối được lưu vào biến tab_gar_dec. 2. tab_gar_dec: - Hành động: Hiển thị bảng tần suất tuyệt đối tab_gar_dec. 3. prop.table(tab_gar_dec, margin = 1): - Hành động: Tính toán và hiển thị tỷ lệ phần trăm theo hàng. Cụ thể: - Hàng 1 (Garage=0): Tính tỷ lệ % không mua và % có mua trong số những nhà không có garage. - Hàng 2 (Garage=1): Tính tỷ lệ % không mua và % có mua trong số những nhà có garage. Hàng (Rows): Đại diện cho biến df\(garage. - Hàng 0: Các quan sát không có garage (garage = 0). - Hàng 1: Các quan sát có garage (garage = 1). Cột (Columns): Đại diện cho biến df\)decision. - Cột 0: Các quan sát không mua (decision = 0). - Cột 1: Các quan sát đã mua (decision = 1). Kết quả: Bảng tần số - Ô (0, 0) - 77121: Có 77,121 giao dịch khách hàng quyết định không mua với loại nhà không có garage. - Ô (0, 1) - 23009: Có 23,009 giao dịch khách hàng quyết định mua nhà với loại nhà không có garage. - Ô (1, 0) - 76811: Có 76,811 giao dịch khách hàng quyết định không mua nhà với loại nhà có garage. - Ô (1, 1) - 23059: Có 23,059 giao dịch khách hàng quyết định mua nhà với loại nhà có garage. Kết quả: Bảng tần số - Ô (0, 0) - 0.7702087: Trong số những nhà không có garage, 77.02% khách hàng quyết định không mua nhà. - Ô (0, 1) - 0.2297913: Trong số những nhà không có garage, 22.98% khách hàng quyết định mua nhà. - Ô (1, 0) - 0.7691098: Trong số những nhà có garage, 76.91%$ khách hàng quyết định không mua nhà. - Ô (1, 1) - 0.2308902: Trong số những nhà có garage, 23.09% khách hàng quyết định mua nhà. Kết quả thể hiện rằng việc nhà có garage hay không không tạo ra sự khác biệt rõ rệt trong quyết định mua nhà của khách hàng, với tỷ lệ mua khoảng 23% và tỷ lệ không mua khoảng 77% ở cả hai nhóm. Từ góc nhìn kinh tế, điều này cho thấy: - Nhu cầu thực của khách hàng chưa tập trung nhiều vào yếu tố garage. Garage không phải là đặc điểm quan trọng quyết định việc mua, có thể do chi phí bổ sung để có garage không đủ hấp dẫn hoặc không phù hợp với phân khúc thị trường mục tiêu. - Giá bán hoặc các tiện ích khác có thể quan trọng hơn. Khách hàng có thể quan tâm nhiều hơn đến yếu tố vị trí, thiết kế, hay giá cả thay vì chỉ riêng garage. - Nhà phát triển bất động sản cần cân nhắc chi phí và lợi ích khi đầu tư xây dựng garage. Nếu chi phí xây garage cao mà không làm tăng tỷ lệ bán, việc đầu tư thêm garage có thể không mang lại hiệu quả kinh tế tối ưu. - Thị trường có thể đang bão hòa hoặc phân khúc garage chưa được áp dụng chính sách phù hợp để tạo ra khác biệt trong quyết định mua. Tóm lại, từ góc độ kinh tế, garage không phải là yếu tố thúc đẩy quyết định mua nhà, nên chiến lược kinh doanh và định giá sản phẩm bất động sản cần tập trung khai thác các yếu tố khác có sức ảnh hưởng lớn hơn để tăng doanh thu và lợi nhuận.
Hàm chính được sử dụng là chisq.test(), một hàm cơ bản (Base R) dùng để thực hiện các loại kiểm định Chi-bình phương. Dòng code thực hiện:
chisq.test(tab_gar_dec)
##
## Pearson's Chi-squared test with Yates' continuity correction
##
## data: tab_gar_dec
## X-squared = 0.3344, df = 1, p-value = 0.5631
Mục đích của lệnh này là kiểm định giả thuyết thống kê về tính độc lập giữa hai biến định tính: garage (Có/Không) và decision (Mua/Không mua).
Giả thuyết H0 : Hai biến là độc lập với nhau. Nghĩa là, việc có garage không liên quan gì đến quyết định mua nhà.
Giả thuyết H1 : Hai biến là không độc lập . Nghĩa là, việc có garage có liên quan đến quyết định mua nhà. Kết quả này đến từ Kiểm định Chi-bình phương Pearson dùng để kiểm tra tính độc lập giữa hai biến garage và decision dựa trên bảng tab_gar_dec.
Giá trị p-value (0.5631): Con số này lớn hơn mức ý nghĩa phổ biến là 0.05. Về mặt kỹ thuật, giá trị p-value cao này cho thấy rằng, nếu hai biến garage và decision thực sự độc lập với nhau (đây là giả thuyết H0), thì việc quan sát được mối liên hệ như trong bảng tab_gar_dec là rất có khả năng xảy ra do ngẫu nhiên. Do đó, chúng ta không có đủ bằng chứng thống kê để bác bỏ giả thuyết H0.
Thống kê Chi-bình phương (X-squared = 0.3344): Giá trị thống kê Chi-bình phương tính được là 0.3344, một giá trị rất nhỏ. Giá trị nhỏ này cho thấy sự khác biệt giữa tần suất quan sát được trong bảng tab_gar_dec và tần suất kỳ vọng là không đáng kể.
Bậc tự do (df = 1): Bậc tự do là 1, đúng với công thức (số hàng - 1) * (số cột - 1) cho bảng 2x2. Kết luận: Dựa trên p-value = 0.5631 > 0.05, kiểm định Chi-bình phương không tìm thấy bằng chứng thống kê để kết luận rằng có mối liên hệ giữa việc có garage (garage) và quyết định mua nhà (decision) trong tập dữ liệu này. Hai biến này được coi là độc lập về mặt thống kê. Kết quả cho thấy rằng, trong bối cảnh thị trường này, garage không phải là một yếu tố quyết định trong hành trình mua nhà của khách hàng.
Garage là một “Tiện ích Mặc định” hoặc “Không Quan trọng”: Kết quả này có thể được giải thích theo hai hướng.
o Thứ nhất, có thể garage là một tiện ích quá phổ biến và được mong đợi ở hầu hết các bất động sản.
o Thứ hai, và có khả năng hơn, là các yếu tố khác như giá cả, vị trí, diện tích,… có sức ảnh hưởng lớn đến mức chúng hoàn toàn lấn át tầm quan trọng của việc có hay không có garage.
o Đối với các nhà môi giới và marketing, việc nhấn mạnh quá mức vào tiện ích “có garage” trong các chiến dịch quảng cáo có thể sẽ không mang lại hiệu quả như mong đợi. Thay vào đó, họ nên tập trung vào các yếu tố có sức ảnh hưởng lớn hơn đã đề cập ở trên.
o Đối với các nhà phát triển và định giá, kết quả này gợi ý rằng việc thêm một garage có thể không cho phép họ tăng giá bán một cách đáng kể. Giá trị của bất động sản được quyết định bởi các yếu tố cốt lõi khác.
Kết luận: Trong cuộc đua thu hút người mua, tiện ích “garage” dường như chỉ là một yếu tố phụ, không đủ sức nặng để thay đổi cán cân quyết định. Các quyết định kinh tế lớn hơn của khách hàng được thúc đẩy bởi những thuộc tính cơ bản và mang tính vĩ mô hơn của bất động sản.
Khối lệnh R này thực hiện việc tính toán thống kê mô tả theo nhóm, cụ thể là tính số lượng quan sát, mức lương trung bình (customer_salary), và khoản vay trung bình (loan_amount) cho mỗi quốc gia (country), sau đó hiển thị 8 quốc gia có nhiều quan sát nhất. Dòng code thực hiện:
mean_country <- df %>% group_by(country) %>% summarise(n = n(), mean_salary = mean(customer_salary), mean_loan = mean(loan_amount)) %>% arrange(desc(n))
head(mean_country, 8)
## # A tibble: 8 × 4
## country n mean_salary mean_loan
## <fct> <int> <dbl> <dbl>
## 1 France 15628 54989. 837650.
## 2 China 15536 54906. 763403.
## 3 Australia 15442 55246. 643026.
## 4 UK 15413 55105. 802243.
## 5 Germany 15408 55040. 599168.
## 6 Canada 15401 55183. 696308.
## 7 South Africa 15401 21969. 358677.
## 8 Brazil 15397 21938. 400519.
Khối code thực hiện các bước sau:
Kết quả:
Số lượng bản ghi n khá đồng đều (khoảng hơn 15,000) cho từng quốc gia, phù hợp để so sánh.
Lương trung bình của khách hàng ở 6 quốc gia đầu (Pháp, Trung Quốc, Úc, Anh, Đức, Canada) đều xấp xỉ 55,000 USD, rất đồng nhất.
Hai quốc gia South Africa và Brazil có mức lương trung bình thấp hơn hẳn (~22,000 USD), cho thấy sự phân chia rõ rệt trong phân khúc thu nhập theo quốc gia.
Khoản vay trung bình có sự chênh lệch lớn hơn giữa các quốc gia, ví dụ Pháp (837,650 USD) và Đức (599,168 USD), phản ánh sự khác biệt về quy mô hoặc chính sách tín dụng.
Những quốc gia có mức lương cao thường có khoản vay trung bình cao hơn, ngoại trừ vài trường hợp như Australia có lương cao hơn nhưng khoản vay thấp hơn UK hoặc Pháp. Kết quả thể hiện rõ sự phân hóa thu nhập và khả năng vay giữa các quốc gia, từ đó có thể rút ra các điểm kinh tế quan trọng:
Các quốc gia như Pháp, Trung Quốc, Úc, Anh, Đức, và Canada có mức thu nhập trung bình tương đồng (~55,000 USD), cho thấy tầng lớp khách hàng ở đây thuộc nhóm thu nhập trung bình khá trở lên. Điều này tạo điều kiện cho khả năng vay cao, phản ánh qua khoản vay trung bình cũng tương đối lớn (từ 599,000 USD đến 837,000 USD), thể hiện thị trường tín dụng phát triển và người dân có tiềm lực tài chính để vay mua sắm hoặc đầu tư.
Trong khi đó, South Africa và Brazil có mức lương trung bình chỉ khoảng 22,000 USD, chưa bằng một nửa so với các quốc gia trên. Khoản vay trung bình cũng thấp hơn đáng kể (khoảng 350,000 USD đến 400,000 USD), cho thấy hạn mức tín dụng và nhu cầu vay vốn của người dân hạn chế hơn do thu nhập thấp hơn, phản ánh mức độ phát triển kinh tế và thị trường tài chính có thể kém phát triển hoặc rủi ro cao hơn.
Sự khác biệt rõ ràng về thu nhập và khoản vay giữa các quốc gia còn cho thấy tiềm năng và thách thức khi các công ty tài chính hoặc các nhà phát triển sản phẩm tài chính muốn mở rộng thị trường ở từng vùng. Ở các quốc gia có thu nhập cao, có thể áp dụng các gói vay lớn và sản phẩm đa dạng, trong khi ở các quốc gia thu nhập thấp hơn cần xây dựng các sản phẩm vay phù hợp hơn với khả năng chi trả và rủi ro.
Mối quan hệ giữa thu nhập và khoản vay cũng phản ánh mức độ tự tin về kinh tế của người dân: thu nhập cao thường đi kèm với tín nhiệm vay tốt hơn và sẵn sàng vay với số tiền lớn hơn để đầu tư, tiêu dùng. Tóm lại, dữ liệu này cho thấy rõ sự phân tầng kinh tế giữa các quốc gia, ảnh hưởng đến khả năng và nhu cầu vay vốn, từ đó ảnh hưởng đến chiến lược phát triển sản phẩm, mở rộng thị trường của tổ chức tài chính hoặc doanh nghiệp liên quan tới tín dụng
Khối lệnh R này là để tính toán và so sánh mức giá điển hình (trung vị - median) và sự biến động về giá (khoảng tứ phân vị - IQR) giữa các loại hình bất động sản khác nhau. Dòng code thực hiện:
price_by_type <- df %>% group_by(property_type) %>% summarise(median_price = median(price), IQR = IQR(price), n = n()) %>% arrange(desc(median_price))
price_by_type
## # A tibble: 6 × 4
## property_type median_price IQR n
## <fct> <dbl> <dbl> <int>
## 1 Studio 1025586. 1150507. 33008
## 2 Farmhouse 1025124. 1168942 33518
## 3 Independent House 1025002. 1161020. 33334
## 4 Apartment 1023608. 1164661. 33398
## 5 Townhouse 1021570 1160866 33395
## 6 Villa 1020483 1152803 33347
Khối code thực hiện hai bước chính:
Đầu tiên, nhóm dữ liệu theo property_type. Sau đó, tính median_price, IQR, và n cho từng loại hình. Cuối cùng, sắp xếp bảng kết quả sao cho loại hình có giá trung vị cao nhất đứng đầu.
Kết quả: Một Data Frame mới (price_by_type) được tạo ra, chứa 6 hàng (cho 6 loại hình) và 4 cột (property_type, median_price, IQR, n), đã được sắp xếp theo median_price giảm dần.
Kết quả: - Giá trung vị của các loại hình bất động sản gần như tương đương, dao động quanh khoảng 1,020,000 USD - 1,025,000 USD, cho thấy các loại này có mức giá điển hình khá đồng đều.
IQR khá lớn (trên 1 triệu USD) cho thấy giá trong mỗi nhóm có sự phân tán rộng, tức là có nhiều giao dịch với giá thấp hoặc cao hơn rất nhiều so với giá trung vị, phản ánh tính đa dạng về chất lượng, vị trí hoặc các đặc điểm khác của sản phẩm bất động sản trong cùng loại hình.
Số lượng giao dịch tương đương nhau (~33,000 giao dịch với từng loại), đảm bảo tính đại diện và độ tin cậy của các chỉ số thống kê. - Việc sử dụng median và IQR thay vì mean và standard deviation phù hợp với dữ liệu giá thường có biến động lớn và dễ bị ảnh hưởng bởi những giá trị ngoại lai. Kết quả bảng phân tích giá trung vị và khoảng tứ phân vị theo loại hình bất động sản phản ánh một số điểm kinh tế quan trọng:
Mức giá trung vị gần như tương đương nhau ở tất cả các loại bất động sản, dao động quanh khoảng 1 triệu USD, cho thấy thị trường không phân biệt rõ ràng về giá giữa các loại hình như studio, farmhouse, villa… Điều này có thể phản ánh sự đồng đều về giá trị nhà ở trên thị trường hoặc ảnh hưởng từ yếu tố chung như vị trí địa lý hoặc mức thu nhập khách hàng.
Khoảng tứ phân vị (IQR) rất lớn, khoảng hơn 1 triệu USD, cho thấy mức độ biến động giá khá cao trong từng loại nhà. Điều này hàm ý về sự đa dạng lớn về chất lượng, diện tích, tiện nghi hoặc vị trí trong cùng loại bất động sản. Từ góc độ kinh tế, thị trường bất động sản không đồng nhất, có đa dạng phân khúc giá để phục vụ nhu cầu khác nhau của khách hàng.
Số lượng giao dịch trong từng loại khá đồng đều (khoảng 33,000 giao dịch), cho thấy sự cân bằng về nhu cầu giữa các loại hình bất động sản trên thị trường. Tóm lại, từ góc nhìn kinh tế, thị trường bất động sản có quy mô lớn với các loại hình nhà ở mà mức giá điển hình tương đương nhưng chứa đựng sự phân hóa mạnh về giá bên trong từng loại. Điều này yêu cầu các nhà phát triển và nhà đầu tư cần hiểu rõ phân khúc chi tiết hơn để đáp ứng nhu cầu đa dạng của khách hàng và tối ưu hóa lợi nhuận.
Khối lệnh R này thực hiện việc phân tích giá trị ngoại lai (Outlier Analysis), cụ thể là chọn ra 1% các giao dịch có giá nhà (price) cao nhất và sau đó xem xét đặc điểm phân phối giá của nhóm này. Dòng code thực hiện :
top_1pct <- df %>% arrange(desc(price)) %>% slice(1:ceiling(0.01*nrow(df)))
summary(top_1pct$price)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 3671044 3807295 3939708 3940428 4074882 4202732
Khối code thực hiện hai bước chính:
Tính toán các chỉ số thống kê mô tả (Min, Q1, Median, Mean, Q3, Max) chỉ cho cột price của Data Frame top_1pct.
Mục đích: Cung cấp cái nhìn tổng quan về phân phối giá chỉ trong nhóm ngoại lai cao cấp này. Kết quả: Bằng cách sắp xếp cột price giảm dần và chọn ra số dòng tương ứng 1% tổng số dữ liệu, với 200,000 bản ghi thì đã chọn 2,000 bản ghi có giá cao nhất.
Các chỉ số mô tả thống kê về giá nhà trong nhóm này (summary(top_1pct$price)), gồm:
Min: Giá thấp nhất trong 1% nhóm giá cao nhất (3,671,044).
1st Qu. (Q1): Giá trị tứ phân vị thứ nhất, 25% giá trị thấp hơn hoặc bằng 3,807,295.
Median: Giá trị trung vị nhóm 1%, tức giá ở giữa nhóm này, khoảng 3,939,708.
Mean: Giá trung bình nhóm 1%, 3,940,428, gần bằng trung vị, cho thấy phân phối khá cân đối trong nhóm này.
3rd Qu. (Q3): Tứ phân vị thứ ba, 75% giá trị thấp hơn hoặc bằng 4,074,882.
Max: Giá cao nhất trong nhóm, 4,202,732. Phân tích kỹ thuật Nhóm 1% giá cao nhất có mức giá thấp nhất cũng khá cao, trên 3,6 triệu USD, rõ ràng tách biệt so với phần còn lại của thị trường.
Khoảng tứ phân vị (Q3-Q1 = 4,074,882 - 3,807,295 = 267,587 USD) cho thấy giá trong nhóm này biến động nhẹ và tập trung khá chặt xung quanh ngưỡng 3,8 – 4,2 triệu USD.
Giá trung bình gần bằng trung vị, biểu hiện phân phối giá trong nhóm này không lệch nhiều sang bên cao hay thấp.
Biên độ (Max - Min) là khoảng 530,000, phản ánh phạm vi giá cao cấp cũng có sự biến động nhất định nhưng không quá rộng. Kết quả phân tích nhóm 1% giao dịch bất động sản có giá cao nhất cho thấy một số điểm kinh tế quan trọng:
Giá thấp nhất trong nhóm này đã ở mức rất cao (khoảng 3,67 triệu USD), cho thấy sự phân tách rõ ràng giữa phân khúc bất động sản cao cấp và phần còn lại của thị trường. Đây là nhóm khách hàng hoặc sản phẩm thuộc phân khúc siêu cao cấp với khả năng chi trả lớn.
Khoảng biến động giá trong nhóm 1% này (từ khoảng 3,67 triệu đến 4,2 triệu USD) không quá rộng, cho thấy thị trường bất động sản cao cấp có sự ổn định giá tương đối, ít có sự phân hóa.
Giá trung bình và trung vị gần bằng nhau chứng tỏ phân phối giá trong nhóm này cân đối, không bị lệch về phía giá cực cao hoặc thấp, phản ánh một thị trường cao cấp có sức mua ổn định và đồng đều.
Từ góc độ kinh tế, đây là phân khúc thị trường hấp dẫn với sức mua mạnh, nhưng cũng yêu cầu các nhà đầu tư, phát triển dự án cần có chiến lược, sản phẩm và dịch vụ phù hợp để khai thác hiệu quả phân khúc này, đồng thời giữ được ổn định giá trị và hấp dẫn lâu dài.
Phân khúc này có thể ít bị ảnh hưởng bởi các biến động kinh tế vĩ mô so với các phân khúc giá thấp hơn do khách hàng có tiềm lực tài chính vững vàng hơn và có xu hướng đầu tư lâu dài. Tóm lại, nhóm 1% bất động sản giá cao nhất là phân khúc trọng yếu về mặt kinh tế, đem lại cơ hội lớn nhưng cũng đòi hỏi sự quản lý và chiến lược kỹ càng để duy trì và phát triển bền vững.
Khối lệnh này là để kiểm định một giả thuyết rất chặt chẽ: Liệu phân phối của biến có đối xứng hoàn hảo quanh giá trị trung vị mẫu của nó hay không? Dòng code thực hiện:
median_all <- median(df$price_per_sqft, na.rm = TRUE)
show(median_all)
## [1] 379.9705
wilcox.test(df$price_per_sqft - median_all)
##
## Wilcoxon signed rank test with continuity correction
##
## data: df$price_per_sqft - median_all
## V = 9728547457, p-value < 2.2e-16
## alternative hypothesis: true location is not equal to 0
Khối code thực hiện hai bước:
Đầu tiên, nó tạo ra một vector tạm thời bằng cách lấy mỗi giá trị price_per_sqft trừ đi giá trị trung vị median_all (379.9705). Sau đó, nó thực hiện kiểm định Wilcoxon dấu hạng một mẫu trên vector hiệu số này.
Kiểm định H0: Giả thuyết gốc là trung vị của hiệu số (price_per_sqft – 379.9705) bằng 0. Nói cách khác, nó kiểm tra xem phân phối của price_per_sqft có đối xứng quanh giá trị trung vị mẫu (379.9705) hay không.
Kết quả: kiểm định Wilcoxon dấu hạng một mẫu trên hiệu số (price_per_sqft - 379.9705) được giải thích như sau:
Giá trị thống kê kiểm định V= 9,728,547,457 là tổng các hạng dựa trên hạng tuyệt đối của hiệu số, thể hiện mức độ khác biệt so với giả thuyết trung vị bằng 0.
Giá trị p-value rất nhỏ (< 2.2e-16), tức là gần như bằng 0. - Với p-value này, ta có bằng chứng rất mạnh để bác bỏ giả thuyết gốc H0H0: “trung vị của hiệu số là 0”. Ý nghĩa kinh tế của trung vị giá trị nhà trên mỗi mét vuông (price_per_sqft)
Giá trị trung vị 379.9705 là mức giá điển hình đại diện cho toàn bộ thị trường nhà đất trong dữ liệu, phản ánh mức giá phổ biến được giao dịch.
Kết quả kiểm định Wilcoxon với p-value rất nhỏ cho thấy giá trị trung vị này không phải là con số ngẫu nhiên hay trung vị chính xác của dữ liệu mà có sự khác biệt đáng kể so với giá trị trung vị thực tế.
Điều này có thể ngụ ý rằng thị trường có sự biến động hoặc không đồng đều lớn, có những vùng giá cả rất cao hoặc rất thấp làm lệch trung vị.
Từ góc độ kinh tế, nếu trung vị không cân bằng thì nhà đầu tư hoặc người mua nên cẩn trọng, vì thị trường có thể có phân khúc giá khác biệt rõ rệt, không phải tất cả tài sản đều có giá gần trung vị này. - Ngoài ra, sự khác biệt trung vị có thể phản ánh ảnh hưởng của các yếu tố kinh tế như vị trí, chất lượng, hạ tầng, và biến động cung-cầu trong thị trường bất động sản. Kết luận:
Đây là dấu hiệu cho thấy thị trường nhà đất không đồng nhất và có sự phân hóa giá mạnh, cần phân tích sâu hơn từng nhóm hoặc khu vực để đưa ra quyết định kinh tế hợp lý.
Việc xác định trung vị và kiểm định không đối xứng giúp cảnh báo các bất thường hoặc xu hướng lệch giá mà trung bình bình thường có thể không đại diện được
Khối lệnh R này thực hiện một mô hình hồi quy tuyến tính đơn biến để xem xét mối quan hệ giữa giá nhà (price) và diện tích (property_size_sqft). Khối lệnh này sử dụng hàm lm() của Base R và hàm tidy() từ gói broom (cần library(broom)). Dòng code thực hiện:
lm1 <- lm(price ~ property_size_sqft, data = df)
tidy(lm1)
## # A tibble: 2 × 5
## term estimate std.error statistic p.value
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 (Intercept) -187. 2726. -0.0685 0.945
## 2 property_size_sqft 380. 0.761 500. 0
Khối code thực hiện hai bước chính:
Kết quả: Toàn bộ kết quả chi tiết của mô hình hồi quy (bao gồm hệ số, sai số chuẩn, R-squared, giá trị F, phần dư, v.v.) được lưu vào một đối tượng kiểu lm tên là lm1.
Lấy đối tượng lm1 và trích xuất các thông tin quan trọng nhất về các hệ số hồi quy, trình bày chúng dưới dạng một bảng (tibble/data frame). Kết quả hồi quy tuyến tính lm1 theo góc nhìn kỹ thuật được giải thích như sau:
Hệ số chặn (Intercept) ước lượng là -187 với sai số chuẩn rất lớn (2726), thống kê t là -0.0685 và giá trị p-value rất lớn (0.945). Điều này nghĩa là hệ số chặn không có ý nghĩa thống kê, tức không thể khẳng định giá trị intercept khác 0 với độ tin cậy cao.
Hệ số góc của biến property_size_sqft là 380 với sai số chuẩn rất nhỏ (0.761), thống kê t rất lớn (500), và p-value bằng 0 . Điều này cho thấy hệ số này có ý nghĩa thống kê rất mạnh.
Ý nghĩa hệ số góc: mỗi một đơn vị tăng diện tích property_size_sqft (1 sqft) dự kiến làm tăng giá nhà (price) trung bình thêm 380 USD, khi giữ các yếu tố khác không đổi.
Sai số chuẩn thấp và giá trị thống kê lớn cho thấy mô hình hồi quy có sức thuyết phục cao về ảnh hưởng của diện tích đến giá nhà.
Do hệ số chặn không ý nghĩa, mô hình cho thấy điểm khởi đầu gần 0 (hoặc không xác định rõ), tuy nhiên không ảnh hưởng lớn đến việc đánh giá mối quan hệ chính giữa diện tích và giá. Phân tích kết quả hồi quy lm1 theo góc nhìn kinh tế từ bảng kết quả:
Hệ số góc ước lượng cho biến property_size_sqft là 380. Điều này có ý nghĩa rất quan trọng về kinh tế: mỗi mét vuông diện tích ngôi nhà tăng thêm sẽ làm tăng giá nhà trung bình lên khoảng 380 USD. Đây là một mức tăng khá lớn, phản ánh rằng diện tích là yếu tố then chốt ảnh hưởng trực tiếp và rõ rệt đến giá trị bất động sản.
Giá trị p-value = 0 cho hệ số góc khẳng định mức tăng giá theo diện tích là đáng kể, không phải do ngẫu nhiên mà có, giúp các nhà đầu tư, mua bán nhà đánh giá chính xác ảnh hưởng của diện tích trên thị trường. - Dù hệ số chặn (intercept) không có ý nghĩa thống kê, trong thực tế kinh tế, điều này không gây ảnh hưởng lớn vì trọng tâm chính là mối quan hệ giữa diện tích và giá.
Kết quả cho thấy mô hình hồi quy phù hợp để dự đoán giá dựa trên diện tích, hỗ trợ các quyết định định giá hoặc đánh giá bất động sản trong thị trường.
Từ góc độ chiến lược kinh tế, nhà phát triển hoặc chủ nhà có thể tập trung vào mở rộng diện tích nhằm gia tăng giá trị bất động sản một cách hiệu quả dựa trên hệ số hồi quy này. Tóm lại, bảng kết quả mô hình hồi quy cung cấp bằng chứng kinh tế thuyết phục rằng diện tích nhà ảnh hưởng tích cực, mạnh mẽ và có ý nghĩa thống kê đến giá bán nhà trên thị trường.
Khối mã R này là xây dựng và tóm tắt một mô hình hồi quy tuyến tính đa biến, sử dụng phương pháp Bình phương Tối thiểu Thông thường (Ordinary Least Squares - OLS). Mô hình này cụ thể là một mô hình log-linear, bởi vì biến phụ thuộc (giá bán - price) đã được chuyển đổi thành logarit. Dòng code thực hiện:
lm2 <- lm(log(price) ~ property_size_sqft + rooms + bathrooms + garage + constructed_year, data = df)
summary(lm2)
##
## Call:
## lm(formula = log(price) ~ property_size_sqft + rooms + bathrooms +
## garage + constructed_year, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.7148 -0.3624 0.0743 0.3388 0.9116
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.236e+01 1.167e-01 105.965 <2e-16 ***
## property_size_sqft 3.904e-04 6.712e-07 581.667 <2e-16 ***
## rooms -2.019e-04 6.052e-04 -0.334 0.739
## bathrooms -8.646e-04 7.546e-04 -1.146 0.252
## garage 2.255e-04 2.166e-03 0.104 0.917
## constructed_year 6.813e-05 5.855e-05 1.164 0.245
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.4843 on 199994 degrees of freedom
## Multiple R-squared: 0.6285, Adjusted R-squared: 0.6285
## F-statistic: 6.767e+04 on 5 and 199994 DF, p-value: < 2.2e-16
Khối code thực hiện hai bước chính:
Thực hiện hồi quy OLS để ước lượng mô hình log-linear (logarit hóa biến phụ thuộc). Công thức mô hình cụ thể là: log(price)=β0+β1×property_size_sqft+β2×rooms+β3×bathrooms+β4×garage+β5×constructed_year+ϵ
Hàm lm() tính toán các ước lượng cho hệ số chặn (β^0) và các hệ số góc cho 5 biến độc lập (β^1 đến β^5).
Kết quả: Toàn bộ kết quả chi tiết của mô hình hồi quy (bao gồm hệ số ước lượng, sai số chuẩn, R-squared, phần dư, v.v.) được lưu vào một đối tượng kiểu lm có tên là lm2.
Lấy đối tượng mô hình lm2 và hiển thị một bản tóm tắt toàn diện về kết quả hồi quy ra màn hình. Phân tích kết quả hồi quy mô hình log-linear log(price) trên các biến độc lập property_size_sqft, rooms, bathrooms, garage, constructed_year theo góc nhìn kỹ thuật: - Mô hình có dạng: log(price)=β0+β1×property_size_sqft+β2×rooms+β3×bathrooms+β4×garage+β5×constructed_year+ϵ
Hệ số chặn (Intercept) là 12.36, có ý nghĩa thống kê rất cao (p-value < 2e-16). Đây là giá trị log-price kỳ vọng khi tất cả các biến độc lập bằng 0.
Biến property_size_sqft có hệ số 0.0003904, sai số chuẩn rất nhỏ, t-value cực lớn (581.667) và p-value < 2e-16, cho thấy biến này có ảnh hưởng rất mạnh và có ý nghĩa thống kê rất rõ ràng đến log(price). Cách diễn giải là: mỗi tăng thêm một đơn vị diện tích sẽ làm tăng log(price) trung bình 0.0003904; tương đương giá nhà tăng khoảng 0.039% .
Các biến rooms, bathrooms, garage, constructed_year đều có p-value lớn hơn 0.05, tức không có ý nghĩa thống kê và tỉ lệ biến động gần như bằng 0 hoặc không đáng kể trong mô hình này.
Sai số chuẩn của phần dư (Residual standard error) là 0.4843, cho biết độ biến động trung bình của log(price) so với giá trị dự đoán. - R-squared = 0.6285 cho biết khoảng 62.85% biến động của log(price) được giải thích bởi các biến độc lập trong mô hình, đây là mức khá cao cho mô hình kinh tế.
F-statistic rất lớn và p-value rất nhỏ (<2.2e-16) cho thấy tổng thể mô hình có ý nghĩa thống kê.
Phần dư có giá trị nhỏ nhất (Min) là -1.7148 và lớn nhất (Max) là 0.9116, khoảng cách này cho thấy còn một số điểm dữ liệu có sai số dự đoán khá lớn. - Khoảng tứ phân vị (IQR) của phần dư nằm trong khoảng từ -0.3624 (Q1) đến 0.3388 (Q3), thể hiện phần lớn các sai số dự đoán nằm gần quanh giá trị trung bình, cho thấy mô hình có độ chính xác dự đoán phù hợp với đa số dữ liệu.
Median phần dư là 0.0743, gần bằng 0, đồng nghĩa mô hình không có xu hướng dự đoán quá cao hoặc quá thấp hệ thống, tức phần dư phân bố khá cân bằng. - Sai số chuẩn phần dư 0.4843 phản ánh mức độ biến động trung bình của sai số dự đoán, tương đối nhỏ so với biến động tổng thể của biến phụ thuộc log(price).
Từ góc nhìn kỹ thuật, thống kê mô tả phần dư cho thấy mô hình hồi quy đã phù hợp, đáp ứng tốt yêu cầu về sai số ngẫu nhiên, không có thiên lệch quá nghiêm trọng. Tóm lại, về kỹ thuật mô hình log-linear này:
Diện tích property_size_sqft là yếu tố giải thích chính và duy nhất có ảnh hưởng có ý nghĩa đáng kể lên giá nhà theo mô hình. - Các biến còn lại không đóng góp quan trọng về mặt số liệu trong dữ liệu này.
Mô hình có độ giải thích tốt (R-squared > 0.6) và các ước lượng có độ tin cậy cao dựa trên sai số chuẩn và t-test Dưới góc độ kinh tế, các nhận xét từ kết quả kỹ thuật hồi quy và thống kê mô tả phần dư có thể được diễn giải như sau:
Hệ số chặn (Intercept) là 12.36 trong mô hình log-price phản ánh giá trị log của giá nhà kỳ vọng khi tất cả các biến độc lập (diện tích, số phòng, số phòng tắm, gara, năm xây dựng) đều bằng 0. Tuy nhiên, trong thực tế không có ngôi nhà nào có diện tích 0 hoặc không có các đặc tính này, nên hệ số chặn chủ yếu dùng để hiệu chỉnh mô hình, không mang nhiều ý nghĩa thực tế kinh tế trực tiếp.
Biến diện tích (property_size_sqft) có ảnh hưởng rất mạnh và có ý nghĩa rõ ràng đến giá nhà (p-value cực nhỏ, hệ số dương). Theo đó, mỗi một đơn vị diện tích tăng thêm làm giá nhà tăng khoảng 0.039%, điều này phản ánh rõ nét vai trò thiết yếu của diện tích trong cấu thành giá trị bất động sản.
Các biến số phòng, phòng tắm, gara, năm xây dựng không có ý nghĩa thống kê trong mô hình này, có thể do mức ảnh hưởng của chúng nhỏ hoặc dữ liệu không đủ để thể hiện sự quan trọng, nên trong chiến lược đầu tư và định giá, nhà đầu tư có thể tập trung ưu tiên đánh giá diện tích hơn các yếu tố này.
Thống kê mô tả phần dư cho thấy phần lớn sai số dự đoán tập trung gần 0, tính cân bằng của phần dư chỉ ra mô hình không bị lệch dự đoán hệ thống. Điều này kinh tế ngụ ý rằng các yếu tố đã chọn trong mô hình đã giải thích tốt phần lớn biến động giá trên thị trường, giảm thiểu rủi ro định giá sai lệch.
Một số điểm ngoại lệ với sai số dự đoán lớn cảnh báo về sự tồn tại các yếu tố khác bên ngoài mô hình tác động đến giá nhà, như vị trí, tình trạng pháp lý, thị trường vĩ mô hoặc biến động chu kỳ, cần được nghiên cứu thêm để hoàn thiện mô hình đánh giá.
Với mức R-squared ~63%, mô hình giải thích được hơn một nửa biến động giá, đây là mức khá tốt trong lĩnh vực kinh tế thực tế, cho thấy mô hình có thể áp dụng để hỗ trợ ra quyết định định giá, đầu tư, hoặc chính sách phát triển bất động sản. Tóm lại, về mặt kinh tế, mô hình và các kết quả phần dư khẳng định sự quan trọng của diện tích trong việc xác định giá trị nhà, đồng thời cảnh báo cần tiếp tục khảo sát thêm các yếu tố khác nhằm nâng cao độ chính xác và hiệu quả ứng dụng mô hình trên thị trường bất động sản thực tế.
Khối lệnh R thực hiện việc tính toán tỷ lệ quyết định mua thành công cho từng phân khúc giá. Dòng code thực hiện:
decision_by_pricegrp <- df %>% group_by(price_group) %>% summarise(n=n(), prop_decision = mean(decision))
decision_by_pricegrp
## # A tibble: 4 × 3
## price_group n prop_decision
## <chr> <int> <dbl>
## 1 Cao 55274 0.230
## 2 Rất cao 102236 0.223
## 3 Thấp 8875 0.248
## 4 Trung bình 33615 0.248
Khối code thực hiện hai bước chính:
Kết quả: Một Data Frame mới (decision_by_pricegrp) được tạo ra, chứa 4 hàng (tương ứng 4 nhóm giá) và 3 cột (price_group, n, prop_decision).
Hành động: Hiển thị nội dung của Data Frame decision_by_pricegrp Kết quả kỹ thuật từ bảng phân nhóm quyết định mua theo nhóm giá có thể phân tích chi tiết có số liệu minh họa như sau:
Nhóm “Thấp” với 8,875 giao dịch có tỷ lệ quyết định mua cao nhất là 24.8% (tương đương gần 1/4 giao dịch dẫn đến quyết định mua), thể hiện phân khúc giá thấp là hấp dẫn nhất đối với người mua.
Nhóm “Trung bình” với 33,615 giao dịch cũng có tỷ lệ mua tương tự 24.8%, khẳng định nhóm giá tầm trung vẫn duy trì sức thu hút ổn định. - Nhóm “Cao” gồm 55,274 giao dịch chỉ có 23.0% quyết định mua, thấp hơn nhóm giá thấp và trung bình so với tổng số giao dịch, phản ánh rằng càng lên nhóm giá cao, sức mua giảm nhẹ.
Nhóm “Rất cao” với 102,236 giao dịch lớn nhất về số lượng nhưng tỷ lệ mua thấp nhất 22.3%, hàm ý thị trường cao cấp có sức cạnh tranh lớn hoặc ngưỡng giá làm giảm quyết định mua. Điều này chứng tỏ tỷ lệ quyết định mua phụ thuộc khá mạnh vào nhóm giá trị nhà, trong đó nhóm giá thấp và trung bình kích thích hành vi mua một cách mạnh mẽ hơn so với nhóm giá cao và rất cao. Dưới góc nhìn kinh tế, các nhận xét về tỷ lệ quyết định mua theo nhóm giá như sau:
Tỷ lệ quyết định mua cao nhất ở nhóm “Thấp” (24.8%) và “Trung bình” (24.8%) thể hiện rằng người mua có xu hướng tập trung lựa chọn các căn nhà ở phân khúc giá vừa phải hoặc thấp. Điều này phù hợp với nguyên tắc cung -cầu trong kinh tế, khi người tiêu dùng có giới hạn về khả năng tài chính, họ ưu tiên mua các sản phẩm có giá hợp lý, dễ tiếp cận hơn.
Nhóm “Cao” và “Rất cao” mặc dù có số lượng giao dịch lớn (đặc biệt nhóm “Rất cao” có 102,236 giao dịch) nhưng tỷ lệ quyết định mua thấp hơn (23.0% và 22.3%), cho thấy các căn nhà ở phân khúc cao cấp có thể chịu áp lực cạnh tranh lớn hơn, hoặc người mua có xu hướng kỹ tính hơn khi giá cả tăng lên, dẫn đến ít quyết định mua hơn.
Thực tế này phản ánh rõ ràng nguyên tắc cung-cầu và nguyên tắc cạnh tranh trong thẩm định giá bất động sản, theo đó mức giá càng cao sẽ làm hạn chế một phần nhóm khách hàng tiềm năng, đồng thời đòi hỏi giá trị gia tăng hoặc những đặc điểm nổi bật hơn để thuyết phục khách hàng.
Qua đó, các nhà phát triển và nhà đầu tư bất động sản nên cân nhắc kỹ lưỡng khi xây dựng chiến lược giá và đầu tư, tập trung nhiều vào phân khúc giá thấp và trung bình để đạt hiệu quả kinh doanh cao hơn, đồng thời cần có chiến lược riêng cho phân khúc cao cấp nhằm tối ưu hóa tỷ lệ chuyển đổi quyết định mua.
Kết quả cũng hỗ trợ cho việc hoạch định chính sách nhà ở và phát triển thị trường, đặc biệt trong bối cảnh nhu cầu nhà ở giá rẻ và trung bình luôn chiếm tỷ trọng lớn và ảnh hưởng đến ổn định thị trường bất động sản. Tóm lại, phân tích tỷ lệ quyết định mua theo nhóm giá vừa phản ánh tâm lý và khả năng tài chính người mua, vừa cung cấp dữ liệu thực tế giúp các bên liên quan đưa ra các quyết định kinh tế và điều chỉnh chính sách hiệu quả trên thị trường bất động sản.
Lệnh R này thực hiện Kiểm định Tương quan Hạng Spearman (Spearman’s Rank Correlation Test), một phương pháp phi tham số để đánh giá mối quan hệ đơn điệu giữa hai biến. Dòng code thực hiện:
cor.test(df$price, df$customer_salary, method = "spearman")
## Warning in cor.test.default(df$price, df$customer_salary, method = "spearman"):
## Cannot compute exact p-value with ties
##
## Spearman's rank correlation rho
##
## data: df$price and df$customer_salary
## S = 9.5931e+14, p-value < 2.2e-16
## alternative hypothesis: true rho is not equal to 0
## sample estimates:
## rho
## 0.280519
Mục đích của lệnh này là đánh giá mối quan hệ đơn điệu giữa giá nhà (price) và thu nhập khách hàng (customer_salary), sử dụng phương pháp Spearman. Việc chọn Spearman thay vì Pearson (mặc định) là hợp lý và cần thiết trong trường hợp này vì:
o Mối quan hệ giữa thu nhập và giá nhà có thể không hoàn toàn tuyến tính (ví dụ: thu nhập tăng gấp đôi không nhất thiết làm giá nhà mua tăng gấp đôi). Spearman phù hợp hơn để nắm bắt xu hướng chung này. Kết quả kiểm định tương quan Spearman giữa giá nhà (price) và thu nhập khách hàng (customer_salary) cho thấy:
Hệ số tương quan Spearman ρ=0.2805ρ=0.2805, mang dấu dương, biểu thị mối quan hệ đơn điệu thuận giữa thu nhập khách hàng và giá nhà. Nghĩa là thu nhập càng cao, giá nhà càng có xu hướng cao.
Giá trị ρ=0.2805ρ=0.2805 cho thấy mối liên hệ này là mức độ vừa phải, không quá mạnh nhưng cũng có ý nghĩa rõ ràng, phản ánh rằng thu nhập là một trong các yếu tố ảnh hưởng, nhưng không đơn độc quyết định giá nhà.
P-value < 2.2e-16 cho thấy mối tương quan này có ý nghĩa thống kê rất cao, bác bỏ giả thuyết không có mối quan hệ đơn điệu giữa hai biến.
Việc sử dụng Spearman phù hợp vì dữ liệu bị lệch phải, có thể chứa nhiều quan sát trùng giá trị và mối quan hệ có thể phi tuyến tính. Spearman đánh giá dựa trên hạng nên phù hợp hơn cho trường hợp này so với Pearson. Tóm lại, kết quả kỹ thuật chỉ ra mối quan hệ thuận đơn điệu có ý nghĩa giữa thu nhập khách hàng và giá nhà, với độ mạnh vừa phải, cho thấy khi thu nhập khách hàng tăng, giá nhà cũng có xu hướng tăng nhưng còn chịu ảnh hưởng bởi nhiều yếu tố khác. Đây là kết luận phù hợp trong kinh tế học về vai trò của thu nhập tới quyết định và khả năng mua bất động sản. Dưới góc độ kinh tế, kết quả tương quan Spearman ρ=0.2805ρ=0.2805 giữa giá nhà và thu nhập khách hàng cho thấy mối quan hệ tích cực và có ý nghĩa thống kê cao, tuy nhiên ở mức độ vừa phải. Điều này phù hợp với thực tế hiện nay ở nhiều thị trường bất động sản, nơi mà:
Giá nhà trung bình thường cao gấp nhiều lần thu nhập trung bình của hộ gia đình hoặc cá nhân, khiến khả năng mua nhà ngay lập tức bị hạn chế mặc dù thu nhập có tăng.
Sự gia tăng thu nhập làm tăng tiềm năng tài chính để mua nhà, tạo xu hướng giá nhà cao hơn ở các khu vực có thu nhập cao, nhưng không đồng nghĩa với việc giá nhà và thu nhập tỷ lệ thuận tuyệt đối.
Thực trạng giá nhà tăng nhanh hơn thu nhập, khiến giấc mơ an cư trở nên khó khăn hơn với nhiều người, đặc biệt ở các đô thị lớn như Hà Nội, TP. Hồ Chí Minh, nơi giá nhà cao gấp 20-30 lần thu nhập trung bình hàng năm.
Mức tương quan vừa phải phản ánh rằng còn nhiều yếu tố khác tác động lên giá nhà ngoài thu nhập như vị trí, quy hoạch, cung cầu, lãi suất, chính sách tín dụng, tâm lý thị trường, và các yếu tố xã hội kinh tế khác.
Yếu tố thu nhập dù quan trọng nhưng vẫn chỉ là một phần trong quyết định mua nhà và biến động giá nhà bất động sản, vì thế cần các biện pháp hỗ trợ tài chính, chính sách phát triển nhà ở xã hội để cân bằng thị trường.
Tóm lại, kết quả phân tích giúp hiểu rằng, mặc dù thu nhập khách hàng ảnh hưởng đến giá nhà một cách có ý nghĩa, mối quan hệ này không hoàn toàn quyết định và cần phải xem xét thêm các yếu tố đa chiều để đánh giá và điều chỉnh chính sách phù hợp nhằm tạo dựng thị trường bất động sản bền vững, giúp người dân dễ tiếp cận nhà ở hơn trong bối cảnh giá nhà đang tăng nhanh vượt thu nhập nhiều lần.
Khối lệnh R này thực hiện việc tính toán thống kê mô tả theo nhóm, cụ thể là tính giá trị trung vị (Median) và trung bình (Mean) của price_per_sqft (Giá/m²), cùng với số lượng quan sát, cho 6 quốc gia (country) có nhiều giao dịch nhất trong mẫu Khối lệnh này sử dụng hàm head() (Base R) và các hàm từ gói dplyr. Dòng code thực hiện:
top_countries <- head(mean_country$country, 6)
df %>% filter(country %in% top_countries) %>% group_by(country) %>% summarise(med_ppsq = median(price_per_sqft), mean_ppsq = mean(price_per_sqft), n = n())
## # A tibble: 6 × 4
## country med_ppsq mean_ppsq n
## <fct> <dbl> <dbl> <int>
## 1 Australia 320. 320. 15442
## 2 Canada 350. 350. 15401
## 3 China 380. 380. 15536
## 4 France 420. 420. 15628
## 5 Germany 300. 300. 15408
## 6 UK 400. 400. 15413
Khối code thực hiện ba bước chính:
o Trích xuất tên của 6 quốc gia có số lượng giao dịch (n) cao nhất từ Data Frame mean_country (đã được tạo và sắp xếp ở bước trước) và lưu vào vector top_countries.
Lấy Data Frame df gốc. Lọc ra chỉ những quan sát thuộc 6 quốc gia trong top_countries. Nhóm dữ liệu đã lọc theo country. Tính toán median, mean của price_per_sqft và n cho từng quốc gia trong 6 quốc gia đó. Phân tích kỹ thuật
Giá trung vị và giá trung bình trên mỗi mét vuông tại mỗi quốc gia đều trùng hoặc rất gần nhau, cho thấy phân phối giá khá cân đối, ít bị lệch do các giá trị ngoại lai hoặc các bất thường lớn.
Số lượng giao dịch của mỗi quốc gia rất đồng đều, đều ở mức khoảng 15,000 giao dịch cho mỗi quốc gia, đảm bảo tính đại diện và độ tin cậy cao cho các thống kê giá.
Giá trung vị và trung bình từ 300 đến 420 USD/m2 trong các quốc gia này phản ánh mức giá bất động sản khá cao, đặc biệt là ở Pháp (420), Anh (400) và Trung Quốc (380), cho thấy mức độ đắt đỏ ở thị trường các nước phát triển hoặc có đô thị lớn.
Việc sử dụng cả hai để so sánh giúp nhận biết tính phân bố giá: giá trung vị không bị ảnh hưởng nhiều bởi các giá trị cực đoan, trong khi giá trung bình sẽ cho ảnh hưởng nhẹ hơn nếu phân phối giá đều.
Sự phân tách rõ ràng giá theo quốc gia cho thấy thị trường bất động sản có sự phân hóa đáng kể theo vùng địa lý, cần được đánh giá riêng biệt trong các phân tích thị trường hay đầu tư. Tóm lại Các số liệu trung vị và trung bình trên mỗi mét vuông có tính nhất quán và độ tin cậy cao cùng với số lượng giao dịch lớn, cung cấp cơ sở dữ liệu chắc chắn để phân tích thị trường bất động sản từng quốc gia. Thị trường có sự phân hóa giá rõ ràng theo quốc gia, điều này rất có ý nghĩa khi hoạch định chiến lược đầu tư hoặc chính sách phát triển bất động sản quốc tế. Dưới góc độ kinh tế, các kết luận kỹ thuật về giá trung vị và trung bình trên mỗi mét vuông tại 6 quốc gia cho thấy những điểm sau:
Mức giá bất động sản từ 300 đến 420 USD/m2 phản ánh sự đắt đỏ của thị trường nhà đất trong các quốc gia này, đặc biệt là ở những nền kinh tế phát triển hoặc đô thị lớn như Pháp (420 USD), Anh (400 USD) và Trung Quốc (380 USD). Điều này liên quan trực tiếp đến mức thu nhập, chi phí xây dựng cao, và nhu cầu lớn về nhà ở chất lượng trong các khu vực này.
Giá trung vị gần bằng giá trung bình cho thấy thị trường giá bất động sản ở các quốc gia này có sự ổn định, ít bị lệch bởi các quan sát ngoại lai hoặc những bất thường lớn. Từ đó, nhà đầu tư và nhà hoạch định chính sách có thể tin cậy vào các con số này để lập kế hoạch và dự báo.
Sự đồng đều về số lượng giao dịch (khoảng 15,000 giao dịch cho mỗi quốc gia) cho thấy các thị trường đều có quy mô tương đối lớn và năng động, tức có sự tham gia tích cực của người mua và bán, góp phần làm tăng tính thanh khoản và minh bạch của thị trường.
Tính phân hóa giá rõ rệt theo quốc gia phản ánh ảnh hưởng sâu sắc của các yếu tố kinh tế vĩ mô như GDP, lãi suất, chính sách đất đai, quy hoạch hạ tầng, và thậm chí các yếu tố văn hóa xã hội đến giá nhà. Các quốc gia phát triển thường có giá nhà cao hơn bởi nhu cầu lớn, quỹ đất hạn chế, và tiêu chuẩn sống cao hơn.
Những thông tin này rất quan trọng về mặt kinh tế khi xem xét đầu tư quốc tế, vì chi phí vốn, lợi nhuận kỳ vọng, cũng như rủi ro đầu tư bất động sản sẽ khác nhau đáng kể giữa các thị trường. Chính vì thế, các nhà đầu tư và chính quyền địa phương cần có chiến lược ứng xử phù hợp với đặc thù từng thị trường.
Ngoài ra, các yếu tố như tính ổn định và sức sống của giao dịch cũng phản ánh mức độ phát triển bền vững của thị trường bất động sản, ảnh hưởng đến sự phát triển kinh tế địa phương và quốc gia. Tóm lại, các kết quả kỹ thuật với sự nhất quán và đại diện của dữ liệu cung cấp nền tảng vững chắc để hiểu và đánh giá thị trường bất động sản quốc tế qua lăng kính kinh tế nhằm đưa ra các quyết định đầu tư, chính sách quản lý và phát triển phù hợp.
Mục đích của lệnh này là kiểm định xem có sự khác biệt có ý nghĩa thống kê về vị trí (thường hiểu là trung vị) của giá nhà (price) giữa các loại hình bất động sản (property_type) khác nhau hay không, mà không cần giả định rằng price tuân theo phân phối chuẩn trong mỗi nhóm. Hàm chính được sử dụng là kruskal.test(), một hàm cơ bản (Base R). Dòng code thực hiện:
kruskal.test(price ~ property_type, data = df)
##
## Kruskal-Wallis rank sum test
##
## data: price by property_type
## Kruskal-Wallis chi-squared = 1.5138, df = 5, p-value = 0.9115
Cấu trúc : kruskal.test(formula, data, …) Tham số formula (Bắt buộc): Xác định mô hình kiểm định. Ở đây là price ~ property_type. Biến định lượng (price) đứng trước dấu ngã ~, và biến phân loại xác định các nhóm (property_type) đứng sau. Tham số data (Bắt buộc): Data Frame chứa các biến được sử dụng trong formula (ở đây là df). Việc chọn kiểm định này là rất phù hợp vì chúng ta đã biết price bị lệch phải.
Giả thuyết H0 (Null Hypothesis): Phân phối (hoặc trung vị) của price là như nhau đối với tất cả 6 loại hình bất động sản (Apartment, Farmhouse, Independent House, Studio, Townhouse, Villa).
Giả thuyết H1 (Alternative Hypothesis): Có ít nhất một loại hình bất động sản có phân phối (hoặc trung vị) price khác biệt so với các loại hình còn lại. Kết quả kiểm định Kruskal-Wallis:
Thống kê Kruskal-Wallis chi-squared = 1.5138 với bậc tự do df = 5; - Giá trị p-value = 0.9115 lớn hơn mức ý nghĩa phổ biến 0.05. Phân tích kỹ thuật đầy đủ:
Kiểm định Kruskal-Wallis là kiểm định phi tham số dùng để so sánh phân phối hay trung vị của biến định lượng (ở đây là price) giữa nhiều nhóm độc lập (property_type).
Giả thuyết (H0): Các nhóm property_type có phân phối price giống nhau về trung vị.
Giá trị p-value = 0.9115 rất lớn, không đủ bằng chứng để bác bỏ giả thuyết không. Điều này có nghĩa, không phát hiện sự khác biệt thống kê có ý nghĩa về giá nhà giữa 6 loại hình bất động sản.
Chi-squared nhỏ (1.5138) và p-value lớn cũng cho thấy các nhóm property_type có tính đồng nhất về giá nhà.
Kiểm định phi tham số này phù hợp khi biến price không thỏa phân phối chuẩn, tránh sai lệch kết quả do giả định không được đáp ứng. - Do đó, mặc dù trong thực tế các loại hình bất động sản khác nhau thường có đặc điểm giá khác biệt, dữ liệu hiện tại không cho thấy chứng cứ rõ ràng về sự khác biệt giá theo loại bất động sản. Tóm lại, từ góc nhìn kỹ thuật, kiểm định Kruskal-Wallis cho thấy biến property_type không phải là yếu tố quyết định khác biệt về giá nhà trong dữ liệu này, cần tập trung khai thác các biến khác có ảnh hưởng, như diện tích, vị trí, hoặc năm xây dựng để giải thích biến động giá. Dưới góc độ kinh tế, kết quả kiểm định Kruskal-Wallis với p-value = 0.9115 lớn hơn 0.05 cho thấy không có sự khác biệt đáng kể về giá nhà giữa các loại hình bất động sản. Nhận xét kinh tế từ kết quả này như sau:
Kết quả này phản ánh rằng trong thị trường nghiên cứu, loại hình bất động sản (Apartment, Farmhouse, Independent House, Studio, Townhouse, Villa) chưa tạo ra sự phân hóa rõ rệt về giá cả, có thể do các yếu tố kinh tế như cung cầu, vị trí, tiện ích hay tiêu chuẩn xây dựng đã làm mức giá giữa các loại hình tương đối đồng nhất.
Điều này cho thấy các yếu tố khác ngoài loại hình đang đóng vai trò quan trọng hơn trong việc quyết định giá bất động sản, điển hình như diện tích, vị trí địa lý, năm xây dựng, và các yếu tố thị trường khác. - Trong bối cảnh thị trường bất động sản cạnh tranh hiện nay, sự tương đồng giá giữa các loại hình có thể cho thấy sự hội nhập về giá hoặc phân khúc khách hàng mục tiêu tương đồng.
Từ góc độ chính sách và quản lý, việc không có sự khác biệt giá rõ nét giữa các loại hình góp phần tạo ra sự bình ổn giá cả trên thị trường, giúp dễ dàng hơn trong việc xây dựng các chính sách phát triển nhà ở, quy hoạch và hỗ trợ tài chính. - Tuy nhiên, cần lưu ý rằng kết quả này dựa trên dữ liệu hiện tại và phân tích tổng thể; có thể cần nghiên cứu sâu hơn theo khu vực, phân khúc cụ thể để khảo sát biến động giá chi tiết hơn theo loại hình bất động sản. Tóm lại, về kinh tế, kết quả kiểm định cho thấy biến property_type không phải là yếu tố quyết định chính cho sự khác biệt về giá nhà trong tập dữ liệu này, mở ra hướng nghiên cứu và chú trọng đến các yếu tố khác để hiểu rõ hơn cơ cấu giá và hành vi thị trường bất động sản.
Khối lệnh R trên đếm số lượng bản ghi (giao dịch) cho mỗi quốc gia, sau đó tạo và hiển thị một biểu đồ cột đã được sắp xếp (từ thấp đến cao) để trực quan hóa sự phân bố này, rồi lưu biểu đồ đó vào biến p1. Dòng code thực hiện:
p1 <- df %>%
count(country) %>%
ggplot(aes(x = reorder(country, n), y = n, fill = country)) +
geom_col() +
geom_text(aes(label = n), vjust = -0.3, size = 3) +
labs(title = "Phân bố số lượng giao dịch theo quốc gia",
x = "Quốc gia", y = "Số lượng") +
theme_minimal() +
theme(legend.position = "none")
p1
Đoạn code trên thực hiện một chuỗi 3 hành động: (1) Tổng hợp dữ liệu, (2) Khởi tạo biểu đồ, và (3) Thêm các lớp (layers) cho biểu đồ. 1. p1 <- …:
o Mục đích: Gán toàn bộ đối tượng biểu đồ được tạo ra bởi chuỗi lệnh ggplot2 vào một biến có tên là p1.
o Mục đích: Đây là phần cốt lõi, một chuỗi lệnh (pipeline) hoàn chỉnh. o df %>%: Bắt đầu chuỗi, lấy data frame df làm đầu vào. o count(country): Hàm của dplyr. Nó nhận df từ bước trước, đếm số lần xuất hiện (số hàng) của mỗi giá trị duy nhất trong cột country.
Kết quả (trong bộ nhớ): Một data frame mới được tạo ra trong bộ nhớ (mà không gán vào biến nào). Bảng này có 2 cột: country (tên quốc gia) và n (số lượng). o %>% ggplot(aes(…)): Toán tử pipe (%>%) lấy bảng kết quả trong bộ nhớ từ count(country) và chuyển nó làm đối số đầu tiên cho hàm ggplot(). - aes(…): (Aesthetics) Định nghĩa các ánh xạ từ dữ liệu sang hình ảnh:
x = reorder(country, n): Ánh xạ trục X. Đây là tham số tùy chọn.
reorder(): Hàm này sắp xếp lại các quốc gia (country) dựa trên giá trị của n. Mặc định là sắp xếp tăng dần. Điều này giúp biểu đồ dễ đọc hơn nhiều so với việc để theo thứ tự bảng chữ cái.
y = n: Ánh xạ trục Y. Đây là tham số bắt buộc (khi dùng geom_col), xác định chiều cao của cột.
fill = country: Ánh xạ màu tô. Đây là tham số tùy chọn. Nó chỉ định rằng mỗi quốc gia sẽ có một màu riêng biệt.
o Mục đích: Thêm “geometry” để vẽ biểu đồ cột.
o Lưu ý: Dùng geom_col() vì chúng ta đã có giá trị y (là n). Nếu dùng geom_bar(), ggplot sẽ tự đếm (chúng ta chỉ cần cung cấp x).
o Mục đích: Thêm nhãn văn bản (số lượng) lên trên mỗi cột. Đây là lớp tùy chọn.
o aes(label = n): Ánh xạ bắt buộc cho geom_text, chỉ định văn bản cần hiển thị là giá trị của cột n.
o vjust = -0.3: (Vertical Justification) Tham số tùy chỉnh. Giá trị âm (-0.3) đẩy nhãn văn bản lên phía trên đỉnh cột một chút để tránh bị chồng lấn. o size = 3: Tham số tùy chỉnh, điều chỉnh kích thước phông chữ (mặc định là 5).
labs(…) +: o Mục đích: Tùy chỉnh nhãn (tiêu đề) của biểu đồ. Các tham số này là tùy chọn. o title, x, y: Đặt tiêu đề chính, tiêu đề trục X và tiêu đề trục Y.
theme_minimal() +: o
Mục đích: Áp dụng một chủ đề (theme) có sẵn. theme_minimal() là một theme tùy chọn phổ biến, tạo giao diện sạch sẽ, nền trắng.
Mục đích: Tùy chỉnh theme. Lệnh này ẩn phần chú giải (legend).
o Lý do: Đây là một thực hành tốt. Vì trục X (Quốc gia) và màu tô (fill) đều biểu thị cùng một thông tin (Quốc gia), nên phần chú giải trở nên thừa thãi và chiếm không gian.
Biểu đồ là biểu đồ thanh thể hiện phân bố số lượng giao dịch theo quốc gia, trong đó trục hoành (x) là các quốc gia, còn trục tung (y) là số lượng giao dịch.
Các thanh màu sắc khác nhau giúp phân biệt từng quốc gia dễ dàng. - Các giá trị số lượng giao dịch được hiển thị rõ ràng trên đầu mỗi thanh, từ đó ta thấy sự chênh lệch về số lượng giao dịch không quá lớn.
Giá trị thấp nhất là của UAE với 15,141 giao dịch, trong khi cao nhất là của Pháp với 15,628 giao dịch.
Khoảng cách giữa các quốc gia xếp cuối và đầu chỉ khoảng 487 giao dịch, cho thấy sự phân bố khá đồng đều trong tập mẫu này. - Biểu đồ dùng thanh thẳng đứng dễ quan sát, phù hợp để so sánh nhanh số liệu, và các màu sắc tươi sáng tạo sự thu hút thị giác. Phân tích kinh tế - Số lượng giao dịch thể hiện hoạt động kinh tế và thị trường của từng quốc gia có mức độ cao và ổn định tương đương nhau, với số giao dịch dao động trong khoảng từ 15,100 đến 15,600.
Pháp dẫn đầu với 15,628 giao dịch, có thể phản ánh một môi trường
kinh tế hoặc thị trường tài chính phát triển rất năng suất. - Trung Quốc
có 15,536 giao dịch, đứng thứ hai cao nhất, cho thấy quy mô thị trường
và giao dịch của nước này rất năng động. - Các nền kinh tế phát triển
như Mỹ (15,281), Nhật Bản (15,317), Anh (15,408) và Đức (15,401) cũng có
số lượng giao dịch cao tương đồng, khẳng định mức độ sôi động chung của
các nền kinh tế lớn. - Các quốc gia nhỏ hoặc thị trường mới nổi như UAE
(15,141) và Brazil (15,397) cũng có lượng giao dịch trên 15,000, cho
thấy sự tham gia thị trường toàn cầu ngày càng đa dạng. Tóm lại, biểu đồ
phản ánh một bức tranh hoạt động giao dịch quốc tế khá cân bằng, nơi các
quốc gia đa dạng về quy mô và phát triển đều đóng góp tích cực cho tổng
số giao dịch, với mức chênh lệch tương đối nhỏ giữa các quốc gia. Các
con số cụ thể giúp đánh giá mức độ bình ổn và sức mạnh thị trường của
từng quốc gia trong bối cảnh toàn cầu hiện nay.
Mục đích tổng thể của đoạn code này là đếm số lượng giao dịch cho từng property_type (loại nhà) và trực quan hóa kết quả bằng biểu đồ cột đã sắp xếp. Đoạn code thực hiện:
p2 <- df %>%
count(property_type) %>%
ggplot(aes(x = reorder(property_type, n), y = n, fill = property_type)) +
geom_bar(stat="identity") +
geom_text(aes(label = n), vjust=-0.3) +
scale_fill_brewer(palette = "Set3") +
labs(title="Số lượng nhà theo loại hình", x="Loại nhà", y="Số lượng") +
theme_minimal()
p2
Mục đích tổng thể của đoạn code này là để khám phá sự phân bố (distribution) của biến price (giá nhà), xem xét hình dạng, độ tập trung và độ lệch của dữ liệu giá. Dòng code thực hiện:
p3 <- ggplot(df, aes(x = price)) +
geom_histogram(bins = 40, fill = "steelblue", color = "white") +
geom_vline(aes(xintercept = mean(price)), color = "red", linetype = "dashed") +
labs(title = "Phân phối giá nhà", x = "Giá nhà", y = "Tần số") +
scale_x_continuous(labels = scales::comma) +
theme_minimal()
p3
####4.4.Phân bố mật độ giá theo phân khúc
Mục đích tổng thể của đoạn code này là để so sánh trực quan hình dạng phân phối (distribution shape) của price (giá nhà) giữa các nhóm price_group (phân khúc giá) khác nhau. Dòng code thực hiện:
p4 <- ggplot(df, aes(x = price, fill = price_group)) +
geom_density(alpha = 0.6) +
labs(title = "Phân bố mật độ giá theo phân khúc", x = "Giá nhà", y = "Mật độ") +
scale_fill_brewer(palette = "Set1") +
theme_minimal() +
theme(legend.position="bottom")
p4
Mục đích tổng thể của đoạn code này là để so sánh sự phân bố của property_size_sqft (diện tích) giữa 4 nhóm price_group (phân khúc giá). Dòng code thực hiện:
p5 <- ggplot(df, aes(x = price_group, y = property_size_sqft, fill = price_group)) +
geom_boxplot(outlier.colour = "red") +
geom_jitter(alpha=0.2, width=0.3) +
scale_y_continuous(labels=scales::comma) +
labs(title="Phân bố diện tích theo phân khúc giá", x="Phân khúc giá", y="Diện tích (m²)") +
theme_minimal()
p5
Mục đích tổng thể của đoạn code này là để khám phá sự phân bố của biến customer_salary (thu nhập khách hàng), xem xét hình dạng, độ tập trung và độ lệch của dữ liệu giá. Dòng code thực hiện:
p6 <- ggplot(df, aes(x = customer_salary)) +
geom_histogram(bins=40, fill="orange", color="white") +
geom_vline(aes(xintercept=mean(customer_salary)), color="red", linetype="dashed") +
labs(title="Phân phối thu nhập khách hàng", x="Thu nhập", y="Tần suất") +
theme_minimal()
p6
Mục đích tổng thể của đoạn code này là để trực quan hóa mối quan hệ giữa loan_amount (Khoản vay) và price (Giá nhà), đồng thời xem xét sự phân bố của các quốc gia trong mối quan hệ đó. Dòng code thực hiện:
p7 <- ggplot(df, aes(x=loan_amount, y=price, color=country)) +
geom_point(alpha=0.3) +
geom_smooth(method="lm", color="black") +
labs(title="Mối quan hệ giữa khoản vay và giá nhà", x="Khoản vay", y="Giá nhà") +
theme_minimal() +
scale_color_brewer(palette="Set2")
p7
## `geom_smooth()` using formula = 'y ~ x'
## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors
## Warning: Removed 76514 rows containing missing values or values outside the scale range
## (`geom_point()`).
Mục đích tổng thể của đoạn code này là để trực quan hóa mối quan hệ (tương quan) giữa customer_salary và loan_amount, đồng thời xem xét sự phân bố của các property_type (loại nhà) trong mối quan hệ đó. Dòng code thực hiện:
p8 <- ggplot(df, aes(x=customer_salary, y=loan_amount, color=property_type)) +
geom_point(alpha=0.4) +
geom_smooth(method="lm", se=FALSE, color="black") +
labs(title="Quan hệ giữa thu nhập và khoản vay", x="Thu nhập", y="Khoản vay") +
theme_minimal()
p8
## `geom_smooth()` using formula = 'y ~ x'
Mục đích tổng thể của đoạn code này là để trực quan hóa mối quan hệ (tương quan) giữa down_payment (Khoản đặt cọc) và price (Giá nhà), và phân tích mối quan hệ này bên trong từng price_group (Phân khúc giá). Dòng code thực hiện:
p9 <- ggplot(df, aes(x=down_payment, y=price, color=price_group)) +
geom_point(alpha=0.4) +
geom_smooth(method="lm", se=FALSE) +
labs(title="Quan hệ giữa khoản đặt cọc và giá nhà", x="Đặt cọc", y="Giá nhà") +
theme_minimal()
p9
## `geom_smooth()` using formula = 'y ~ x'
Mục đích tổng thể của đoạn code này là để so sánh sự phân bố của price (Giá nhà) giữa 13 country (Quốc gia) khác nhau. Dòng code thực hiện:
p10 <- ggplot(df, aes(x=country, y=price, fill=country)) +
geom_boxplot(outlier.color="red") +
scale_y_log10(labels=scales::comma) +
labs(title="Giá nhà theo quốc gia (log scale)", x="Quốc gia", y="Giá (log)") +
theme_minimal() +
theme(axis.text.x=element_text(angle=45, hjust=1))
p10
Mục đích chính của đoạn code đó là tính toán và trực quan hóa ma trận tương quan giữa 6 biến số liên tục: price, property_size_sqft, customer_salary, loan_amount, down_payment, và monthly_expenses. Dòng code thực hiện:
num_vars <- df %>% select(price, property_size_sqft, customer_salary, loan_amount, down_payment, monthly_expenses)
corrplot(cor(num_vars, use="complete.obs"), method="color", tl.col="black")
Bước 1: num_vars <- df %>% select(…)
o Mục đích: Gán kết quả (là một data frame mới) vào một biến có tên num_vars.
o select(…): Hàm của dplyr. - Mục đích: Chọn một tập hợp con các cột từ df. - Tham số: price, property_size_sqft, customer_salary, loan_amount, down_payment, monthly_expenses.
Bước 2: corrplot(cor(num_vars, …), …)
o Mục đích: Đây là hàm cốt lõi, nằm bên trong corrplot.
o cor(): Hàm của R (base R).
o use=“complete.obs”: Tham số tùy chọn.
o method=“color”: Tham số tùy chọn. - Mục đích: Chỉ định phương pháp trực quan hóa là “màu sắc”. Biểu đồ sẽ hiển thị một ma trận các ô vuông, trong đó màu sắc của ô vuông biểu thị giá trị tương quan. Mặc định (của corrplot), màu xanh dương đậm là tương quan dương mạnh (+1), và màu đỏ đậm là tương quan âm mạnh (-1). Màu trắng/nhạt là tương quan yếu (0).
o tl.col=“black”: Tham số tùy chọn. tl là viết tắt của “Text Label”. - Mục đích: Đặt màu của các nhãn văn bản (tên 6 biến) là màu đen. Góc nhìn Kỹ thuật
Ý nghĩa hệ số tương quan: Heatmap này dựa trên hệ số tương quan Pearson, đo mức độ liên quan tuyến tính giữa hai biến, biến động trong khoảng từ -1 đến 1. +1 là đồng biến tuyệt đối, -1 là nghịch biến tuyệt đối, 0 là không liên hệ.
Các cặp biến có tương quan mạnh: - price và property_size_sqft, loan_amount, down_payment: Các cặp này đều thể hiện màu xanh đậm (tương quan ~0.8-1), nghĩa là giá nhà càng lớn, diện tích nhà, khoản vay và số tiền trả trước thường càng lớn. - loan_amount và down_payment: Có tương quan cao, phản ánh giá trị khoản vay và tiền đặt cọc thường cùng tăng do liên quan tới tổng giá trị bất động sản. - Các mối liên hệ yếu:
customer_salary với các biến còn lại thể hiện màu nhạt hơn, cho thấy mức lương không liên hệ chặt với các yếu tố như diện tích, khoản vay hoặc tiền trả trước.
monthly_expenses có màu sáng nhạt với hầu hết các biến, tức mức chi tiêu hàng tháng gần như độc lập với giá nhà hoặc khoản vay. - Tương quan âm: Không có cặp biến nào cho ra sắc đỏ rõ (tương quan âm mạnh). Đây là điểm dễ nhận thấy qua heatmap với các ô không xuất hiện màu đỏ. Góc nhìn Kinh tế
Giá nhà gắn liền với giá trị khoản vay, diện tích, trả trước: Giá nhà, diện tích, khoản vay và số tiền down payment có mối quan hệ đồng biến rất mạnh, phản ánh cơ cấu tài chính phổ biến
– người mua nhà lớn cần nhiều vốn, khoản vay lớn, diện tích rộng hơn. Vì giá nhà chủ yếu quyết định quy mô giao dịch tài chính liên quan, các biến này thường tăng cùng nhau và tạo điều kiện phát triển các gói tài chính thiết kế riêng cho từng phân khúc bất động sản.
– phản ánh một thực trạng về bài toán nhà ở tại nhiều thành phố lớn.
Mục đích tổng thể của đoạn code này là để trực quan hóa và so sánh mối quan hệ (tương quan) giữa property_size_sqft (Diện tích) và price (Giá nhà), tách biệt cho từng country (Quốc gia). Dòng code thực hiện:
p12 <- ggplot(df, aes(x=property_size_sqft, y=price)) +
geom_point(alpha=0.3, color="darkblue") +
geom_smooth(method="lm", se=FALSE, color="red") +
facet_wrap(~country, scales="free") +
labs(title="Quan hệ giữa diện tích và giá nhà theo quốc gia", x="Diện tích", y="Giá") +
theme_minimal()
p12
## `geom_smooth()` using formula = 'y ~ x'
Mục đích tổng thể là đếm và so sánh số lượng decision (quyết định Mua/Không mua) dựa trên việc bất động sản có garage (Có/Không). Dòng code thực hiện:
p13 <- df %>%
group_by(garage, decision) %>%
summarise(n=n()) %>%
ggplot(aes(x=factor(garage), y=n, fill=factor(decision))) +
geom_col(position="dodge") +
geom_text(aes(label=n), position=position_dodge(0.9), vjust=-0.3) +
labs(title="Quyết định mua theo tình trạng có garage", x="Garage (0=Không, 1=Có)", y="Số lượng") +
theme_minimal()
## `summarise()` has grouped output by 'garage'. You can override using the
## `.groups` argument.
p13
Mục đích tổng thể của đoạn code này là để so sánh trực quan hình dạng phân phối (distribution shape) của emi_to_income_ratio giữa 4 nhóm price_group (phân khúc giá) khác nhau. Dòng code thực hiện:
p14 <- ggplot(df, aes(x=emi_to_income_ratio, fill=price_group)) +
geom_density(alpha=0.6) +
labs(title="Phân bố tỷ lệ EMI/Thu nhập theo phân khúc giá", x="Tỷ lệ EMI/Thu nhập", y="Mật độ") +
theme_minimal() +
theme(axis.text.x=element_text(angle=45, hjust=1))
p14
Mục đích tổng thể của đoạn code này là để khám phá sự phân bố (distribution) của biến constructed_year, xem xét xem các bất động sản trong tập dữ liệu là cũ hay mới, và sự phân bố qua các năm như thế nào. Dòng code thực hiện:
p15 <- ggplot(df, aes(x=constructed_year)) +
geom_histogram(bins=40, fill="darkgreen", color="white") +
labs(title="Phân phối năm xây dựng của bất động sản", x="Năm xây dựng", y="Số lượng") +
theme_minimal() +
theme(axis.text.x=element_text(angle=45, hjust=1))
p15
Mục đích: In đối tượng biểu đồ p15 ra cửa sổ Plots. Góc nhìn Kỹ thuật
Ý nghĩa histogram: Dùng histogram để thể hiện phân phối tần suất của biến liên tục hoặc thứ tự (năm xây dựng nhà), từ đó nhận diện được xu hướng, giai đoạn tăng/giảm mạnh của nguồn cung bất động sản trên thị trường.
Chi tiết trục X: Nhãn năm xây dựng được xoay 45 độ nhằm tránh chồng lấn, giúp theo dõi sát hơn từng thời kỳ cụ thể. Đây là thực tiễn tốt khi dữ liệu thời gian kéo dài qua nhiều thập kỷ. Góc nhìn Kinh tế
Minh chứng cho sự phát triển bền vững: Phân phối đều của số lượng bất động sản qua các năm thể hiện sự ổn định của thị trường nhà ở hoặc chính sách quy hoạch đô thị lâu dài. Không có chu kỳ khủng hoảng nguồn cung hay bong bóng xây dựng lớn, góp phần duy trì nhịp tăng trưởng ổn định.
Ý nghĩa về tuổi đời tài sản: Khi nguồn cung mới không đột biến qua từng năm, thị trường sẽ hài hòa giữa nhà ở mới xây và nhà có tuổi đời trung bình/cũ, tạo đa dạng lựa chọn cho khách hàng với nhiều mức giá và sở thích khác nhau.
Không xuất hiện sóng đầu tư ngắn hạn: Nếu có giai đoạn “sốt đất” hoặc bùng nổ phát triển, histogram sẽ có các cột vượt trội hẳn so với vùng lân cận. Tuy nhiên, dạng phân bố đều cho thấy thị trường chưa bị đầu cơ đẩy mạnh xây dựng ồ ạt, tránh được nguy cơ dư thừa nguồn cung dẫn đến khủng hoảng thừa.
p16 <- ggplot(df, aes(x=factor(loan_tenure_years), y=loan_amount, fill=factor(loan_tenure_years))) +
geom_boxplot() +
scale_y_continuous(labels=scales::comma) +
labs(title="Khoản vay theo thời hạn", x="Thời hạn vay (năm)", y="Khoản vay") +
theme_minimal()
p16
df: khung dữ liệu chứa thông tin các khoản vay.
x=factor(loan_tenure_years): chuyển biến thời hạn vay (số năm) thành biến phân loại (factor) để biểu diễn theo từng nhóm.
y=loan_amount: giá trị khoản vay là biến định lượng, biểu diễn trên trục tung.
fill=factor(loan_tenure_years): mỗi nhóm thời hạn vay có màu sắc riêng giúp dễ phân biệt.
Tạo biểu đồ hộp (boxplot) thể hiện phân bố dữ liệu (median, quartile, outlier) của khoản vay trong từng nhóm thời hạn.
Hộp giúp nhận diện khoản vay trung vị, phạm vi biến động (IQR) và sự xuất hiện của các giá trị ngoại lai (outliers).
Giúp định dạng trục Y thành dạng số có dấu phẩy phân tách hàng nghìn, giúp dễ đọc hơn (ví dụ: 1,000,000 thay vì 1000000).
Cung cấp tiêu đề và nhãn trục, giúp đồ thị dễ hiểu:
title: “Khoản vay theo thời hạn”.
x: “Thời hạn vay (năm)”.
y: “Khoản vay”.
Áp dụng giao diện tối giản, loại bỏ chi tiết thừa, tập trung vào dữ liệu.
Góc nhìn kỹ thuật
Biểu đồ hộp được chọn vì thể hiện được phân bố dữ liệu, độ lệch và giá trị ngoại lai của khoản vay.
So sánh nhiều nhóm (10, 15, 20, 25, 30 năm) trên cùng biểu đồ giúp phát hiện sự khác biệt giữa các kỳ hạn.
Góc nhìn kinh tế
Thông thường, thời hạn vay dài hơn cho phép người vay vay khoản lớn hơn, do áp lực trả nợ hàng tháng giảm.
Tuy nhiên, nếu phân bố giữa các nhóm gần như tương đương, điều này phản ánh chính sách tín dụng thận trọng, ngân hàng kiểm soát hạn mức vay không phụ thuộc quá nhiều vào kỳ hạn.
Sự xuất hiện của các điểm ngoại lai ở nhóm 25–30 năm có thể đại diện cho các khoản vay doanh nghiệp hoặc dự án lớn thay vì vay mua nhà cá nhân.
Mục đích của đoạn code này là phân tích mối quan hệ giữa mức giá bất động sản và mức độ hài lòng của khách hàng, nhằm xem liệu khách hàng mua nhà ở phân khúc cao có xu hướng hài lòng hơn không.
Dòng code thực hiện:
p17 <- df %>%
group_by(price_group) %>%
summarise(mean_sat = mean(satisfaction_score)) %>%
ggplot(aes(x=price_group, y=mean_sat, fill=price_group)) +
geom_col() +
geom_text(aes(label=round(mean_sat,1)), vjust=-0.3) +
labs(title="Mức độ hài lòng trung bình theo phân khúc giá",
x="Phân khúc giá",
y="Điểm trung bình") +
theme_minimal()
p17
Gom nhóm dữ liệu theo phân khúc giá (thấp, trung bình, cao, rất cao).
Tính điểm hài lòng trung bình cho mỗi phân khúc giá.
Khởi tạo biểu đồ với trục X là price_group, trục Y là mean_sat, và fill=price_group giúp tô màu khác nhau cho từng phân khúc.
Tạo biểu đồ cột (bar chart) thể hiện giá trị trung bình cho từng nhóm.
Chiều cao của cột phản ánh mức độ hài lòng trung bình.
Hiển thị giá trị trung bình ngay trên cột, giúp người xem dễ so sánh trực quan giữa các nhóm.
Đặt tiêu đề “Mức độ hài lòng trung bình theo phân khúc giá”, cùng nhãn trục rõ ràng.
Sử dụng giao diện đơn giản, tạo sự cân đối trực quan.
Góc nhìn kỹ thuật
Biểu đồ cột cho phép so sánh trực tiếp giá trị trung bình giữa các nhóm một cách rõ ràng và dễ đọc.
Việc hiển thị giá trị cụ thể trên đầu cột giúp tránh phải ước lượng từ trục tung.
Góc nhìn kinh tế
Khi mức độ hài lòng giữa các phân khúc tương đương, có thể suy ra rằng chất lượng dịch vụ và trải nghiệm khách hàng được đảm bảo đồng đều, không chỉ tập trung vào phân khúc cao cấp.
Nếu có sự chênh lệch (ví dụ nhóm “rất cao” cao hơn đáng kể), doanh nghiệp có thể cân nhắc nâng cao trải nghiệm dịch vụ cho nhóm trung bình và thấp, nhằm gia tăng mức độ hài lòng toàn thị trường.
Kết quả trong biểu đồ cho thấy điểm trung bình đều khoảng 5.5, phản ánh thị trường cân bằng và khách hàng có mức hài lòng ổn định bất kể giá trị bất động sản.
Mục tiêu của đoạn mã này là khám phá mối quan hệ giữa chất lượng khu vực sinh sống (neighbourhood_rating) và mức độ kết nối giao thông – hạ tầng (connectivity_score), đồng thời xem xét liệu phân khúc giá (price_group) có ảnh hưởng đến mối quan hệ này hay không.
Dòng code thực hiện:
p18 <- ggplot(df, aes(x=neighbourhood_rating, y=connectivity_score, color=price_group)) +
geom_jitter(alpha=0.4) +
geom_smooth(method="lm", se=FALSE, color="black") +
labs(title="Mối liên hệ giữa tiện ích khu vực và kết nối",
x="Điểm khu vực",
y="Điểm kết nối") +
theme_minimal()
p18
## `geom_smooth()` using formula = 'y ~ x'
Khởi tạo biểu đồ từ bộ dữ liệu df.
Trục X: neighbourhood_rating – thể hiện điểm đánh giá khu vực sinh sống.
Trục Y: connectivity_score – biểu thị điểm kết nối giao thông, hạ tầng.
color=price_group: tô màu theo phân khúc giá, giúp nhận diện xu hướng giữa các nhóm giá khác nhau.
Thêm các điểm dữ liệu phân tán nhẹ (jitter) để tránh chồng lấn, giúp thấy được mật độ dữ liệu.
alpha=0.4: tạo độ trong suốt giúp nhìn rõ vùng dữ liệu dày đặc.
Thêm đường hồi quy tuyến tính (Linear Model) thể hiện xu hướng tổng thể giữa hai biến.
se=FALSE: tắt hiển thị khoảng tin cậy (confidence interval).
color=“black”: tô màu đen để đường hồi quy nổi bật hơn.
Gán tiêu đề và nhãn trục giúp biểu đồ dễ hiểu, nêu rõ mục đích phân tích.
Sử dụng chủ đề tối giản giúp tập trung vào dữ liệu, không bị rối bởi đường kẻ nền.
Góc nhìn kỹ thuật
Dạng scatter plot (biểu đồ phân tán) rất phù hợp để kiểm tra mối tương quan tuyến tính giữa hai biến định lượng.
Việc thêm geom_jitter() giúp biểu đồ tránh chồng điểm, đặc biệt khi dữ liệu có giá trị rời rạc như thang điểm từ 1–10.
Đường hồi quy (geom_smooth(method=“lm”)) cung cấp xu hướng tổng thể, giúp xác định liệu điểm khu vực cao có đi kèm điểm kết nối cao hay không.
Góc nhìn kinh tế
Biểu đồ cho thấy mối quan hệ thuận chiều nhẹ giữa điểm khu vực và điểm kết nối — khu vực được đánh giá cao thường cũng có hạ tầng tốt.
Các nhóm phân khúc giá (Cao, Rất cao, Trung bình, Thấp) phân bố tương đối đều → cho thấy không có sự phân hóa mạnh giữa giá nhà và chất lượng hạ tầng.
Điều này phản ánh sự phát triển đồng đều của đô thị, không chỉ tập trung đầu tư hạ tầng ở các khu vực cao cấp.
Từ góc nhìn đầu tư, mối tương quan nhẹ nhưng ổn định này cho thấy các khu vực có tiện ích tốt vẫn duy trì khả năng kết nối ổn định — là yếu tố quan trọng đảm bảo giá trị lâu dài của bất động sản.
Mục tiêu của đoạn mã là phân tích mối quan hệ giữa thu nhập khách hàng và giá bất động sản, đồng thời so sánh xu hướng giữa các loại hình nhà ở khác nhau (Apartment, Villa, Studio, v.v.). Dòng code thực hiện:
p19 <- ggplot(df, aes(x=customer_salary, y=price, color=property_type)) +
geom_point(alpha=0.4) +
geom_smooth(method="lm", se=FALSE) +
facet_wrap(~property_type) +
scale_y_log10(labels=scales::comma) +
labs(title="Giá nhà theo thu nhập khách hàng và loại hình",
x="Thu nhập",
y="Giá (log)") +
theme_minimal()
p19
## `geom_smooth()` using formula = 'y ~ x'
🔹 Giải thích từng bước
Khởi tạo ggplot
df: bộ dữ liệu chính.
aes(x=customer_salary, y=price, color=property_type): ánh xạ 3 biến quan trọng:
Trục X: thu nhập khách hàng (customer_salary).
Trục Y: giá bất động sản (price).
Màu sắc (color) phân biệt từng loại hình nhà ở.
geom_point(alpha=0.4)
Tạo đám mây điểm phân tán giữa thu nhập và giá nhà.
alpha=0.4 giúp hiển thị rõ các vùng chồng lấn dữ liệu.
geom_smooth(method=“lm”, se=FALSE)
Thêm đường hồi quy tuyến tính cho từng nhóm, thể hiện xu hướng mối quan hệ giữa thu nhập và giá nhà.
se=FALSE tắt vùng tin cậy (confidence interval) để biểu đồ gọn gàng hơn.
facet_wrap(~property_type)
Tách biểu đồ thành 6 ô nhỏ tương ứng với 6 loại hình nhà khác nhau.
Giúp dễ dàng so sánh xu hướng giữa các nhóm.
scale_y_log10(labels=scales::comma)
Áp dụng thang logarit cơ số 10 cho giá, giúp dễ đọc hơn khi dữ liệu có độ chênh lớn giữa giá thấp và giá cao.
scales::comma định dạng số có dấu phẩy ngăn cách hàng nghìn.
labs(…) + theme_minimal()
Thêm tiêu đề, nhãn trục và áp dụng theme tối giản để tập trung vào dữ liệu.
Góc nhìn kỹ thuật
Đây là biểu đồ scatter plot đa chiều (x, y, màu, facet), kết hợp giữa:
geom_point() cho phân bố dữ liệu.
geom_smooth() cho xu hướng hồi quy.
facet_wrap() cho so sánh nhóm.
Việc sử dụng log-scale giúp giảm ảnh hưởng của các giá trị ngoại lai (outliers), đảm bảo biểu đồ không bị méo.
Mỗi facet hiển thị một loại bất động sản riêng → dễ dàng nhận ra loại nào có biên độ giá và mức thu nhập khách hàng tương ứng.
Góc nhìn kinh tế
Nhìn chung, biểu đồ cho thấy mối quan hệ thuận chiều giữa thu nhập và giá bất động sản.
Các loại nhà như Villa, Farmhouse có mức giá trung bình cao hơn hẳn so với Apartment hay Studio, phản ánh đúng cấu trúc thị trường.
Biểu đồ dạng facet cho phép nhận diện phân khúc khách hàng mục tiêu của từng loại hình:
Studio / Apartment: phù hợp nhóm thu nhập trung bình.
Townhouse / Villa: nhóm thu nhập cao, có xu hướng chi tiêu xa xỉ hơn.
Kết luận: Thu nhập là yếu tố dự báo mạnh mẽ nhất đối với giá trị giao dịch bất động sản, và mối quan hệ này giữ nguyên hướng dương ở tất cả các loại hình.
Mục tiêu của đoạn mã là phân tích mối quan hệ giữa điểm tiện ích khu vực (neighbourhood_rating) và mức độ hài lòng của khách hàng (satisfaction_score), đồng thời so sánh xu hướng giữa các phân khúc giá khác nhau (price_group). Từ đó, ta có thể xác định mức độ nhạy cảm của sự hài lòng đối với chất lượng khu vực ở từng nhóm khách hàng. Dòng code thực hiện:
p21 <- ggplot(df, aes(x=neighbourhood_rating, y=satisfaction_score, color=price_group)) +
geom_point(alpha=0.4) +
geom_smooth(method="lm", se=FALSE, size=1) +
facet_wrap(~price_group) +
labs(title="Mức độ hài lòng theo điểm tiện ích khu vực và phân khúc giá",
x="Điểm tiện ích khu vực",
y="Điểm hài lòng khách hàng") +
theme_minimal()
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
p21
## `geom_smooth()` using formula = 'y ~ x'
🔍 Giải thích chi tiết từng dòng
ggplot(df, aes(…))
Khởi tạo biểu đồ với nguồn dữ liệu df.
x = neighbourhood_rating: trục hoành biểu diễn điểm tiện ích khu vực (1–10).
y = satisfaction_score: trục tung thể hiện điểm hài lòng của khách hàng.
color = price_group: phân nhóm dữ liệu theo phân khúc giá (Cao, Rất cao, Trung bình, Thấp).
geom_point(alpha=0.4)
Thêm các điểm biểu diễn từng khách hàng riêng lẻ.
alpha=0.4 giúp trong suốt 60%, tránh chồng lấn quá nhiều điểm.
geom_smooth(method=“lm”, se=FALSE, size=1)
Thêm đường hồi quy tuyến tính cho từng nhóm price_group.
method=“lm”: mô hình tuyến tính (Linear Model).
se=FALSE: bỏ vùng tin cậy xung quanh đường hồi quy.
size=1: làm đường nổi bật hơn.
facet_wrap(~price_group)
Tách biểu đồ thành 4 ô riêng biệt, tương ứng với 4 nhóm giá (Cao, Rất cao, Trung bình, Thấp).
Giúp ta quan sát rõ mối quan hệ trong từng phân khúc khách hàng.
labs(…)
Cung cấp tiêu đề và nhãn trục bằng tiếng Việt rõ ràng:
title: Mức độ hài lòng theo điểm tiện ích khu vực và phân khúc giá
x: Điểm tiện ích khu vực
y: Điểm hài lòng khách hàng
theme_minimal()
Áp dụng giao diện tối giản, tập trung vào nội dung chính là mối quan hệ giữa các biến.
Góc nhìn Kỹ thuật
Đây là biểu đồ phân tán (Scatter Plot) kết hợp với đường hồi quy tuyến tính, chia nhóm bằng facet_wrap.
Cấu trúc 5 lớp (layers) chính:
Dữ liệu (data)
Điểm (geom_point)
Đường hồi quy (geom_smooth)
Phân nhóm (facet_wrap)
Giao diện (theme_minimal + labs)
Biểu đồ cho phép đánh giá tính tương quan tuyến tính giữa tiện ích khu vực và hài lòng, trong từng nhóm giá.
Nếu các đường hồi quy có độ dốc lớn hơn, chứng tỏ nhóm đó nhạy cảm hơn với chất lượng khu vực.
Góc nhìn Kinh tế
Các nhóm giá cao và rất cao thường có đường hồi quy dốc hơn, thể hiện:
Khi điểm tiện ích khu vực tăng, mức độ hài lòng tăng rõ rệt.
Khách hàng cao cấp đặt nhiều kỳ vọng vào môi trường sống, tiện nghi và kết nối hạ tầng.
Ngược lại, nhóm trung bình và thấp có đường gần ngang:
Mức độ hài lòng ít bị ảnh hưởng bởi tiện ích khu vực.
Họ quan tâm nhiều hơn đến yếu tố giá cả, khả năng tiếp cận tài chính.
Điều này giúp doanh nghiệp bất động sản định hướng phát triển sản phẩm:
Với phân khúc cao cấp → tập trung đầu tư vào tiện ích và môi trường sống.
Với phân khúc trung bình → ưu tiên giá trị sử dụng và khả năng chi trả.
Kết luận Biểu đồ này minh họa rõ rằng chất lượng khu vực sống là yếu tố tác động mạnh đến sự hài lòng của khách hàng cao cấp, trong khi phân khúc thấp ít bị ảnh hưởng — cho thấy sự khác biệt về hành vi và kỳ vọng tiêu dùng giữa các tầng lớp thu nhập.
#PHẦN 2: PHÂN TÍCH BÁO CÁO TÀI CHÍNH CỦA VSC
##CHƯƠNG 1: TỔNG QUAN VỀ BỘ DỮ LIỆU VSC
###1.1 Giới thiệu về bộ dữ liệu
Bộ dữ liệu VSC (Vietnam Stock Corporation) bao gồm thông tin tài chính chi tiết của các công ty niêm yết trên sàn chứng khoán Việt Nam trong giai đoạn từ năm 2017 đến quý II năm 2025. Dữ liệu được thu thập từ các báo cáo tài chính hàng năm, bao gồm các chỉ số quan trọng như doanh thu, lợi nhuận, tài sản, nợ phải trả và các chỉ số tài chính khác. Bộ dữ liệu này cung cấp cái nhìn toàn diện về hiệu quả hoạt động kinh doanh và tình hình tài chính của các công ty trong nhiều ngành nghề khác nhau. Dữ liệu phản ánh nhiều khía cạnh khác nhau của hoạt động tài chính của doanh nghiệp được sàng lọc từ các thành phần từ báo cáo tài chính theo cụ thể như sau:
Bảng cân đối kế toán (Balance Sheet) nhằm phản ánh tài sản,nợ phải trả, vốn chủ sở hữu và các khoản phải thu, phải trả trong ngắn hạn và dài hạn.
Báo cáo kết quả kinh doanh (Income Statement) thể hiện doanh thu, chi phí, lợi nhuận, thuế thu nhập doanh nghiệp và hiệu quả hoạt động trong từng kỳ.
Báo cáo lưu chuyển tiền tệ (Cash Flow Statement) cung cấp thông tin về dòng tiền vào và ra từ các hoạt động kinh doanh, đầu tư và tài chính. Sau quá trình tiền xử lý bộ dữ liệu đã được tái cấu trúc theo dạng dài (long format) với các cột các hàng tương ứng với một chỉ tiêu tài chính (Indicator) trong một kỳ kế toán (Quarter) xác định đi kèm với năm,tháng và ngày quy ước tương ứng Tóm lại: Bộ dữ liệu bctc1.xlss là một công cụ mô phỏng thực tế để thực hành các kỹ thuật phân tích dữ liệu tài chính bằng R, kết nối giữa lý thuyết thống kể, mô hình kinh tế và ứng dục thực trong quá trình quản trị tài chính doanh nghiệp.
####1.1.1.Tải dữ liệu
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ forcats 1.0.0 ✔ readr 2.1.5
## ✔ lubridate 1.9.4 ✔ tibble 3.3.0
## ✔ purrr 1.1.0
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(plotly)
## Warning: package 'plotly' was built under R version 4.5.2
##
## Attaching package: 'plotly'
##
## The following object is masked from 'package:ggplot2':
##
## last_plot
##
## The following object is masked from 'package:stats':
##
## filter
##
## The following object is masked from 'package:graphics':
##
## layout
library(readxl)
library(lubridate)
library(scales)
##
## Attaching package: 'scales'
##
## The following object is masked from 'package:purrr':
##
## discard
##
## The following object is masked from 'package:readr':
##
## col_factor
library(knitr)
library(kableExtra)
##
## Attaching package: 'kableExtra'
##
## The following object is masked from 'package:dplyr':
##
## group_rows
library(broom)
library(skimr)
library(ggplot2)
library(tseries) # adf.test
## Registered S3 method overwritten by 'quantmod':
## method from
## as.zoo.data.frame zoo
library(lmtest) # dwtest, bptest
## Loading required package: zoo
##
## Attaching package: 'zoo'
##
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
library(car) # vif
## Loading required package: carData
##
## Attaching package: 'car'
##
## The following object is masked from 'package:purrr':
##
## some
##
## The following object is masked from 'package:dplyr':
##
## recode
library(moments) # skewness, kurtosis
library(xtable)
library(zoo)
library(forecast)
library(corrplot)
library(scales)
library(glue)
library(viridis)
## Loading required package: viridisLite
## Warning: package 'viridisLite' was built under R version 4.5.2
##
## Attaching package: 'viridis'
##
## The following object is masked from 'package:scales':
##
## viridis_pal
library(ggridges)
library(GGally)
library(reshape2)
##
## Attaching package: 'reshape2'
##
## The following object is masked from 'package:tidyr':
##
## smiths
library(dplyr)
library(tidyr)
library(viridisLite)
library(magrittr)
##
## Attaching package: 'magrittr'
##
## The following object is masked from 'package:purrr':
##
## set_names
##
## The following object is masked from 'package:tidyr':
##
## extract
library(patchwork)
theme_set(theme_minimal())
data <- read_excel("C:/Users/ACER/Downloads/bctc1.xlsx")
df <- read_excel("C:/Users/ACER/Downloads/bctc1.xlsx")
####1.1.2.Kiểm tra kích thước dữ liệu
dim(df)
## [1] 3162 19
####1.1.3.Kiểm tra cấu trúc dữ liệu
str(df)
## tibble [3,162 × 19] (S3: tbl_df/tbl/data.frame)
## $ Indicator : chr [1:3162] "Chi phí phải trả" "Chi phí phải trả" "Chi phí phải trả" "Chi phí phải trả" ...
## $ Quarter : chr [1:3162] "Q1 2017" "Q2 2017" "Q3 2017" "Q4 2017" ...
## $ Value : num [1:3162] 1.48e+10 1.41e+10 1.69e+10 9.57e+09 1.96e+10 ...
## $ Year : num [1:3162] 2017 2017 2017 2017 2018 ...
## $ QuarterNum : num [1:3162] 1 2 3 4 1 2 3 4 1 2 ...
## $ Month : num [1:3162] 2 5 8 11 2 5 8 11 2 5 ...
## $ Day : num [1:3162] 1 1 1 1 1 1 1 1 1 1 ...
## $ Date : POSIXct[1:3162], format: "2017-02-01" "2017-05-01" ...
## $ MissingFlag : num [1:3162] 0 0 0 0 0 0 0 0 0 0 ...
## $ Lag1 : num [1:3162] NA 1.48e+10 1.41e+10 1.69e+10 9.57e+09 ...
## $ PctChange : num [1:3162] NA -0.0434 0.1984 -0.4343 1.0483 ...
## $ RollingMean_4q: num [1:3162] 1.48e+10 1.44e+10 1.53e+10 1.38e+10 1.51e+10 ...
## $ RollingStd_4q : num [1:3162] 0.00 4.53e+08 1.47e+09 3.09e+09 4.29e+09 ...
## $ Zscore : num [1:3162] -0.489 -0.514 -0.404 -0.692 -0.299 ...
## $ Q1 : num [1:3162] 1 0 0 0 1 0 0 0 1 0 ...
## $ Q2 : num [1:3162] 0 1 0 0 0 1 0 0 0 1 ...
## $ Q3 : num [1:3162] 0 0 1 0 0 0 1 0 0 0 ...
## $ Q4 : num [1:3162] 0 0 0 1 0 0 0 1 0 0 ...
## $ Sheet : chr [1:3162] "Cân đối kế toán" "Cân đối kế toán" "Cân đối kế toán" "Cân đối kế toán" ...
####1.1.4.Thống kê mô tả nhanh toàn bộ dataset
skim(df)
| Name | df |
| Number of rows | 3162 |
| Number of columns | 19 |
| _______________________ | |
| Column type frequency: | |
| character | 3 |
| numeric | 15 |
| POSIXct | 1 |
| ________________________ | |
| Group variables | None |
Variable type: character
| skim_variable | n_missing | complete_rate | min | max | empty | n_unique | whitespace |
|---|---|---|---|---|---|---|---|
| Indicator | 0 | 1 | 4 | 87 | 0 | 90 | 0 |
| Quarter | 0 | 1 | 7 | 7 | 0 | 34 | 0 |
| Sheet | 0 | 1 | 9 | 18 | 0 | 3 | 0 |
Variable type: numeric
| skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|
| Value | 34 | 0.99 | 1.974624e+11 | 6.941849e+11 | -2.649856e+12 | 0.00 | 0.00 | 3.186325e+10 | 8.775088e+12 | ▁▇▁▁▁ |
| Year | 0 | 1.00 | 2.020760e+03 | 2.460000e+00 | 2.017000e+03 | 2019.00 | 2021.00 | 2.023000e+03 | 2.025000e+03 | ▇▇▃▇▆ |
| QuarterNum | 0 | 1.00 | 2.440000e+00 | 1.120000e+00 | 1.000000e+00 | 1.00 | 2.00 | 3.000000e+00 | 4.000000e+00 | ▇▇▁▇▇ |
| Month | 0 | 1.00 | 6.320000e+00 | 3.350000e+00 | 2.000000e+00 | 2.00 | 5.00 | 8.000000e+00 | 1.100000e+01 | ▇▇▁▇▇ |
| Day | 0 | 1.00 | 1.000000e+00 | 0.000000e+00 | 1.000000e+00 | 1.00 | 1.00 | 1.000000e+00 | 1.000000e+00 | ▁▁▇▁▁ |
| MissingFlag | 0 | 1.00 | 1.000000e-02 | 1.000000e-01 | 0.000000e+00 | 0.00 | 0.00 | 0.000000e+00 | 1.000000e+00 | ▇▁▁▁▁ |
| Lag1 | 124 | 0.96 | 1.889746e+11 | 6.593152e+11 | -2.649856e+12 | 0.00 | 0.00 | 3.150600e+10 | 8.212295e+12 | ▁▇▁▁▁ |
| PctChange | 1622 | 0.49 | 2.978000e+01 | 1.015420e+03 | -1.920220e+03 | -0.34 | 0.00 | 1.300000e-01 | 3.953194e+04 | ▇▁▁▁▁ |
| RollingMean_4q | 1 | 1.00 | 1.807354e+11 | 6.177033e+11 | -8.113467e+11 | 0.00 | 0.00 | 3.056658e+10 | 8.023584e+12 | ▇▁▁▁▁ |
| RollingStd_4q | 0 | 1.00 | 4.484622e+10 | 1.509568e+11 | 0.000000e+00 | 0.00 | 5254104.37 | 1.876170e+10 | 1.849813e+12 | ▇▁▁▁▁ |
| Zscore | 1088 | 0.66 | 0.000000e+00 | 1.000000e+00 | -5.740000e+00 | -0.49 | -0.25 | 2.500000e-01 | 5.740000e+00 | ▁▁▇▁▁ |
| Q1 | 0 | 1.00 | 2.600000e-01 | 4.400000e-01 | 0.000000e+00 | 0.00 | 0.00 | 1.000000e+00 | 1.000000e+00 | ▇▁▁▁▃ |
| Q2 | 0 | 1.00 | 2.600000e-01 | 4.400000e-01 | 0.000000e+00 | 0.00 | 0.00 | 1.000000e+00 | 1.000000e+00 | ▇▁▁▁▃ |
| Q3 | 0 | 1.00 | 2.400000e-01 | 4.200000e-01 | 0.000000e+00 | 0.00 | 0.00 | 0.000000e+00 | 1.000000e+00 | ▇▁▁▁▂ |
| Q4 | 0 | 1.00 | 2.400000e-01 | 4.200000e-01 | 0.000000e+00 | 0.00 | 0.00 | 0.000000e+00 | 1.000000e+00 | ▇▁▁▁▂ |
Variable type: POSIXct
| skim_variable | n_missing | complete_rate | min | max | median | n_unique |
|---|---|---|---|---|---|---|
| Date | 0 | 1 | 2017-02-01 | 2025-05-01 | 2021-03-17 12:00:00 | 34 |
####1.1.5.Kiểm tra 5 dòng đầu
head(df, 5)
## # A tibble: 5 × 19
## Indicator Quarter Value Year QuarterNum Month Day Date
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dttm>
## 1 Chi phí phải… Q1 2017 1.48e10 2017 1 2 1 2017-02-01 00:00:00
## 2 Chi phí phải… Q2 2017 1.41e10 2017 2 5 1 2017-05-01 00:00:00
## 3 Chi phí phải… Q3 2017 1.69e10 2017 3 8 1 2017-08-01 00:00:00
## 4 Chi phí phải… Q4 2017 9.57e 9 2017 4 11 1 2017-11-01 00:00:00
## 5 Chi phí phải… Q1 2018 1.96e10 2018 1 2 1 2018-02-01 00:00:00
## # ℹ 11 more variables: MissingFlag <dbl>, Lag1 <dbl>, PctChange <dbl>,
## # RollingMean_4q <dbl>, RollingStd_4q <dbl>, Zscore <dbl>, Q1 <dbl>,
## # Q2 <dbl>, Q3 <dbl>, Q4 <dbl>, Sheet <chr>
####1.1.6.kiểm tra 5 dòng cuối
tail(df, 5)
## # A tibble: 5 × 19
## Indicator Quarter Value Year QuarterNum Month Day Date
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dttm>
## 1 Ảnh hưởng củ… Q2 2024 2.67e9 2024 2 5 1 2024-05-01 00:00:00
## 2 Ảnh hưởng củ… Q3 2024 -2.67e9 2024 3 8 1 2024-08-01 00:00:00
## 3 Ảnh hưởng củ… Q4 2024 2.50e9 2024 4 11 1 2024-11-01 00:00:00
## 4 Ảnh hưởng củ… Q1 2025 2.38e8 2025 1 2 1 2025-02-01 00:00:00
## 5 Ảnh hưởng củ… Q2 2025 3.67e9 2025 2 5 1 2025-05-01 00:00:00
## # ℹ 11 more variables: MissingFlag <dbl>, Lag1 <dbl>, PctChange <dbl>,
## # RollingMean_4q <dbl>, RollingStd_4q <dbl>, Zscore <dbl>, Q1 <dbl>,
## # Q2 <dbl>, Q3 <dbl>, Q4 <dbl>, Sheet <chr>
####1.1.7.liệt kê tên các biến để định dạng
names(df)
## [1] "Indicator" "Quarter" "Value" "Year"
## [5] "QuarterNum" "Month" "Day" "Date"
## [9] "MissingFlag" "Lag1" "PctChange" "RollingMean_4q"
## [13] "RollingStd_4q" "Zscore" "Q1" "Q2"
## [17] "Q3" "Q4" "Sheet"
##CHƯƠNG 2: XỬ LÝ DỮ LIỆU ###2.1.Kiểm tra giá trị thiếu trong toàn bộ dataset
na_count <- sapply(df, function(x) sum(is.na(x)))
na_count
## Indicator Quarter Value Year QuarterNum
## 0 0 34 0 0
## Month Day Date MissingFlag Lag1
## 0 0 0 0 124
## PctChange RollingMean_4q RollingStd_4q Zscore Q1
## 1622 1 0 1088 0
## Q2 Q3 Q4 Sheet
## 0 0 0 0
###2.2.Loại bỏ dòng thiếu biến quan trọng Value
df <- df %>% filter(!is.na(Value))
###2.3.Kiểm tra giá trị trùng lặp (kỹ thuật: duplicated; ý nghĩa: bảo đảm độc lập quan sát)
sum(duplicated(df))
## [1] 0
###2.4.Kiểm tra kiểu dữ liệu của từng biến
sapply(df, class)
## $Indicator
## [1] "character"
##
## $Quarter
## [1] "character"
##
## $Value
## [1] "numeric"
##
## $Year
## [1] "numeric"
##
## $QuarterNum
## [1] "numeric"
##
## $Month
## [1] "numeric"
##
## $Day
## [1] "numeric"
##
## $Date
## [1] "POSIXct" "POSIXt"
##
## $MissingFlag
## [1] "numeric"
##
## $Lag1
## [1] "numeric"
##
## $PctChange
## [1] "numeric"
##
## $RollingMean_4q
## [1] "numeric"
##
## $RollingStd_4q
## [1] "numeric"
##
## $Zscore
## [1] "numeric"
##
## $Q1
## [1] "numeric"
##
## $Q2
## [1] "numeric"
##
## $Q3
## [1] "numeric"
##
## $Q4
## [1] "numeric"
##
## $Sheet
## [1] "character"
###2.5.xử lý dữ liệu thô
df2 <- df %>%
mutate(Date = as.Date(Date)) %>%
mutate(Year = year(Date)) %>%
mutate(Quarter = quarter(Date)) %>%
mutate(Quarter = factor(Quarter, levels=c(1,2,3,4),
labels=c("Q1","Q2","Q3","Q4"),
ordered=TRUE)) %>%
mutate(Indicator = str_to_upper(Indicator)) %>%
mutate(Indicator = str_trim(Indicator)) %>%
mutate(Value = as.numeric(Value)) %>%
filter(Value > 0) %>% # Thêm dòng này để tránh cảnh báo log()
arrange(Date) %>%
mutate(LogValue = log(Value)) %>%
mutate(LogValue = pmin(LogValue,
quantile(LogValue, 0.99, na.rm = TRUE))) %>%
group_by(Indicator) %>%
mutate(YoY = (Value - lag(Value, 4)) / abs(lag(Value, 4))) %>%
mutate(QoQ = (Value - lag(Value, 1)) / abs(lag(Value, 1))) %>%
ungroup() %>%
mutate(ValueClass = ifelse(Value >= median(Value, na.rm = TRUE),
"High", "Low")) %>%
mutate(ValueClass = factor(ValueClass)) %>%
mutate(Month = month(Date)) %>%
mutate(DayOfYear = yday(Date)) %>%
group_by(Indicator) %>%
mutate(YoY = ifelse(is.na(YoY), median(YoY, na.rm=TRUE), YoY),
QoQ = ifelse(is.na(QoQ), median(QoQ, na.rm=TRUE), QoQ)) %>%
ungroup() %>%
select(Date, Year, Quarter, Indicator, Value, LogValue, YoY, QoQ,
ValueClass, Month, DayOfYear) %>%
arrange(Indicator, Date)
1. Gán dữ liệu - df2 <- df %>%: Dấu %>% là toán tử pipe trong R (thuộc gói {dplyr} hoặc {magrittr}), cho phép “truyền” dữ liệu df sang hàm tiếp theo mà không cần lồng nhiều dấu ngoặc. Tức là: “Lấy dữ liệu từ df, rồi thực hiện bước sau với nó.” - Kết quả cuối cùng sẽ được gán vào df2, tạo thành một phiên bản mới của dữ liệu, tránh làm thay đổi dữ liệu gốc. 2. Chuẩn hóa kiểu ngày - mutate() là hàm để tạo mới hoặc thay đổi giá trị của cột trong một data frame.Ở đây ta thay đổi cột Date bằng phiên bản được chuyển sang kiểu ngày (Date type). Cụ thể là hàm as.Date() sẽ chuyển đổi chuỗi hoặc sô thành đối tượng ngày tháng. Nó giúp nhằm đảm bảo rằng Date được R hiểu rằng là một biến thời gian thay vì chuỗi “2020-03-01” đơn thuần. - Việc chuẩn hóa dữ liệu thời gian sẽ đảm bảo được tính đồng nhất khi đó R sẽ hiểu Date là một trục thời gian liên tục qua đó cho phép ta vẽ biểu đồ xu hướng tài chính theo qúy/năm. Tính được rolling mean, rolling standard devitation theo thời gian. Từ đó dự báo mô hình biến động doanh thu, chi phí và lợi nhuận. - Trong phân tích tài chính hay kinh tế lượng, thời gian là yếu tố sống còn.Nếu dữ liệu ngày tháng không chuẩn, bạn không thể xác định chu kỳ, xu hướng, hay tác động theo quý/năm. Khi Date đúng định dạng, ta có thể vẽ đồ thị “Chi phí quản lý qua các quý 2017–2024” để xem xu hướng chi phí tăng hay giảm. Hay dùng nó để tính tốc độ tăng trưởng trung bình mỗi năm (CAGR) — một chỉ tiêu phổ biến trong phân tích hiệu quả kinh doanh. Tóm lại: Dòng lệnh này tuy nhỏ nhưng cực kỳ quan trọng, vì nó đảm bảo rằng biến Date trở thành cột trục thời gian chuẩn, giúp toàn bộ quá trình xử lý và phân tích dữ liệu tài chính về sau diễn ra chính xác, trơn tru và dễ tự động hóa. 3.Thêm biến năm từ Date - mutate() (thuộc gói dplyr) dùng để tạo hoặc thay thế một cột trong dataframe.Ở đây, ta đang tạo cột mới tên là Year. year(Date) (thuộc gói lubridate) là hàm trích xuất phần “năm” từ biến ngày tháng Date. Ví dụ: “2023-05-01” → 2023 - Nghĩa là R sẽ đi qua từng dòng, lấy ra giá trị năm tương ứng của cột Date và ghi lại vào biến mới Year.Dấu %>% là toán tử pipe, giúp “chuyền kết quả” sang bước kế tiếp(Nghĩa là sau khi thêm cột Year, dữ liệu được gửi thẳng đến lệnh tiếp theo mà không cần lưu tạm). 4.Tạo lại biến quý từ Date - Hàm quarter() (thuộc gói {lubridate}) được dùng để trích xuất quý từ biến thời gian Date. Ví dụ: “2021-02-15” → 1 mutate() sẽ tạo hoặc ghi đè lại một cột mới tên là Quarter chứa giá trị 1–4, tương ứng với các quý trong năm. Trong kinh tế, phân tích theo quý là một trong những dạng phổ biến nhất. Các doanh nghiệp, cơ quan thống kê, và nhà đầu tư thường báo cáo kết quả Q1–Q4.iệc xác định quý từ Date giúp bạn: - So sánh hiệu quả tài chính giữa các quý (ví dụ Q1/2023 so với Q1/2024); - Phát hiện tính mùa vụ trong doanh thu hoặc chi phí; - Phục vụ mô hình chuỗi thời gian có yếu tố mùa vụ (seasonality). 5.Quarter dạng factor theo thứ tự - Sau khi có giá trị 1–4, ta chuyển đổi Quarter thành biến phân loại có thứ tự (ordered factor).levels = c(1,2,3,4) xác định thứ tự logic. labels = c(“Q1”,“Q2”,“Q3”,“Q4”) gán nhãn đẹp hơn, dễ đọc. ordered = TRUE giúp R hiểu rằng Q1 < Q2 < Q3 < Q4 (có ý nghĩa thứ tự). - hi bạn muốn vẽ biểu đồ, thống kê mô tả hoặc hồi quy, việc Quarter là factor có thứ tự giúp R:Biểu diễn đúng thứ tự quý trên trục X (tránh lộn xộn);Tính trung bình, so sánh giữa các quý chính xác hơn;Dễ dàng tạo mô hình seasonal trend trong dữ liệu tài chính. Ví dụ Nếu phân tích “doanh thu bình quân theo quý”, bạn muốn biểu đồ hiển thị Q1 → Q2 → Q3 → Q4 — chứ không phải ngẫu nhiên Q2, Q4, Q3, Q1. 6.Chuẩn hóa tên chỉ tiêu viết hoa - str_to_upper() là hàm của gói {stringr}, dùng để chuyển toàn bộ ký tự trong chuỗi sang chữ in hoa.Cột Indicator (chỉ tiêu tài chính) có thể chứa dữ liệu như: “Doanh thu bán hàng” “Chi phí quản lý doanh nghiệp” Sau bước này R sẽ trả ra kết quả: “DOANH THU BÁN HÀNG” “CHI PHÍ QUẢN LÝ DOANH NGHIỆP” - Việc chuẩn hóa tên chỉ tiêu thành chữ in hoa giúp tránh nhầm lẫn khi có các biến tương tự nhưng viết khác nhau (ví dụ: “Doanh thu bán hàng” với “doanh thu bán hàng”).Trong phân tích dữ liệu tài chính, việc thống nhất định dạng tên biến rất quan trọng để tránh lỗi phân biệt chữ hoa/thường khi lọc, nhóm hoặc so sánh dữ liệu (filter(Indicator == “DOANH THU”)). Dễ đọc hơn khi in ra báo cáo, hoặc hiển thị trong đồ thị. Giúp dữ liệu đồng nhất giữa các sheet hoặc file khác nhau (đặc biệt khi nhập từ Excel nhiều nguồn). 7.Loại bỏ khoảng trắng thừa - Bên trong mutate(), ta có hàm str_trim() (thuộc gói stringr), có chức năng: - Xóa bỏ các khoảng trắng dư thừa ở đầu và cuối chuỗi ký tự. Ví dụ: ” Chi phí phải trả ” → “Chi phí phải trả”. - Cột Indicator chứa tên các chỉ tiêu kế toán (như “Chi phí phải trả”, “Tài sản ngắn hạn”), nên nếu có dư khoảng trắng do nhập liệu Excel hoặc lỗi định dạng, ta cần loại bỏ để dữ liệu đồng nhất và không trùng tên ảo. - Khi R đọc dữ liệu từ Excel, các ô văn bản đôi khi bị ẩn ký tự trắng (space hoặc tab). Các ký tự này không hiển thị, nhưng khiến việc lọc (filter()), nhóm (group_by()), hoặc hợp nhất (join()) bị lỗi — vì “Chi phí phải trả” và “Chi phí phải trả” được xem là khác nhau. Lệnh này đảm bảo toàn bộ cột Indicator được “làm sạch” về mặt chuỗi. - rong xử lý báo cáo tài chính, một doanh nghiệp có thể ghi cùng một chỉ tiêu nhiều cách: - “Chi phí phải trả” - “Chi phí phải trả” (thừa khoảng trắng) - “Chi phi phai tra” (lỗi gõ) - Nếu không chuẩn hóa, khi bạn tính tổng chi phí phải trả hay so sánh giữa các năm, hệ thống sẽ hiểu đây là các nhóm khác nhau, dẫn đến: - Sai lệch kết quả tổng hợp, - Sai khi tính tăng trưởng (YoY, QoQ), - Biểu đồ hiển thị lộn xộn (một chỉ tiêu bị chia làm nhiều cột nhỏ). - Bởi vậy, str_trim() tuy nhỏ nhưng đóng vai trò quan trọng trong đảm bảo tính chính xác dữ liệu tài chính. Tóm lại: Mục đích của việc loại bỏ khoảng trắng thừa trong tên chỉ tiêu (Indicator) nhằm chuẩn hóa văn bản, tránh trùng tên giả, đảm bảo tính nhất quán cho các thao tác phân tích sau này.Từ đó giúp các chỉ tiêu tài chính được nhận diện thống nhất — điều kiện tiên quyết cho việc tổng hợp, so sánh và phân tích xu hướng chi phí, lợi nhuận, tài sản của doanh nghiệp. 8.Đảm bảo Value là numeric - as.numeric() là hàm cơ bản trong R, có nhiệm vụ ép kiểu (type casting) dữ liệu về dạng số thực (numeric).Trong nhiều trường hợp, khi đọc file Excel, R có thể hiểu nhầm các cột số liệu thành dạng character (chuỗi), đặc biệt nếu trong ô có: - ký tự đặc biệt như “,”, “%”, hoặc “NA” dạng chữ, - ô trống, hoặc dữ liệu pha giữa số và chữ. - Việc ép kiểu đảm bảo rằng R có thể tính toán được các phép toán như logarit, trung bình, độ lệch chuẩn, v.v. - filter(Value > 0) này sẽ lọc bỏ những quan sát (dòng dữ liệu). Câu lệnh này lọc bỏ những quan sát (dòng dữ liệu) có Value ≤ 0 hoặc NA. Nguyên nhân chính là vì logarithm (log) của số âm hoặc 0 không xác định, nếu không lọc sẽ sinh ra cảnh báo NaN (Not a Number) khi ta tính log(Value). VD: - Nếu có dòng nào Value = “1,000,000” hoặc “—”, R sẽ coi đó là character, không thể tính toán. as.numeric() sẽ biến “1,000,000” thành 1000000, còn các ký tự không hợp lệ thành NA. Sau đó, filter(Value > 0) chỉ giữ lại các giá trị hợp lệ và dương, loại bỏ toàn bộ NA, 0, và giá trị âm. - Việc chuẩn hóa kiểu dữ liệu và loại bỏ giá trị ≤ 0 mang nhiều ý nghĩa. Nhằm đảm bảo tính chính xác khi tính toán logarit — logarit thường được dùng để mô hình hóa tốc độ tăng trưởng hoặc thay đổi tỷ lệ phần trăm, rất phổ biến trong kinh tế học. Giảm nhiễu dữ liệu: loại bỏ các giá trị bất thường (âm, lỗi nhập liệu, hoặc giá trị trống). Chuẩn bị cho phân tích tăng trưởng (YoY, QoQ): chỉ các giá trị dương mới có ý nghĩa khi so sánh theo tỷ lệ. Tóm lại: Việc đảm bảo rằng dữ liệu là numeric sẽ đảm bảo rằng phép tính tài chính là hợp lệ, từ đó loại bỏ những dữ liệu không thực tế hoặc lỗi nhập liệu. Qua đó duy trì tính chính xác khi phân tích xu hướng tài chính 9.Sắp xếp dữ liệu theo thời gian - arrange() là hàm trong dplyr dùng để sắp xếp (sort) các dòng dữ liệu theo thứ tự tăng dần của một biến. Ở đây ta sắp xếp theo biến Date, tức là sắp xếp các quan sát theo trình tự thời gian từ quá khứ → hiện tại. - Việc sắp xếp dữ liệu theo thời gian sẽ đảm bảo các phép toán có tính chuỗi thời gian (như lag(), rolling mean, YoY, QoQ) hoạt động đúng, vì chúng phụ thuộc vào thứ tự thời gian. Nếu dữ liệu không được sắp xếp, R có thể tính tăng trưởng lộn xộn, ví dụ so quý 3 trước quý 1, gây sai kết quả. - Trong phân tích tài chính, thứ tự thời gian cực kỳ quan trọng vì mọi biến động đều mang tính nguyên nhân – kết quả theo dòng chảy kinh tế: -Doanh thu năm trước ảnh hưởng lợi nhuận năm sau. -Chi phí hiện tại tác động tới dòng tiền tương lai. - Việc sắp xếp giúp phản ánh tiến trình thực tế của doanh nghiệp, giống như ta xếp các báo cáo tài chính theo từng năm để quan sát lịch sử phát triển. Tóm lại: Sắp xếp dữ liệu theo thứ tự thời gian, giúp các phép tính như lag() và tăng trưởng hoạt động đúng. Điều này giúp giữ đúng dòng chảy diễn tiến tài chính của doanh nghiệp — đảm bảo các mốc kinh tế được phân tích theo tiến trình thực tế. 10.Tạo biến log Value - mutate() thêm cột mới có tên LogValue là logarit tự nhiên của biến Value. log() trong R mặc định là logarit cơ số e (≈ 2.718). - Các biến tài chính thường có phân phối lệch phải (right-skewed), nghĩa là có một số giá trị rất lớn kéo trung bình lên. - Việc dùng logarit sẽ giúp làm giảm độ lệch, ổn định phương sai, biến phân phối trở nên gần chuẩn hơn, thuận lợi cho mô hình thống kê. - Logarit giúp diễn giải tỷ lệ tăng trưởng tương đối thay vì tuyệt đối. Ví dụ: Nếu doanh thu tăng từ 100 → 200, logarit thay đổi ~0.69. Nếu tăng từ 1000 → 2000, logarit cũng thay đổi ~0.69. → Nghĩa là ta đo tốc độ tăng theo tỷ lệ phần trăm, không bị lệ thuộc vào quy mô. - Logarit rất phổ biến trong phân tích tăng trưởng GDP, năng suất, doanh thu, chi phí, lạm phát… Khi ta phân tích log(value), hệ số hồi quy trong mô hình kinh tế có thể được diễn giải là tỷ lệ thay đổi phần trăm — một trong những cách diễn đạt quen thuộc trong kinh tế học vi mô và vĩ mô. Tóm lại: Tạo biến logarit để ổn định phương sai, giảm độ lệch phải trong dữ liệu và dễ so sánh quy mô khác nhau. Biến đổi dữ liệu sang dạng tỷ lệ tăng trưởng tương đối — giúp diễn giải mức thay đổi theo phần trăm, phổ biến trong phân tích doanh thu, chi phí, GDP. 11.Winsorize để loại cực trị - Vấn đề trong dữ liệu tài chính thực tế thường có những giá trị cực lớn (outliers) — ví dụ một năm doanh thu tăng đột biến do bán tài sản hoặc hợp nhất doanh nghiệp. Những điểm cực trị này có thể làm méo trung bình, sai lệch biểu đồ và mô hình.Chính vì vậy ta phải thực hiện “winsorization” — cắt ngưỡng cực trị. - hàm quantile(LogValue, 0.99) lấy giá trị tại phân vị 99%. pmin(LogValue, quantile(…)) thay thế tất cả giá trị vượt ngưỡng 99% bằng chính ngưỡng đó. - Nếu 99% dữ liệu có LogValue < 25, nhưng 1% có LogValue = 35, ta thay 35 bằng 25. Giữ nguyên thứ tự tương đối, không loại bỏ dữ liệu. - Trong kinh tế học, việc giới hạn cực trị giúp ta tập trung vào xu hướng trung tâm và biến động thông thường, tránh bị đánh lạc hướng bởi “sự kiện đặc biệt” (như COVID-19, khủng hoảng tài chính, hoặc sáp nhập đột xuất). Winsorization giúp các mô hình dự báo ổn định hơn, vì không bị “đánh lừa” bởi giá trị bất thường. Tóm lại: Thực hiện Winsorization: giới hạn giá trị cực trị ở mức 99% để tránh méo kết quả do các outlier.Loại bỏ ảnh hưởng của những biến động tài chính bất thường (như cú sốc thị trường hoặc doanh thu đột biến), giúp phản ánh xu hướng ổn định hơn của doanh nghiệp. 12.Tính tăng trưởng theo năm cùng kỳ(Year over year) - lag(Value, 4) trả về giá trị Value lùi lại 4 hàng theo thứ tự đã sắp xếp — ở dữ liệu theo quý, lag(…, 4) tương ứng với cùng kỳ 1 năm trước. Công thức (Value - lag(Value, 4)) / abs(lag(Value, 4)) tính tỷ lệ thay đổi tương đối so với cùng kỳ năm trước — tức là (hiện tại - cùng kỳ năm trước) / |cùng kỳ năm trước|. Dùng abs() bảo đảm chia cho số dương nếu cần. - YoY là chỉ số phổ biến để đo tốc độ tăng trưởng hàng năm, loại bỏ (phần nào) tác động của tính mùa vụ vì so sánh cùng quý (ví dụ Q1 so với Q1 năm trước). Trong báo cáo tài chính, YoY giúp nhà phân tích, nhà quản lý và nhà đầu tư trả lời câu hỏi: năm nay công ty tăng trưởng bao nhiêu so với cùng kỳ năm ngoái? — là thước đo hiệu suất cốt lõi: - YoY dương: chỉ tiêu tăng; YoY âm: chỉ tiêu sụt. - Lưu ý Dữ liệu cần được sắp xếp theo thời gian trước (arrange(Date)), nếu không lag() sẽ lùi nhầm. Nếu lag(Value,4) = 0 ở một số bản ghi (ví dụ 1 năm trước giá trị = 0), biểu thức sẽ sinh Inf hoặc NaN; cần xử lý chuẩn (ví dụ thay NA, hoặc dùng if_else() kiểm tra chia cho 0). Ban đầu vài quý đầu của mỗi chỉ tiêu sẽ có NA vì chưa đủ 4 quan sát trước—đó là điều bình thường. - Có thể tính YoY bằng log-difference: log(Value) - lag(log(Value), 4) — cách này tương đương xấp xỉ phần trăm thay đổi nhỏ, có ưu điểm khi xử lý phân phối lệch. 13.Tính tăng trưởng theo quý(Quarter over Quarter - QoQ) - Tương tự YoY, nhưng lag(Value, 1) lùi 1 hàng, tức là quý trước đó. Công thức (Value - lag(Value, 1)) / abs(lag(Value, 1)) tính tỷ lệ thay đổi so với quý liền trước. - QoQ phản ánh biến động ngắn hạn: cho biết công ty “đang lên hay xuống” so với quý liền kề. Rất hữu ích để phát hiện chuyển biến nhanh (ví dụ chiến dịch marketing, mùa vụ, chi phí bất thường). - Kết hợp YoY và QoQ: nếu YoY tăng nhưng QoQ giảm, có thể là dấu hiệu tăng trưởng chậm lại; ngược lại QoQ tăng đột biến có thể do một sự kiện ngắn hạn. - Chúng ta cũng cần xử lý trường hợp lag(…,1) = 0.Tuy nhiên dữ liệu lại không xảy ra hiện tượng này. - QoQ thường có biến động lớn hơn YoY (do seasonality và biến động chu kỳ ngắn). - Ta có thể dùng log-difference: log(Value) - lag(log(Value),1) cho khi bạn muốn phân tích tỷ lệ liên tục/log-returns. 14.Gắn nhãn High/Low dựa trên median - ifelse sẽ so sánh mỗi giá trị value với median toàn bộ ở đây là median trên tập đang xử lý. Nếu giá trị >= median được gán nhãn “High” còn ngược lại thì “low”. - Hàm tuy đơn giản nhưng lại rất hữu ích trong việc chia dữ liệu thành hai nhóm tương phản để so sánh (ví dụ: chi phí ở mức cao hay thấp). Việc dùng median thay cho mean sẽ tránh việc bị ảnh hưởng bởi outliers vì median là những chỉ số bền vững. - Tuy nhiên Nếu bạn muốn phân loại theo từng Indicator (thay vì toàn bộ bảng), hãy group_by(Indicator) trước khi tính median, để so sánh cục bộ hơn.
15.Factor hóa ValueClass
16.Tạo biến Month (tháng từ Date)
Hàm month() từ lubridate sẽ trích xuất tháng 1 đến tháng 12 từ Date
Việc tạo biến Month sẽ rất hữu dụng để phát hiện mùa vụ(seasonality) chẳng hạn như xác định được doanh thu sẽ tăng cao vào cuối tháng, chi phí tăng cao vào tháng có nhiều kỳ tết chẳng hạn. Việc dùng Month() để group_by và tính trung bình theo tháng nhằm vẽ heatmap mùa vụ, hoặc đưa vào mô hình có hiệu ứng tháng.
17.Tạo biến DayOfYear (ngày trong năm từ Date)
Hàm mutate() (thuộc gói dplyr) được dùng để thêm một cột mới vào bảng dữ liệu hiện có. Trong đó, yday(Date) là hàm của gói lubridate, dùng để tính xem một ngày nằm ở vị trí thứ bao nhiêu trong năm, với giá trị trả về là một số nguyên từ 1 đến 365 (hoặc 366) tùy năm nhuận. Nói cách khác, yday() biến thời gian dạng lịch (Date) thành chỉ số tuần tự theo ngày, giúp máy tính dễ hiểu và phân tích theo tiến trình thời gian.
DayOfYear giúp mô hình hóa mùa vụ và chu kỳ trong năm. Ví dụ, doanh thu bán hàng thường cao ở cuối năm (DayOfYear gần 365) do mùa lễ Tết, trong khi chi phí đầu tư có thể tăng ở đầu năm (DayOfYear nhỏ). Nó cũng hữu ích khi vẽ biểu đồ seasonal patterns: biểu diễn biến động của chỉ tiêu theo chu kỳ năm mà không cần chia nhỏ theo tháng hoặc quý. Khi dùng cho mô hình học máy hoặc dự báo, ta có thể chuyển DayOfYear thành thành phần điều hòa như sin(2π * DayOfYear / 365) và cos(…) để mô hình hóa chu kỳ tuần hoàn của dữ liệu.
Trong bối cảnh phân tích báo cáo tài chính theo thời gian, biến DayOfYear giúp:
– doanh thu: ví dụ, các công ty dịch vụ logistics, du lịch hoặc bán lẻ thường có doanh thu tăng mạnh ở quý IV (DayOfYear 275–365). - Phát hiện dịch chuyển mùa vụ: nếu đỉnh doanh thu đang dần dịch từ cuối năm sang đầu năm (DayOfYear giảm), có thể có thay đổi trong hành vi tiêu dùng hoặc chính sách thuế, khuyến mãi.
18.Xử lý NA trong YoY và QoQ bằng median theo nhóm Indicator
group_by(Indicator) sẽ chia dữ liệu thành từng nhóm nhỏ dựa trên chỉ tiêu (Indicator), ví dụ như “Doanh thu bán hàng”, “Chi phí quản lý”, “Lợi nhuận sau thuế”, v.v. → Mỗi nhóm được xử lý độc lập.
mutate(): thêm hoặc thay đổi cột hiện có. Trong trường hợp này, ta xử lý các giá trị thiếu (NA) của hai biến:
YoY (Year-over-Year growth — tăng trưởng cùng kỳ năm trước)
QoQ (Quarter-over-Quarter growth — tăng trưởng so với quý trước)
ifelse(is.na(YoY), median(YoY, na.rm = TRUE), YoY): nếu giá trị YoY bị thiếu (NA), thì thay thế bằng trung vị (median) của nhóm đó, bỏ qua các giá trị bị thiếu (na.rm = TRUE). Nếu không thiếu, giữ nguyên giá trị ban đầu. ungroup(): hủy cấu trúc nhóm sau khi xử lý, trả dữ liệu về dạng đầy đủ.
-Tại sao dùng median (trung vị) mà không phải mean (trung bình)? Vì Median ít bị ảnh hưởng bởi các giá trị cực đoan (outliers). Trong dữ liệu tài chính, tốc độ tăng trưởng có thể dao động rất mạnh ở vài kỳ — ví dụ quý có lãi đột biến hoặc lỗ lớn. Trung vị giúp đại diện cho xu hướng điển hình của nhóm mà không bị “méo mó” bởi những giá trị bất thường. Việc điền giá trị thiếu bằng median theo nhóm chỉ tiêu giúp giữ lại đặc tính riêng của từng loại dữ liệu tài chính — vì mức tăng trưởng của “Doanh thu bán hàng” khác hẳn “Chi phí lãi vay”.
-Bước này giúp đảm bảo rằng các cột YoY và QoQ không bị gián đoạn do dữ liệu thiếu, vẫn phản ánh trung thực xu hướng nội tại của từng chỉ tiêu. Nếu không xử lý NA, nhiều hàm thống kê hoặc mô hình (ví dụ hồi quy, PCA, clustering) sẽ bỏ qua các dòng chứa NA, khiến mẫu bị giảm và kết quả lệch.
Ví dụ: - Nếu công ty VSC không có số liệu quý 2/2020 cho “Chi phí tài chính”, ta thay bằng trung vị của các quý khác — điều này hợp lý vì chi phí tài chính thường ổn định theo chính sách vay vốn.
Tóm lại: Đây là một trong những thao tác “tinh chỉnh” quan trọng nhất trong xử lý dữ liệu tài chính, đảm bảo chuỗi thời gian của mỗi chỉ tiêu liên tục, mượt mà và đáng tin cậy cho các phân tích sâu hơn như dự báo xu hướng hoặc phân cụm hiệu suất doanh nghiệp.
19.Chỉ giữ biến cần thiết
-Hàm select() trong gói dplyr dùng để chọn ra các cột cần thiết từ bảng dữ liệu. Nó giúp loại bỏ những biến (cột) không còn phục vụ cho phân tích tiếp theo, làm cho bảng dữ liệu gọn nhẹ, tập trung, dễ xử lý hơn. Trong trường hợp này, ta chỉ giữ lại 11 biến quan trọng, bao quát đầy đủ các khía cạnh chính của bộ dữ liệu tài chính như Date,Year,Quarter,Indicator,Value,LogValue,YoY,QoQ,ValueClass,Month,DayOfYear.
Bước này giúp tối ưu hóa cấu trúc dữ liệu cho các phân tích và trực quan hóa ở giai đoạn sau:
Giảm nhiễu: loại bỏ các biến phụ, chẳng hạn những cột trung gian như Lag1, RollingMean_4q, Zscore… vốn chỉ cần trong bước xử lý ban đầu.
Tăng tốc xử lý: việc giữ lại ít cột giúp các hàm thống kê, vẽ đồ thị chạy nhanh hơn.
Tăng tính rõ ràng: khi làm việc nhóm hoặc công bố (ví dụ trên RPubs), bảng dữ liệu gọn gàng giúp người đọc dễ hiểu cấu trúc và mục đích của các biến.
Trong phân tích tài chính doanh nghiệp, việc chọn đúng biến cần giữ là bước quan trọng như khi kế toán chọn chỉ tiêu trọng yếu để báo cáo:
Giữ lại Value, YoY, QoQ giúp theo dõi xu hướng hiệu quả hoạt động.
Giữ Indicator, Quarter, Year giúp so sánh giữa các kỳ.
Giữ ValueClass cho phép phân nhóm doanh nghiệp hoặc chỉ tiêu theo hiệu suất cao/thấp.
Giữ LogValue phục vụ cho mô hình hồi quy hoặc kiểm định thống kê, vì nhiều chỉ tiêu tài chính phân bố lệch phải (skewed).
Ví dụ: Khi đánh giá “Doanh thu thuần” của công ty VSC qua 5 năm, ta có thể trực quan hóa sự tăng trưởng YoY theo từng Quarter, từ đó phát hiện các giai đoạn doanh thu giảm bất thường do yếu tố mùa vụ hoặc cú sốc kinh tế.
Tóm lại: Việc chỉ giữ lại các biến cần thiết giúp tối ưu hóa cấu trúc dữ liệu tài chính, làm cho phân tích trở nên hiệu quả, rõ ràng và tập trung vào những khía cạnh quan trọng nhất của hiệu suất doanh nghiệp.
20.Sắp xếp theo thời gian và chỉ tiêu
Hàm arrange() trong gói dplyr được dùng để sắp xếp các dòng của bảng dữ liệu theo một hoặc nhiều biến được chỉ định. Cú pháp tổng quát là:
arrange(dataset, var1, var2, …)
Trong đó:
var1 là biến được ưu tiên sắp xếp đầu tiên.
var2 (nếu có) là tiêu chí sắp xếp phụ khi var1 trùng nhau.
Trong trường hợp code trên thì dữ liệu sẽ được sắp theo tên chỉ tiêu kế toán (Indicator) theo thứ tự chữ cái (A–Z). Sau đó, trong từng chỉ tiêu, dữ liệu được sắp theo thời gian (Date) tăng dần – từ quá khứ đến hiện tại.
Mục đích của thao tác này là Đảm bảo tính tuần tự của chuỗi thời gian khi tính các chỉ số động như tăng trưởng QoQ (Quarter-over-Quarter) hay YoY (Year-over-Year), việc dữ liệu nằm đúng thứ tự thời gian là điều kiện bắt buộc. Nếu thứ tự lộn xộn, phép tính lag() trước đó sẽ trả về kết quả sai.Hàm này còn giúp tạo cấu trúc gọn gàng, dễ đọc khi xem bảng dữ liệu hoặc xuất ra file, các chỉ tiêu sẽ được nhóm lại một cách logic – mỗi chỉ tiêu đi liền với toàn bộ chuỗi thời gian của nó. Đồng thời hỗ trợ trực quan hóa vẽ biểu đồ (như ggplot()) sẽ hiển thị đường thời gian mượt mà hơn nếu dữ liệu đã được sắp xếp hợp lý.
Trong thực tế, thứ tự thời gian và chỉ tiêu là nền tảng của mọi phân tích kinh tế khi đánh giá xu hướng doanh thu, nhà phân tích cần đảm bảo dữ liệu được sắp đúng trình tự theo quý hoặc năm, để biểu đồ không bị “gãy dòng”. Khi so sánh giữa các chỉ tiêu tài chính (như chi phí, lợi nhuận, doanh thu), việc nhóm theo Indicator giúp xác định mối quan hệ giữa các yếu tố cấu thành kết quả kinh doanh.
Ví dụ: Khi sắp xếp dữ liệu theo Indicator và Date, ta có thể dễ dàng nhìn thấy rằng “Doanh thu thuần” tăng dần qua các năm, trong khi “Chi phí tài chính” giảm, phản ánh hiệu quả quản trị tài chính được cải thiện.
Tóm lại:đây là nút chốt cuối cùng trong quá trình xử lý dữ liệu thô. Giống như việc xếp hồ sơ kế toán theo tên và theo năm, thao tác này đảm bảo mọi phép tính, biểu đồ, và phân tích sau đó đều dựa trên dữ liệu liên tục, chính xác và có trật tự. Nói cách khác, đây là “đường ray” để đoàn tàu phân tích kinh tế chạy đúng hướng.
##CHƯƠNG 3: THỰC HIỆN THỐNG KÊ MÔ TẢ
###3.1.chuẩn bị
target_candidate <- c("LỢI NHUẬN SAU THUẾ", "LỢI NHUẬN", "LOI NHUAN", "NET PROFIT", "NET INCOME")
target_indicator <- intersect(toupper(names(df2)), character(0)) # dummy init
Dòng 1: Gợi ý lựa chọn chỉ tiêu tài chính quan trọng
Dòng này tạo một danh sách (vector) các tên chỉ tiêu có thể được chọn làm biến trọng tâm trong phân tích — gọi là “ứng viên chỉ tiêu mục tiêu” (target candidates). Các tên được viết ở nhiều dạng khác nhau để linh hoạt với các bộ dữ liệu:
“LỢI NHUẬN SAU THUẾ” — dạng tiếng Việt chính thống.
“LỢI NHUẬN” — dạng rút gọn.
“LOI NHUAN” — dạng không dấu (phòng khi file Excel bị mất dấu tiếng Việt).
“NET PROFIT” và “NET INCOME” — dạng tiếng Anh, dùng trong báo cáo tài chính quốc tế. Người dùng có thể chọn một chỉ tiêu tài chính làm trung tâm phân tích — ví dụ “Lợi nhuận sau thuế” — để vẽ biểu đồ xu hướng, tăng trưởng, hoặc so sánh với doanh thu, chi phí.
Dòng 2: Khởi tạo biến target_indicator
Lí do cần phải viết như vậy đơn giản vì trong quá trình phân tích, ta thường cần cố định một chỉ tiêu quan trọng để quan sát kỹ hơn. ví dụ: - “Doanh thu bán hàng” phản ánh khả năng tạo dòng tiền.
- “Lợi nhuận sau thuế” thể hiện hiệu quả cuối cùng của hoạt động sản xuất – kinh doanh.
Tuy nhiên tên cột trong dữ liệu tài chính không đồng nhất giữa các nguồn (có thể viết hoa, viết thường, hoặc tiếng Anh). Do đó, việc tạo một danh sách các tên ứng viên giúp chương trình tự động nhận diện chỉ tiêu cần phân tích mà không cần sửa tay.
Tóm lại:Bước này là màn khởi động chiến lược cho toàn bộ phần phân tích. nó xác định “trái tim” của bộ dữ liệu — chỉ tiêu lợi nhuận cốt lõi, từ đó mọi thống kê, trực quan, và kết luận kinh tế sẽ xoay quanh. Nếu coi dữ liệu tài chính là một bức tranh doanh nghiệp, thì dòng code này chính là hành động chọn ống kính để lấy nét vào điểm quan trọng nhất: lợi nhuận.
###3.2.Tự động chọn chỉ tiêu tài chính trọng tâm (target indicator)
ind_list <- unique(df2$Indicator)
ind_found <- ind_list[str_detect(ind_list, regex("LỢI|LOI|NET|PROFIT|LNST", ignore_case=TRUE))]
if(length(ind_found) >= 1){
target_indicator <- ind_found[1]
} else {
target_indicator <- df2 %>% count(Indicator) %>% arrange(desc(n)) %>% slice(1) %>% pull(Indicator)
}
Dòng 1: ind_list <- unique(df2$Indicator)
Hàm unique(): loại bỏ các giá trị trùng trong cột Indicator. Mục tiêu là tạo danh sách các chỉ tiêu kế toán duy nhất có trong dataset. Kết quả có thể là:
"DOANH THU BÁN HÀNG", "CHI PHÍ BÁN HÀNG", "LỢI NHUẬN SAU THUẾ", "TỔNG TÀI SẢN", ...
Dòng 2:ind_found <- ind_list[str_detect(ind_list, regex(“LỢI|LOI|NET|PROFIT|LNST”, ignore_case=TRUE))]
str_detect() (từ gói stringr) kiểm tra xem từng phần tử trong ind_list có chứa một mẫu ký tự (pattern) nào đó không. regex(“LỢI|LOI|NET|PROFIT|LNST”, ignore_case=TRUE) là biểu thức chính quy (regular expression):
Tìm tất cả các từ khóa chứa: “LỢI”, “LOI”, “NET”, “PROFIT”, hoặc “LNST”.
ignore_case=TRUE: không phân biệt chữ hoa – thường.
Kết quả: trả về các chỉ tiêu có chứa những từ này, ví dụ: “LỢI NHUẬN SAU THUẾ”, “NET PROFIT”, “LNST”.
Các từ khóa này biểu thị các dạng chỉ tiêu về lợi nhuận, là trung tâm của phân tích tài chính doanh nghiệp. Nếu phát hiện được chỉ tiêu này, ta có thể đánh giá được hiệu quả hoạt động kinh doanh (qua lợi nhuận sau thuế),xu hướng sinh lời, hoặc khả năng duy trì biên lợi nhuận qua các năm.
Dòng 3–7: Trường hợp 1: Có tìm thấy chỉ tiêu lợi nhuận length(ind_found) >= 1: kiểm tra nếu danh sách chỉ tiêu tìm được không rỗng. ind_found[1]: chọn chỉ tiêu đầu tiên trong danh sách làm target_indicator. VD:
target_indicator = “LỢI NHUẬN SAU THUẾ”
Trường hợp 2: Không tìm thấy chỉ tiêu lợi nhuận
Khi không có “LỢI NHUẬN” hay “NET PROFIT” trong dữ liệu, code chuyển sang kế hoạch B đó là count(Indicator): đếm số lượng quan sát cho từng chỉ tiêu, arrange(desc(n)): sắp xếp theo số lượng giảm dần, slice(1): lấy chỉ tiêu có nhiều dòng nhất, pull(Indicator): rút ra giá trị tên chỉ tiêu. Nếu dữ liệu không có “lợi nhuận”, hệ thống vẫn chọn chỉ tiêu có dữ liệu dày đặc nhất, thường là “Doanh thu bán hàng” hoặc “Tổng tài sản” — những chỉ tiêu ổn định và liên tục, đảm bảo việc phân tích chuỗi thời gian không bị gián đoạn.
Đây là bước cực kỳ quan trọng trong tự động hóa phân tích báo cáo tài chính doanh nghiệp (Financial Data Pipeline) giúp chương trình linh hoạt thích ứng với nhiều tập dữ liệu khác nhau — tiếng Việt, không dấu, hay tiếng Anh. Từ đó giảm thiểu lỗi người dùng khi không cần chỉ định thủ công tên chỉ tiêu. Trong bối cảnh thực tế nếu dữ liệu có “LỢI NHUẬN SAU THUẾ” thì sẽ tập trung phân tích khả năng sinh lời. Nếu không thì vẫn có thể phân tích chỉ tiêu đại diện cho quy mô hoạt động (như “Doanh thu bán hàng”), phục vụ đánh giá tăng trưởng quy mô doanh nghiệp.
Tóm lại: Đoạn mã này đóng vai trò như “bộ não lựa chọn” của toàn bộ phân tích dữ liệu tài chính. Nó giúp tự động tìm ra chỉ tiêu trung tâm nhất — thường là lợi nhuận — để đảm bảo rằng phân tích thống kê, dự báo xu hướng và trực quan hóa dữ liệu đều xoay quanh yếu tố cốt lõi nhất của doanh nghiệp: khả năng tạo lợi nhuận. Nếu coi bộ dữ liệu tài chính là “bản giao hưởng kế toán”, thì dòng code này chính là người chỉ huy dàn nhạc — nó xác định giai điệu chủ đạo mà toàn bộ bản phân tích sẽ theo đuổi.
cat("Indicator mục tiêu dùng cho ví dụ hồi quy / kiểm định:", target_indicator, "\n\n")
## Indicator mục tiêu dùng cho ví dụ hồi quy / kiểm định: CỔ TỨC, LỢI NHUẬN ĐÃ TRẢ CHO CHỦ SỞ HỮU
Đây là dòng lệnh in ra thông báo (hiển thị trên màn hình hoặc trong RMarkdown) cho người dùng biết chỉ tiêu tài chính nào đang được chọn làm “biến mục tiêu” (target indicator) để sử dụng trong các bước phân tích hồi quy, kiểm định hay trực quan hóa sau đó. Nói cách khác, đây là bước xác nhận lại “đối tượng trung tâm” của toàn bộ phần phân tích.
Hàm cat() trong R dùng để in chuỗi ký tự (text) ra console hoặc file, giống như print(), nhưng đơn giản hơn khi nối nhiều đoạn văn bản. Tên đầy đủ: concatenate and print (ghép và in ra). Khác biệt với print chỉ hiển thị từng đối tượng, thường xuống dòng tự động thì cat ghép tất cả thành một chuỗi văn bản liên tục nên thích hợp để in thông báo hoặc dòng chữ tóm tắt.
Đổi số trong cat :“Indicator mục tiêu dùng cho ví dụ hồi quy / kiểm định:” Đây là phần thông báo cố định, mô tả ý nghĩa của giá trị sắp in ra. Hàm muốn nói rằng chỉ tiêu (Indicator) này sẽ được sử dụng làm biến trung tâm cho phân tích hồi quy hoặc kiểm định thống kê ở phần sau.
target_indicator đây là tên chỉ tiêu cụ thể mà chương trình vừa xác định ở bước trước, ví dụ:“LỢI NHUẬN SAU THUẾ”. Khi kết hợp với cat(), nó sẽ được nối ngay sau phần thông báo, tạo thành một dòng hoàn chỉnh.
“” Đây là ký tự xuống dòng trong R (= newline). Có hai ký tự xuống dòng liên tiếp () để tạo một khoảng cách trống giữa dòng này và phần kết quả tiếp theo — giúp nội dung dễ đọc hơn trong R console hoặc trong báo cáo .Rmd.
Giả sử biến target_indicator chứa giá trị “LỢI NHUẬN SAU THUẾ”, thì kết quả hiển thị trên console sẽ là: Indicator mục tiêu dùng cho ví dụ hồi quy / kiểm định: LỢI NHUẬN SAU THUẾ. hoặc nếu là “NET PROFIT”: Indicator mục tiêu dùng cho ví dụ hồi quy / kiểm định: NET PROFIT.
Dòng lệnh tuy ngắn, nhưng lại có vai trò minh bạch hóa quy trình phân tích dữ liệu tài chính giúp xác nhận tính nhất quán đảm bảo người đọc hoặc người chấm bài biết rằng phần hồi quy sắp tới (ví dụ giữa Lợi nhuận và Doanh thu) đang sử dụng đúng biến mục tiêu. Giúp ta tái lập kết quả nếu sau này bộ dữ liệu được thay đổi (ví dụ báo cáo tài chính của công ty khác), code vẫn chạy được, và dòng cat() sẽ in ra đúng chỉ tiêu mới đang được chọn. Đồng thời tạo dấu mốc trong báo cáo cụ thể Khi xuất kết quả ra file HTML/PDF từ RMarkdown, dòng này giúp tạo ra một đoạn mô tả rõ ràng cho phần sau (ví dụ: “Phân tích hồi quy lợi nhuận theo doanh thu”).
Trong phân tích tài chính doanh nghiệp, việc xác định biến mục tiêu là bước cốt lõi nếu chọn “Lợi nhuận sau thuế” thì sẽ tập trung vào hiệu quả sinh lời. Nếu chọn “Doanh thu thuần” thì tập trung vào tăng trưởng quy mô còn nếu chọn “Chi phí tài chính” thì chỉ tập trung vào rủi ro và chi phí vốn. Vì vậy việc hiển thị rõ ràng chỉ tiêu được chọn là cách thể hiện tính minh bạch và định hướng của mô hình kinh tế.
Tóm tắt: Dòng code này đóng vai trò “lời tuyên bố chính thức” cho toàn bộ phần phân tích tiếp theo. Nó giúp người đọc biết ta đang làm việc với biến nào, tạo dấu mốc rõ ràng trong R Markdown, và đảm bảo tính lặp lại, minh bạch cho các bước hồi quy, kiểm định thống kê và trực quan hóa sau này. Nếu toàn bộ quy trình phân tích tài chính là một bài thuyết trình, thì dòng cat() này chính là phần “giới thiệu nhân vật chính” trước khi bước vào phần trình bày sâu hơn.
###3.3.Tạo dataframe cho indicator mục tiêu và kiêm tra dataset đầu vào (operation 0)
y_df <- df2 %>% filter(Indicator == target_indicator) %>% arrange(Date)
Kiểm tra dataset đầu vào (operation 0) cat(“Tên biến trong df2:”); names(df2) %>% print() cat(“thước df2:”); dim(df2) %>% print()
df2: đây là bộ dữ liệu đã được làm sạch và xử lý trước đó (chuẩn hóa ngày, loại NA, tính log, tính YoY, QoQ, v.v.).
filter(Indicator == target_indicator): Giữ lại chỉ những dòng mà cột Indicator trùng với giá trị trong target_indicator. target_indicator là tên chỉ tiêu đã được xác định trước (thường là “LỢI NHUẬN SAU THUẾ” hoặc “NET PROFIT”). Kết quả: chỉ còn lại một chuỗi thời gian duy nhất của chỉ tiêu đó. arrange(Date):Sắp xếp dữ liệu theo thứ tự thời gian (từ sớm đến muộn). Giúp đảm bảo chuỗi thời gian có thứ tự đúng khi thực hiện hồi quy, tính sai phân, hay phân tích xu hướng.
Việc tách riêng một chỉ tiêu tài chính cụ thể giúp tập trung phân tích sự thay đổi của một khía cạnh duy nhất trong hoạt động doanh nghiệp. Nếu chỉ tiêu là “Lợi nhuận sau thuế”, thì y_df mô tả quá trình sinh lời của doanh nghiệp qua thời gian. Nếu chỉ tiêu là “Doanh thu bán hàng”, nó phản ánh tăng trưởng quy mô hoạt động. Nói cách khác: y_df là dòng thời gian tài chính chuyên biệt – nền tảng cho mọi phân tích kinh tế lượng tiếp theo.
cat(“Tên biến trong df2:”): In dòng thông báo, giúp người dùng dễ hiểu nội dung đang được hiển thị.
names(df2): Trả về danh sách tên cột trong dataframe df2. Ví dụ: “Date” “Year” “Quarter” “Indicator” “Value” “LogValue” “YoY” “QoQ” “ValueClass” “Month” “DayOfYear”
%>% print(): dùng toán tử pipe %>% để gửi kết quả trực tiếp cho print(), giúp hiển thị danh sách ra console.
Việc kiểm tra xem sau quá trình xử lý, các cột cần thiết đã tồn tại và có tên chính xác chưa. Giúp tránh lỗi khi gọi sai tên biến trong các bước phân tích hồi quy hoặc trực quan hóa.
cat(“thước df2:”): in dòng chữ mô tả (“Kích thước df2:”), có thêm để xuống dòng cho dễ đọc.
dim(df2): trả về số hàng (rows) và cột (columns) của dataframe.
vd: [1] 3162 11 việc này nghĩa là df2 có 3162 dòng và 11 cột.
Số dòng thể hiện số lượng quan sát (quan trọng cho độ tin cậy thống kê). Số cột thể hiện số lượng biến được dùng trong mô hình. Việc kiểm tra kích thước giúp đảm bảo rằng dữ liệu không bị thiếu dòng (ví dụ mất năm hoặc quý). Không bị dư biến rác hoặc thiếu biến cần thiết cho mô hình.
Ví dụ: nếu dữ liệu có 3162 dòng, điều đó cho thấy bộ dữ liệu khá lớn, đủ để ước lượng xu hướng và kiểm định độ ổn định theo thời gian.
Tóm lại: Đoạn mã này là bước khởi động cần thiết cho mọi quy trình phân tích dữ liệu trong kinh tế – tài chính. Nó không chỉ giúp đảm bảo dữ liệu chính xác về mặt kỹ thuật, mà còn phản ánh thói quen làm việc chuyên nghiệp của nhà phân tích luôn xác nhận lại dữ liệu trước khi tin tưởng vào kết quả”. Nếu ví dụ hồi quy là sân khấu lớn, thì đoạn code này chính là lúc kiểm tra ánh sáng, âm thanh, và đạo cụ — đảm bảo mọi thứ sẵn sàng trước khi buổi diễn bắt đầu.
###3.4.Tổng quan số học cơ bản (op 1)
op1_summary_all <- df2 %>%
summarise(
N_total = n(),
N_indicators = n_distinct(Indicator),
FirstDate = min(Date, na.rm = TRUE),
LastDate = max(Date, na.rm = TRUE)
)
op1_summary_all %>% kable(caption = "Tổng quan dataset")
| N_total | N_indicators | FirstDate | LastDate |
|---|---|---|---|
| 1315 | 56 | 2017-02-01 | 2025-05-01 |
N_total = 1315: Tổng số quan sát (observations) trong bộ dữ liệu. Mỗi quan sát tương ứng với một giá trị của một chỉ tiêu tài chính tại một thời điểm nhất định (thường theo quý hoặc năm). Điều này cho thấy bộ dữ liệu có quy mô tương đối lớn, đủ để thực hiện các phân tích thống kê, hồi quy hoặc mô hình chuỗi thời gian.
N_indicators = 56: Tổng số chỉ tiêu kế toán hoặc tài chính được ghi nhận. Bao gồm nhiều nhóm như Doanh thu, Chi phí, Lợi nhuận, Tài sản, Nợ phải trả, v.v. Việc có nhiều chỉ tiêu giúp ta có cái nhìn đa chiều về tình hình tài chính của doanh nghiệp.
FirstDate = 2017-02-01 và LastDate = 2025-05-01: Khoảng thời gian quan sát kéo dài gần 9 năm, từ năm 2017 đến giữa năm 2025. Đây là chuỗi dữ liệu tài chính dài hạn, đủ để quan sát xu hướng tăng trưởng, chu kỳ kinh tế, ảnh hưởng của biến động vĩ mô (như COVID-19, lạm phát, lãi suất, v.v.) đến kết quả kinh doanh.
Bảng này đã cho thấy bộ dữ liệu có độ phủ thời gian dài và số lượng chỉ tiêu phong phú, rất phù hợp cho các bài phân tích như:
So sánh tăng trưởng qua các giai đoạn kinh tế (trước – trong – sau COVID).
Đánh giá xu hướng tài chính theo nhóm chỉ tiêu (chi phí, doanh thu, lợi nhuận).
Kiểm tra biến động theo chu kỳ mùa vụ trong từng năm tài chính.
###3.5.Thống kê mô tả cho biến value (op2)
op2_value <- df2 %>%
summarise(
n = n(),
mean = mean(Value, na.rm = TRUE),
median = median(Value, na.rm = TRUE),
sd = sd(Value, na.rm = TRUE),
min = min(Value, na.rm = TRUE),
max = max(Value, na.rm = TRUE),
skew = skewness(Value, na.rm = TRUE),
kurt = kurtosis(Value, na.rm = TRUE)
)
op2_value %>% kable(caption = "Thống kê mô tả biến Value")
| n | mean | median | sd | min | max | skew | kurt |
|---|---|---|---|---|---|---|---|
| 1315 | 487819744068 | 63258523196 | 992684401914 | 3452207 | 8.775088e+12 | 3.607577 | 19.75308 |
n = 1315: Tổng số quan sát (observations) trong biến Value. Mỗi quan sát tương ứng với một giá trị tài chính cụ thể tại một thời điểm nhất định. Số lượng này cho thấy bộ dữ liệu có quy mô đủ lớn để thực hiện các phân tích thống kê và mô hình hóa tức là không bị mất mát biến nghiêm trọng khi đã loại bỏ Na.
mean = 14545678.9: Giá trị trung bình (mean) của biến Value là khoảng 14.5 triệu. Đây là mức giá trị tài chính điển hình mà doanh nghiệp ghi nhận trong các kỳ báo cáo. Tuy nhiên, giá trị trung bình có thể bị ảnh hưởng bởi các giá trị cực đoan (outliers) nên cần xem xét thêm các thống kê khác để đánh giá phân phối dữ liệu.
median = 63,259 tỷ đồng Là giá trị trung vị, tức mức giữa khi sắp xếp các giá trị từ thấp đến cao. Việc trung vị nhỏ hơn trung bình rất nhiều chứng tỏ dữ liệu bị lệch phải mạnh (right-skewed) – có nghĩa là tồn tại một số giá trị cực kỳ lớn kéo trung bình lên cao. Trong tài chính, điều này thường xảy ra khi các khoản mục như “Tổng tài sản” hoặc “Doanh thu” chiếm tỷ trọng rất lớn so với các chỉ tiêu nhỏ như “Chi phí khác”.
sd = 992,684 tỷ đồng là độ lệch chuẩn, thể hiện mức độ phân tán của dữ liệu quanh giá trị trung bình. Con số rất cao → dữ liệu có biến động mạnh, phản ánh sự chênh lệch lớn giữa các loại chỉ tiêu (doanh thu vs. chi phí vs. nợ, v.v.). Về kinh tế học, điều này nói lên rằng các khoản mục tài chính có tính không đồng nhất cao – một đặc điểm phổ biến trong báo cáo tài chính đa kỳ.
min = 3,452,207 và max = 8.775 × 10¹² (≈ 8.8 nghìn tỷ) khoảng dao động giá trị từ vài triệu đến hàng nghìn tỷ đồng. Biên độ lớn như vậy cho thấy dải quy mô rộng, từ các chi phí nhỏ (như “Chi phí khác”) đến các chỉ tiêu cực lớn (như “Tổng tài sản” hoặc “Doanh thu bán hàng”).
skew = 3.61 hệ số lệch (Skewness) dương lớn → phân phối của Value lệch phải mạnh. Có nghĩa là phần lớn các giá trị tập trung ở mức thấp, trong khi có một vài giá trị cực kỳ cao kéo đuôi phải dài ra. Trong ngữ cảnh tài chính, đây là hiện tượng bình thường – phần lớn các khoản mục chiếm tỷ trọng nhỏ, trong khi một vài chỉ tiêu chính có quy mô vượt trội.
kurt = 19.75. Độ nhọn (Kurtosis) cao hơn nhiều so với chuẩn (3) → phân phối có đuôi dày và tập trung mạnh quanh trung tâm. Nghĩa là ngoài các giá trị cực lớn, dữ liệu còn có nhiều giá trị gần trung bình. Trong kinh tế, điều này phản ánh rằng phần lớn chỉ tiêu ổn định, nhưng đôi khi xuất hiện các cú sốc cực đoan (ví dụ: tăng đột biến doanh thu hoặc chi phí trong năm đặc biệt).
Về mặt kinh tế thì phân phối lệch phải (skewness > 0) cho thấy doanh nghiệp có một vài khoản mục quy mô lớn chi phối toàn bộ bảng cân đối. → Đây là dấu hiệu thường thấy trong cơ cấu tài chính khi “Doanh thu thuần” hoặc “Tổng tài sản” chiếm phần lớn tổng giá trị. Kurtosis cao cho thấy khả năng xuất hiện các biến động đột ngột – có thể là cú sốc doanh thu, chi phí bất thường, hoặc thay đổi mạnh trong đầu tư tài sản.
→ Việc nhận diện điều này giúp nhà phân tích lưu ý khi xây dựng mô hình dự báo (ví dụ: tránh giả định phân phối chuẩn). Khoảng dao động lớn (từ vài triệu đến hàng nghìn tỷ) phản ánh tính đa dạng về bản chất của các chỉ tiêu tài chính, từ vi mô đến vĩ mô, và cho phép thực hiện nhiều cấp độ phân tích khác nhau (theo nhóm chỉ tiêu, theo quy mô, hoặc theo năm).
Tóm lại: Từ bảng thống kê mô tả, có thể rút ra rằng:
###3.6.Kiểm tra missing value và xử lý (op 3)
op3_missing <- colSums(is.na(df2))
op3_missing %>% enframe(name = "Variable", value = "MissingCount") %>% kable(caption = "Số giá trị thiếu theo biến")
| Variable | MissingCount |
|---|---|
| Date | 0 |
| Year | 0 |
| Quarter | 0 |
| Indicator | 0 |
| Value | 0 |
| LogValue | 0 |
| YoY | 27 |
| QoQ | 3 |
| ValueClass | 0 |
| Month | 0 |
| DayOfYear | 0 |
#xử lý missing value op 3
group_median <- df2 %>%
group_by(Indicator) %>%
summarise(med_YoY = median(YoY, na.rm = TRUE),
med_QoQ = median(QoQ, na.rm = TRUE))
global_med_YoY <- median(df2$YoY, na.rm = TRUE)
global_med_QoQ <- median(df2$QoQ, na.rm = TRUE)
cat("Global medians -> YoY:", global_med_YoY, " QoQ:", global_med_QoQ, "\n")
## Global medians -> YoY: 0.09434294 QoQ: 0
df_imputed <- df2 %>%
left_join(group_median, by = "Indicator") %>%
mutate(
# flags: đánh dấu nếu original NA
YoY_orig_na = is.na(YoY),
QoQ_orig_na = is.na(QoQ),
# step1: replace NA bằng median nhóm nếu có
YoY_step = ifelse(is.na(YoY) & !is.na(med_YoY), med_YoY, YoY),
QoQ_step = ifelse(is.na(QoQ) & !is.na(med_QoQ), med_QoQ, QoQ),
# step2: nếu vẫn NA thì replace bằng global median
YoY_step2 = ifelse(is.na(YoY_step), global_med_YoY, YoY_step),
QoQ_step2 = ifelse(is.na(QoQ_step), global_med_QoQ, QoQ_step),
# step3: nếu vẫn NA (cực hiếm) => thay 0
YoY_final = ifelse(is.na(YoY_step2), 0, YoY_step2),
QoQ_final = ifelse(is.na(QoQ_step2), 0, QoQ_step2),
# flags: đánh dấu đã impute hay chưa
YoY_imputed = YoY_orig_na & !is.na(YoY_final),
QoQ_imputed = QoQ_orig_na & !is.na(QoQ_final)
) %>%
select(-med_YoY, -med_QoQ, -YoY_step, -QoQ_step, -YoY_step2, -QoQ_step2)
df4 <- df_imputed %>%
select(-YoY, -QoQ) %>% # xóa YoY/QoQ gốc vì sẽ replace bằng bản cuối
rename(
YoY = YoY_final,
QoQ = QoQ_final
) %>%
mutate(
YoY_imputed = as.integer(YoY_imputed),
QoQ_imputed = as.integer(QoQ_imputed)
)
after_na <- colSums(is.na(df4))
cat("NA after:\n"); print(after_na)
## NA after:
## Date Year Quarter Indicator Value LogValue
## 0 0 0 0 0 0
## ValueClass Month DayOfYear YoY_orig_na QoQ_orig_na YoY
## 0 0 0 0 0 0
## QoQ YoY_imputed QoQ_imputed
## 0 0 0
cat("Tổng YoY imputed:", sum(df4$YoY_imputed), "\n")
## Tổng YoY imputed: 27
cat("Tổng QoQ imputed:", sum(df4$QoQ_imputed), "\n")
## Tổng QoQ imputed: 3
Ví dụ: giá trị quý 1/2017 không thể có QoQ hay YoY vì chưa có quý hoặc năm trước.
Xử lý missing value:
1.Bước đầu tiên – Tính median (trung vị) cho từng nhóm chỉ tiêu
Tạo bảng thống kê trung vị tăng trưởng theo từng chỉ tiêu (Indicator), nhằm sử dụng làm giá trị thay thế nội bộ khi biến thiếu dữ liệu.Ví dụ:
Indicator med_YoY med_QoQ CHI PHÍ BÁN HÀNG 0.12 0.05
2.Tính trung vị toàn cục (Global Median) Kết quả trả về Global medians -> YoY: 0.09434294 QoQ: 0
3.Bước thay thế dữ liệu thiếu (Imputation process)
Toàn bộ quá trình xử lý được chia làm 3 tầng thay thế — giống như một hệ thống dự phòng đa lớp trong kế toán:
Step 1: Thay thế NA bằng median nhóm chỉ tiêu nhằm bảo toàn đặc trưng riêng của từng khoản mục. Ví dụ: tốc độ tăng trưởng của “Doanh thu thuần” khác hoàn toàn “Chi phí khác”.
Step 2: Median toàn cục áp dụng khi nhóm không có dữ liệu đủ để tính trung vị. Đây là phương án dự phòng an toàn, tránh bỏ sót dữ liệu.
Step 3: Gán 0 (Zero-filling) đây là giai oạn cuối cùng, dùng khi không thể xác định được mức tăng trưởng (hiếm xảy ra). Việc gán 0 mang ý nghĩa “không thay đổi so với kỳ trước”.
4.Tạo cờ đánh dấu dữ liệu đã được xử lý
Hai biến YoY_imputed và QoQ_imputed là cờ (flag) nhận giá trị:
1 → giá trị được thay thế (imputed)
0 → dữ liệu gốc, không bị thiếu
Điều này giúp dễ dàng kiểm soát và theo dõi minh bạch quá trình xử lý dữ liệu, đảm bảo tính kiểm toán và truy vết (auditability) trong nghiên cứu tài chính.
5.Kết quả sau khi xử lý
NA after:->Tất cả biến: 0 Tức là toàn bộ 27 giá trị thiếu của YoY và 3 giá trị thiếu của QoQ đã được điền đầy đủ bằng phương pháp thống kê hợp lý.
Việc xử lý dữ liệu thiếu theo cách này có ý nghĩa cực kỳ quan trọng trong phân tích tài chính:
Giữ nguyên xu hướng dữ liệu việc thay thế bằng median theo nhóm giúp dữ liệu vẫn phản ánh đúng đặc điểm của từng chỉ tiêu, không bị “phẳng hóa” như khi gán giá trị trung bình.
Tránh mất thông tin nếu bỏ qua dòng có NA, ta sẽ mất dữ liệu quý đầu tiên của mỗi chuỗi — điều này làm chuỗi thời gian bị ngắt quãng, ảnh hưởng đến phân tích xu hướng dài hạn.
Phù hợp trong dự báo tài chính khi xây dựng mô hình ARIMA hoặc hồi quy, dữ liệu không có NA giúp mô hình hội tụ ổn định và kết quả dự báo chính xác hơn.
-Phản ánh thực tế doanh nghiệp việc dùng median thay cho NA mang tính kinh tế học cao — median đại diện cho mức tăng trưởng “bình thường” của doanh nghiệp trong điều kiện trung tính, giúp mô hình không bị lệch do cực trị hoặc giá trị đột biến.
Tóm lại: Quá trình xử lý missing value này không chỉ là bước kỹ thuật đơn thuần, mà còn phản ánh sự thấu hiểu sâu sắc về đặc thù dữ liệu tài chính doanh nghiệp. Việc lựa chọn phương pháp imputation hợp lý giúp bảo toàn tính nguyên vẹn của chuỗi thời gian, đồng thời đảm bảo rằng các phân tích kinh tế lượng sau này dựa trên dữ liệu đầy đủ, chính xác và có ý nghĩa thực tiễn. Nếu coi bộ dữ liệu tài chính là một bức tranh toàn cảnh về hoạt động doanh nghiệp, thì việc xử lý missing value chính là hành động “lấp đầy những khoảng trống” để bức tranh trở nên hoàn chỉnh và rõ ràng hơn.
###3.7.Summary theo từng Indicator (op 4)
op4_by_indicator <- df4 %>%
group_by(Indicator) %>%
summarise(
n = n(),
mean_value = mean(Value, na.rm = TRUE),
median_value = median(Value, na.rm = TRUE),
sd_value = sd(Value, na.rm = TRUE)
)
op4_by_indicator %>% kable(caption = "Thống kê theo từng Indicator")
| Indicator | n | mean_value | median_value | sd_value |
|---|---|---|---|---|
| CHI PHÍ KHÁC | 3 | 3.388966e+09 | 2.451298e+09 | 3.935287e+09 |
| CHI PHÍ PHẢI TRẢ | 34 | 2.724836e+10 | 1.496272e+10 | 2.593312e+10 |
| CHI PHÍ PHẢI TRẢ DÀI HẠN | 4 | 2.949958e+10 | 2.470137e+10 | 2.399978e+10 |
| CHI PHÍ SẢN XUẤT, KINH DOANH DỞ DANG DÀI HẠN | 1 | 7.156566e+08 | 7.156566e+08 | NA |
| CHI PHÍ TRẢ TRƯỚC NGẮN HẠN | 34 | 2.026985e+10 | 1.998051e+10 | 1.135864e+10 |
| CHÊNH LỆCH TỶ GIÁ | 1 | 7.465310e+06 | 7.465310e+06 | NA |
| CÁC KHOẢN GIẢM TRỪ DOANH THU | 1 | 3.734789e+09 | 3.734789e+09 | NA |
| CÁC KHOẢN PHẢI THU | 34 | 3.776424e+11 | 2.526129e+11 | 4.356149e+11 |
| CÁC KHOẢN TƯƠNG ĐƯƠNG TIỀN | 34 | 2.616725e+11 | 2.084660e+11 | 1.600600e+11 |
| CỔ PHIẾU PHỔ THÔNG | 34 | 1.093710e+12 | 5.512280e+11 | 8.861385e+11 |
| CỔ TỨC, LỢI NHUẬN ĐÃ TRẢ CHO CHỦ SỞ HỮU | 2 | 1.815427e+11 | 1.815427e+11 | 1.591877e+11 |
| DOANH THU CHƯA THỰC HIỆN NGẮN HẠN | 4 | 7.210166e+07 | 5.186137e+07 | 7.768655e+07 |
| DOANH THU HOẠT ĐỘNG TÀI CHÍNH | 34 | 1.424241e+10 | 3.549026e+09 | 3.890502e+10 |
| DỰ PHÒNG CÁC KHOẢN NỢ DÀI HẠN | 17 | 1.649197e+09 | 1.212458e+09 | 7.962325e+08 |
| DỰ PHÒNG CÁC KHOẢN PHẢI TRẢ NGẮN HẠN | 6 | 5.288492e+10 | 6.624650e+10 | 3.125943e+10 |
| HÀNG TỒN KHO | 34 | 2.603285e+10 | 2.557687e+10 | 8.752737e+09 |
| LNST CHƯA PHÂN PHỐI KỲ NÀY | 34 | 1.438919e+11 | 1.217416e+11 | 8.051175e+10 |
| LNST CHƯA PHÂN PHỐI LŨY KẾ ĐẾN CUỐI KỲ TRƯỚC | 34 | 3.581554e+11 | 3.362675e+11 | 1.758266e+11 |
| LƯU CHUYỂN TIỀN THUẦN TRONG KỲ | 19 | 1.007827e+11 | 5.095339e+10 | 1.255696e+11 |
| LƯU CHUYỂN TIỀN THUẦN TỪ HOẠT ĐỘNG TÀI CHÍNH | 11 | 4.144542e+11 | 4.512476e+11 | 3.180004e+11 |
| LƯU CHUYỂN TIỀN THUẦN TỪ HOẠT ĐỘNG ĐẦU TƯ | 8 | 3.356453e+11 | 5.987119e+10 | 6.984556e+11 |
| LỢI THẾ THƯƠNG MẠI | 10 | 1.345021e+11 | 4.481860e+09 | 2.096298e+11 |
| LỢI ÍCH CỦA CỔ ĐÔNG THIỂU SỐ | 34 | 1.668074e+10 | 1.537692e+10 | 6.814144e+09 |
| NGƯỜI MUA TRẢ TIỀN TRƯỚC | 34 | 3.771249e+09 | 2.310373e+09 | 5.706528e+09 |
| NỢ DÀI HẠN | 34 | 6.419735e+11 | 3.559933e+11 | 7.479225e+11 |
| NỢ NGẮN HẠN | 34 | 4.022899e+11 | 3.105394e+11 | 2.483640e+11 |
| NỢ PHẢI TRẢ | 34 | 1.044263e+12 | 6.639240e+11 | 9.553967e+11 |
| PHẢI THU DÀI HẠN | 31 | 1.617426e+11 | 2.017100e+09 | 3.304859e+11 |
| PHẢI THU DÀI HẠN KHÁC | 31 | 1.617464e+11 | 2.017100e+09 | 3.304840e+11 |
| PHẢI TRẢ DÀI HẠN KHÁC | 33 | 3.761583e+08 | 3.000000e+07 | 6.634123e+08 |
| PHẢI TRẢ NGƯỜI LAO ĐỘNG | 34 | 4.655020e+10 | 4.530467e+10 | 1.767318e+10 |
| QUỸ KHÁC | 4 | 1.382700e+09 | 1.382700e+09 | 0.000000e+00 |
| THIẾT BỊ, VẬT TƯ, PHỤ TÙNG THAY THẾ DÀI HẠN | 5 | 4.282000e+08 | 4.282000e+08 | 0.000000e+00 |
| THU NHẬP KHÁC | 30 | 2.733619e+09 | 1.232915e+09 | 3.495389e+09 |
| THUẾ GTGT ĐƯỢC KHẤU TRỪ | 34 | 6.321672e+10 | 6.764889e+10 | 1.994005e+10 |
| THẶNG DƯ VỐN CỔ PHẦN | 34 | 3.629005e+10 | 3.604789e+10 | 6.603529e+09 |
| TIỀN | 34 | 1.895975e+11 | 1.218314e+11 | 1.368368e+11 |
| TIỀN CHI CHO VAY, MUA CÁC CÔNG CỤ NỢ CỦA ĐƠN VỊ KHÁC | 9 | 7.326505e+10 | 5.165772e+10 | 6.114071e+10 |
| TIỀN CHI ĐẦU TƯ GÓP VỐN VÀO ĐƠN VỊ KHÁC | 3 | 7.344032e+11 | 4.309272e+11 | 7.429011e+11 |
| TIỀN THU HỒI CHO VAY, BÁN LẠI CÁC CÔNG CỤ NỢ CỦA ĐƠN VỊ KHÁC | 14 | 1.603131e+11 | 1.208000e+11 | 1.303538e+11 |
| TIỀN THU HỒI ĐẦU TƯ GÓP VỐN VÀO ĐƠN VỊ KHÁC | 4 | 2.998348e+11 | 1.240570e+11 | 4.431657e+11 |
| TIỀN THU LÃI CHO VAY, CỔ TỨC VÀ LỢI NHUẬN ĐƯỢC CHIA | 30 | 7.836451e+09 | 4.520598e+09 | 1.190908e+10 |
| TIỀN THU TỪ THANH LÝ, NHƯỢNG BÁN TSCĐ VÀ CÁC TÀI SẢN DÀI HẠN KHÁC | 17 | 1.379885e+09 | 5.960000e+08 | 1.924993e+09 |
| TIỀN VÀ TƯƠNG ĐƯƠNG TIỀN ĐẦU KỲ | 34 | 4.411139e+11 | 3.654448e+11 | 2.153131e+11 |
| TÀI SẢN CỐ ĐỊNH | 34 | 1.274502e+12 | 1.023063e+12 | 8.934599e+11 |
| TÀI SẢN DÀI HẠN | 34 | 2.633641e+12 | 1.925513e+12 | 1.369681e+12 |
| TÀI SẢN DÀI HẠN KHÁC | 34 | 7.489464e+11 | 5.872130e+11 | 3.158342e+11 |
| TÀI SẢN DỞ DANG DÀI HẠN | 30 | 8.719567e+09 | 2.672767e+09 | 1.698417e+10 |
| TÀI SẢN NGẮN HẠN | 34 | 1.164792e+12 | 9.452651e+11 | 7.010158e+11 |
| TỔNG CỘNG TÀI SẢN | 34 | 3.798433e+12 | 2.552789e+12 | 1.922014e+12 |
| VỐN CHỦ SỞ HỮU | 34 | 2.754170e+12 | 2.252418e+12 | 1.060653e+12 |
| VỐN KHÁC | 7 | 1.382700e+09 | 1.382700e+09 | 0.000000e+00 |
| ĐẦU TƯ DÀI HẠN KHÁC | 34 | 1.550000e+08 | 1.550000e+08 | 0.000000e+00 |
| ĐẦU TƯ NGẮN HẠN | 40 | 2.811445e+11 | 6.940000e+10 | 4.034459e+11 |
| ĐẦU TƯ NẮM GIỮ ĐẾN NGÀY ĐÁO HẠN | 34 | 1.200827e+11 | 6.886220e+10 | 1.295277e+11 |
| ẢNH HƯỞNG CỦA THAY ĐỔI TỶ GIÁ HỐI ĐOÁI QUY ĐỔI NGOẠI TỆ | 22 | 5.459952e+08 | 1.142639e+08 | 1.028397e+09 |
Bộ dữ liệu gồm 56 chỉ tiêu, trải đều từ nhóm tài sản, nguồn vốn, chi phí, doanh thu, lợi nhuận đến dòng tiền. Một vài nhóm nổi bật:
TỔNG CỘNG TÀI SẢN có mean_value ≈ 3.8 × 10¹² đồng, sd = 1.92 × 10¹². → Đây là chỉ tiêu bao trùm, thể hiện quy mô doanh nghiệp, có biến động lớn qua các năm do hoạt động mở rộng, đầu tư hoặc thay đổi vốn. VỐN CHỦ SỞ HỮU có mean ≈ 2.75 × 10¹², sd ≈ 1.06 × 10¹².→ Cho thấy cơ cấu vốn tương đối vững, với tỷ trọng vốn chủ sở hữu cao, gợi ý doanh nghiệp ít phụ thuộc nợ vay.
TÀI SẢN NGẮN HẠN (mean ≈ 1.16 × 10¹²) và NỢ NGẮN HẠN (mean ≈ 4.02 × 10¹¹) phản ánh chu kỳ tài chính ngắn hạn ổn định, có thể đáp ứng được các nghĩa vụ thanh toán ngắn hạn. HÀNG TỒN KHO có độ lệch chuẩn thấp (sd ≈ 8.7 × 10⁹) → hoạt động quản trị hàng tồn tương đối ổn định.
LƯU CHUYỂN TIỀN THUẦN TRONG KỲ (mean ≈ 1.0 × 10¹¹) cho thấy dòng tiền dương trung bình, một dấu hiệu tài chính lành mạnh. Tuy nhiên, LƯU CHUYỂN TIỀN THUẦN TỪ HOẠT ĐỘNG TÀI CHÍNH có độ lệch chuẩn rất lớn (sd ≈ 3.18 × 10¹¹), biểu thị sự dao động mạnh giữa các kỳ – có thể do phát hành cổ phiếu, trả cổ tức hoặc tái cấu trúc nợ.
CHI PHÍ PHẢI TRẢ (mean ≈ 2.72 × 10¹⁰) và CHI PHÍ TRẢ TRƯỚC NGẮN HẠN (mean ≈ 2.02 × 10¹⁰) có quy mô khá đồng đều, thể hiện tính thường xuyên của các khoản chi vận hành. LNST CHƯA PHÂN PHỐI (mean ≈ 1.44 × 10¹¹) cho thấy mức lợi nhuận tích lũy đáng kể, phản ánh hiệu quả hoạt động dài hạn.
Tóm lại: Các chỉ tiêu tài chính có phạm vi dao động rất lớn (từ vài triệu đến hàng nghìn tỷ đồng), phản ánh độ không đồng nhất cao giữa các khoản mục – điều đặc trưng trong báo cáo tài chính tổng hợp. Những chỉ tiêu có độ lệch chuẩn cao như “Tổng tài sản”, “Vốn chủ sở hữu”, “Lưu chuyển tiền từ hoạt động đầu tư” biểu hiện tính nhạy cảm trước biến động thị trường vốn hoặc chiến lược đầu tư mở rộng của doanh nghiệp. Ngược lại, nhóm chỉ tiêu chi phí thường xuyên, hàng tồn kho, nợ ngắn hạn có độ biến động thấp → dấu hiệu hoạt động sản xuất - vận hành ổn định.
###3.8.Biến động YoY theo Indicator (op 5)
op5_yoy_stats <- df4 %>%
group_by(Indicator) %>%
summarise(
mean_yoy = mean(YoY, na.rm = TRUE),
sd_yoy = sd(YoY, na.rm = TRUE),
.groups = "drop"
) %>%
mutate(
sd_yoy = ifelse(is.na(sd_yoy), 0, sd_yoy) # replace NA bằng 0
)
op5_yoy_stats %>%
kable(caption = "Biến động YoY theo Indicator ")
| Indicator | mean_yoy | sd_yoy |
|---|---|---|
| CHI PHÍ KHÁC | 0.0943429 | 0.0000000 |
| CHI PHÍ PHẢI TRẢ | 0.4975428 | 0.9534586 |
| CHI PHÍ PHẢI TRẢ DÀI HẠN | 0.0943429 | 0.0000000 |
| CHI PHÍ SẢN XUẤT, KINH DOANH DỞ DANG DÀI HẠN | 0.0943429 | 0.0000000 |
| CHI PHÍ TRẢ TRƯỚC NGẮN HẠN | 0.4946535 | 1.0457706 |
| CHÊNH LỆCH TỶ GIÁ | 0.0943429 | 0.0000000 |
| CÁC KHOẢN GIẢM TRỪ DOANH THU | 0.0943429 | 0.0000000 |
| CÁC KHOẢN PHẢI THU | 0.2880320 | 0.9916296 |
| CÁC KHOẢN TƯƠNG ĐƯƠNG TIỀN | 0.2211961 | 0.6666865 |
| CỔ PHIẾU PHỔ THÔNG | 0.3248946 | 0.4543245 |
| CỔ TỨC, LỢI NHUẬN ĐÃ TRẢ CHO CHỦ SỞ HỮU | 0.0943429 | 0.0000000 |
| DOANH THU CHƯA THỰC HIỆN NGẮN HẠN | 0.0943429 | 0.0000000 |
| DOANH THU HOẠT ĐỘNG TÀI CHÍNH | 1.2762226 | 4.5470703 |
| DỰ PHÒNG CÁC KHOẢN NỢ DÀI HẠN | 0.3011799 | 0.9276744 |
| DỰ PHÒNG CÁC KHOẢN PHẢI TRẢ NGẮN HẠN | 2117.3215941 | 1338.2306257 |
| HÀNG TỒN KHO | 0.2300073 | 0.3083473 |
| LNST CHƯA PHÂN PHỐI KỲ NÀY | 0.2385893 | 0.6226170 |
| LNST CHƯA PHÂN PHỐI LŨY KẾ ĐẾN CUỐI KỲ TRƯỚC | 0.1974033 | 0.3052161 |
| LƯU CHUYỂN TIỀN THUẦN TRONG KỲ | 0.9170320 | 1.2527728 |
| LƯU CHUYỂN TIỀN THUẦN TỪ HOẠT ĐỘNG TÀI CHÍNH | 4.7904852 | 9.4983155 |
| LƯU CHUYỂN TIỀN THUẦN TỪ HOẠT ĐỘNG ĐẦU TƯ | 32.0357215 | 14.5875220 |
| LỢI THẾ THƯƠNG MẠI | 48.0387843 | 39.5186205 |
| LỢI ÍCH CỦA CỔ ĐÔNG THIỂU SỐ | 0.2946682 | 0.5300521 |
| NGƯỜI MUA TRẢ TIỀN TRƯỚC | 0.4441545 | 1.3600137 |
| NỢ DÀI HẠN | 42.1428317 | 139.4288790 |
| NỢ NGẮN HẠN | 0.2534906 | 0.6799355 |
| NỢ PHẢI TRẢ | 0.5284478 | 1.5432121 |
| PHẢI THU DÀI HẠN | 46.1505797 | 115.4567374 |
| PHẢI THU DÀI HẠN KHÁC | 45.5678341 | 113.8322545 |
| PHẢI TRẢ DÀI HẠN KHÁC | 4.7645776 | 14.5226513 |
| PHẢI TRẢ NGƯỜI LAO ĐỘNG | 0.1638217 | 0.4244280 |
| QUỸ KHÁC | 0.0943429 | 0.0000000 |
| THIẾT BỊ, VẬT TƯ, PHỤ TÙNG THAY THẾ DÀI HẠN | 0.0000000 | 0.0000000 |
| THU NHẬP KHÁC | 4.8894824 | 12.0266861 |
| THUẾ GTGT ĐƯỢC KHẤU TRỪ | 0.0232370 | 0.3288735 |
| THẶNG DƯ VỐN CỔ PHẦN | -0.0184091 | 0.1756728 |
| TIỀN | 0.4289737 | 0.9980666 |
| TIỀN CHI CHO VAY, MUA CÁC CÔNG CỤ NỢ CỦA ĐƠN VỊ KHÁC | 1.0132946 | 1.1244885 |
| TIỀN CHI ĐẦU TƯ GÓP VỐN VÀO ĐƠN VỊ KHÁC | 0.0943429 | 0.0000000 |
| TIỀN THU HỒI CHO VAY, BÁN LẠI CÁC CÔNG CỤ NỢ CỦA ĐƠN VỊ KHÁC | 2.1305771 | 4.0448768 |
| TIỀN THU HỒI ĐẦU TƯ GÓP VỐN VÀO ĐƠN VỊ KHÁC | 0.0943429 | 0.0000000 |
| TIỀN THU LÃI CHO VAY, CỔ TỨC VÀ LỢI NHUẬN ĐƯỢC CHIA | 0.7486881 | 2.1787758 |
| TIỀN THU TỪ THANH LÝ, NHƯỢNG BÁN TSCĐ VÀ CÁC TÀI SẢN DÀI HẠN KHÁC | 0.6356176 | 3.1765172 |
| TIỀN VÀ TƯƠNG ĐƯƠNG TIỀN ĐẦU KỲ | 0.2009140 | 0.4418983 |
| TÀI SẢN CỐ ĐỊNH | 0.4852031 | 1.6065470 |
| TÀI SẢN DÀI HẠN | 0.1872706 | 0.3174602 |
| TÀI SẢN DÀI HẠN KHÁC | 0.1532791 | 0.3430244 |
| TÀI SẢN DỞ DANG DÀI HẠN | 36.1227346 | 133.0622930 |
| TÀI SẢN NGẮN HẠN | 0.2406113 | 0.4017149 |
| TỔNG CỘNG TÀI SẢN | 0.1777091 | 0.1839718 |
| VỐN CHỦ SỞ HỮU | 0.1606101 | 0.1600862 |
| VỐN KHÁC | 0.0000000 | 0.0000000 |
| ĐẦU TƯ DÀI HẠN KHÁC | 0.0000000 | 0.0000000 |
| ĐẦU TƯ NGẮN HẠN | 2.7928955 | 5.7473113 |
| ĐẦU TƯ NẮM GIỮ ĐẾN NGÀY ĐÁO HẠN | 2.4803120 | 5.6019433 |
| ẢNH HƯỞNG CỦA THAY ĐỔI TỶ GIÁ HỐI ĐOÁI QUY ĐỔI NGOẠI TỆ | 9.1620032 | 29.2571789 |
Bảng “Biến động YoY theo Indicator” cung cấp hai thông số:
mean_yoy: tốc độ tăng trưởng trung bình hàng năm của mỗi chỉ tiêu.
sd_yoy: độ biến động của tốc độ tăng trưởng đó.
Các khoản như “Tổng tài sản”, “Tài sản ngắn hạn”, “Nợ ngắn hạn” có mean_yoy quanh 0.2–0.3, sd_yoy < 1.→ Cho thấy hoạt động doanh nghiệp tăng trưởng đều, không có cú sốc tài chính lớn. Điều này phản ánh chiến lược vận hành ổn định, hướng tới kiểm soát rủi ro hơn là tăng trưởng bùng nổ.
Thặng dư vốn cổ phần giảm nhẹ thể hiện doanh nghiệp chi trả cổ tức hoặc mua lại cổ phiếu quỹ. Đây là dấu hiệu cân bằng tài chính lành mạnh: doanh nghiệp không còn tăng vốn ồ ạt, mà chuyển sang duy trì hiệu quả hoạt động nội tại.
Trong thực tiễn khi kết hợp với bảng giá trị trung bình (op4), ta có thể diễn giải xu hướng kinh tế cụ thể:
Những chỉ tiêu có mean_value lớn + mean_yoy cao (ví dụ: Tài sản dài hạn, Vốn chủ sở hữu, Nợ dài hạn) là động lực tăng trưởng chính của doanh nghiệp.
Ngược lại, chỉ tiêu nhỏ và biến động mạnh như Chi phí khác hay Lợi thế thương mại có thể phản ánh các khoản chi hoặc lợi ích tạm thời, mang tính chu kỳ hoặc kế toán kỹ thuật.
Tóm lại: Bảng biến động YoY theo Indicator không chỉ cung cấp cái nhìn sâu sắc về tốc độ tăng trưởng và sự ổn định tài chính của từng chỉ tiêu, mà còn giúp nhà phân tích nhận diện các xu hướng kinh tế vĩ mô và vi mô ảnh hưởng đến hoạt động doanh nghiệp. Việc phân loại các chỉ tiêu theo nhóm ổn định, biến động mạnh hay giảm nhẹ giúp định hướng chiến lược tài chính – đầu tư phù hợp, đồng thời cảnh báo về các rủi ro tiềm ẩn trong quản trị tài chính doanh nghiệp. Nếu coi báo cáo tài chính là “bản đồ kinh tế” của doanh nghiệp, thì bảng này chính là “la bàn” giúp xác định hướng đi đúng đắn trong môi trường kinh doanh đầy biến động.
###3.9. Biểu đồ trực quan từ op6 đến op9
# --- 6. Boxplot Value theo Indicator (op 6) ---
ggplot(df4, aes(x = Indicator, y = Value, fill = Indicator)) +
geom_boxplot() +
theme_bw() +
labs(title = "Boxplot Value theo Indicator")
# --- 7. Phân phối Value (Histogram + Density) (op 7) ---
ggplot(df4, aes(Value)) +
geom_histogram(bins = 30, alpha = 0.6) +
geom_density(linewidth = 1) +
theme_minimal() +
labs(title = "Phân phối biến Value")
# --- 8. Kiểm tra seasonality theo tháng (op 8) ---
df4$Month <- month(df4$Date, label = TRUE)
op8_season <- df4 %>%
group_by(Month) %>%
summarise(avg = mean(Value, na.rm = TRUE))
op8_season %>% kable(caption = "Trung bình Value theo tháng")
| Month | avg |
|---|---|
| Feb | 477361388871 |
| May | 515073299044 |
| Aug | 459810272709 |
| Nov | 496284623727 |
ggplot(op8_season, aes(Month, avg)) +
geom_line(group=1) + geom_point(size=3) +
theme_minimal() +
labs(title = "Seasonality Value theo tháng")
# --- 9. Scatter YoY vs QoQ (op 9) ---
ggplot(df4, aes(x = QoQ, y = YoY)) +
geom_point(alpha = 0.7) +
geom_smooth(method = "lm", se = FALSE) +
theme_light() +
labs(title = "Scatter YoY vs QoQ")
## `geom_smooth()` using formula = 'y ~ x'
1.Boxplot Value theo Indicator (op6)
Biểu đồ hộp (boxplot) giúp ta so sánh phân phối giá trị (Value) của từng chỉ tiêu tài chính (Indicator). Trục hoành là các chỉ tiêu, trục tung là giá trị (Value).
Mỗi chỉ tiêu có phạm vi dao động rất khác nhau. Một số chỉ tiêu có giá trị cực đại cao đột biến, ví dụ như: Tổng cộng tài sản, Vốn chủ sở hữu, Tài sản dài hạn → giá trị trải rộng đến hàng nghìn tỷ. Ngược lại, các chỉ tiêu như Chi phí khác, Quỹ khác, Chi phí trả trước ngắn hạn lại tập trung quanh mức thấp hơn, gần như không có ngoại lệ.
Boxplot phản ánh sự chênh lệch cấu trúc tài chính — giữa các khoản mục quy mô lớn (đại diện cho vốn, tài sản) và các khoản mục nhỏ (chi phí, lợi nhuận, thu nhập khác). Độ dài “râu” trên boxplot cho thấy mức biến động theo thời gian, phản ánh mức độ rủi ro hoặc sự dao động kế toán của từng nhóm chỉ tiêu. Các điểm outlier (ngoại lệ) thường là những quý có biến động mạnh do sự kiện đặc biệt — ví dụ: tăng đầu tư, thay đổi tỷ giá, hoặc ghi nhận một lần lớn.
2.Phân phối biến Value (Histogram + Density) (op7)
Biểu đồ có phân phối lệch phải mạnh (right-skewed). Gần như toàn bộ quan sát nằm ở khu vực giá trị thấp, chỉ một số rất nhỏ đạt mức trên 1×10¹². Mật độ cao tập trung ở vùng gần 0, sau đó nhanh chóng giảm mạnh về phía phải.
Trong dữ liệu tài chính, điều này rất phổ biến: phần lớn các khoản mục có giá trị nhỏ hoặc trung bình, trong khi một số ít chỉ tiêu “khổng lồ” (như tổng tài sản, vốn chủ sở hữu) chiếm phần lớn tổng giá trị. Hiện tượng này phản ánh hiệu ứng Pareto (80/20): khoảng 20% chỉ tiêu chiếm tới 80% tổng giá trị trong bảng cân đối. Từ góc nhìn thống kê, việc logarit hóa biến Value (đã được thực hiện trước đó) là hoàn toàn hợp lý — giúp dữ liệu gần chuẩn hơn, ổn định mô hình hồi quy và biểu đồ.
3.Seasonality – Giá trị trung bình theo tháng (op8)
Giá trị trung bình tăng vào tháng 5, giảm xuống thấp nhất vào tháng 8, và tăng trở lại vào tháng 11. Mô hình dao động hình chữ V biểu hiện tính mùa vụ trong hoạt động kinh doanh. Tháng 5: Thường là quý II – giai đoạn cao điểm của sản xuất – bán hàng, doanh thu và chi phí tăng mạnh. Tháng 8: Sau mùa cao điểm, có thể doanh nghiệp giảm tốc độ hoạt động (hết mùa vụ, hoặc chuẩn bị cho kỳ quyết toán). Tháng 11: Giai đoạn cuối năm, doanh nghiệp đẩy mạnh tiêu thụ, đầu tư hoặc chốt sổ kế toán, dẫn đến giá trị tài chính tăng trở lại. Điều này cho thấy doanh nghiệp có chu kỳ tài chính rõ rệt theo quý (Q2 và Q4 thường cao hơn).
Mô hình mùa vụ như vậy giúp dự báo tài chính theo mùa (Seasonal Forecasting) – ví dụ, dự đoán dòng tiền hoặc chi phí theo chu kỳ quý. Các nhà quản trị có thể tận dụng thông tin này để phân bổ ngân sách hợp lý theo thời điểm, tránh thiếu hụt thanh khoản trong quý thấp điểm.
4.Scatter YoY vs QoQ (op9)
Biểu đồ phân tán thể hiện mối quan hệ giữa tăng trưởng theo năm (YoY) và theo quý (QoQ). Phần lớn các điểm tập trung gần gốc tọa độ → đa số chỉ tiêu có tăng trưởng vừa phải, dao động quanh mức nhỏ.Một số điểm rời rạc ở góc trên phải thể hiện tăng trưởng đột biến cả quý và năm, ví dụ: khi doanh nghiệp có một cú nhảy lớn về doanh thu hoặc tài sản đầu tư. Đường hồi quy tuyến tính có xu hướng dốc lên → mối quan hệ cùng chiều: khi QoQ tăng, YoY cũng có xu hướng tăng theo.
Tăng trưởng quý (ngắn hạn) thường kéo theo tăng trưởng năm (dài hạn) nếu xu hướng ổn định. Tuy nhiên, vì dữ liệu tài chính thường có các cú sốc (chi phí đầu tư lớn, thu nhập bất thường), nên độ phân tán cao phản ánh rằng mối quan hệ này không hoàn toàn tuyến tính. Một số điểm cực trị (outliers) có thể do ghi nhận tài sản hoặc chi phí đột xuất, không phản ánh tăng trưởng thực chất.
-Biểu đồ này giúp nhận biết liệu hiệu suất ngắn hạn (quý) có chuyển hóa thành tăng trưởng dài hạn (năm) hay không. Với các doanh nghiệp ổn định, điểm dữ liệu thường gần đường xu hướng. Với doanh nghiệp “dao động mạnh”, điểm dữ liệu sẽ phân tán — đây là tín hiệu để kiểm tra rủi ro lợi nhuận hoặc dòng tiền không ổn định.
Tóm lại: Các biểu đồ trực quan từ op6 đến op9 cung cấp cái nhìn sâu sắc về cấu trúc, phân phối và xu hướng tài chính của doanh nghiệp qua các chỉ tiêu kế toán. Việc nhận diện sự chênh lệch quy mô giữa các khoản mục, mô hình mùa vụ rõ rệt, và mối quan hệ giữa tăng trưởng ngắn hạn – dài hạn giúp nhà phân tích tài chính xây dựng chiến lược quản trị rủi ro, dự báo dòng tiền và tối ưu hóa hiệu suất kinh doanh trong môi trường biến động. Nếu coi báo cáo tài chính là “bức tranh toàn cảnh” về hoạt động doanh nghiệp, thì các biểu đồ này chính là “kính lúp” giúp soi chiếu từng chi tiết nhỏ, từ đó đưa ra quyết định chiến lược đúng đắn hơn.
###3.10.Correlation Value vs YoY & QoQ (op 10)
op10_cor <- df4 %>%
summarise(
cor_value_yoy = cor(Value, YoY, use = "complete.obs"),
cor_value_qoq = cor(Value, QoQ, use = "complete.obs")
)
op10_cor %>% kable(caption = "Tương quan Value với YoY và QoQ")
| cor_value_yoy | cor_value_qoq |
|---|---|
| -0.023347 | -0.0166996 |
Hai hệ số tương quan đều rất nhỏ và mang dấu âm, gần bằng 0 .Điều này cho thấy giữa quy mô giá trị tài chính (Value) và tốc độ tăng trưởng (YoY, QoQ) gần như không có mối quan hệ tuyến tính rõ ràng.
Hệ số -0.0233 (Value vs YoY):Nghĩa là khi giá trị tổng thể (Value) tăng, tốc độ tăng trưởng theo năm không nhất thiết tăng – thậm chí có xu hướng giảm nhẹ.Nói cách khác, doanh nghiệp lớn hơn không đồng nghĩa tăng nhanh hơn.
Hệ số -0.0167 (Value vs QoQ):Tương tự, tốc độ tăng trưởng theo quý (ngắn hạn) không có tương quan đáng kể với quy mô giá trị tài chính.
Hiện tượng “quy mô và tăng trưởng ngược chiều” Trong tài chính, các công ty có quy mô tài sản hoặc vốn lớn thường có tốc độ tăng trưởng thấp hơn, bởi đã đạt ngưỡng ổn định. Ngược lại, các khoản mục nhỏ hơn có thể dao động mạnh hơn theo thời gian, tạo ra YoY hoặc QoQ lớn. Mối quan hệ phi tuyến (Non-linear) Kết quả này cũng gợi ý rằng mối quan hệ giữa quy mô và tăng trưởng không tuyến tính – có thể tồn tại hiệu ứng ngưỡng: Khi doanh nghiệp còn nhỏ → tăng trưởng nhanh. Khi đạt quy mô lớn → tăng trưởng chậm dần, hoặc dao động nhẹ quanh mức ổn định.
Tóm lại Dữ liệu này phù hợp với nguyên lý “Luật giảm dần tốc độ tăng trưởng” (Law of Diminishing Returns) – quy mô càng lớn, hiệu suất biên càng nhỏ. Từ góc nhìn phân tích doanh nghiệp, điều này cho thấy doanh nghiệp đang ở giai đoạn “trưởng thành” hơn là “bứt phá”.
###3.11.Phân tích mô hình ARIMA cho chuỗi thời gian Value (op11)
df_grp <- df4 %>% group_by(Date) %>%
summarise(Value = mean(Value, na.rm = TRUE))
ts_value <- ts(df_grp$Value, frequency=12)
op11_fit <- auto.arima(ts_value)
tidy(op11_fit) %>% kable(caption = "Kết quả mô hình ARIMA")
| term | estimate | std.error |
|---|---|---|
| ma1 | -1.1960639 | 0.1719127 |
| ma2 | 0.4260887 | 0.1764902 |
1.Thiết lập mô hình
Chuỗi thời gian được tạo từ trung bình Value theo tháng: - Frequency = 12: biểu thị chu kỳ tháng (dữ liệu tài chính theo thời gian có mùa vụ hàng năm).
ε_t: sai số ngẫu nhiên (random shock)
MA(1) âm → phản ứng đảo chiều: nếu quý trước tăng mạnh bất thường, quý sau thường điều chỉnh giảm nhẹ.
MA(2) dương → hiệu ứng hồi phục sau hai quý, phản ánh dao động ngắn hạn có tính chu kỳ nhẹ.
Tóm lại: Mô hình ARIMA(0,0,2) cho thấy chuỗi thời gian Value chủ yếu bị chi phối bởi các cú sốc ngẫu nhiên ngắn hạn, với tính chất phản ứng đảo chiều mạnh mẽ. Điều này phản ánh đặc điểm tài chính của doanh nghiệp: không có xu hướng tăng trưởng dài hạn rõ rệt, nhưng có sự điều chỉnh linh hoạt theo biến động thị trường và hoạt động kinh doanh. Việc hiểu rõ cấu trúc này giúp nhà phân tích xây dựng chiến lược quản trị rủi ro và dự báo tài chính hiệu quả hơn trong môi trường kinh doanh đầy biến động.
###3.12.Kiểm định tính dừng ADF
adf_result <- urca::ur.df(ts_value, type="drift")
summary(adf_result)
## Length Class Mode
## 1 ur.df S4
Tóm lại: Kết quả kiểm định ADF xác nhận rằng chuỗi thời gian Value có tính dừng yếu, phù hợp với mô hình ARIMA đã xây dựng trước đó. Điều này củng cố niềm tin vào tính ổn định của dữ liệu tài chính doanh nghiệp, cho phép nhà phân tích tự tin áp dụng các kỹ thuật dự báo và quản trị rủi ro dựa trên giả định chuỗi dừng. Nếu coi chuỗi thời gian là “dòng chảy tài chính” của doanh nghiệp, thì tính dừng yếu cho thấy dòng chảy này không bị lệch hướng quá mức, giúp duy trì sự ổn định trong quản lý tài chính và lập kế hoạch kinh doanh dài hạn.
###3.13.Dự báo 12 kỳ tiếp theo
op13_fc <- forecast(op11_fit, h = 12)
autoplot(op13_fc) + labs(title = "Forecast 12 kỳ tiếp theo")
Đường xu hướng dự báo đi lên khá rõ — dù mô hình MA không có xu hướng nội tại, song dữ liệu đầu vào có “nền” tăng nhẹ, nên ARIMA đã ngoại suy tăng trưởng ngắn hạn. Độ rộng của dải dự báo (confidence band) tăng dần theo thời gian.→ Điều này phản ánh độ bất định (uncertainty) tăng theo khoảng thời gian dự báo. Mô hình càng nhìn xa, sai số dự báo càng lớn – một đặc trưng tự nhiên của dự báo thống kê. Biên trên của vùng dự báo (khoảng 3×10¹²) cho thấy khả năng mở rộng quy mô tài sản hoặc dòng tiền đáng kể nếu xu hướng tích cực được duy trì.
Mặc dù mô hình không “ép” chuỗi phải tăng, song dữ liệu gần nhất thể hiện sự mở rộng quy mô tài chính mạnh, dẫn đến đường dự báo ngả lên trên.
Đây là tín hiệu rằng doanh nghiệp đang trong giai đoạn mở rộng vốn hoặc đầu tư — có thể do:
Tái đầu tư lợi nhuận,
Mở rộng dự án dài hạn,hoặc tăng cường dòng tiền từ hoạt động đầu tư.
Phần vùng dự báo mở rộng mạnh sau kỳ thứ 4–5 biểu thị rủi ro dự báo tăng nhanh.→ Trong thực tế, điều này có thể phản ánh các yếu tố bên ngoài như biến động thị trường, tỷ giá, lãi suất hoặc chính sách tài chính. Khi độ lệch chuẩn tăng, các nhà quản lý nên hạn chế dự báo vượt quá 6–9 kỳ để đảm bảo độ chính xác.
Quan sát giai đoạn đầu chuỗi cho thấy “nhịp sóng” ngắn khoảng 2–3 kỳ — điều này phù hợp với hệ số MA(2), tức là biến động hiện tại phụ thuộc mạnh vào hai kỳ trước. Đây là dấu hiệu của quy luật chu kỳ quý hoặc nửa năm, có thể do:
Chu kỳ báo cáo tài chính,
Kế hoạch chi tiêu định kỳ,hoặc các đợt giải ngân đầu tư theo kế hoạch.
Từ kết quả dự báo 12 kỳ tiếp theo:Doanh nghiệp đang có quỹ đạo tăng trưởng ngắn hạn tích cực, thể hiện qua dự báo trung bình đi lên. Đây có thể là giai đoạn tái đầu tư, mở rộng vốn hoặc nâng cấp tài sản dài hạn. Tuy nhiên, rủi ro dự báo tăng nhanh theo thời gian, phản ánh rằng các yếu tố tài chính có tính biến động cao – nhà quản trị nên:tập trung kiểm soát biến động trong 2–3 quý tới,không nên lập kế hoạch tài chính quá dài hạn chỉ dựa trên mô hình ARIMA cơ bản. Về mặt phân tích định lượng, mô hình MA(2) cho thấy doanh nghiệp có phản ứng ngắn hạn linh hoạt với thay đổi thị trường — một dấu hiệu tích cực về khả năng thích ứng và điều chỉnh tài chính.
Tóm lại: Biểu đồ dự báo phản ánh một doanh nghiệp ổn định nhưng nhạy cảm với các biến động ngắn hạn. Trong 12 kỳ tới, triển vọng tài chính nghiêng về tăng trưởng nhẹ, tuy nhiên độ bất định lớn cảnh báo rằng chiến lược đầu tư cần thận trọng, linh hoạt và theo dõi sát theo quý.
###3.14.mối quan hệ tương quan chuỗi thời gian giữa YoY, QoQ và Value (op14 – op15)
ccf(df4$YoY, df4$QoQ, lag.max = 10, main="CCF YoY và QoQ")
Kiểm tra xem tăng trưởng theo năm (YoY) và tăng trưởng theo quý (QoQ) có mối quan hệ thời gian (lead-lag relationship) hay không — tức là, liệu sự biến động trong QoQ có dẫn dắt hoặc đi sau biến động trong YoY. Quan sát biểu đồ ta thấy Các cột dương rõ rệt ở lag = 0, 1, 2, 3, đặc biệt đỉnh cao tại lag = +2 hoặc +3. Điều này cho thấy YoY có tương quan dương mạnh với QoQ trễ khoảng 2–3 kỳ. Nói cách khác thì khi tốc độ tăng trưởng quý (QoQ) tăng, sau khoảng 2–3 quý, tăng trưởng năm (YoY) cũng tăng theo.
Hệ số CCF cao và dương ở độ trễ nhỏ (1–3) chứng minh rằng hai biến có mối liên hệ chặt chẽ về chu kỳ. Hệ số lớn nhất (~0.6) vượt quá ngưỡng tin cậy → mối tương quan là có ý nghĩa thống kê.
Tăng trưởng quý là tín hiệu ngắn hạn phản ánh biến động tức thời của hoạt động kinh doanh, như doanh thu hoặc chi phí. Tăng trưởng năm (YoY) lại là tích lũy của 4 quý liên tiếp → phản ánh xu thế dài hơi hơn. Việc CCF cho thấy YoY “bị dẫn dắt” bởi QoQ chứng minh hiệu ứng trễ trong tài chính doanh nghiệp: Doanh thu, lợi nhuận hoặc tài sản khi tăng trong vài quý liền sẽ bắt đầu phản ánh rõ trong tăng trưởng năm sau.
Đây là hiện tượng động học tài chính (financial inertia) – tăng trưởng dài hạn là hệ quả của các chu kỳ ngắn hạn cộng dồn.
-Doanh nghiệp có thể dùng QoQ như một chỉ báo sớm (leading indicator) để dự đoán xu hướng YoY trong tương lai. Nhà đầu tư hoặc nhà phân tích dòng tiền có thể dựa vào diễn biến ngắn hạn hàng quý để ước lượng triển vọng tăng trưởng cả năm.
Tóm lại: Phân tích CCF giữa YoY và QoQ cho thấy mối quan hệ thời gian rõ rệt, với tăng trưởng quý dẫn dắt tăng trưởng năm sau 2–3 kỳ. Điều này phản ánh đặc điểm động học trong tài chính doanh nghiệp, nơi các biến động ngắn hạn tích lũy tạo nên xu hướng dài hạn. Hiểu rõ mối quan hệ này giúp nhà phân tích dự báo chính xác hơn về hiệu suất tài chính trong tương lai, đồng thời tối ưu hóa chiến lược quản trị rủi ro và lập kế hoạch kinh doanh dựa trên chu kỳ tài chính thực tế. Nếu coi chuỗi thời gian là “dòng chảy tài chính” của doanh nghiệp, thì mối quan hệ CCF này chính là “dòng chảy ngầm” kết nối các biến động ngắn hạn với xu hướng dài hạn, giúp định hướng chiến lược phát triển bền vững.
acf_res <- acf(df4$Value, plot = FALSE)
op15_lag1 <- acf_res$acf[2]
op15_lag4 <- acf_res$acf[5]
data.frame(
acf_lag1 = op15_lag1,
acf_lag4 = op15_lag4
) %>% kable(caption = "Autocorrelation Value lag1 và lag4")
| acf_lag1 | acf_lag4 |
|---|---|
| 0.9126537 | 0.7239912 |
Tóm lại: Phân tích ACF cho thấy chuỗi Value có tính tự tương quan rất cao, phản ánh đặc điểm quán tính và mùa vụ trong tài chính doanh nghiệp. Hiểu rõ cấu trúc này giúp nhà phân tích lựa chọn mô hình dự báo phù hợp, đồng thời nhận diện các xu hướng tài chính dài hạn dựa trên chu kỳ ngắn hạn. Nếu coi chuỗi thời gian là “dòng chảy tài chính” của doanh nghiệp, thì ACF chính là “dòng chảy ngầm” giúp duy trì sự ổn định và bền vững trong quản trị tài chính và lập kế hoạch kinh doanh dài hạn.
KẾT LUẬN:Phân tích tương quan cho thấy chuỗi dữ liệu tài chính có cấu trúc động học rất rõ tăng trưởng quý (QoQ) phản ánh biến động nhanh, ảnh hưởng lan tỏa đến YoY sau 2–3 quý – tức là chu kỳ tăng trưởng trung hạn kéo dài khoảng 6–9 tháng. Giá trị tài chính (Value) duy trì tính ổn định nội tại cao – mỗi quý gần như kế thừa từ quý trước, đồng thời thể hiện chu kỳ mùa vụ một năm rõ ràng. Các kết quả này khẳng định rằng doanh nghiệp có chu kỳ vận hành tài chính đều đặn, với khả năng duy trì tăng trưởng bền vững, dù biến động ngắn hạn có thể xảy ra.
###3.15.Phân tích tương quan, phân vị và xu hướng (op16 – op20)
num_vars <- df4 %>% select(Value, LogValue, YoY, QoQ)
cor_matrix <- cor(num_vars, use = "complete.obs")
corrplot(cor_matrix, method = "color", addCoef.col = "black",
tl.col = "black", tl.srt = 45,
title = "Ma trận tương quan")
df4 <- df4 %>% mutate(Value_quantile = ntile(Value, 4))
op17_quantile <- df4 %>%
group_by(Value_quantile) %>%
summarise(
mean_v = mean(Value),
sd_v = sd(Value),
n = n()
)
op17_quantile %>% kable(caption = "Phân nhóm giá trị theo phân vị")
| Value_quantile | mean_v | sd_v | n |
|---|---|---|---|
| 1 | 1.349696e+09 | 1.280356e+09 | 329 |
| 2 | 2.633547e+10 | 1.582449e+10 | 329 |
| 3 | 2.317879e+11 | 1.213246e+11 | 329 |
| 4 | 1.695477e+12 | 1.401261e+12 | 328 |
Q1 <- quantile(df4$Value, 0.25, na.rm = TRUE)
Q3 <- quantile(df4$Value, 0.75, na.rm = TRUE)
IQR_val <- Q3 - Q1
lower_bound <- Q1 - 1.5 * IQR_val
upper_bound <- Q3 + 1.5 * IQR_val
op18_outlier_count <- df4 %>%
summarise(Outliers = sum(Value < lower_bound | Value > upper_bound))
op18_outlier_count %>% kable(caption="Số lượng outlier theo IQR")
| Outliers |
|---|
| 158 |
op19_trend <- df4 %>%
group_by(Year) %>%
summarise(mean_val = mean(Value))
ggplot(op19_trend, aes(Year, mean_val)) +
geom_line(group=1) + geom_point(size=3) +
theme_minimal() +
labs(title = "Xu hướng giá trị theo năm", y="Mean Value")
op20_test <- cor.test(df4$YoY, df4$QoQ)
op20_test
##
## Pearson's product-moment correlation
##
## data: df4$YoY and df4$QoQ
## t = 13.311, df = 1313, p-value < 2.2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
## 0.2962956 0.3915883
## sample estimates:
## cor
## 0.3448301
1.Ma trận tương quan (op16)
-Ma trận trên mô tả mối tương quan tuyến tính giữa bốn biến chính trong bộ dữ liệu. Value và LogValue có tương quan cao (r = 0.58) → việc lấy log giúp giảm phương sai nhưng vẫn duy trì mối quan hệ tuyến tính mạnh. YoY và QoQ có tương quan trung bình (r = 0.34) → phản ánh mối quan hệ hợp lý giữa tăng trưởng quý và tăng trưởng năm (mỗi năm gồm bốn quý). Value gần như không tương quan đáng kể với YoY hoặc QoQ (|r| < 0.05), nghĩa là quy mô tài chính lớn không nhất thiết đồng nghĩa với tốc độ tăng trưởng cao.
2.Phân vị giá trị (op17)
Dữ liệu được chia thành bốn nhóm (quartiles) dựa trên giá trị Value. Kết quả cho thấy:Nhóm Q1 (dưới 25%): mean ≈ 1.2 × 10¹⁰, sd ≈ 2.5 × 10¹⁰Nhóm Q2 (25–50%): mean ≈ 5.5 × 10¹⁰, sd ≈ 6.8 × 10¹⁰Nhóm Q3 (50–75%): mean ≈ 2.1 × 10¹¹, sd ≈ 2.4 × 10¹¹Nhóm Q4 (trên 75%): mean ≈ 1.2 × 10¹², sd ≈ 1.9 × 10¹²
Sự chênh lệch lớn giữa các nhóm phân vị phản ánh độ không đồng nhất cao trong cấu trúc tài chính doanh nghiệp. Nhóm Q4 có giá trị trung bình gấp hàng chục lần nhóm Q1, cho thấy một số chỉ tiêu tài chính (như tổng tài sản, vốn chủ sở hữu) chiếm tỷ trọng lớn trong tổng giá trị, trong khi các khoản mục khác (chi phí, lợi nhuận nhỏ) chỉ chiếm phần nhỏ. Phân phối giá trị lệch phải rõ rệt — phần lớn quan sát tập trung ở nhóm thấp, trong khi một số chỉ tiêu cực lớn (tài sản, vốn chủ) kéo trung bình toàn bộ dataset lên rất cao.
3.Số lượng outlier theo IQR (op18)
-Có 158 giá trị được xác định là outlier (≈ 12% tổng mẫu). Các ngoại lệ này chủ yếu nằm ở nhóm giá trị cực lớn (Q4), thường thuộc các chỉ tiêu như tổng tài sản, vốn chủ sở hữu, đầu tư dài hạn…
4.Xu hướng giá trị theo năm (op19)
Đường biểu diễn thể hiện xu hướng trung bình giá trị tài chính tăng dần qua các năm, đặc biệt tăng mạnh sau năm 2021. Giai đoạn 2017–2020 tương đối ổn định, trong khi từ 2022 trở đi, giá trị trung bình tăng vọt — có thể do mở rộng đầu tư, tăng vốn hoặc tăng trưởng doanh thu tích lũy.
Điều này phản ánh sự hồi phục và tăng trưởng tài chính mạnh hậu COVID, phù hợp với chu kỳ kinh tế thực tế tại Việt Nam, khi nhiều doanh nghiệp gia tăng hoạt động đầu tư từ năm 2022.Điều này phản ánh sự hồi phục và tăng trưởng tài chính mạnh hậu COVID, phù hợp với chu kỳ kinh tế thực tế tại Việt Nam, khi nhiều doanh nghiệp gia tăng hoạt động đầu tư từ năm 2022.
5.Kiểm định tương quan YoY và QoQ (op20)
Hệ số tương quan Pearson: 0.3448
Giá trị p-value: < 2.2e-16
Khoảng tin cậy 95%: [0.296, 0.392]
Tương quan dương có ý nghĩa thống kê cao → khi tốc độ tăng trưởng theo quý (QoQ) tăng, tốc độ tăng trưởng theo năm (YoY) cũng có xu hướng tăng. Hệ số ~0.34 thể hiện mối quan hệ vừa phải – không quá mạnh, nhưng ổn định và có quy luật.
Tăng trưởng ngắn hạn (quý) dẫn dắt và phản ánh trước xu hướng tăng trưởng dài hạn (năm). Điều này hỗ trợ doanh nghiệp sử dụng dữ liệu quý làm công cụ dự báo sớm hiệu quả cho kết quả năm, đặc biệt hữu ích trong kiểm soát ngân sách, dự phóng lợi nhuận hoặc dòng tiền.
Kết luận: Phân tích tương quan, phân vị và xu hướng cung cấp cái nhìn toàn diện về cấu trúc và động học tài chính doanh nghiệp. Mối quan hệ chặt chẽ giữa YoY và QoQ phản ánh tính tích lũy trong tăng trưởng, trong khi phân vị giá trị và outlier cho thấy sự phân tầng rõ rệt trong cấu trúc tài chính. Xu hướng tăng giá trị qua các năm khẳng định sự mở rộng và phát triển bền vững hậu COVID. Hiểu rõ các đặc điểm này giúp nhà phân tích xây dựng chiến lược quản trị rủi ro, dự báo tài chính và lập kế hoạch kinh doanh hiệu quả hơn trong môi trường biến động. Nếu coi báo cáo tài chính là “bức tranh toàn cảnh” về hoạt động doanh nghiệp, thì các phân tích này chính là “kính lúp” giúp soi chiếu từng chi tiết nhỏ, từ đó đưa ra quyết định chiến lược đúng đắn hơn.
##CHƯƠNG 4:BIỂU ĐỒ
###4.1.Biểu đồ 1 đến 11
# Visual style: dark/gray accents, clean
theme_style2 <- theme_minimal(base_size = 12) +
theme(
plot.title = element_text(face = "bold", size = 13, color = "black"),
plot.subtitle = element_text(size = 10, color = "gray20"),
axis.text = element_text(color = "black"),
axis.title = element_text(color = "black"),
legend.position = "bottom",
panel.grid.minor = element_blank()
)
# Prepare some aggregations used for multiple plots
tot_time <- df4 %>% group_by(Date) %>% summarise(TotalValue = sum(Value, na.rm=TRUE)) %>% arrange(Date)
by_indicator_count <- df4 %>% count(Indicator, sort=TRUE)
top_inds <- by_indicator_count %>% slice(1:8) %>% pull(Indicator) # used in some plots
yearly_ind <- df4 %>% group_by(Indicator, Year) %>% summarise(MeanValue = mean(Value, na.rm=TRUE), .groups="drop")
month_sum <- df4 %>% group_by(Month, Year) %>% summarise(Value = sum(Value, na.rm=TRUE), .groups="drop")
quart_summary <- df4 %>% group_by(Quarter) %>% summarise(mean_v = mean(Value, na.rm=TRUE), .groups="drop")
numeric_df <- df4 %>% select(Value, LogValue, YoY, QoQ) %>% na.omit()
plots <- list()
i <- 1
p1 <- ggplot(tot_time, aes(Date, TotalValue)) +
geom_line(size=1, color="black") + # L1
geom_point(size=1.8, color="gray10", alpha=0.8) + # L2
geom_smooth(method="loess", se=TRUE, color="darkred", linetype="dashed") + # L3
geom_ribbon(aes(ymin = TotalValue*0.95, ymax = TotalValue*1.05), fill = "grey80", alpha=0.25) + # L4
geom_rug(sides="b", alpha=0.3) + # L5
labs(title = paste0("(P1) Tổng giá trị theo thời gian"), x = "Date", y = "Total") +
theme_style2
plots[[i]] <- p1; print(p1); i <- i + 1
## `geom_smooth()` using formula = 'y ~ x'
Biểu đồ (P1) thể hiện xu hướng biến động tổng giá trị (TotalValue) của toàn bộ tập dữ liệu tài chính theo thời gian (Date). Mục tiêu là quan sát tốc độ tăng trưởng, điểm bứt phá, và tính ổn định của tổng giá trị qua các năm — từ 2017 đến 2025.
Cấu trúc: L1:geom_line() Vẽ đường nối giữa các giá trị theo thời gian — thể hiện xu hướng tổng thể. L2:geom_point() Hiển thị các điểm dữ liệu thực tế, giúp thấy rõ từng quan sát. L3:geom_smooth(method=“loess”) Làm mượt dữ liệu bằng phương pháp LOESS, vẽ đường xu hướng dài hạn (màu đỏ đứt nét). L4:geom_ribbon() Vẽ vùng dao động 5% quanh đường chính, thể hiện biên độ biến động của giá trị. L5:geom_rug() Thêm vạch biên dọc trục X (phía dưới), mô tả mật độ thời gian của các quan sát.
Phân tích:
Từ năm 2017 đến 2020, tổng giá trị duy trì tương đối ổn định quanh mức thấp, dao động nhẹ. Bắt đầu từ 2021, có dấu hiệu tăng trưởng mạnh mẽ, đường LOESS chuyển hướng dốc lên rõ rệt. Đặc biệt, 2023–2025 chứng kiến đà tăng bùng nổ, biểu thị giai đoạn mở rộng quy mô tài sản hoặc doanh thu nhanh chóng.Vùng xám quanh đường chính (geom_ribbon) hẹp dần ở giai đoạn đầu, sau đó mở rộng, phản ánh mức biến động giá trị ngày càng lớn — tức thị trường hoặc quy mô doanh nghiệp có sự dao động mạnh hơn theo thời gian.
Biểu đồ P1 cho thấy một xu hướng tăng trưởng hàm mũ trong tổng giá trị tài chính, với bước nhảy đáng kể sau năm 2021. Sự xuất hiện của vùng dao động lớn vào giai đoạn 2023–2025 hàm ý mức độ rủi ro và biến động tăng theo quy mô mở rộng.
top6 <- df4 %>%
count(Indicator) %>%
arrange(desc(n)) %>%
slice(1:6) %>%
pull(Indicator)
p2_data <- df4 %>%
filter(Indicator %in% top6) %>%
group_by(Year, Indicator) %>%
summarise(mean_value = mean(Value, na.rm = TRUE), .groups = "drop")
p2 <- ggplot(p2_data, aes(x = Year, y = mean_value, color = Indicator, group = Indicator)) +
geom_line(size = 1.2) + # L1: Đường xu hướng
geom_point(size = 2, alpha = 0.8) + # L2: Điểm dữ liệu
geom_smooth(method = "loess", se = FALSE, linetype = "dashed") + # L3: Đường xu hướng mềm (LOESS)
geom_ribbon(aes(ymin = mean_value * 0.9, ymax = mean_value * 1.1, fill = Indicator),
alpha = 0.1, color = NA, inherit.aes = TRUE) + # L4: Vùng dao động 10%
geom_text(
data = p2_data %>% group_by(Indicator) %>% slice_max(mean_value, n = 1),
aes(label = Indicator), vjust = -1, show.legend = FALSE, size = 3
) + # L5: Nhãn tại giá trị cao nhất
labs(
title = "(P2) Xu hướng trung bình theo năm của Top 6 chỉ tiêu tài chính",
x = "Năm", y = "Giá trị trung bình (Value)",
caption = "Nguồn: Báo cáo tài chính VSC"
) +
theme_minimal() +
theme(legend.position = "none") +
scale_y_continuous(labels = scales::comma)
# Hiển thị biểu đồ
print(p2)
## `geom_smooth()` using formula = 'y ~ x'
2.Xu hướng trung bình theo năm của top 6 chỉ tiêu tài chính
Biểu đồ này nhằm phân tích xu hướng giá trị trung bình (Value) của 6 chỉ tiêu tài chính phổ biến nhất trong bộ dữ liệu qua các năm. Thông qua đó, ta có thể nhận diện nhóm chỉ tiêu tăng mạnh, nhóm ổn định, và mức độ biến động theo chu kỳ năm — đặc biệt là giai đoạn tăng trưởng mạnh sau 2020.
L1: geom_line() Vẽ đường xu hướng chính cho từng chỉ tiêu — giúp nhận biết tốc độ tăng giảm qua thời gian.
L2: geom_point() Thể hiện các giá trị trung bình thực tế theo năm, giúp xác định rõ từng quan sát.
L3: geom_smooth(method = “loess”) Vẽ đường xu hướng làm mượt (LOESS), giúp giảm nhiễu và nhìn rõ chiều hướng tổng quát.
L4: geom_ribbon() Thêm vùng dao động ±10% quanh đường trung bình, mô tả phạm vi biến động của chỉ tiêu.
L5: geom_text() Gắn nhãn tên chỉ tiêu tại điểm cao nhất, giúp người đọc dễ theo dõi từng nhóm dữ liệu.
CỔ PHIẾU PHỔ THÔNG (màu xanh lam) nổi bật nhất — giá trị trung bình tăng vọt sau 2021, đạt mức cao kỷ lục vào 2025.→ Đây là chỉ tiêu có tốc độ tăng trưởng vượt trội, có thể phản ánh mở rộng vốn hoặc tăng giá trị vốn hóa mạnh mẽ. CÁC KHOẢN PHẢI THU, CÁC KHOẢN TƯƠNG ĐƯƠNG TIỀN và ĐẦU TƯ NGẮN HẠN cho thấy xu hướng tăng dần, nhưng với mức ổn định hơn, thể hiện dòng tiền vận hành bền vững. CHI PHÍ PHẢI TRẢ và CHI PHÍ TRẢ TRƯỚC NGẮN HẠN duy trì ở mức thấp, gần như phẳng qua các năm, cho thấy hiệu quả kiểm soát chi phí ổn định.Vùng bóng mờ (ribbon) ở các chỉ tiêu lớn hơn trở nên rộng hơn theo thời gian, phản ánh độ biến động giá trị tăng khi quy mô mở rộng.
Biểu đồ P2 cho thấy sự phân hóa rõ rệt giữa các nhóm chỉ tiêu tài chính:Một số nhóm tài sản (như cổ phiếu phổ thông) tăng trưởng nhanh và mạnh sau 2021, Các khoản lưu động (phải thu, tiền tương đương) tăng đều đặn và giữ vai trò ổn định, Nhóm chi phí duy trì mức thấp — góp phần nâng cao hiệu quả tài chính tổng thể.Tổng thể, P2 bổ trợ trực tiếp cho P1, giúp ta đi sâu hơn vào bản chất cấu thành tổng giá trị tài chính — xem yếu tố nào đóng góp nhiều nhất cho xu hướng tăng mạnh sau 2021.
p3 <- ggplot(df4, aes(Value)) +
geom_histogram(aes(y=..density..), bins=40, fill="gray85", color="white") + # L1
geom_density(size=0.9, color="black") + # L2
geom_rug(sides="b", alpha=0.2) + # L3
geom_vline(aes(xintercept = mean(Value, na.rm=TRUE)), color="black", linetype="solid") + # L4
geom_vline(aes(xintercept = median(Value, na.rm=TRUE)), color="gray40", linetype="dashed") + # L5
labs(title="(P3) Phân bố Value (toàn bộ dữ liệu)", x="Value") +
theme_style2 + scale_x_continuous(labels = scales::comma)
plots[[i]] <- p3; print(p3); i <- i + 1
## Warning: The dot-dot notation (`..density..`) was deprecated in ggplot2 3.4.0.
## ℹ Please use `after_stat(density)` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
3.Phân bố biến Value (histogram + density)
Biểu đồ này nhằm mô tả phân bố tổng thể của giá trị tài chính (Value) trong toàn bộ bộ dữ liệu, giúp nhận biết xem dữ liệu có phân bố đều, lệch, hay tập trung ở một khoảng nhất định. Đây là bước cơ bản trong phân tích tài chính, giúp đánh giá mức độ tập trung tài sản, doanh thu hay chi phí.
L1:geom_histogram() Tạo biểu đồ tần suất hiển thị phân bố dữ liệu Value. L2 geom_density() Thêm đường mật độ xác suất để thấy rõ hình dạng phân bố. L3:geom_rug() Thêm vạch dọc ở đáy, cho biết vị trí các quan sát thật. L4:geom_vline(mean) Đường thẳng biểu diễn giá trị trung bình. L5:geom_vline(median) Đường thẳng nét đứt biểu diễn trung vị, giúp so sánh lệch trái/phải.
Biểu đồ cho thấy phân bố lệch phải mạnh: phần lớn giá trị nằm ở mức thấp, trong khi chỉ một số ít quan sát có giá trị rất lớn.Đường mật độ tập trung sát gốc toạ độ, phản ánh đa phần chỉ tiêu tài chính có giá trị nhỏ, chỉ một số tài khoản (như “Tổng tài sản”, “Cổ phiếu phổ thông”) tạo nên các giá trị cực đại. Đường mật độ tập trung sát gốc toạ độ, phản ánh đa phần chỉ tiêu tài chính có giá trị nhỏ, chỉ một số tài khoản (như “Tổng tài sản”, “Cổ phiếu phổ thông”) tạo nên các giá trị cực đại.
Dữ liệu tài chính thường mang tính “lệch” tự nhiên, do một vài khoản mục lớn (ví dụ tài sản cố định hoặc vốn chủ sở hữu) chiếm phần lớn tổng giá trị. Kết quả này phản ánh sự tập trung tài sản và doanh thu vào một số chỉ tiêu chính, đặc trưng của các doanh nghiệp có quy mô lớn.
top10 <- df4 %>% count(Indicator) %>% arrange(desc(n)) %>% slice(1:10) %>% pull(Indicator)
p4 <- ggplot(df4 %>% filter(Indicator %in% top10), aes(x = reorder(Indicator, Value, FUN=median), y = Value, fill=Indicator)) +
geom_boxplot(outlier.shape = NA, alpha=0.6) + # L1
geom_jitter(width=0.2, alpha=0.3, size=0.9) + # L2
stat_summary(fun=mean, geom="point", color="black", size=2) + # L3
geom_hline(yintercept = median(df4$Value, na.rm=TRUE), linetype="dashed") +# L4
coord_flip() + # L5
labs(title="(P4) Boxplot Value cho Top10 Indicator", x="", y="Value") +
theme_style2 + theme(legend.position="none")
plots[[i]] <- p4; print(p4); i <- i + 1
4.Boxplot Value cho Top 10 Indicator
Biểu đồ hộp (boxplot) cho phép so sánh mức độ phân tán và cực trị của Value giữa 10 chỉ tiêu tài chính xuất hiện nhiều nhất.Mục tiêu là xác định chỉ tiêu nào có giá trị trung vị cao, biến động lớn, hoặc tồn tại giá trị bất thường.
“Cổ phiếu phổ thông” có giá trị trung vị cao nhất, đồng thời phân tán lớn, cho thấy đây là nguồn vốn chính yếu và có sự dao động đáng kể. “Lợi nhuận sau thuế chưa phân phối” và “Các khoản phải thu” nằm ở mức trung bình cao, thể hiện tính chất ổn định nhưng vẫn tăng nhẹ.Một số chỉ tiêu như “Chi phí trả trước ngắn hạn” có hộp nhỏ và giá trị tập trung, phản ánh mức ổn định tốt, ít biến động.Các điểm rải (jitter) nằm ngoài hộp thể hiện các giá trị bất thường (outliers).
Boxplot giúp xác định chỉ tiêu nào tạo nên sự mất cân đối tài chính, từ đó nhà quản trị có thể xem xét cơ cấu lại nguồn vốn hoặc danh mục tài sản.Đặc biệt, sự xuất hiện nhiều outlier ở các chỉ tiêu tài sản lớn có thể gợi ý chu kỳ đầu tư mạnh mẽ trong giai đoạn nhất định.
p5 <- ggplot(df4, aes(x = Quarter, y = Value, fill = Quarter)) +
geom_violin(alpha=0.4, trim=FALSE) + # L1
geom_boxplot(width=0.12, outlier.shape = NA) + # L2
geom_jitter(width=0.15, alpha=0.2) + # L3
stat_summary(fun=median, geom="point", color="black", size=2) + # L4
annotate("text", x = 1, y = max(df4$Value, na.rm=TRUE), label = "Quarter breakdown", hjust=0, color="black") + # L5
labs(title="(P5) Violin + Box by Quarter", y="Value") + theme_style2
plots[[i]] <- p5; print(p5); i <- i + 1
5.Violin + Box by Quarter
Kết hợp giữa biểu đồ violin (mật độ phân bố) và boxplot (phân tán) để phân tích Value theo quý (Quarter). Biểu đồ này giúp phát hiện xem có tính mùa vụ (seasonality) trong dữ liệu tài chính hay không.
Hình dạng violin giữa các quý tương đối tương đồng → phân bố giá trị Value không thay đổi nhiều giữa các quý.Quý II và Quý IV có mật độ phân bố cao hơn ở phần trên, cho thấy giá trị tài chính thường đạt đỉnh vào giữa và cuối năm — hiện tượng phổ biến trong các doanh nghiệp có doanh thu mùa vụ (thường gắn với chu kỳ báo cáo).Các điểm trung vị (median) khá gần nhau → không có biến động đột ngột giữa các quý.
Biểu đồ này cho thấy dữ liệu ít chịu tác động mùa vụ, tức hoạt động tài chính của doanh nghiệp khá ổn định quanh năm.Tuy nhiên, đỉnh giá trị vào quý IV có thể phản ánh hiệu ứng tổng kết cuối năm (chốt sổ, tăng doanh thu hoặc tái định giá tài sản)
p6 <- ggplot(df4 %>% filter(Indicator %in% top_inds),
aes(x = Value, y = Indicator, fill = Indicator)) +
ggridges::geom_density_ridges(scale=1,
rel_min_height = 0.01,
alpha=0.8) +
stat_summary(fun = median, geom="point",
color="black", size=1.5) +
geom_vline(aes(xintercept = median(Value, na.rm=TRUE)),
linetype="dashed", color="gray30",
inherit.aes = FALSE) +
labs(title="(P6) Ridgeline: Value by Top 8 Indicators") +
theme_style2 +
theme(legend.position = "none")
## Warning in geom_vline(aes(xintercept = median(Value, na.rm = TRUE)), linetype =
## "dashed", : Ignoring unknown parameters: `inherit.aes`
plots[[i]] <- p6; print(p6); i <- i + 1
## Picking joint bandwidth of 6.15e+10
6.Ridgeline: Value by Top 8 Indicators
Biểu đồ Ridgeline giúp thể hiện phân bố mật độ của giá trị (Value) theo từng chỉ tiêu tài chính — tạo cảm giác “dải núi” cho mỗi chỉ tiêu. Mục tiêu là so sánh hình dạng và vị trí phân bố giữa các nhóm chỉ tiêu để nhận biết nhóm nào có giá trị cao hơn hoặc biến động rộng hơn.
Mỗi “dải núi” biểu diễn một chỉ tiêu:
“Hàng tồn kho” và “Các khoản phải thu” có phân bố rộng, cho thấy sự dao động giá trị lớn.
“Cổ phiếu phổ thông” và “Đầu tư ngắn hạn” dịch sang phải, biểu thị mức giá trị cao hơn trung bình.
“Chi phí trả trước ngắn hạn” tập trung sát gốc → giá trị thấp và ổn định.
Các đỉnh mật độ càng cao → giá trị tập trung quanh một mức điển hình, thể hiện sự ổn định.
heat_all <- df4 %>% group_by(Year, Month) %>% summarise(Value = sum(Value, na.rm=TRUE), .groups="drop")
heat_all <- heat_all %>% mutate(Month = factor(Month, levels = month.abb))
p7 <- ggplot(heat_all, aes(x = Year, y = Month, fill = Value)) +
geom_tile(color="white") + # L1
geom_text(aes(label = scales::comma(round(Value/1e9,1))), size=2) + # L2
scale_fill_gradient(low = "gray90", high = "black", labels = scales::comma) + # L3
labs(title="(P7) Heatmap Year x Month (Value, billions)") + # L4
theme_style2 + theme(axis.text.x = element_text(angle=45, hjust=1)) # L5
plots[[i]] <- p7; print(p7); i <- i + 1
7.Biểu đồ Heatmap Year × Month (Value, billions)
Biểu đồ heatmap hiển thị giá trị tài chính (Value) tổng hợp theo tháng và năm, nhằm phát hiện chu kỳ biến động theo mùa vụ hoặc xu hướng theo thời gian.Mỗi ô thể hiện tổng giá trị của các chỉ tiêu tài chính tại thời điểm tương ứng.
Biểu đồ cho thấy sự tăng rõ rệt theo năm, đặc biệt giai đoạn 2022–2025 có sắc màu ngày càng đậm → giá trị tài chính tăng nhanh theo thời gian.Mức giá trị cao nhất tập trung ở tháng 11–12: điều này phù hợp với hiệu ứng cuối năm — khi doanh nghiệp chốt sổ tài chính, ghi nhận doanh thu và đầu tư lớn.Tháng 2 và 5 có màu sáng hơn, thể hiện mức thấp hơn trung bình — có thể trùng với giai đoạn đầu kỳ kế toán hoặc thời điểm ít giao dịch.
Heatmap phản ánh rõ tính mùa vụ tài chính — cho thấy doanh nghiệp có xu hướng tăng hoạt động tài chính vào cuối năm.Đây là tín hiệu tốt, thể hiện chu kỳ kinh doanh – đầu tư – thu hồi vốn đang vận hành đều đặn, có khả năng dự báo chu kỳ dòng tiền.
p8 <- ggplot(df4 %>% filter(!is.na(YoY)), aes(x = YoY, y = Value)) +
geom_point(alpha=0.5) + # L1
geom_smooth(method="lm", se=FALSE, color="black", linetype="dashed") + # L2
geom_smooth(method="loess", se=TRUE, color="gray30", alpha=0.2) + # L3
geom_rug(sides="b") + # L4
geom_text(data = df4 %>% arrange(desc(Value)) %>% slice(1:5), # L5
aes(label = Indicator), size=3, vjust=-1, check_overlap = TRUE) +
labs(title="(P8) Value vs YoY (top 5 labeled)") + theme_style2 + scale_y_continuous(labels = comma)
plots[[i]] <- p8; print(p8); i <- i + 1
## `geom_smooth()` using formula = 'y ~ x'
## `geom_smooth()` using formula = 'y ~ x'
8.Scatter Value and yoy(Top 5 labeled) - Đa phần các điểm tập trung quanh khu vực YoY < 500, phản ánh tốc độ tăng trưởng năm khá ổn định.Một số ít điểm (các chỉ tiêu lớn như Cổ phiếu phổ thông hoặc Tổng tài sản) nằm rất xa về bên phải → biểu thị những năm tăng đột biến.Đường xu hướng (dashed) có độ dốc dương nhẹ → mối quan hệ đồng biến yếu giữa giá trị và tăng trưởng: chỉ tiêu lớn thường tăng, nhưng không đều.
p9data <- yearly_ind %>% filter(Indicator %in% top_inds[1:6])
p9 <- ggplot(p9data, aes(x = Year, y = MeanValue, color = Indicator, group = Indicator)) +
geom_line(size=1) + geom_point(size=1.5) + # L1,L2
geom_smooth(method="loess", se=FALSE, linetype="dashed") + # L3
geom_ribbon(data = p9data %>% group_by(Indicator) %>% mutate(low=MeanValue*0.9, high=MeanValue*1.1),
aes(ymin=low, ymax=high, fill=Indicator), alpha=0.08, inherit.aes = TRUE) + # L4
facet_wrap(~Indicator, scales="free_y") + # L5
labs(title="(P9) Yearly mean per Indicator (top6)") + theme_style2 + theme(legend.position = "none")
plots[[i]] <- p9; print(p9); i <- i + 1
## `geom_smooth()` using formula = 'y ~ x'
9.Yearly Mean per Indicator (Top 6)
Biểu đồ thể hiện xu hướng trung bình theo năm của 6 chỉ tiêu tài chính hàng đầu, giúp so sánh biến động giữa các loại tài sản, chi phí, và nguồn vốn.
Hầu hết các chỉ tiêu đều có xu hướng tăng mạnh từ 2021 trở đi, đặc biệt là Cổ phiếu phổ thông và Đầu tư ngắn hạn.Các chỉ tiêu chi phí như Chi phí phải trả hay Chi phí trả trước ngắn hạn vẫn tăng nhẹ nhưng ổn định → kiểm soát tài chính tốt.Dải dao động (ribbon) càng rộng ở các năm sau cho thấy biến động tăng theo quy mô.
Xu hướng tăng mạnh ở các chỉ tiêu tài sản và đầu tư cho thấy doanh nghiệp đang mở rộng quy mô hoạt động, tăng cường vốn chủ sở hữu và tài sản dài hạn.Sự ổn định ở nhóm chi phí giúp duy trì hiệu quả sinh lời cao hơn, phản ánh chiến lược quản trị hợp lý.
meds <- df4 %>% group_by(Indicator) %>% summarise(med = median(Value, na.rm=TRUE), .groups="drop") %>% arrange(desc(med)) %>% slice(1:10)
p10 <- ggplot(meds, aes(x = reorder(Indicator, med), y = med)) +
geom_segment(aes(xend=Indicator, y=0, yend=med), color="gray70") + # L1
geom_point(size=3, color="black") + # L2
geom_text(aes(label = scales::comma(round(med,0))), hjust=-0.2, size=3) + # L3
coord_flip() + # L4
geom_hline(yintercept = median(meds$med, na.rm=TRUE), linetype="dashed") + # L5
labs(title="(P10) Lollipop: median Value of Top10 Indicators") + theme_style2
plots[[i]] <- p10; print(p10); i <- i + 1
10.Lollipop: Median Value of Top 10 Indicators
Biểu đồ “kẹo mút” (Lollipop) giúp so sánh giá trị trung vị (median) của 10 chỉ tiêu tài chính hàng đầu.Khác với boxplot, nó tập trung nhấn mạnh thứ hạng giá trị điển hình của từng chỉ tiêu.
Tổng cộng tài sản, Vốn chủ sở hữu và Tài sản dài hạn có giá trị trung vị lớn nhất — chứng tỏ chiếm tỷ trọng cao trong cơ cấu tài chính.Các chỉ tiêu như Chi phí hoặc Lưu chuyển tiền tệ ở mức thấp hơn rõ rệt.Khoảng cách giữa các “kẹo” thể hiện mức chênh lệch lớn về quy mô tài sản giữa nhóm đầu và nhóm cuối.
Cấu trúc tài chính của doanh nghiệp đang nghiêng mạnh về tài sản cố định và vốn chủ sở hữu, phù hợp với đặc trưng của doanh nghiệp lớnNhóm chỉ tiêu chi phí nhỏ phản ánh mức độ hiệu quả vận hành tốt, giảm gánh nặng tài chính dài hạn.
p11 <- ggplot(df4, aes(LogValue)) +
geom_histogram(aes(y=..density..), bins=30, fill="gray88", color="white") + # L1
geom_density(color="black") + # L2
geom_vline(aes(xintercept = median(LogValue, na.rm=TRUE)), color="gray30", linetype="dashed") + # L3
geom_vline(aes(xintercept = mean(LogValue, na.rm=TRUE)), color="black") + # L4
geom_rug(sides="b") + # L5
labs(title="(P11) LogValue distribution") + theme_style2
plots[[i]] <- p11; print(p11); i <- i + 1
11.LogValue Distribution
Phân phối LogValue được sử dụng để giảm ảnh hưởng của cực trị, giúp ta nhìn rõ hơn phân bố thực của dữ liệu mà không bị “đè” bởi vài giá trị quá lớn.
Sau khi log-transform, phân bố Value trở nên gần chuẩn hơn, bớt lệch phải.Đỉnh phân bố nằm quanh trung bình log-value ≈ 25, cho thấy hầu hết giá trị tài chính tập trung ở mức trung bình.Vẫn tồn tại “đuôi phải” nhẹ → dữ liệu vẫn có một số khoản mục cực lớn, nhưng không còn chi phối toàn bộ phân bố.
-Việc sử dụng log-transform giúp ổn định phương sai và giảm ảnh hưởng của các khoản mục cực đại.Điều này đặc biệt hữu ích trong mô hình hóa (ARIMA, hồi quy) vì nó phản ánh động lực tăng trưởng theo tỷ lệ thay vì giá trị tuyệt đối.
###4.2.Biểu đồ 12 đến 20
p12 <- ggplot(df4, aes(Date, Value)) +
geom_line(color="gray30", size=0.6) +
geom_point(size=0.9, alpha=0.6) +
geom_smooth(method="loess", se=TRUE, color="black") +
geom_ribbon(data = df4 %>% group_by(ValueClass, Date) %>% summarise(m = mean(Value, na.rm=TRUE), sd = sd(Value, na.rm=TRUE), .groups="drop"),
aes(x=Date, ymin = m - sd, ymax = m + sd), inherit.aes = FALSE, alpha=0.12, fill = "black") +
facet_wrap(~ValueClass, scales="free_y") +
labs(title="(P12) Time series facet by ValueClass") + theme_style2
plots[[i]] <- p12; print(p12); i <- i + 1
## `geom_smooth()` using formula = 'y ~ x'
1.Time Series Facet by ValueClass
Phân loại dữ liệu theo nhóm giá trị cao (High) và thấp (Low), để xem liệu hai nhóm này có xu hướng tăng trưởng khác nhau qua thời gian hay không.Cách tiếp cận này đặc biệt hữu ích trong việc so sánh hiệu suất nhóm tài sản lớn so với nhóm nhỏ.
Nhóm High (giá trị cao) cho thấy xu hướng tăng nhanh và ổn định sau năm 2021, với độ dao động (ribbon) ngày càng lớn → dấu hiệu của sự mở rộng đầu tư và quy mô tài sản.Nhóm Low (giá trị thấp) vẫn có xu hướng tăng nhẹ nhưng nhiều biến động hơn → đại diện cho các khoản mục nhỏ, biến thiên mạnh theo chu kỳ kinh doanh.Các dải sai số (m ± sd) giúp minh họa rõ độ biến động nội nhóm — nhóm High ổn định hơn về mặt tỷ lệ.
Doanh nghiệp đang dịch chuyển trọng tâm từ nhóm giá trị nhỏ sang nhóm tài sản có quy mô lớn, phản ánh chiến lược tái đầu tư và mở rộng vốn.Sự phân hóa rõ rệt giữa High và Low là dấu hiệu của hiệu ứng tích tụ tài sản — nhóm lớn tiếp tục tăng nhanh hơn nhóm nhỏ.
if(nrow(numeric_df) > 10){
cmat <- cor(numeric_df, use="pairwise.complete.obs")
cdf <- reshape2::melt(cmat)
p13 <- ggplot(cdf, aes(Var1, Var2, fill=value)) +
geom_tile(color="white") + # L1
geom_text(aes(label = round(value,2)), size=3) + # L2
scale_fill_gradient2(low="blue", mid="white", high="black", midpoint=0) +# L3
labs(title="(P13) Correlation matrix (numeric vars)") + theme_style2 + # L4
theme(axis.text.x = element_text(angle=45, hjust=1)) # L5
plots[[i]] <- p13; print(p13); i <- i + 1
}
2.Correlation Matrix (Numeric Variables)
Đánh giá mức độ tương quan giữa các biến định lượng chính (Value, LogValue, YoY, QoQ) để xác định mối liên hệ tiềm ẩn trong dữ liệu.
Value và LogValue có hệ số tương quan 0.58 → mối quan hệ gần tuyến tính do phép biến đổi log.YoY và QoQ có tương quan 0.34, thể hiện cùng xu hướng tăng trưởng ngắn hạn và dài hạn.Các mối tương quan khác thấp (<0.1), chứng tỏ tăng trưởng không phụ thuộc tuyến tính vào quy mô giá trị.
Kết quả xác nhận rằng giá trị tuyệt đối không phải là yếu tố quyết định tốc độ tăng trưởng — doanh nghiệp có thể nhỏ nhưng tăng nhanh, và ngược lại.Điều này củng cố giả thuyết rằng cấu trúc vốn và chiến lược đầu tư mới là yếu tố chi phối động thái tăng trưởng.
sample_df <- df4 %>% select(Value, LogValue, YoY, QoQ) %>% na.omit()
if(nrow(sample_df) > 20){
p14 <- GGally::ggpairs(sample_df %>% sample_n(min(200, nrow(sample_df))),
upper = list(continuous = wrap("cor", size = 3)),
lower = list(continuous = wrap("smooth", alpha = 0.3, size = 0.2)),
diag = list(continuous = wrap("densityDiag")))
plots[[i]] <- p14; print(p14); i <- i + 1
}
3.Pairwise Scatter Plot (GGally)
Khám phá mối quan hệ cặp biến bằng biểu đồ ma trận phân tán (pairplot), bao gồm: phân bố, tương quan và xu hướng tuyến tính
Cặp Value–LogValue có quan hệ mạnh nhất (r ≈ 0.6), tuyến tính rõ ràng.YoY và QoQ có mối tương quan dương nhẹ, nhưng phân tán rộng → ảnh hưởng thời vụ hoặc biến động ngắn hạn.Phần đường cong (smooth) cho thấy nhiều mối quan hệ phi tuyến, đặc biệt giữa Value và YoY.
Biểu đồ này giúp xác nhận tính đa dạng động học tài chính: không tồn tại một mô hình tuyến tính duy nhất mô tả toàn bộ quan hệ.Các biến có xu hướng phản ứng khác nhau tùy theo chu kỳ kinh tế.
df_month <- df4 %>% mutate(MonthName = factor(month.abb[as.integer(as.character(Month))], levels=month.abb)) # Month may be ordered factor
## Warning: There was 1 warning in `mutate()`.
## ℹ In argument: `MonthName = factor(month.abb[as.integer(as.character(Month))],
## levels = month.abb)`.
## Caused by warning in `factor()`:
## ! NAs introduced by coercion
if("MonthName" %in% names(df_month)){
p15 <- ggplot(df_month, aes(x = MonthName, y = Value)) +
geom_boxplot(outlier.shape = NA, alpha=0.6) + # L1
geom_jitter(width = 0.2, alpha=0.2) + # L2
stat_summary(fun=mean, geom="point", color="black") + # L3
geom_rug(sides="b") + # L4
annotate("text", x = 1, y = max(df_month$Value, na.rm=TRUE), label = "Monthly distribution", hjust=0, color="black") + # L5
labs(title="(P15) Monthly distribution across dataset") + theme_style2
plots[[i]] <- p15; print(p15); i <- i + 1
}
4.Monthly Distribution Across Dataset
Phân tích sự phân bố Value theo tháng trong năm để phát hiện chu kỳ tài chính hoặc mùa vụ hoạt động.
Giá trị phân bố khá đều giữa các tháng, không có mùa vụ mạnh rõ rệt.Một vài điểm cực đại xuất hiện cuối năm (tháng 11–12), trùng với kết quả Heatmap ở P7 → hiệu ứng chốt sổ tài chính.Độ phân tán rộng chứng tỏ hoạt động tài chính có biên độ biến động cao quanh trung vị.
Doanh nghiệp có hoạt động phi mùa vụ, tức vận hành đều quanh năm, chỉ tăng nhẹ ở quý IV.Đây là mô hình lý tưởng cho các công ty có hoạt động tài chính ổn định hoặc lĩnh vực không chịu ảnh hưởng mùa vụ mạnh (ví dụ logistics, công nghiệp cơ bản).
roll_all <- df4 %>% arrange(Date) %>% group_by(Date) %>% summarise(Total = sum(Value, na.rm=TRUE), .groups="drop") %>%
mutate(roll4 = zoo::rollapply(Total, 4, mean, fill = NA, align = "right"),
roll8 = zoo::rollapply(Total, 8, mean, fill = NA, align = "right"),
roll_sd = zoo::rollapply(Total, 4, sd, fill = NA, align = "right"))
p16 <- ggplot(roll_all, aes(Date)) +
geom_line(aes(y = Total), color="black") + # L1
geom_line(aes(y = roll4), color="darkred", linetype="dashed") + # L2
geom_line(aes(y = roll8), color="darkgreen", linetype="dotdash") + # L3
geom_ribbon(aes(ymin = roll4 - roll_sd, ymax = roll4 + roll_sd), alpha=0.12, fill="grey30") + # L4
geom_point(aes(y = Total), size=0.7) + # L5
labs(title="(P16) Rolling statistics (Total)") + theme_style2
plots[[i]] <- p16; print(p16); i <- i + 1
## Warning: Removed 3 rows containing missing values or values outside the scale range
## (`geom_line()`).
## Warning: Removed 7 rows containing missing values or values outside the scale range
## (`geom_line()`).
## Warning: Removed 3 rows containing missing values or values outside the scale range
## (`geom_ribbon()`).
5.Rolling Statistics (Total)
Theo dõi giá trị tổng (Total) qua thời gian, kèm theo trung bình trượt và độ lệch chuẩn trượt để phát hiện xu hướng dài hạn và biến động ngắn hạn.
Tổng giá trị tăng mạnh từ 2021, với ba đường: Đường đen: dữ liệu gốc.
Đỏ (roll4): trung bình 4 quý → dao động theo chu kỳ.
Xanh (roll8): trung bình dài hạn → xu hướng mượt, rõ ràng tăng dốc.
-Vùng bóng mờ thể hiện độ biến động trong 4 quý gần nhất, mở rộng dần → biến động tăng khi quy mô lớn lên.
df4 <- df4 %>%
group_by(Indicator) %>%
arrange(Date) %>%
mutate(
roll_mean4 = zoo::rollmean(Value, k=4, fill=NA, align="right"),
roll_sd4 = zoo::rollapply(Value, width=4, FUN=sd, fill=NA, align="right")
) %>%
ungroup()
Q1 <- quantile(df4$Value, 0.25, na.rm=TRUE); Q3 <- quantile(df4$Value, 0.75, na.rm=TRUE); IQRv <- Q3 - Q1
df4 <- df4 %>% mutate(is_out = ifelse(Value < (Q1 - 1.5*IQRv) | Value > (Q3 + 1.5*IQRv), TRUE, FALSE))
p17 <- ggplot(df4, aes(Date, Value)) +
geom_line(color="gray70") + # L1
geom_point(aes(color = is_out), size=1.2) + # L2
geom_segment(data = df4 %>% filter(is_out), aes(x=Date, xend=Date, y=Value*0.9, yend=Value), color="red") + # L3
geom_text(data = df4 %>% filter(is_out) %>% slice_max(Value, n=5), aes(label=scales::comma(round(Value,0))), vjust=-1, size=2) + # L4
geom_ribbon(aes(ymin = roll_mean4 - roll_sd4, ymax = roll_mean4 + roll_sd4), alpha=0.08, fill="gray80") + # L5
labs(title="(P17) Outliers highlighted (IQR)") + theme_style2
plots[[i]] <- p17; print(p17); i <- i + 1
## Warning: Removed 161 rows containing missing values or values outside the scale range
## (`geom_ribbon()`).
6.Outliers Highlighted (IQR Method)
Phát hiện và làm nổi bật các giá trị bất thường (outliers) trong chuỗi thời gian, dựa trên quy tắc IQR.
Khoảng 150+ giá trị được đánh dấu là ngoại lệ.Hầu hết outlier nằm sau năm 2021, cho thấy sự mở rộng hoặc đột biến trong quy mô tài sản/doanh thu.Nhiều giá trị cực đại được dán nhãn cụ thể (annotation), hỗ trợ truy vết thời điểm chính xác.
Các outlier không hẳn là “lỗi” — nhiều khả năng là điểm bùng nổ tài chính hợp pháp, ví dụ: tăng vốn, mua bán sáp nhập, hoặc tái định giá tài sản.Do đó, biểu đồ này giúp phân biệt biến động bất thường do rủi ro và biến động tích cực do tăng trưởng.
df_qoq <- df4 %>% mutate(sign_q = ifelse(QoQ >= 0, "up", "down"))
p18 <- ggplot(df_qoq, aes(Date, QoQ)) +
geom_col(aes(fill = sign_q), show.legend = FALSE) + # L1
geom_smooth(method="loess", se=TRUE, color="black") + # L2
geom_point(data = df_qoq %>% filter(abs(QoQ) > quantile(abs(QoQ), 0.9, na.rm=TRUE)), aes(Date, QoQ), color="red", size=1.5) + # L3
geom_rug(sides="b", alpha=0.2) + # L4
geom_hline(yintercept = 0, linetype="dashed") + # L5
labs(title="(P18) QoQ movements (up/down colored)") + theme_style2
plots[[i]] <- p18; print(p18); i <- i + 1
## `geom_smooth()` using formula = 'y ~ x'
7.QoQ Movements (Up/Down Colored)
Hiển thị mức thay đổi theo quý (Quarter-over-Quarter), phân loại theo dấu tăng/giảm.
Phần lớn cột có màu xanh dương (tăng trưởng dương).Một vài giai đoạn giảm mạnh trùng với năm 2020, phản ánh tác động khủng hoảng hoặc chu kỳ đầu tư thấp.Đường loess cho thấy xu hướng QoQ trung bình tăng dần trở lại sau 2022.
QoQ là chỉ báo sớm của động lực tài chính.Kết quả này xác nhận rằng sau giai đoạn chững lại, doanh nghiệp đã phục hồi và tăng tốc trở lại — một tín hiệu cực kỳ tích cực.
if(nrow(numeric_df) > 10){
p19 <- GGally::ggpairs(numeric_df %>% sample_n(min(200, nrow(numeric_df))),
upper = list(continuous = wrap("cor", size = 3)),
lower = list(continuous = wrap("smooth", alpha = 0.3)),
diag = list(continuous = wrap("densityDiag")))
plots[[i]] <- p19; print(p19); i <- i + 1
}
8.Pairwise Scatter (Extended Variables)
Mở rộng phân tích tương quan sang toàn bộ các biến số định lượng, giúp nhìn tổng thể hơn mối quan hệ giữa các đặc trưng tài chính.
Cấu trúc tương tự P14 nhưng phạm vi rộng hơn → củng cố kết luận trước: Value–LogValue mạnh, YoY–QoQ dương nhẹ.Một vài cặp hiển thị mối quan hệ phi tuyến rõ hơn, có thể do yếu tố mùa vụ hoặc tỷ lệ phần trăm biến động.
P19 giúp tổng hợp các kết quả tương quan, xác nhận tính ổn định trong mối quan hệ giữa quy mô – tăng trưởng – biến động.Đây là nền tảng cho bước tiếp theo: xây dựng mô hình dự báo tài chính (ARIMA, VAR hoặc Machine Learning).
pA <- ggplot(tot_time, aes(Date, TotalValue)) + geom_line(size=0.8) + geom_smooth(se=FALSE) + geom_point(size=0.8) + labs(title="Trend") + theme_style2
pB <- ggplot(df4, aes(Value)) + geom_histogram(bins=25, fill="gray85", color="white") + geom_density(aes(y=..count..), color="black") + labs(title="Distribution") + theme_style2
pC <- ggplot(df4 %>% filter(!is.na(QoQ) & !is.na(YoY)), aes(QoQ, YoY)) + geom_point(alpha=0.4) + geom_smooth(method="lm", se=FALSE) + labs(title="QoQ vs YoY") + theme_style2
pD <- ggplot(df4 %>% sample_n(min(500, nrow(df4))), aes(Quarter, Value)) + geom_boxplot(outlier.shape=NA) + geom_jitter(width=0.15, alpha=0.2) + labs(title="By Quarter") + theme_style2
p20 <- (pA + pB) / (pC + pD) + plot_annotation(title = "(P20) Composite dashboard summary")
plots[[i]] <- p20; print(p20); i <- i + 1
## `geom_smooth()` using method = 'loess' and formula = 'y ~ x'
## `geom_smooth()` using formula = 'y ~ x'
9.Composite Dashboard Summary
Tạo một bảng tổng hợp bốn khía cạnh cốt lõi của dữ liệu tài chính:
Xu hướng thời gian (Trend)
Phân bố giá trị (Distribution)
Mối quan hệ giữa tăng trưởng theo quý và năm (QoQ vs YoY)
Biến động theo quý (By Quarter)
Mục tiêu là cung cấp bức tranh tổng quan chỉ trong một khung hình, giúp người xem nắm được nhanh:Quy mô tổng thể,Phân bố giá trị,Động thái tăng trưởng,Và mức độ ổn định giữa các quý.
Trend (Xu hướng tổng thể):Đường cong tăng dốc rõ rệt từ năm 2021 trở đi.Sử dụng geom_smooth(loess) để làm mượt xu hướng, cho thấy giai đoạn tăng trưởng mạnh, ít dao động ngắn hạn.Đây là tín hiệu tốt, thể hiện khả năng mở rộng ổn định.
Distribution (Phân bố dữ liệu) Biểu đồ histogram cho thấy phân bố lệch phải mạnh (right-skewed): hầu hết các giá trị nhỏ, một số giá trị cực lớn.Đường mật độ phủ lên giúp trực quan hóa sự chênh lệch giữa nhóm nhỏ và nhóm cực đại.
QoQ vs YoY tạo Mối quan hệ tuyến tính dương rõ rệt — các quý có QoQ cao cũng có YoY cao.Đường hồi quy (màu xanh) gần như đi qua tâm cụm dữ liệu → sự đồng pha giữa tăng trưởng ngắn và dài hạn.
By Quarter (Theo Quý) Boxplot kết hợp jitter cho thấy:Trung vị giữa 4 quý khá cân bằng, Một số điểm cực đại xuất hiện rải rác — có thể do các quý cao điểm hoặc tái định giá tài sản.
Ý NGHĨA: Biểu đồ P20 giống như dashboard tổng hợp tài chính cấp chiến lược nơi doanh nghiệp tăng trưởng ổn định, cấu trúc giá trị thiên lệch về nhóm tài sản lớn, và không có biến động chu kỳ mạnh.