1 . MỞ ĐẦU

1.1 . Lý do chọn đề tài

Trong bối cảnh chuyển đổi số và sự phát triển mạnh mẽ của khoa học dữ liệu, việc thu thập, xử lý và phân tích dữ liệu đang trở thành xu hướng tất yếu trong nhiều lĩnh vực. Thị trường bất động sản là một trong những lĩnh vực có lượng dữ liệu lớn, phong phú và mang tính biến động cao, phản ánh rõ nét tình hình kinh tế – xã hội của từng khu vực. Việc phân tích dữ liệu bất động sản giúp cung cấp cái nhìn khoa học và toàn diện hơn về xu hướng giá nhà, diện tích, vị trí và các yếu tố ảnh hưởng đến thị trường.

Ngôn ngữ R và môi trường RStudio được xem là công cụ hiệu quả trong việc xử lý, phân tích và trực quan hóa dữ liệu nhờ vào khả năng tính toán mạnh mẽ, kho thư viện phong phú và giao diện thân thiện. Thông qua RStudio, người học có thể thực hiện các thao tác phân tích dữ liệu mô tả, trích lọc, phân tổ và thể hiện dữ liệu trực quan bằng biểu đồ một cách khoa học.

Từ những lý do đó, tác giả lựa chọn đề tài “Phân tích dữ liệu thị trường bất động sản Hoa Kỳ” với mục tiêu vận dụng R và RStudio để khai thác và khám phá bộ dữ liệu USA Real Estate Dataset. Thông qua quá trình thực hiện, người học không chỉ củng cố kiến thức và kỹ năng phân tích dữ liệu trong R mà còn hiểu rõ hơn về đặc điểm và xu hướng của thị trường bất động sản Hoa Kỳ trong thực tế.

1.2 . Mục tiêu nghiên cứu

Mục tiêu tổng quát: Phân tích, mô tả và trực quan hóa dữ liệu về thị trường bất động sản Hoa Kỳ nhằm khám phá đặc điểm, xu hướng và sự phân bố của giá nhà, diện tích, vị trí địa lý và trạng thái bất động sản, thông qua việc ứng dụng ngôn ngữ R trong môi trường RStudio trên bộ dữ liệu USA Real Estate Dataset.

Mục tiêu chi tiết:

Tổng hợp và mô tả các đặc điểm cơ bản của thị trường bất động sản Hoa Kỳ, bao gồm giá nhà, diện tích đất, diện tích sử dụng, số phòng và trạng thái giao dịch.

Phân tổ dữ liệu theo bang, thành phố và mã vùng để nhận diện sự khác biệt về giá trị trung bình, quy mô và phân bố giữa các khu vực.

Trực quan hóa dữ liệu bằng biểu đồ và đồ thị để minh họa xu hướng, mối quan hệ và đặc điểm nổi bật của các biến trong bộ dữ liệu.

Rút ra các nhận xét về đặc điểm và xu hướng chung của thị trường bất động sản Hoa Kỳ, đồng thời đưa ra một số khuyến nghị định hướng cho các nghiên cứu mở rộng trong tương lai.

1.3 . Đối tượng và phạm vi nghiên cứu

Đối tượng nghiên cứu: Đề tài tập trung nghiên cứu dữ liệu về thị trường bất động sản tại Hoa Kỳ, được thu thập và tổng hợp trong bộ dữ liệu USA Real Estate Dataset. Bộ dữ liệu bao gồm các thông tin cơ bản của bất động sản như giá bán, diện tích đất, diện tích sử dụng, số lượng phòng ngủ, số phòng tắm, vị trí địa lý (bang, thành phố, mã vùng) và trạng thái giao dịch (đang bán hoặc đã bán).

Phạm vi nghiên cứu:

Về không gian: Dữ liệu bao phủ các bang, thành phố và khu vực trên toàn lãnh thổ Hoa Kỳ, phản ánh đặc điểm và xu hướng của thị trường bất động sản tại từng vùng.

Về thời gian: Nghiên cứu sử dụng dữ liệu được cập nhật trong giai đoạn gần đây, phản ánh tình hình thị trường tại thời điểm thu thập.

Về nội dung: Đề tài chỉ tập trung vào phân tích mô tả và trực quan hóa dữ liệu nhằm nhận diện đặc điểm và xu hướng của thị trường, từ đó đưa ra các kết luận và giải pháp, không thực hiện các mô hình dự đoán hay hồi quy thống kê.

1.4 . Phương pháp nghiên cứu

Đề tài sử dụng phương pháp phân tích mô tả và trực quan hóa dữ liệu, được thực hiện bằng ngôn ngữ R trong môi trường RStudio. Dữ liệu được nhập từ bộ USA Real Estate Dataset, sau đó được kiểm tra, xử lý và làm sạch để đảm bảo tính chính xác trước khi phân tích. Quá trình nghiên cứu bao gồm việc tính toán các thống kê mô tả, như giá trị trung bình, trung vị, độ lệch chuẩn và phân bố của các biến, nhằm hiểu rõ đặc điểm tổng quát của dữ liệu. Tiếp theo, dữ liệu được phân tổ theo các tiêu chí như bang, thành phố hoặc trạng thái bất động sản, giúp nhận diện sự khác biệt giữa các khu vực. Kết quả được trực quan hóa bằng các biểu đồ và đồ thị thông qua các hàm và gói lệnh trong R, giúp thể hiện xu hướng, mối quan hệ và đặc điểm nổi bật của thị trường bất động sản Hoa Kỳ một cách rõ ràng và trực quan.

1.5 . Đóng góp của nghiên cứu

Đề tài “Phân tích dữ liệu thị trường bất động sản Hoa Kỳ” mang lại những đóng góp thiết thực cả về mặt học tập và ứng dụng thực tiễn. Trước hết, nghiên cứu giúp người học vận dụng ngôn ngữ R trong môi trường RStudio để thực hành các bước cơ bản trong quy trình phân tích dữ liệu: nhập dữ liệu, làm sạch, thống kê mô tả, phân tổ và trực quan hóa. Thông qua quá trình này, người học rèn luyện được kỹ năng thao tác và tư duy xử lý dữ liệu – nền tảng quan trọng trong học tập và nghiên cứu khoa học dữ liệu hiện nay. Bên cạnh đó, đề tài còn giúp minh họa rõ quy trình phân tích dữ liệu thực tế bằng các hàm và gói lệnh trong R, từ đó tạo cầu nối giữa lý thuyết thống kê và ứng dụng phần mềm. Việc trực quan hóa dữ liệu bằng biểu đồ và đồ thị giúp kết quả trở nên dễ hiểu, trực quan và có tính thuyết phục hơn, đồng thời hỗ trợ người học nhận diện nhanh các đặc điểm và xu hướng nổi bật của thị trường bất động sản Hoa Kỳ. Ngoài giá trị học tập, nghiên cứu cũng mang ý nghĩa tham khảo cho những người mới bắt đầu học R, đặc biệt là trong lĩnh vực phân tích dữ liệu kinh tế và bất động sản. Kết quả của đề tài có thể là tiền đề cho các hướng mở rộng trong tương lai như xây dựng mô hình dự báo giá nhà, phân tích nhân tố ảnh hưởng hoặc đánh giá xu hướng thị trường trên phạm vi rộng hơn.

1.6 . Kết cấu của đề tài

Chương 1: Mở đầu – Trình bày lý do chọn đề tài, mục tiêu, đối tượng, phạm vi, phương pháp, đóng góp và kết cấu của nghiên cứu.

Chương 2: Giới thiệu bộ dữ liệu – Mô tả nguồn gốc, cấu trúc, ý nghĩa và các biến trong bộ dữ liệu USA Real Estate Dataset.

Chương 3: Phân tích và trực quan hóa dữ liệu – Thực hiện các bước đọc, xử lý, thống kê mô tả, phân tổ và biểu diễn kết quả thông qua các biểu đồ, đồ thị trong RStudio.

Chương 4: Kết luận và khuyến nghị – Tóm tắt các kết quả đạt được, nêu những hạn chế của đề tài và đề xuất hướng phát triển cho các nghiên cứu sau.

2 . Tổng quan về bộ dữ liệu

2.1 . Giới thiệu về bộ dữ liệu

Bộ dữ liệu “USA Real Estate Dataset” được thu thập tại trang web Kaggle . Đây là một bộ dữ liệu công khai được xây dựng nhằm phục vụ mục đích học tập và thực hành phân tích dữ liệu trong lĩnh vực bất động sản. Bộ dữ liệu mô tả thông tin chi tiết của hơn 2,2 triệu bất động sản trên khắp các bang, thành phố và mã vùng của Hoa Kỳ. Mỗi dòng dữ liệu tương ứng với một bất động sản cụ thể, bao gồm các thông tin như giá bán, diện tích đất, diện tích nhà, số phòng ngủ, số phòng tắm, vị trí địa lý, mã vùng bưu điện, trạng thái giao dịch và ngày bán gần nhất. Dữ liệu được tổ chức dưới dạng dữ liệu định lượng và định tính, phản ánh đặc điểm và xu hướng chung của thị trường nhà ở Hoa Kỳ.

2.2 . Thông tin chung về bộ dữ liệu

Ta tiến hành đọc tệp CSV và gán dữ liệu vào một đối tượng có tên là tieuluan để phục vụ cho quá trình phân tích.

library(ggplot2)
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(DT)
library(readxl)
#Đọc dữ liệu
tieuluan <- read.csv("~/Downloads/TLHK3:2025/Ngôn ngữ lập trình R/realtor-data.zip.csv")
#Xem 6 dòng đầu tiên
head(tieuluan) 
#Xem 6 dòng cuối cùng 
tail(tieuluan)

Ta sử dụng sử dụng một số lệnh bên dưới để xem thông tin chung của tieuluan.

# 4. Kiểm tra kích thước bộ dữ liệu
dim(tieuluan)
## [1] 2226382      12

Nhận xét: Sử dụng lệnh dim() kết quả trả về cho thấy bộ dữ liệu tieuluan có 2.226.382 hàng (quan sát) và 12 cột (biến), phản ánh quy mô và số lượng biến của tập dữ liệu.

#Xem tên các biến 
names(tieuluan)
##  [1] "brokered_by"    "status"         "price"          "bed"           
##  [5] "bath"           "acre_lot"       "street"         "city"          
##  [9] "state"          "zip_code"       "house_size"     "prev_sold_date"

Nhận xét: Sử dụng lệnh names() kết quả trả về tên các biến trong bộ dữ liệu. Trong bộ dữ liệu tieuluan,có 12 biến bao gồm: “brokered_by”, “status”, “price”, “bed”, “bath”, “acre_lot”, “street”, “city”, “state”, “zip_code”, “house_size”, “prev_sold_date”.

Dữ liệu bao gồm các biến sau, được tổng hợp chi tiết trong bảng dưới đây nhằm phục vụ cho quá trình phân tích:

library(kableExtra)
## 
## Attaching package: 'kableExtra'
## The following object is masked from 'package:dplyr':
## 
##     group_rows
# Tạo bảng dữ liệu mô tả các biến bất động sản
des <- data.frame(
  STT = 1:12,
  Tên_biến = c("brokered_by", "status", "price", "bed", "bath",
               "acre_lot", "street", "city", "state", "zip_code",
               "house_size", "prev_sold_date"),
  Mô_tả = c(
    "Tên công ty hoặc đại lý môi giới bất động sản (đã mã hóa)",
    "Trạng thái bất động sản (đang bán hoặc sẵn sàng xây dựng)",
    "Giá của bất động sản (hiện tại hoặc gần nhất)",
    "Số lượng phòng ngủ của bất động sản",
    "Số lượng phòng tắm của bất động sản",
    "Diện tích đất (tính bằng mẫu Anh - acre)",
    "Tên đường (đã mã hóa để bảo mật thông tin)",
    "Tên thành phố nơi bất động sản tọa lạc",
    "Tên bang nơi bất động sản thuộc về",
    "Mã vùng bưu điện (ZIP code) của khu vực",
    "Diện tích sử dụng hoặc diện tích sàn của ngôi nhà (tính bằng feet vuông)",
    "Ngày bán gần nhất trước đó (nếu có)"
  )
)

# Hiển thị bảng đẹp
des %>%
  kable("html", align = "c", 
        col.names = c("**STT**", "**Tên biến**", "**Mô tả**"),
        caption = "<div style='text-align: center;'><b>Bảng 1. Danh sách các biến trong dữ liệu bất động sản</b></div>") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"),
                full_width = F, position = "center", font_size = 14) %>%
  row_spec(0, bold = TRUE, background = "#6495ED") %>%
  column_spec(1:3, border_right = TRUE)
Bảng 1. Danh sách các biến trong dữ liệu bất động sản
STT Tên biến Mô tả
1 brokered_by Tên công ty hoặc đại lý môi giới bất động sản (đã mã hóa)
2 status Trạng thái bất động sản (đang bán hoặc sẵn sàng xây dựng)
3 price Giá của bất động sản (hiện tại hoặc gần nhất)
4 bed Số lượng phòng ngủ của bất động sản
5 bath Số lượng phòng tắm của bất động sản
6 acre_lot Diện tích đất (tính bằng mẫu Anh - acre)
7 street Tên đường (đã mã hóa để bảo mật thông tin)
8 city Tên thành phố nơi bất động sản tọa lạc
9 state Tên bang nơi bất động sản thuộc về
10 zip_code Mã vùng bưu điện (ZIP code) của khu vực
11 house_size Diện tích sử dụng hoặc diện tích sàn của ngôi nhà (tính bằng feet vuông)
12 prev_sold_date Ngày bán gần nhất trước đó (nếu có)
#Xem cấu trúc tổng quát của dữ liệu
str(tieuluan)
## 'data.frame':    2226382 obs. of  12 variables:
##  $ brokered_by   : num  103378 52707 103379 31239 34632 ...
##  $ status        : chr  "for_sale" "for_sale" "for_sale" "for_sale" ...
##  $ price         : num  105000 80000 67000 145000 65000 179000 50000 71600 100000 300000 ...
##  $ bed           : int  3 4 2 4 6 4 3 3 2 5 ...
##  $ bath          : int  2 2 1 2 2 3 1 2 1 3 ...
##  $ acre_lot      : num  0.12 0.08 0.15 0.1 0.05 0.46 0.2 0.08 0.09 7.46 ...
##  $ street        : num  1962661 1902874 1404990 1947675 331151 ...
##  $ city          : chr  "Adjuntas" "Adjuntas" "Juana Diaz" "Ponce" ...
##  $ state         : chr  "Puerto Rico" "Puerto Rico" "Puerto Rico" "Puerto Rico" ...
##  $ zip_code      : int  601 601 795 731 680 612 639 731 730 670 ...
##  $ house_size    : num  920 1527 748 1800 NA ...
##  $ prev_sold_date: chr  "" "" "" "" ...

Nhận xét: Sử dụng lệnh str() kết quả trả về cấu trúc tổng quát của bộ dữ liệu, bao gồm số quan sát, số biến, tên biến và kiểu dữ liệu của từng biến. Trong bộ dữ liệu tieuluan có 100.000 quan sát và 10 biến với các kiểu dữ liệu như int (số nguyên), num (số thực) và chr (chuỗi ký tự).

#Xem kiểu dữ liệu theo từng cột 
sapply(tieuluan, class)
##    brokered_by         status          price            bed           bath 
##      "numeric"    "character"      "numeric"      "integer"      "integer" 
##       acre_lot         street           city          state       zip_code 
##      "numeric"      "numeric"    "character"    "character"      "integer" 
##     house_size prev_sold_date 
##      "numeric"    "character"

Nhận xét: Sử dụng lệnh sapply() với class kết quả trả về kiểu dữ liệu của từng biến trong bộ dữ liệu. Trong tieuluan, các biến có kiểu integer (CustomerID, Quantity), numeric (Price, DiscountApplied…, TotalAmount) và character (ProductID, TransactionDate, PaymentMethod, StoreLocation, ProductCategory).

#Xem thống kê mô tả cơ bản 
summary(tieuluan)
##   brokered_by        status              price                bed        
##  Min.   :     0   Length:2226382     Min.   :0.000e+00   Min.   :  1.00  
##  1st Qu.: 23861   Class :character   1st Qu.:1.650e+05   1st Qu.:  3.00  
##  Median : 52884   Mode  :character   Median :3.250e+05   Median :  3.00  
##  Mean   : 52940                      Mean   :5.242e+05   Mean   :  3.28  
##  3rd Qu.: 79183                      3rd Qu.:5.500e+05   3rd Qu.:  4.00  
##  Max.   :110142                      Max.   :2.147e+09   Max.   :473.00  
##  NA's   :4533                        NA's   :1541        NA's   :481317  
##       bath           acre_lot             street            city          
##  Min.   :  1.0    Min.   :     0.00   Min.   :      0   Length:2226382    
##  1st Qu.:  2.0    1st Qu.:     0.15   1st Qu.: 506313   Class :character  
##  Median :  2.0    Median :     0.26   Median :1012766   Mode  :character  
##  Mean   :  2.5    Mean   :    15.22   Mean   :1012325                     
##  3rd Qu.:  3.0    3rd Qu.:     0.98   3rd Qu.:1521173                     
##  Max.   :830.0    Max.   :100000.00   Max.   :2001357                     
##  NA's   :511771   NA's   :325589      NA's   :10866                       
##     state              zip_code       house_size        prev_sold_date    
##  Length:2226382     Min.   :    0   Min.   :4.000e+00   Length:2226382    
##  Class :character   1st Qu.:29617   1st Qu.:1.300e+03   Class :character  
##  Mode  :character   Median :48382   Median :1.760e+03   Mode  :character  
##                     Mean   :52187   Mean   :2.714e+03                     
##                     3rd Qu.:78070   3rd Qu.:2.413e+03                     
##                     Max.   :99999   Max.   :1.040e+09                     
##                     NA's   :299     NA's   :568484

Nhận xét: Sử dụng lệnh summary() kết quả trả về thống kê mô tả cơ bản của từng biến trong bộ dữ liệu, bao gồm giá trị nhỏ nhất (Min), giá trị lớn nhất (Max), trung vị (Median), trung bình (Mean) và các phần trăm phân vị (1st Qu., 3rd Qu.) cho biến số, đồng thời hiển thị số lượng và phân bố của các giá trị cho biến phân loại. Ví dụ, trong tieuluan, biến Quantity dao động từ 1–9 với trung bình 5.009, Price từ 10–100 với trung bình 55.07, và các phương thức thanh toán được phân bố tương đối đều với khoảng 25.000 quan sát cho mỗi loại.

3 . Làm sạch dữ liệu

3.1 . Xử lý giá trị thiếu (Missing Values – NA)

# Kiểm tra toàn bộ giá trị bị thiếu trong dữ liệu (hiển thị 6 dòng đầu tiên)
head(is.na(tieuluan))
##      brokered_by status price   bed  bath acre_lot street  city state zip_code
## [1,]       FALSE  FALSE FALSE FALSE FALSE    FALSE  FALSE FALSE FALSE    FALSE
## [2,]       FALSE  FALSE FALSE FALSE FALSE    FALSE  FALSE FALSE FALSE    FALSE
## [3,]       FALSE  FALSE FALSE FALSE FALSE    FALSE  FALSE FALSE FALSE    FALSE
## [4,]       FALSE  FALSE FALSE FALSE FALSE    FALSE  FALSE FALSE FALSE    FALSE
## [5,]       FALSE  FALSE FALSE FALSE FALSE    FALSE  FALSE FALSE FALSE    FALSE
## [6,]       FALSE  FALSE FALSE FALSE FALSE    FALSE  FALSE FALSE FALSE    FALSE
##      house_size prev_sold_date
## [1,]      FALSE          FALSE
## [2,]      FALSE          FALSE
## [3,]      FALSE          FALSE
## [4,]      FALSE          FALSE
## [5,]       TRUE          FALSE
## [6,]      FALSE          FALSE

Nhận xét: Sử dụng lệnh is.na()) kết quả trả về kiểm tra giá trị bị thiếu (NA) trong bộ dữ liệu. Trong tieuluan, kết quả cho thấy không có (FALSE) giá trị thiếu ở bất kỳ biến nào, tất cả các quan sát đều đầy đủ.

# Kiểm tra xem dữ liệu có giá trị NA nào không
any(is.na(tieuluan))
## [1] TRUE

Nhận xét: Sử dụng lệnh any(is.na()) kết quả trả về kiểm tra xem có bất kỳ giá trị thiếu nào trong toàn bộ bộ dữ liệu hay không. Trong tieuluan, kết quả là FALSE, cho thấy toàn bộ dữ liệu đều đầy đủ, không có giá trị NA.

# Đếm tổng số giá trị bị thiếu trong toàn bộ dataset
sum(is.na(tieuluan))
## [1] 1904400

Nhận xét: Sử dụng lệnh sum(is.na()) kết quả trả về tổng số giá trị bị thiếu trong toàn bộ bộ dữ liệu. Trong tieuluan, kết quả là 0, nghĩa là không có bất kỳ giá trị NA nào trong dataset.

# Đếm số lượng giá trị NA trong từng cột
colSums(is.na(tieuluan))
##    brokered_by         status          price            bed           bath 
##           4533              0           1541         481317         511771 
##       acre_lot         street           city          state       zip_code 
##         325589          10866              0              0            299 
##     house_size prev_sold_date 
##         568484              0

Nhận xét: Sử dụng lệnh colSums(is.na()) kết quả trả về số lượng giá trị NA trong từng biến của bộ dữ liệu. Trong tieuluan, tất cả các biến đều có giá trị NA bằng 0, cho thấy không có giá trị thiếu ở bất kỳ cột nào.

# Liệt kê tên các cột có giá trị bị thiếu
names(tieuluan)[colSums(is.na(tieuluan)) > 0]
## [1] "brokered_by" "price"       "bed"         "bath"        "acre_lot"   
## [6] "street"      "zip_code"    "house_size"

Nhận xét: Sử dụng lệnh names()[colSums(is.na()) > 0] kết quả trả về tên các biến có giá trị bị thiếu. Trong tieuluan, kết quả là character(0), nghĩa là không có cột nào bị thiếu giá trị.

Nhận xét: Sử dụng lệnh which(is.na()) kết quả trả về vị trí (hàng, cột) của các giá trị bị thiếu trong bộ dữ liệu. Trong tieuluan, kết quả là integer(0), nghĩa là không có giá trị NA nào trong toàn bộ dataset.

# Loại bỏ tất cả giá trị NA trong dữ liệu tieuluan
tieuluan <- na.omit(tieuluan)

# Kiểm tra tổng số giá trị NA còn lại (sau khi loại bỏ)
sum(is.na(tieuluan))
## [1] 0

3.2 . Xác định và loại bỏ dữ liệu trùng lặp

# Xem 6 giá trị đầu tiên của kết quả duplicated()
head(duplicated(tieuluan))
## [1] FALSE FALSE FALSE FALSE FALSE FALSE

Nhận xét: Sử dụng lệnh duplicated() kết quả trả về kiểm tra các quan sát trùng lặp trong bộ dữ liệu. Trong tieuluan, 6 giá trị đầu tiên đều là FALSE, nghĩa là các quan sát này không bị trùng lặp.

# Đếm tổng số dòng bị trùng
sum(duplicated(tieuluan))
## [1] 0

Nhận xét: Sử dụng lệnh sum(duplicated()) kết quả trả về tổng số quan sát trùng lặp trong bộ dữ liệu. Trong tieuluan, kết quả là 0, nghĩa là không có dòng nào bị trùng lặp.

3.3 . Sửa lỗi dữ liệu

# Xem thống kê biến Price để phát hiện giá trị bất thường
summary(tieuluan$Price)
## Length  Class   Mode 
##      0   NULL   NULL

Nhận xét: Sử dụng lệnh summary() cho biến Price kết quả trả về thống kê mô tả cơ bản, giúp phát hiện giá trị bất thường. Biến Price dao động từ 10 đến 100, với trung bình 55.07 và trung vị 55.12, cho thấy không có giá trị ngoại lệ quá khác biệt trong dữ liệu.

#Xem thống kê biến price để phát hiện giá trị bất thường
summary(tieuluan$price)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
##         1    239000    379000    572264    600000 515000000
# 2. Xem thống kê biến house_size
summary(tieuluan$house_size)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##     100    1360    1812    2121    2478 1560780
# 3. Xem thống kê biến bed (số phòng ngủ)
summary(tieuluan$bed)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   1.000   3.000   3.000   3.386   4.000 444.000
# 4. Xem thống kê biến bath (số phòng tắm)
summary(tieuluan$bath)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   1.000   2.000   2.000   2.542   3.000 222.000
# 5. Xem thống kê biến acre_lot (diện tích đất)
summary(tieuluan$acre_lot)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
##      0.00      0.14      0.21     12.76      0.46 100000.00

Nhận xét: Sử dụng lệnh subset() để tìm các giá trị Price nhỏ hơn 0 (sai logic). Kết quả trả về 0 hàng, nghĩa là không có giá trị bất thường nào về giá âm trong bộ dữ liệu tieuluan.

# 6. Tìm các giao dịch có giá âm hoặc bằng 0
subset(tieuluan, price <= 0)
# 7. Tìm các giao dịch có house_size <= 0
subset(tieuluan, house_size <= 0)
# 8. Tìm các giao dịch có bed <= 0
subset(tieuluan, bed <= 0)
# 9. Tìm các giao dịch có bath <= 0
subset(tieuluan, bath <= 0)
# 10. Kiểm tra giá trị status để phát hiện lỗi chính tả
unique(tieuluan$status)
## [1] "for_sale" "sold"

Nhận xét: Sử dụng lệnh subset() để tìm các giao dịch có TotalAmount âm. Kết quả trả về 0 hàng, nghĩa là không có giao dịch nào có tổng tiền âm, dữ liệu hợp lý về mặt logic.

Nhận xét: Sử dụng lệnh unique() để liệt kê các giá trị khác nhau của phương thức thanh toán. Kết quả cho thấy chỉ có các giá trị “Cash”, “Credit Card”, “Debit Card” và “PayPal”, nghĩa là không có lỗi chính tả hay giá trị lạ trong cột PaymentMethod.

3.4 . Chuyển đổi kiểu dữ liệu

# Chuyển biến status từ character sang factor
tieuluan$status <- as.factor(tieuluan$status)
# Nếu định dạng ngày là "YYYY-MM-DD"
tieuluan$prev_sold_date <- as.Date(tieuluan$prev_sold_date, format = "%Y-%m-%d")
# Kiểm tra kiểu dữ liệu của toàn bộ biến
sapply(tieuluan, class)
##    brokered_by         status          price            bed           bath 
##      "numeric"       "factor"      "numeric"      "integer"      "integer" 
##       acre_lot         street           city          state       zip_code 
##      "numeric"      "numeric"    "character"    "character"      "integer" 
##     house_size prev_sold_date 
##      "numeric"         "Date"

4 . TRUY XUẤT DỮ LIỆU

4.1 . Truy cập dữ liệu

Tác giả thực hiện các lệnh để truy cập dữ liệu dưới đây:

#-----------------------------
# 1. Truy cập cột
#-----------------------------

# Lấy cột price, xem 10 giá trị đầu
head(tieuluan$price, 10)
##  [1] 105000  80000  67000 145000 179000  50000  71600 100000 300000  89000
# Lấy cột bed, xem 12 giá trị cuối
tail(tieuluan$bed, 12)
##  [1] 4 3 3 4 3 4 4 4 3 6 2 5
# Lấy cột status, xem 15 giá trị đầu
head(tieuluan$status, 15)
##  [1] for_sale for_sale for_sale for_sale for_sale for_sale for_sale for_sale
##  [9] for_sale for_sale for_sale for_sale for_sale for_sale for_sale
## Levels: for_sale sold
# Lấy cột house_size bằng [, "column_name"]
head(tieuluan[, "house_size"], 10)   # Xem 10 giá trị đầu cột diện tích nhà
##  [1]  920 1527  748 1800 2520 2040 1050 1092 5403 1106
# Lấy cột acre_lot bằng [, column index]
head(tieuluan[, 6], 10)       # Xem 10 giá trị đầu cột thứ 6 (acre_lot)
##  [1]  0.12  0.08  0.15  0.10  0.46  0.20  0.08  0.09  7.46 13.39
# Lấy nhiều cột cùng lúc bằng tên
head(tieuluan[, c("price", "bed", "house_size")], 10)  
# Xem 10 giá trị đầu của các cột price, bed, house_size


# Lấy nhiều cột cùng lúc theo index
head(tieuluan[, c(3,4,11)], 10)  
# Cột 3: price, 4: bed, 11: house_size, 10 giá trị đầu
#-----------------------------
# 2. Truy cập dòng
#-----------------------------

# 1. Lấy dòng đầu tiên
tieuluan[1, ]
# 2. Lấy 10 dòng đầu tiên
tieuluan[1:10, ]
# 3. Lấy dòng cuối cùng
tieuluan[nrow(tieuluan), ]
# 4. Lấy 5 dòng cuối cùng bằng tail()
tail(tieuluan, 5)
# 5. Lấy dòng thứ 100
tieuluan[100, ]
# 6. Lấy các dòng có giá > 300000
tieuluan[tieuluan$price > 300000, ]
# 7. Lấy các dòng có trạng thái for_sale
tieuluan[tieuluan$status == "for_sale", ]
# 8. Lấy các dòng có >=4 phòng ngủ và giá < 200000
tieuluan[tieuluan$bed >= 4 & tieuluan$price < 200000, ]

4.2 . Lọc dữ liệu theo điều kiện

Tác giả thực hiện các lệnh lọc dữ liệu cơ bản theo điều kiện:

#-----------------------------
# 3. Truy cập theo điều kiện
#-----------------------------

# 1. Lấy tất cả nhà đang for_sale
tieuluan[tieuluan$status == "for_sale", ]
# 2. Lấy tất cả nhà đã sold
tieuluan[tieuluan$status == "sold", ]
# 3. Lấy các nhà giá > 300000
tieuluan[tieuluan$price > 300000, ]
# 4. Lấy các nhà giá từ 100000 đến 200000
tieuluan[tieuluan$price >= 100000 & tieuluan$price <= 200000, ]
# 5. Lấy các nhà có >=4 phòng ngủ
tieuluan[tieuluan$bed >= 4, ]
# 6. Lấy các nhà >=4 phòng ngủ và giá < 200000
tieuluan[tieuluan$bed >= 4 & tieuluan$price < 200000, ]
# 7. Lấy các nhà có house_size > 1500
tieuluan[tieuluan$house_size > 1500, ]
# 8. Lấy các nhà ở thành phố "Ponce"
tieuluan[tieuluan$city == "Ponce", ]
#-----------------------------
# 4. Truy cập một giá trị cụ thể
#-----------------------------

# 1. Giá của dòng đầu tiên
tieuluan[1, "price"]
## [1] 105000
# 2. Thành phố của dòng thứ 5
tieuluan[5, "city"]
## [1] "San Sebastian"
# 3. Số phòng ngủ của dòng thứ 10
tieuluan[10, "bed"]
## [1] 3
# 4. Trạng thái của dòng cuối cùng
tieuluan[nrow(tieuluan), "status"]
## [1] sold
## Levels: for_sale sold
# 5. Dùng $ để lấy giá của dòng thứ 3
tieuluan$price[3]
## [1] 67000
# 6. Dùng which.max() để tìm dòng có giá cao nhất
tieuluan[which.max(tieuluan$price), ]
# 7. Dùng which.min() để tìm dòng có giá thấp nhất
tieuluan[which.min(tieuluan$price), ]
# 8. Dùng which() để tìm tất cả nhà có >=4 phòng ngủ
tieuluan[which(tieuluan$bed >= 4), ]
# 9. Dùng which() để tìm tất cả nhà có trạng thái "sold"
tieuluan[which(tieuluan$status == "sold"), ]
# 10. Kết hợp which.max() và select cột price để chỉ lấy giá cao nhất
tieuluan$price[which.max(tieuluan$price)]
## [1] 5.15e+08

4.3 . Thêm và sửa dữ liệu

Tác giả thực hiện các lệnh thêm và sửa dữ liệu dưới đây:

#-----------------------------
# 5. Thêm và sửa dữ liệu (chỉnh sửa)
#-----------------------------

# 1. Chỉnh lại trạng thái ở dòng đầu tiên
tieuluan$status[1] <- "FOR_SALE"
## Warning in `[<-.factor`(`*tmp*`, 1, value = structure(c(NA, 1L, 1L, 1L, :
## invalid factor level, NA generated
# 2. Cập nhật lại tên thành phố cho dòng thứ 5
tieuluan$city[5] <- "San Juan"

# 3. Thêm cột đánh số thứ tự giao dịch
tieuluan$TransactionNo <- 1:nrow(tieuluan)

# 4. Xóa dòng thứ 3 khỏi dữ liệu
tieuluan <- tieuluan[-3, ]

# 6. Sửa giá dòng thứ 2 tăng 10%
tieuluan$price[2] <- tieuluan$price[2] * 1.10

# 7. Cập nhật price dòng cuối cùng tăng 5%
tieuluan$price[nrow(tieuluan)] <- tieuluan$price[nrow(tieuluan)] * 1.05

# 8. Sửa giá dòng thứ 4 giảm 7%
tieuluan$price[4] <- tieuluan$price[4] * 0.93

# 9. Sửa trạng thái dòng thứ 6 thành "SOLD"
tieuluan$status[6] <- "SOLD"
## Warning in `[<-.factor`(`*tmp*`, 6, value = structure(c(NA, 1L, 1L, 1L, :
## invalid factor level, NA generated
# 10. Xóa tất cả dòng có price <= 0
tieuluan <- tieuluan[tieuluan$price > 0, ]

# 5. Kiểm tra lại kích thước dữ liệu sau khi xóa
dim(tieuluan)
## [1] 1354379      13

5 . Phân tổ các biến

5.1 . Phân tổ biến Price theo mức giá

6 . VẼ ĐỒ THỊ CƠ BẢN VỚI R

# 2️⃣ Biểu đồ đường (Line Plot): Giá theo thứ tự quan sát
plot(tieuluan$price, type = "l", col = "blue",
     main = "Xu hướng giá theo thứ tự quan sát đầu tiên",
     xlab = "Thứ tự quan sát", ylab = "Giá (USD)")

# 3️⃣ Biểu đồ cột (Bar Plot): Số lượng nhà theo trạng thái bán

# Tạo bảng tần suất của trạng thái bất động sản
status_count <- table(tieuluan$status)

# Vẽ biểu đồ cột và lưu vị trí các cột
bp <- barplot(status_count,
              main = "Số lượng nhà theo trạng thái",
              xlab = "Trạng thái",
              ylab = "Số lượng",
              col = c("blue", "#FF4FF0"),
              ylim = c(0, max(status_count) * 1.3))  # tăng giới hạn trục Y để không bị che

# Thêm nhãn số lượng trên đỉnh cột (cao hơn một chút)
text(x = bp,
     y = status_count + max(status_count)*0.05,   # đẩy số lên trên 5% chiều cao max
     labels = status_count,
     pos = 3,              # đặt phía trên cột
     cex = 0.9,            # cỡ chữ
     font = 2,             # chữ in đậm
     col = "black")        # màu chữ

# 6️⃣ Biểu đồ tròn (Pie Chart): Tỷ lệ nhà theo trạng thái

# Tạo bảng đếm số lượng từng trạng thái (for_sale, sold,...)
status_count <- table(tieuluan$status)

# Tính tỷ lệ phần trăm cho từng nhóm
percent <- round(100 * status_count / sum(status_count), 1)   # Làm tròn 1 chữ số thập phân

# Tạo nhãn hiển thị cả tên và tỷ lệ %
labels <- paste(names(status_count), "-", percent, "%")

# Vẽ biểu đồ tròn với nhãn % rõ ràng
pie(status_count,
    labels = labels,                         # Hiển thị nhãn gồm tên + %
    main = "Tỷ lệ nhà theo trạng thái",      # Tiêu đề biểu đồ
    col = c("blue", "#FF4FF0"),           # Màu sắc từng phần
    border = "white")                        # Viền trắng cho rõ ràng

 #Biểu đồ hộp (Box Plot): Giá theo trạng thái bán
boxplot(price ~ status, data = tieuluan,
        main = "So sánh giá giữa các trạng thái bất động sản",
        xlab = "Trạng thái", ylab = "Giá (USD)",
        col = c("orange", "lightblue"))

boxplot(price ~ status,
        data = tieuluan[tieuluan$price <= 1e7, ],
        main = "So sánh giá giữa các trạng thái (≤ 10 triệu USD)",
        xlab = "Trạng thái",
        ylab = "Giá (USD)",
        col = c("#FF6E00", "green"))

# 8️⃣ Biểu đồ hộp (Boxplot) cho số phòng ngủ
boxplot(tieuluan$bed,
        main = "Phân phối số phòng ngủ",
        ylab = "Số lượng phòng ngủ",
        col = "#9D00FF")

# Biểu đồ hộp (Box Plot): Phân phối số lượng phòng ngủ của các bất động sản có <= 10 phòng
boxplot(tieuluan$bed[tieuluan$bed <= 10],   # Lấy các giá trị bed nhỏ hơn hoặc bằng 10
        main = "Phân phối số phòng ngủ (<= 10)",  # Tiêu đề biểu đồ
        ylab = "Số lượng phòng ngủ",              # Nhãn trục tung
        col = "#9D00FF")                        # Màu của hộp

# 1️⃣ Biểu đồ cột thể hiện tần suất số phòng ngủ
bedroom_freq <- table(tieuluan$bed)

barplot(bedroom_freq,
        main = "Tần suất số phòng ngủ",
        xlab = "Số phòng ngủ", ylab = "Số lượng nhà",
        col = "yellow",
        ylim = c(0, max(bedroom_freq) * 1.2)) # Chừa chỗ hiển thị số

# Hiển thị số lượng trên đầu cột
text(x = seq_along(bedroom_freq),
     y = bedroom_freq,
     labels = bedroom_freq,
     pos = 3, cex = 0.8, col = "black")

# 2️⃣ Biểu đồ cột thể hiện tần suất số phòng tắm
bathroom_freq <- table(tieuluan$bath)

barplot(bathroom_freq,
        main = "Tần suất số phòng tắm",
        xlab = "Số phòng tắm", ylab = "Số lượng nhà",
        col = "lightcoral",
        ylim = c(0, max(bathroom_freq) * 1.2))

# Hiển thị số lượng trên đầu cột
text(x = seq_along(bathroom_freq),
     y = bathroom_freq,
     labels = bathroom_freq,
     pos = 3, cex = 0.8, col = "black")

# 9️⃣ Biểu đồ cột (Barplot) — 10 thành phố có nhiều nhà nhất
# 🟢 B1: Lấy top 10 thành phố có nhiều nhà nhất
top_cities <- sort(table(tieuluan$city), decreasing = TRUE)[1:10]

# 🟢 B2: Vẽ biểu đồ cột
bp <- barplot(top_cities,
              las = 2,                       # Xoay nhãn trục X cho dễ đọc
              col = "turquoise",
              main = "Top 10 thành phố có nhiều nhà nhất",
              ylab = "Số lượng nhà",
              cex.names = 0.8)

# 🟢 B3: Thêm số lên đỉnh cột
text(x = bp, 
     y = top_cities, 
     labels = top_cities, 
     pos = 3,               # Hiển thị phía trên đầu cột
     cex = 0.8, 
     col = "black",
     font = 2)              # In đậm số

# 🍩 Biểu đồ tròn (Pie Chart) — Top 10 bang có nhiều nhà nhất
library(dplyr)

# 🟢 B1: Lấy top 10 bang có nhiều nhà nhất
top_state <- tieuluan %>%
  count(state, sort = TRUE) %>%
  top_n(10, n)

# 🟢 B2: Vẽ biểu đồ tròn không có đường viền
pie(top_state$n,
    labels = paste(top_state$state, round(100 * top_state$n / sum(top_state$n), 1), "%"),
    main = "Top 10 bang có nhiều nhà nhất",
    col = rainbow(10),
    border = NA)   # Bỏ đường phân chia giữa các mảnh

# 1️⃣ Đếm số lượng nhà theo thành phố
city_count <- sort(table(tieuluan$city), decreasing = FALSE)

# 2️⃣ Lấy 10 thành phố có ít nhà nhất
city_min10 <- head(city_count, 10)

# 3️⃣ Vẽ biểu đồ cột ngang
barplot(city_min10,
        horiz = TRUE,
        main = "Top 10 thành phố có ÍT nhà nhất",
        xlab = "Số lượng nhà",
        col = "#FF66A3",
        las = 1)  # Xoay nhãn trục Y cho dễ đọc

# 4️⃣ Thêm số lượng bên phải mỗi cột
text(x = city_min10,
     y = seq_along(city_min10),
     labels = city_min10,
     pos = 4,   # Hiện bên phải cột
     cex = 0.8)

# 1️⃣ Đếm số lượng nhà theo bang
state_count <- sort(table(tieuluan$state), decreasing = FALSE)

# 2️⃣ Lấy 10 bang có ít nhà nhất
state_min10 <- head(state_count, 10)

# 3️⃣ Vẽ biểu đồ cột ngang
barplot(state_min10,
        horiz = TRUE,
        main = "Top 10 bang có ÍT nhà nhất",
        xlab = "Số lượng nhà",
        col = "#006600",
        las = 1)

# 4️⃣ Thêm số lượng bên phải mỗi cột
text(x = state_min10,
     y = seq_along(state_min10),
     labels = state_min10,
     pos = 4,
     cex = 0.8)

# 🍩 Biểu đồ tròn — Tỷ lệ nhà ở 10 bang có ít nhà nhất
# 🟠 1️⃣ Lấy top 10 bang có ít nhà nhất
state_freq <- sort(table(tieuluan$state))    # Đếm số lượng nhà theo bang
top10_states <- head(state_freq, 10)             # Lấy 10 bang ít nhà nhất

# 🟠 2️⃣ Tính tỷ lệ %
percent_labels <- round(top10_states / sum(top10_states) * 100, 2)
labels <- paste(names(top10_states), "-", percent_labels, "%")

# 🟠 3️⃣ Vẽ biểu đồ tròn (không viền giữa các mảnh)
pie(top10_states,
    labels = labels,
    main = "Tỷ lệ nhà ở 10 bang có ít nhà nhất",
    col = rainbow(length(top10_states)),
    border = NA)  # Loại bỏ đường phân chia

# 🔟 Biểu đồ phân tán (Scatter Plot - log scale, có lọc dữ liệu)
tieuluan_log <- subset(tieuluan, acre_lot > 0 & house_size > 0)

plot(log10(tieuluan_log$acre_lot), log10(tieuluan_log$house_size),
     main = "Mối quan hệ giữa diện tích đất và diện tích nhà (thang log)",
     xlab = "Log10(Acre_lot)", ylab = "Log10(House Size)",
     col = "lightpink", pch = 19, cex = 0.5)

# Thêm đường hồi quy tuyến tính
abline(lm(log10(house_size) ~ log10(acre_lot), data = tieuluan_log), col = "black", lwd = 3)

plot(
log(tieuluan$house_size), log(tieuluan$price),
  main = "Log-Log Plot: Diện tích nhà và Giá bán",
  xlab = "log(Diện tích nhà)",
  ylab = "log(Giá bán)",
  col = "lightgreen", pch = 19
)
abline(lm(log(price) ~ log(house_size), data = tieuluan), col = "red", lwd = 3)

unique(tieuluan$state)
##  [1] "Puerto Rico"          "Virgin Islands"       "Massachusetts"       
##  [4] "Connecticut"          "New Jersey"           "New York"            
##  [7] "New Hampshire"        "Vermont"              "Rhode Island"        
## [10] "Wyoming"              "Maine"                "Pennsylvania"        
## [13] "West Virginia"        "Delaware"             "Ohio"                
## [16] "Maryland"             "Virginia"             "Colorado"            
## [19] "District of Columbia" "North Carolina"       "Kentucky"            
## [22] "South Carolina"       "Tennessee"            "Georgia"             
## [25] "Alabama"              "Florida"              "Mississippi"         
## [28] "Texas"                "Missouri"             "Arkansas"            
## [31] "Louisiana"            "Indiana"              "Illinois"            
## [34] "Michigan"             "Wisconsin"            "Iowa"                
## [37] "Minnesota"            "South Dakota"         "Nebraska"            
## [40] "North Dakota"         "Montana"              "Idaho"               
## [43] "Kansas"               "Oklahoma"             "New Mexico"          
## [46] "Utah"                 "Nevada"               "Washington"          
## [49] "Oregon"               "Arizona"              "California"          
## [52] "Hawaii"               "Guam"                 "Alaska"
summary(tieuluan$price)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
##         1    239000    379000    572265    600000 515000000
range(tieuluan$price, na.rm = TRUE)
## [1] 1.00e+00 5.15e+08