Bài học này Tui hướng dẫn các bạn cách lập trình K-Means để gom cụm khách hàng theo thu nhập, độ tuổi và ngân sách chi tiêu. Chúng ta dùng phương pháp Elbow để tìm ra K Cụm tối ưu nhất để gom, bài học này chúng ta trực quan hóa các cụm bằng 2D và 3D. Đồng thời phân loại chi tiết Khách hàng theo từng cụm để dễ dàng đưa ra các chính sách chăm sóc và tiếp thị phù hợp. Lý thuyết K-Means các bạn đọc trong bài 57
Bạn tải cơ sở dữ liệu MySQL Server “SalesDatabase “trong link:
Trong MySQL Workbench bạn nhớ tạo một Schema tên là “salesdatabase” sau đó dùng chức năng import đã được học để import dữ liệu vào.
Đồng thời trong quá trình lập trình liên quan tới kết nối cơ sở dữ liệu thì nhớ đổi các thông số kết nối theo máy tính của bạn đã cài đặt.
Cấu trúc bảng dữ liệu như hình dưới đây:
Chúng ta sẽ gom cụm khách hàng bằng K-Means theo nhiều chiều khác nhau, và cho nhận xét. Yêu cầu các thuộc tính liên quan tới Customer dùng để gom cụm sẽ được trích suất từ 2 bảng dữ liệu “customer” và “customer_spend_score” theo SQL script như sau:
select distinct customer.CustomerID, Age, Annual_Income, Spending_Score
from customer, customer_spend_score
where customer.CustomerID=customer_spend_score.CustomerID
Kết quả tiền xử lý dữ liệu:
Trong Pycharm, ta tạo một dự án tên “KMeansCustomer“:
(1) File “Connector.py” là mã lệnh để kết nối, truy vấn dữ liệu:
Tiếp theo ta viết mã lệnh cho “CustomerCluster.py” và thử nghiệm các chức năng:
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
from sklearn.cluster import KMeans
import numpy as np
from Connector import Connector
conn=Connector('localhost',3306,'salesdatabase','root','@Obama123')
conn.connect()
sql1="select * from customer"
df1=conn.queryDataset(sql1)
print(df1)
Thực thi mã lệnh trên ta có kết quả:
Tiếp tục bổ sung nối đuôi các mã lệnh tiền xử lý dữ liệu để rút trích thông tin khách hàng, tuổi, thu nhập, điểm tiêu dùng và cuối tập tin “CustomerCluster.py”:
Tiếp tục bổ sung nối đuôi mã lệnh dưới đây vào “CustomerCluster.py” để thực hiển Histogram:
def showHistogram(df,columns):
plt.figure(1, figsize=(7,8))
n = 0
for column in columns:
n += 1
plt.subplot(3, 1, n)
plt.subplots_adjust(hspace=0.5, wspace=0.5)
sns.distplot(df[column], bins=32)
plt.title(f'Histogram of {column}')
plt.show()
showHistogram(df2,df2.columns[1:])
Thực thi mã lệnh trên ta có kết quả:
Nhìn vào biểu đồ Histogram, ta thấy được sự phân phối có tần suất của các tập dữ liệu liên quan tới Age, Annual Income và Spending Score.
Trong thuật toán K-Means thì chúng ta cần phải xác định trước số cụm. Và ta thường phân vân đâu là số lượng cụm cần phân chia tốt nhất đối với một bộ dữ liệu cụ thể, thì phương pháp Elbow là một cách giúp ta lựa chọn được số lượng các cụm phù hợp dựa vào đồ thị trực quan hoá bằng cách nhìn vào sự suy giảm của hàm biến dạng và lựa chọn ra điểm khuỷ tay (elbow point)
Tiếp tục bổ sung nối đuôi mã lệnh vào “CustomerCluster.py” để tính phương pháp Elbow nhằm tìm ra K cụm tối ưu cho K-Means:
def elbowMethod(df,columnsForElbow):
X = df.loc[:, columnsForElbow].values
inertia = []
for n in range(1 , 11):
model = KMeans(n_clusters = n,
init='k-means++',
max_iter=500,
random_state=42)
model.fit(X)
inertia.append(model.inertia_)
plt.figure(1 , figsize = (15 ,6))
plt.plot(np.arange(1 , 11) , inertia , 'o')
plt.plot(np.arange(1 , 11) , inertia , '-' , alpha = 0.5)
plt.xlabel('Number of Clusters') , plt.ylabel('Cluster sum of squared distances')
plt.show()
-init: dùng k-means++
-max_iter: 500 lần
-random_state=42 để dùng cùng 1 phân bố dữ liệu chỗ mỗi lần chạy đảm bảo nhất quán
Ta thử nghiệm tìm K cụm theo phương pháp Elbow với 2 đặc trưng: Age và Spending Score. Ta bổ sung nối đuôi mã lệnh dưới đây vào “CustomerCluster.py”
Thử nghiệm triệu gọi hàm runKMeans(), với columns[Age, Spending Score], cluster=4. Tiếp tục bổ sung mã lệnh nối đuôi vào “CustomerCluster.py” để xem kết quả:
Mảng X được lấy theo các columns (các thuộc tính mà customer ta muốn clustering). Đồng thời tạo cột cluster cho df2, nhằm phục vụ cho truy vấn danh sách Customer chi tiết theo từng cụm
Thực thi mã lệnh ở trên ta có kết quả:
Tiếp tục Viết hàm visualizeKMeans nhận vào 6 đối số, hàm này trực quan hóa kết quả gom cụm. Hàm này viết nối đuôi vào “CustomerCluster.py“:
•X: ma trận đã gom cụm
•y_kmeans: nhãn các phần tử được gom cụm
•cluster: số cụm
•title: tiêu đề
•xlabel: nhãn trục x
•ylabel: nhãn trục y
•colors: mảng màu
def visualizeKMeans(X,y_kmeans,cluster,title,xlabel,ylabel,colors):
plt.figure(figsize=(10, 10))
for i in range(cluster):
plt.scatter(X[y_kmeans == i, 0],
X[y_kmeans == i, 1],
s=100,
c=colors[i],
label='Cluster %i'%(i+1))
plt.title(title)
plt.xlabel(xlabel)
plt.ylabel(ylabel)
plt.legend()
plt.show()
Triệu gọi hàm visualizeKMeans, bằng cách tiếp tục viết nối đuôi mã lệnh vào “CustomerCluster.py“:
visualizeKMeans(X,
y_kmeans,
cluster,
"Clusters of Customers - Age X Spending Score",
"Age",
"Spending Score",
colors)
Kết quả sau khi triệu gọi hàm visualizeKMeans.
Gom cụm customer theo cột Age và Spending Score
Thực nghiệm gom cụm theo 2 cột khác: Cột Annual Income và Spending Score
Tiếp tục bổ sung nối đuôi mã lệnh sau vào “CustomerCluster.py”:
Mảng X được lấy theo các columns (các thuộc tính mà customer ta muốn clustering). Đồng thời tạo cột cluster cho df2, nhằm phục vụ cho truy vấn danh sách Customer chi tiết theo từng cụm
Thực thi mã lệnh trên ta có kết quả:
Triệu gọi hàm visualizeKMeans để trực quan hóa gom cụm với K=5. Tiếp tục bổ sung nối đuôi mã lệnh vào “CustomerCluster.py”:
visualizeKMeans(X,
y_kmeans,
cluster,
"Clusters of Customers - Annual Income X Spending Score",
"Annual Income",
"Spending Score",
colors)
Kết quả sau khi triệu gọi hàm visualizeKMeans. Gom cụm customer theo cột Annual Income và Spending Score với K=5
Tiếp tục thực nghiệm gom cụm theo 3 cột khác: Cột Age, Annual Income và Spending Score
Ta tiếp tục viết nối đuôi mã lệnh vào “CustomerCluster.py”:
Mảng X được lấy theo các columns (các thuộc tính mà customer ta muốn clustering). Đồng thời tạo cột cluster cho df2, nhằm phục vụ cho truy vấn danh sách Customer chi tiết theo từng cụm
Thực thi mã lệnh ta có kết quả:
Vì trường hợp này dạng 3 thuộc tính (cột) nên ta bổ sung thêm 1 hàm trực quan hóa 3D kết quả gom cụm:
•df: tập dữ liệu đã gom cụm.
•columns: dữ liệu các trục. Là dữ liệu có các cột mà ta gom cụm
•hover_data: Khi di chuyển chuột vào node sẽ hiển thị chi tiết.
•cluser: số cụm
Ta tiếp tục bổ sung mã lệnh vào cuối “CustomerCluster.py”:
Ta có thể tương tác trên chart 3D này, di chuyển tới từng cụm, vào từng node để xem dữ liệu.
Có thể tương tác trên các node góc phải màn hình, xuất chart ra tập tin ảnh
(Máy tính cần có cấu hình mạnh để render 3D)
minh họa thêm tương các node và cụm:
Dưới đây là coding đầy đủ của “CustomerCluster.py”:
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
from sklearn.cluster import KMeans
import numpy as np
from Connector import Connector
conn=Connector('localhost',3306,'salesdatabase','root','@Obama123')
conn.connect()
sql1="select * from customer"
df1=conn.queryDataset(sql1)
print(df1)
sql2="select distinct customer.CustomerId, Age, Annual_Income, Spending_Score " \
"from customer, customer_spend_score " \
"where customer.CustomerId=customer_spend_score.CustomerID"
df2=conn.queryDataset(sql2)
df2.columns = ['CustomerId', 'Age', 'Annual Income', 'Spending Score']
print(df2)
print(df2.head())
print(df2.describe())
def showHistogram(df,columns):
plt.figure(1, figsize=(7,8))
n = 0
for column in columns:
n += 1
plt.subplot(3, 1, n)
plt.subplots_adjust(hspace=0.5, wspace=0.5)
sns.distplot(df[column], bins=32)
plt.title(f'Histogram of {column}')
plt.show()
showHistogram(df2,df2.columns[1:])
def elbowMethod(df,columnsForElbow):
X = df.loc[:, columnsForElbow].values
inertia = []
for n in range(1 , 11):
model = KMeans(n_clusters = n,
init='k-means++',
max_iter=500,
random_state=42)
model.fit(X)
inertia.append(model.inertia_)
plt.figure(1 , figsize = (15 ,6))
plt.plot(np.arange(1 , 11) , inertia , 'o')
plt.plot(np.arange(1 , 11) , inertia , '-' , alpha = 0.5)
plt.xlabel('Number of Clusters') , plt.ylabel('Cluster sum of squared distances')
plt.show()
columns=['Age','Spending Score']
elbowMethod(df2,columns)
def runKMeans(X,cluster):
model = KMeans(n_clusters=cluster,
init='k-means++',
max_iter=500,
random_state=42)
model.fit(X)
labels = model.labels_
centroids = model.cluster_centers_
y_kmeans = model.fit_predict(X)
return y_kmeans,centroids,labels
X = df2.loc[:, columns].values
cluster=4
colors=["red","green","blue","purple","black","pink","orange"]
y_kmeans,centroids,labels=runKMeans(X,cluster)
print("y_kmeans:")
print(y_kmeans)
print("centroids:")
print(centroids)
print("labels:")
print(labels)
df2["cluster"]=labels
print("df2:")
print(df2)
def visualizeKMeans(X,y_kmeans,cluster,title,xlabel,ylabel,colors):
plt.figure(figsize=(10, 10))
for i in range(cluster):
plt.scatter(X[y_kmeans == i, 0],
X[y_kmeans == i, 1],
s=100,
c=colors[i],
label='Cluster %i'%(i+1))
plt.title(title)
plt.xlabel(xlabel)
plt.ylabel(ylabel)
plt.legend()
plt.show()
visualizeKMeans(X,
y_kmeans,
cluster,
"Clusters of Customers - Age X Spending Score",
"Age",
"Spending Score",
colors)
columns=['Annual Income','Spending Score']
elbowMethod(df2,columns)
X = df2.loc[:, columns].values
cluster=5
y_kmeans,centroids,labels=runKMeans(X,cluster)
print("y_kmeans:")
print(y_kmeans)
print("centroids:")
print(centroids)
print("labels:")
print(labels)
df2["cluster"]=labels
print("df2:")
print(df2)
visualizeKMeans(X,
y_kmeans,
cluster,
"Clusters of Customers - Annual Income X Spending Score",
"Annual Income",
"Spending Score",
colors)
columns=['Age','Annual Income','Spending Score']
elbowMethod(df2,columns)
X = df2.loc[:, columns].values
cluster=6
y_kmeans,centroids,labels=runKMeans(X,cluster)
print("y_kmeans:")
print(y_kmeans)
print("centroids:")
print(centroids)
print("labels:")
print(labels)
df2["cluster"]=labels
print("df2:")
print(df2)
def visualize3DKmeans(df,columns,hover_data,cluster):
fig = px.scatter_3d(df,
x=columns[0],
y=columns[1],
z=columns[2],
color='cluster',
hover_data=hover_data,
category_orders={"cluster": range(0, cluster)},
)
fig.update_layout(margin=dict(l=0, r=0, b=0, t=0))
fig.show()
hover_data=df2.columns
visualize3DKmeans(df2,columns,hover_data,cluster)
Như vậy tới đây Tui đã hoàn tất hướng dẫn các bạn cách lập trình K-Means để gom cụm khách hàng theo thu nhập, độ tuổi và ngân sách chi tiêu. Chúng ta dùng phương pháp Elbow để tìm ra K Cụm tối ưu nhất để gom, bài học này chúng ta trực quan hóa các cụm bằng 2D và 3D. Đồng thời phân loại chi tiết Khách hàng theo từng cụm để dễ dàng đưa ra các chính sách chăm sóc và tiếp thị phù hợp.
Bài học này rất thiết thực, ứng dụng vào thực tiễn rất lớn, các bạn cố gắng thực hành nhiều lần để hiểu rõ về nó, cũng như có thể áp dụng vào dự án thực tế của doanh nghiệp
Bắt đầu từ bài học sau Tui sẽ viết các bài Blog liên quan tới Hợp đồng thông minh và Công nghệ Blockchain, một trong những kiến thức và công nghệ nổi đình nổi đám hiện nay, các bạn chú ý theo dõi.
K-Means là một trong những thuật toán phổ biến và được sử dụng rất nhiều trong kỹ thuật gom cụm. Ý tưởng chính của thuật toán này là tìm cách phân nhóm các đối tượng đã cho vào K cụm sao cho tổng khoảng cách giữa các đối tượng đến tâm nhóm là nhỏ nhất. Thường K là số cụm được xác định trước và thường lấy ý kiến của chuyên gia, K phải nguyên dương. Khoảng cách giữa các đối tượng thường được sử dụng là khoảng cách Euclid.
Trong toán học, khoảng cách Euclid (tiếng Anh: Euclidean distance) giữa hai điểm trong không gian Euclid là độ dài của đoạn thẳng nối hai điểm đó. Có thể tính nó từ tọa độ Descartes của hai điểm bằng cách sử dụng định lý Pythagoras, do đó còn có tên gọi khác là khoảng cách Pythagoras (tiếng Anh: Pythagorean distance).
Src: Wikipedia
Đầu vào:
•Tập các đối tượng X = {xi| i = 1, 2, …, N},
•Số cụm: K
Đầu ra:
•Các cụm Ci ( i = 1 ÷ K) tách rời và hàm tiêu chuẩn E đạt giá trị tối thiểu.
Trong đó cj là trọng tâm của cụm Cj
Chu trình hoạt động thuật toán:
Thuật toán hoạt động trên 1 tập vectơ d chiều, tập dữ liệu X gồm N phần tử:
X = {xi | i = 1, 2, …, N}
K-Mean lặp lại nhiều lần quá trình:
Gán dữ liệu.
Cập nhật lại vị trí trọng tâm.
Quá trình lặp dừng lại khi trọng tâm hội tụ và mỗi đối tượng là 1 bộ phận của 1 cụm.
Các bước thực hiện thuật toán:
Dựa vào chu trình và lưu đồ thuật toán, ta có thể diễn giải các bước thực hiện như sau:
•Bước 1 – Khởi tạo
Chọn K trọng tâm {ci} ngẫu nhiên hoặc theo tiêu chuẩn (i = 1÷K).
•Bước 2 – Tính toán khoảng cách
•Bước 3 – Cập nhật lại trọng tâm, ta thường tính giá trị trung bình
•Bước 4 – Điều kiện dừng
Ví dụ gom cụm bằng K-Means với bảng dữ liệu sau (k=2) và giả sử r3, r5 là center mặc định:
…
Value 1
Value 2
Value 3
Value 4
Value 5
Value 6
Value 7
Value 8
Value 9
Value 10
Value 11
r1
1
1
0.5
0.5
0
0
0
0.5
0.25
0
0
r2
1
1
0.5
0.5
0
0
0
0.5
0.25
0
0
r3
0.5
0.5
1
1
0
0
0
0.33
0.33
0
0
r4
0.5
0.5
1
1
0
0
0
0.33
0.33
0
0
r5
0
0
0
0
1
1
1
0
0
0
0
r6
0
0
0
0
1
1
1
0
0
0
0
r7
0
0
0
0
1
1
1
0
0
0
0
r8
0.5
0.5
0.33
0.33
0
0
0
1
0.25
0.17
0.17
r9
0.25
0.25
0.33
0.33
0
0
0
0.25
1
0.75
0.75
r10
0
0
0
0
0
0
0
0.17
0.75
1
1
r11
0
0
0
0
0
0
0
0.17
0.75
1
1
Sau đây là chi tiết các bước thực hiện giải thuật K-Means để gom cụm. Dùng Euclid để tính các khoảng cách:
Bước 1: Khởi tạo Giả sử chọn ngẫu nhiên 2 Trọng tâm ban đầu: r3; r5
* Gọi V1 là véc tơ trọng tâm của C1: Ta tính được V1(0.5,0.5,1,1,0,0,0,0.33,0.33,0,0) * Gọi V2 là véc tơ trọng tâm của C2: Ta tính được V2(0,0,0,0,1,1,1,0,0,0,0)
Bước 2: Tính khoảng cách từ các đỉnh tới các Vector trọng tâm:
– d([r1],V1)=1.02 < d([r1],V2)=2.41. Vậy ta gom [r1] vào cụm C1.
Lý giải:
Ta tính được d([r1],V1)=1.02 từ công thức sau:
Tương tự ta cũng tính được d([r1],V2)=2.41 từ công thức sau:
Tương tự ta tính được khoảng cách tới trọng tâm cho các đỉnh còn lại (Các bạn cần viết lại công thức chi tiết như minh họa ở trên):
– d([r2],V1)=1.02 < d([r2],V2)=2.41. Vậy ta gom [r2] vào cụm C1. – d([r4],V1)=0 < d([r4],V2)=2.39.Vậy ta gom [r4] vào cụm C1. – d([r6],V1)=2.39 > d([r6],V2)=0. Vậy ta gom [r6] vào cụm C2. – d([r7],V1)=2.39 > d([r7],V2)=0. Vậy ta gom [r7] vào cụm C2. – d([r8],V1)=1.18 < d([r8],V2)=2.2. Vậy ta gom [r8] vào cụm C1. – d([r9],V1)=1.61 < d([r9],V2)=2.35. Vậy ta gom [r9] vào cụm C1 – d([r10],V1)=2.17 < d([r10],V2)=2.36. Vậy ta gom [r10] vào cụm C1 – d([r11],V1)=2.17 < d([r11],V2)=2.36. Vậy ta gom [r11] gom vào cụm C1
*Vậy ta có phân bố cụm lần 1:
U=1
r1
r2
r3
r4
r5
r6
r7
r8
r9
r10
r11
C1
1
1
1
1
0
0
0
1
1
1
1
C2
0
0
0
0
1
1
1
0
0
0
0
Bước 3: Cập nhật lại vector trọng tâm * Gọi V1 là véc tơ trọng tâm mới của C1: Ta tính được V1(0.47,0.47,0.46,0.46,0,0,0,0.41,0.49,0.36,0.36)
Lý giải trọng tâm mới được tính như sau:
Tương tự cho các thuộc tính và Vector còn lại. * Gọi V2 là véc tơ trọng tâm mới của C2: Ta tính được V2(0,0,0,0,1,1,1,0,0,0,0)
Bước 4:Kiểm tra điều kiện dừng Ta so sánh Vector trọng tâm mới được tạo ở bước 3 so với Vector trọng tâm cũ: Tập Vector Trọng tâm cũ: V1(0.5,0.5,1,1,0,0,0,0.33,0.33,0,0)
V2(0,0,0,0,1,1,1,0,0,0,0) Tập Vector Trọng tâm Mới: V1(0.47,0.47,0.46,0.46,0,0,0,0.41,0.49,0.36,0.36)
V2(0,0,0,0,1,1,1,0,0,0,0) Ta kiểm tra thấy trọng tâm bị thay đổi nên lặp lại bước 2.
Bước 2: Tính khoảng cách từ các đỉnh tới các Vector trọng tâm: – d([r1],V1)=0.95 < d([r1],V2)=2.41. Vậy ta gom [r1] vào cụm C1. – d([r2],V1)=0.95 < d([r2],V2)=2.41. Vậy ta gom [r2] vào cụm C1. – d([r3],V1)=0.94 < d([r3],V2)=2.39. Vậy ta gom [r3] vào cụm C1. – d([r4],V1)=0.94 < d([r4],V2)=2.39. Vậy ta gom [r4] vào cụm C1. – d([r5],V1)=2.13 > d([r5],V2)=0 . Vậy ta gom [r5] vào cụm C2. – d([r6],V1)=2.13 > d([r6],V2)=0. Vậy ta gom [r6] vào cụm C2. – d([r7],V1)=2.13 > d([r7],V2)=0. Vậy ta gom [r7] vào cụm C2. – d([r8],V1)=0.72 < d([r8],V2)=2.2. Vậy ta gom [r8] vào cụm C1. – d([r9],V1)=0.84 < d([r9],V2)=2.35. Vậy ta gom [r9] vào cụm C1. – d([r10],V1)=1.34 < d([r10],V2)=2.36. Vậy ta gom [r10] vào cụm C1. – d([r11],V1)=1.34 < d([r11],V2)=2.36. Vậy ta gom [r11] vào cụm C1. *Vậy ta có phân bố cụm lần 2:
U=2
r1
r2
r3
r4
r5
r6
r7
r8
r9
r10
r11
C1
1
1
1
1
0
0
0
1
1
1
1
C2
0
0
0
0
1
1
1
0
0
0
0
Bước 3: Cập nhật lại vector trọng tâm
* Gọi V1 là véc tơ trọng tâm mới của C1:
Ta tính được V1(0.47,0.47,0.46,0.46,0,0,0,0.41,0.49,0.36,0.36)
* Gọi V2 là véc tơ trọng tâm mới của C2:
Ta tính được V2(0,0,0,0,1,1,1,0,0,0,0)
Bước 4: Kiểm tra điều kiện dừng Ta so sánh Vector trọng tâm mới được tạo ở bước 3 so với Vector trọng tâm cũ:
Vậy ta thấy U2 và U1 Không có sự thay đổi. Giải thuật K-Means kết thúc.
Cuối cùng ta được 2 cụm như sau:
Cụm 1 {r1;r2;r3;r4;r8;r9;r10;r11}
Cụm 2 {r5;r6;r7}
Bài tập tương tự: Cho bảng ma trận dữ liệu dưới đây, hãy giải thích từng bước quá trình K-Means hoạt động, với số cụm là 3, center vector khởi tạo ban đầu là Daisy, Vitor, Real
Customer
Attribute 1
Attribute 2
John
1
1
Peter
11
12
Daisy
2
3
Case
1
2
Ronie
2
6
Vitor
9
8
Rehm
0
1
Tom
11
10
Bob
0
2
Lie
10
11
Tide
10
12
Real
7
4
Jassor
5
6
Bài học sau Tui hướng dẫn cách sử dụng K-Means để gom cụm khách hàng theo thu nhập, độ tuổi và ngân sách chi tiêu. Chúng ta dùng phương pháp Elbow để tìm ra K Cụm tối ưu nhất để gom, trực quan hóa các cụm bằng 2D và 3D. Đồng thời phân loại chi tiết Khách hàng theo từng cụm để dễ dàng đưa ra các chính sách chăm sóc và tiếp thị phù hợp.
Bài học này Tui hướng dẫn các bạn xây dựng mô hình máy học sử dụng hồi quy tuyến tính đa biến để thống kê và dự báo chi tiêu của khách hàng, sử dụng cơ sở dữ liệu “lecturer_retails” có Số lượng dữ liệu gồm 99457 dòng, dữ liệu tải ở đây: https://tranduythanh.com/datasets/PurchaseDataset.rar
Chương trình được tách ra làm 2 nhóm chức năng chính:
Chức năng thống kê:
(1.1) Thống kê tỉ lệ mua hàng theo giới tính
(1.2) Thống kê số lượng mua hàng theo độ tuổi
(1.3) Thống kê số lượng mua hàng theo danh mục sản phẩm
(1.4) Thống kê trị giá hàng hóa bán được theo danh mục
(1.5) Thống kê lượng hàn bán ra theo độ tuổi và danh mục
(1.6) Thống kê số lượng giao dịch theo phương thức thanh toán
(1.7) Thống kê tỉ lệ bán hàng theo Trung tâm thương mại (Shopping Mall)
(1.8) Thống kê trị giá hàng hóa bán được theo danh mục và giới tính
(1.9) Thống kê tần suất mua hàng theo độ tuổi và giới tính
(1.10) Thống kê biến động doanh thu theo tháng
(1.11) Thống kê biến động doanh thu theo tháng và theo năm
Chức năng huấn luyện mô hình máy học:
Dự báo biến động giá theo giới tính và độ tuổi
Dự báo biến động giá theo giới tính, độ tuổi và phương thức thanh toán
Giao diện phần mềm cho Thống kê tương tự như dưới đây:
Giao diện phần mềm cho Máy học tương tự như dưới đây:
Dữ liệu “lecturer_retails“, được lưu trong MySQL Server có cấu trúc như mô tả chi tiết dưới đây:
Cấu trúc bảng purchasehistory như sau:
invoice_no
customer_id
gender
age
category
quantity
price
payment_method
invoice_date
shopping_mall
Số lượng dữ liệu gồm 99457 dòng. Dataset các bạn tải ở link sau, và cần import vào MySQL Server của bạn trước:
Các bạn giải nén ra, trong MySQL Workbench tạo Schema tên “lecturer_retails“, sau đó import dataset vào Schema này. Cách import dataset các bạn xem lại bài học 47 cách import và export dataset
Tạo dự án “MLBAProject” có cấu trúc như dưới đây:
Vì bài này dài, và nó tổng hợp nhiều kiến thức nên Tui giải thích tổng quan như này:
(1) Thư mục “Assets” là thư mục tạo ra để lưu trữ các mô hình máy học được train thành công, dĩ nhiên người sử dụng có thể lưu ở bất cứ nơi nào, nhưng đề xuất lưu vào đây để dễ quản lý
(2) Thư mục “Connectors” là thư mục lưu thư viện kết nối và tương tác cơ sở dữ liệu MySQL Server
(3) Thư mục “Images” là thư mục lưu trữ các hình ảnh để sử dụng cho phần mềm
(4) Thư mục “Models” là thư mục chưa các lớp thư viện liên quan tới thống kê và máy học
(5) Thư mục “Tests” Là thư mục chứa các tập tin mã lệnh để thử nghiệm các hàm thống kê và máy học trước khi sử dụng thực tế. Thư mục này bạn có thể bỏ qua
(6) Thư mục “UI” là thư mục chứa các file thiết kế giao diện và generate python code cho giao diện, cũng như đồ thị
(7) Thư mục “Utils” là thư mục chưa thư viện xử lý tập tin, dùng để lưu mô hình máy học và tải mô hình máy học
(8) Cuối cùng là tập tin “App.py” để thực thi chương trình.
Vì không có nhiều thời gian, Tui không giải thích hết được các hàm trong các mã lệnh dưới đây, tuy nhiên Tui có đặt tên hàm và tên biến có ý nghĩa, nên các bạn đọc vào có thể hiểu ngay chức năng của nó. Đồng thời các thông số kết nối cơ sở dữ liệu các bạn cần đổi lại cho đúng với trên máy tính của mình.
Bước 1: Tạo và viết mã lệnh cho “Connector.py” trong thư mục “Connectors“
Thư viện trên dùng để kết nối, đóng kết nối, truy vấn dữ liệu và danh sách các bảng trong cơ sở dữ liệu.
Bước 2: Trong thư mục “Models“, lần lượt tạo các tập tin mã lệnh Python sau:
Bước 2.1: Tạo lớp “MetricsResult.py” để lưu kết quả đánh giá mô hình, lớp này lưu các thông số: MAE, MSE, RMSE, R2_SCORE
class MetricsResult:
def __init__(self,mae,mse,rmse,r2_score):
self.MAE=mae
self.MSE=mse
self.RMSE=rmse
self.R2_SCORE=r2_score
def __str__(self):
result="MAE=%s"%self.MAE+"\n"+"MSE=%s"%self.MSE+"\n"+"RMSE=%s"%self.RMSE+"\n"+"R2_SCORE=%s"%self.R2_SCORE+"\n"
return result
Bước 2.2: Tạo lớp “TrainedModel.py” để lưu trữ mô hình máy học được trained (model) các dữ liệu của biến độc lập và biến phụ thuộc, cũng như tên các biến này. Mục đích để lưu và nạp lại mô hình theo chủ ý của ta.
Bước 2.4: Tạo lớp “PurchaseMLModel.py” lớp này cung cấp tiền xử lý và chuyển đổi dữ liệu (transformation) để phục vụ các mô hình máy học
# Features encoding
from sklearn.preprocessing import LabelEncoder
import seaborn as sns
from matplotlib import pyplot as plt
from Models.PurchaseStatistic import PurchaseStatistic
class PurchaseMLModel(PurchaseStatistic):
def __init__(self,connector=None):
super().__init__(connector)
self.le = LabelEncoder()
def processTransformByColumns(self,df,columns):
for col in columns:
x=df[col]
df[col] = self.le.fit_transform(x)
def processTransform(self):
categorical_feature = ['gender', 'category', 'payment_method', 'shopping_mall']
numerical_feature = ['age', 'quantity', 'month', 'year']
dropping = ['customer_id', 'invoice_no', 'day', 'invoice_date']
result = ['price']
self.dfTransform=self.df.copy(deep=True)
self.dfTransform[["day", "month", "year"]] = self.dfTransform["invoice_date"].str.split("/", expand=True)
self.dfTransform.drop(dropping, axis=1, inplace=True)
for col in categorical_feature:
x=self.dfTransform[col]
self.dfTransform[col] = self.le.fit_transform(x)
return self.dfTransform
def buildCorrelationMatrix(self,df):
plt.figure(figsize=(8, 6))
df_corr = df.corr(numeric_only=True) # Generate correlation matrix
ax = sns.heatmap(df_corr, annot=True)
plt.show()
Bước 2.5: Tạo lớp “PurchaseLinearRegression.py” lớp này cung cấp chức năng train mô hình máy học với hồi quy đa biến:
import pandas as pd
from matplotlib import pyplot as plt
from sklearn.model_selection import train_test_split
#data modelling
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, mean_squared_error,r2_score
from sklearn.preprocessing import StandardScaler, LabelEncoder
from Models.MetricsResult import MetricsResult
from Models.PurchaseMLModel import PurchaseMLModel
from Models.TrainedModel import TrainedModel
from Utils.FileUtil import FileUtil
class PurchaseLinearRegression(PurchaseMLModel):
def __init__(self,connector=None):
super().__init__(connector)
self.le = LabelEncoder()
self.sc_std = StandardScaler()
def processTrain(self,columns_input,column_target,test_size,random_state):
self.execPurchaseHistory()
self.processTransform()
print(self.dfTransform.columns)
print(self.dfTransform.iloc[0])
y = self.dfTransform[column_target]
X = self.dfTransform[columns_input]
print("X=",X)
print("y=", y)
self.X_train, self.X_test, self.y_train, self.y_test = train_test_split(X, y, test_size=test_size, random_state=random_state)
self.trainedmodel=TrainedModel()
self.trainedmodel.X_train=self.X_train
self.trainedmodel.X_test=self.X_test
self.trainedmodel.y_train=self.y_train
self.trainedmodel.y_test=self.y_test
self.trainedmodel.columns_input=columns_input
self.trainedmodel.column_target=column_target
#self.sc_std = StandardScaler()
self.X_train = self.sc_std.fit_transform(self.X_train)
self.X_test = self.sc_std.transform(self.X_test)
self.lr = LinearRegression()
self.model = self.lr.fit(self.X_train, self.y_train)
self.trainedmodel.model=self.model
def visualizeActualAndPredictResult(self):
plt.figure(figsize=(8, 6))
plt.scatter(self.lr.predict(self.X_train), self.y_train)
plt.xlabel('Predicted value of Y')
plt.ylabel('Real value of Y')
plt.show()
def evaluate(self):
pred = self.model.predict(self.X_test)
mae=mean_absolute_error(self.y_test, pred)
mse = mean_squared_error(self.y_test, pred, squared=True)
rmse = mean_squared_error(self.y_test, pred, squared=False)
r2score=r2_score(self.y_test, pred)
return MetricsResult(mae,mse,rmse,r2score)
def predictPriceFromGenderAndAge(self,gender,age):
data_gender = {'gender': ["Male", "Female"]}
df_gender = pd.DataFrame(data=data_gender)
df_gender_transform = self.le.fit_transform(df_gender)
col_gender = 0
if gender == 'Male':
col_gender = 0
else:
col_gender = 1
data = [[df_gender_transform[col_gender], age]]
input_transform = self.sc_std.transform(data)
pred = self.predict(input_transform)
return pred
def predictPriceFromGenderAndAgeAndPayment(self,gender,age,payment_method):
data_gender= {'gender': ["Male", "Female"]}
df_gender=pd.DataFrame(data=data_gender)
df_gender_transform=self.le.fit_transform(df_gender)
data_payment_method={"payment_method":["Credit Card","Debit Card","Cash"]}
df_payment_method=pd.DataFrame(data=data_payment_method)
df_payment_method_transform=self.le.fit_transform(df_payment_method)
col_gender=0
if gender == 'Male':
col_gender=0
else:
col_gender = 1
col_payment=0
if payment_method=="Credit Card":
col_payment=0
elif payment_method=="Debit Card":
col_payment=1
else:
col_payment = 2
data = [[df_gender_transform[col_gender], age,df_payment_method_transform[col_payment]]]
input_transform = self.sc_std.transform(data)
pred = self.predict(input_transform)
return pred
def predict(self,columns_input):
pred = self.model.predict(columns_input)
return pred
def saveModel(self,fileName):
ret=FileUtil.saveModel(self.trainedmodel,fileName)
return ret
def loadModel(self,fileName):
self.trainedmodel=FileUtil.loadModel(fileName)
self.sc_std.fit_transform(self.trainedmodel.X_train)
self.model=self.trainedmodel.model
return self.model
Bước 3: Tạo các lớp test các thư viện ở bước 2 trong thư mục “Tests“
Bước 3.1: Tạo “AppStatistic.py” để test các hàm thống kê. Ta có thể chạy tập tin này độc lập để test:
from Connectors.Connector import Connector
from Models.PurchaseStatistic import PurchaseStatistic
connector=Connector(server="localhost",port=3306,database="lecturer_retails",username="root",password="@Obama123")
connector.connect()
pm=PurchaseStatistic()
pm.connector=connector
pm.execPurchaseHistory()
dfGender=pm.processGenderDistribution()
print(dfGender)
pm.visualizePieChart(dfGender,"gender","count","Gender Distribution")
dfAge=pm.processAgeDistribution(30,50)
print(dfAge)
pm.visualizePlotChart(dfAge,"age","count","Age Distribution 30~50")
dfCategory=pm.processCategoryDistribution()
print(dfCategory)
pm.visualizePieChart(dfCategory,"category","count","Categories Distribution",legend=False)
dfCateSpending=pm.processCategorySpending()
print(dfCateSpending)
pm.visualizeBarChart(dfCateSpending,"category","price","Distribution category and Spending")
dfGenderCategory=pm.processGenderAndCategoryCounter()
print(dfGenderCategory)
pm.visualizeCountPlot(pm.df,"category","count","gender","Distribution gender and category")
dfPayment=pm.processPaymentMethod()
print(dfPayment)
pm.visualizePieChart(dfPayment,"payment_method","count","Payment Distribution",legend=False)
dfShoppingMall=pm.processShoppingMall()
print(dfShoppingMall)
pm.visualizePieChart(dfShoppingMall,"shopping_mall","count","Shopping Mall Distribution",legend=False)
dfGenderCateSpending=pm.processGenderCategorySpending()
print(dfGenderCateSpending)
pm.visualizeBarPlot(dfGenderCateSpending,"category","price","gender","Male and Female category Total Price Spend")
dfAgeGender=pm.processAgeOrderFrequence()
print(dfAgeGender)
pm.visualizeScatterPlot(dfAgeGender,"age","count","Age VS Order Frequence")
dfMonthlySalesAmount=pm.processMonthlySalesAmount()
print(dfMonthlySalesAmount)
pm.visualizeLinePlotChart(dfMonthlySalesAmount,"month","sales_amount","Monthly Variation in Sales Amount")
dfMonthlyAndYearSalesAmount=pm.processMonthlyAndYearSalesAmount()
print(dfMonthlyAndYearSalesAmount)
pm.visualizeLinePlotChart(dfMonthlyAndYearSalesAmount,"month","sales_amount","Monthly Variation in Sales Amount Over Years",hue="year")
Bước 3.2: Tạo “AppModel.py” để test tiền xử lý và transformation. Ta có thể chạy tập tin này độc lập để test:
from Connectors.Connector import Connector
from Models.PurchaseMLModel import PurchaseMLModel
connector=Connector(server="localhost",port=3306,database="lecturer_retails",username="root",password="@Obama123")
connector.connect()
pm=PurchaseMLModel(connector)
pm.execPurchaseHistory()
dfTransform=pm.processTransform()
print(dfTransform.head())
pm.buildCorrelationMatrix(dfTransform)
Bước 3.3: Tạo “AppLinearRegression.py” để test train mô hình máy học, cũng như prediction. Ta có thể chạy tập tin này độc lập để test:
from Connectors.Connector import Connector
from Models.PurchaseLinearRegression import PurchaseLinearRegression
connector=Connector(server="localhost",port=3306,database="lecturer_retails",username="root",password="@Obama123")
connector.connect()
pm=PurchaseLinearRegression(connector=connector)
pm.processTrain(["gender","age"],"price",0.2,0)
#pm.processTrain(["gender","age","payment_method"],"price")
#pm.visualizeActualAndPredictResult()
eresult=pm.evaluate()
print(eresult)
gender="Male"
age=61
pred=pm.predictPriceFromGenderAndAge(gender,age)
print("Gender=%s and Age=%s=>Price=%s"%(gender,age,pred))
gender="Female"
age=61
pred=pm.predictPriceFromGenderAndAge(gender,age)
print("Gender=%s and Age=%s=>Price=%s"%(gender,age,pred))
print("------------------"*10)
pm=PurchaseLinearRegression()
pm.connector=connector
pm.processTrain(["gender","age","payment_method"],"price",0.2,0)
eresult=pm.evaluate()
print(eresult)
gender="Male"
age=61
payment="Credit Card"
pred=pm.predictPriceFromGenderAndAgeAndPayment(gender,age,payment)
print("Gender=%s and Age=%s and payment=%s=>Price=%s"%(gender,age,payment,pred))
gender="Male"
age=61
payment="Debit Card"
pred=pm.predictPriceFromGenderAndAgeAndPayment(gender,age,payment)
print("Gender=%s and Age=%s and payment=%s=>Price=%s"%(gender,age,payment,pred))
gender="Male"
age=61
payment="Cash"
pred=pm.predictPriceFromGenderAndAgeAndPayment(gender,age,payment)
print("Gender=%s and Age=%s and payment=%s=>Price=%s"%(gender,age,payment,pred))
ret=pm.saveModel("../Assets/LR_mymodel.zip")
print("ret save model=%s"%ret)
Bước 3.4: Tạo “TestLoadModel.py” để nạp mô hình từ ổ cứng , cũng như prediction. Ta có thể chạy tập tin này độc lập để test:
from Models.PurchaseLinearRegression import PurchaseLinearRegression
pm=PurchaseLinearRegression()
pm.loadModel("../Assets/TrainedModel_GenderAgePayment.zip")
gender="Female"
age=61
payment="Cash"
pred=pm.predictPriceFromGenderAndAgeAndPayment(gender,age,payment)
print("Gender=%s and Age=%s and payment=%s=>Price=%s"%(gender,age,payment,pred))
#Gender=Female and Age=61 and payment=Cash=>Price=[692.98688316]
Bước 4: Tạo lớp tập tin “FileUtils.py” trong thư mục Utils để lưu mô hình máy học và nạp mô hình máy học:
Bước 5.5: Tạo Python code kế “DatabaseConnectEx.py” thừa Ui_MainWindow của mã lệnh Generate Python code giao diện “DatabaseConnect.py” để xử lý sự kiện tương tác người dùng:
import traceback
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QMessageBox
from Connectors.Connector import Connector
from UI.DatabaseConnect import Ui_MainWindow
class DatabaseConnectEx(Ui_MainWindow):
def setupUi(self, MainWindow):
super().setupUi(MainWindow)
self.MainWindow=MainWindow
self.pushButtonConnect.clicked.connect(self.connectDatabase)
def connectDatabase(self):
try:
self.connector=Connector()
self.connector.server=self.lineEditServer.text()
self.connector.port=(int)(self.lineEditPort.text())
self.connector.database=self.lineEditDatabase.text()
self.connector.username=self.lineEditUser.text()
self.connector.password=self.lineEditPassword.text()
self.connector.connect()
self.msg=QMessageBox()
self.msg.setText("Connect database successful!")
self.msg.setWindowTitle("Info")
#self.msg.show()
self.MainWindow.close()
if self.parent!=None:
self.parent.checkEnableWidget(True)
except:
traceback.print_exc()
self.msg = QMessageBox()
self.msg.setText("Connect database failed")
self.msg.setWindowTitle("Info")
self.msg.show()
def show(self):
self.MainWindow.setWindowModality(Qt.WindowModality.ApplicationModal)
self.MainWindow.show()
Bước 5.6: Tạo giao diện chính của dự án để thực hiện chức năng thống kê và máy học “MainWindow.ui“:
Thiết kế giao diện và đặt tên các Widget như trên.
Bước 5.7: Generate Python code cho “MainWindow.ui“, ta có dữ liệu mã lệnh “MainWindow.py“:
Bước 5.8: Tạo Python code kế “MainWindowEx.py” thừa Ui_MainWindow của mã lệnh Generate Python code giao diện “MainWindow.py” để xử lý sự kiện tương tác người dùng:
from PyQt6.QtWidgets import QApplication, QMainWindow
from UI.MainWindowEx import MainWindowEx
qApp=QApplication([])
qmainWindow=QMainWindow()
window=MainWindowEx()
window.setupUi(qmainWindow)
window.show()
qApp.exec()
Chạy App.py ta có kết quả:
Vào menu System chọn Connect Database:
Nhập thông số kết nối:
Bấm “Connect” để kết nối, nếu kết nối thành công ta có giao diện như dưới đây:
Các bạn thử 11 chức năng thông kế sẽ có các kết quả như mong muốn
(1.1) Thống kê tỉ lệ mua hàng theo giới tính
(1.2) Thống kê số lượng mua hàng theo độ tuổi
(1.3) Thống kê số lượng mua hàng theo danh mục sản phẩm
(1.4) Thống kê trị giá hàng hóa bán được theo danh mục
(1.5) Thống kê lượng hàn bán ra theo độ tuổi và danh mục
(1.6) Thống kê số lượng giao dịch theo phương thức thanh toán
(1.7) Thống kê tỉ lệ bán hàng theo Trung tâm thương mại (Shopping Mall)
(1.8) Thống kê trị giá hàng hóa bán được theo danh mục và giới tính
(1.9) Thống kê tần suất mua hàng theo độ tuổi và giới tính
(1.10) Thống kê biến động doanh thu theo tháng
(1.11) Thống kê biến động doanh thu theo tháng và theo năm
Về thử nghiệm chức năng máy học:
Như vậy tới đây Tui đã trình bày hoàn chỉnh dự án MLBAProject. dự án phục vụ thống kê và train mô hình máy học, dự án kết nối cơ sở dữ liệu, tổng hợp nhiều kiến thức.
Các bạn nhớ thực hành nhiều lần để hiểu rõ hơn về dự án.
Source code dự án đầy đủ các bạn tham khảo tại đây:
Bài 53 và bài 54 các bạn đã hiểu rõ và thực hành thuần thục mô hình hồi quy đơn biến. Trong bài học này Tui trình bày về hồi quy đa biến – Multiple Linear Regression. Hầu hết các dự báo với bài toán hồi quy thường rơi vào mô hình đa biến. Ví dụ bài 54 ta tính giá nhà dựa trên mô hình đơn biến, nhưng trong thực tế giá nhà nó lệ thuộc vào nhiều yếu tố như Số phòng, số tầng, mặt tiền… Đó chính là dấu hiệu của đa biến.
Hay dự báo giá sản phẩm lệ thuộc vào màu sắc, chất lượng… đây cũng là dấu hiệu của đa biến.
Multiple Linear Regression: Mô hình hồi quy tuyến tính đa biến có nhiều hơn một biến độc lập, biểu diễn mối quan hệ tuyến tính giữa các biến độc lập và biến phụ thuộc.
Về cơ bản không có sự khác biệt giữa hồi quy tuyến tính ‘giản đơn’ và ‘đa biến’. Cả hai đều làm việc tuân theo nguyên tắc OLS –Ordinary Least Square và thuật toán để có được đường hồi quy tối ưu nhất cũng tương tự. Trong trường hợp sau, phương trình hồi quy sẽ có một hình dạng như sau:
Công thức chung:
Y=B0+B1*X1+B2*X2+B3*X3…..+Bn*Xn
Bi: Các hệ số khác nhau
Xi: Các biến độc lập khác nhau
Bài học này chúng ta dùng Microsoft Excel để lập trình các dữ kiện phục cho cho mô hình hồi quy đa biến.
Giả sử ta có tập dữ liệu như dưới đây:
Color
Quality
Price
7
5
65
3
7
38
5
8
51
8
1
38
9
3
55
5
4
43
4
0
25
2
6
33
8
7
71
6
4
51
9
2
49
Quan sát tập dữ liệu trên gồm có 3 biến Color, Quality, Price.
Biến độc lập: Color, Quality
Biến phụ thuộc: Price
Dùng hồi quy tuyến tính để tính Price predicted, thực hiện các công thức trong Microsoft Excel.
Ta Cần tính được 4 đại lượng dưới đây:
(X’X)
(X’X)_inv
(X’Y)
B_hat
Sau đó lắp ráp công thức:
Y=B0+B1*X1+B2*X2+B3*X3…..+Bn*Xn
Ta bắt đầu thực hiện:
Bước 1: Chèn cột x0 có giá trị như hình dưới đây (mặc định là 1)
Bạn quan sát, dữ liệu gốc gồm có 3 cột trong Excel: Color, Quality, Price
Sau đó ta chèn x0 là cột đầu tiền trong ma trận dữ liệu mới
Bước 2: Tính (X’X)
Nhập công thức, lựa chọn các cột như hình và nhấn Enter
Lưu ý tích ma trận (X’X) sử dụng công thức MMULT trả về tích ma trận của 2 mảng, và TRANSPOSE chuyển vị ma trận.
Sau khi nhập công thức và nhấn Enter, ta có kết quả:
Ta thấy Excel ra giá trị #VALUE!, lưu ý đây không phải lỗi.
Ta bôi đen 3 Cell của (X’Y) và thực hiện theo đúng thứ tự 3 bước sau:
(01) Giá trị sẽ xuất hiện
(02) Bôi đen cột K, bôi đủ 3 cell cho (X’Y)
(03) Di chuyển trỏ chuột tới ô địa chỉ của Excel: Nhấn CTRL+SHIFT+ENTER
Sau khi nhấn CTRL+SHIFT+ENTER, ta có kết quả:
Bước 5:Tính B_hat
Nhập công thức, lựa chọn các cột như hình và nhấn Enter
Lưu ý B_hat tính tích 2 ma trận (X’X)_inv và (X’Y), ta dùng công thức MMULT như trên, cùng với kéo các ma trận đúng địa chỉ ô Cell.
Sau khi nhận công thức hoàn tất, nhấn phím Enter, ta có kết quả:
Ta tiếp tục thực hiện đúng thứ tự các bước sau:
(01) Giá trị sẽ xuất hiện
(02) Bôi đen cột N, bôi đủ 3 cell cho B_hat
(03) Di chuyển trỏ chuột tới ô địa chỉ của Excel: Nhấn CTRL+SHIFT+ENTER
Sau khi nhấn CTRL+SHIFT+ENTER, ta có kết quả:
Bước 6:Lắp ráp công thức để tính:
Y=B0+B1*X1+B2*X2+B3*X3…..+Bn*Xn
Bổ sung thêm cột Predicted Price cho file excel, và lắp ráp công thức như dưới đây:
Bạn quan sát kỹ cách ráp công thức B_hat để nhân vào các biến độc lập cho cột Predicted Price.
N9 là B0
N10 là B1
N11 là B2
predicted price=N9+N10*F2+N11*G2
Các dấu $ là địa chỉ tuyệt đối vì B0, B1, B2 là cố định.
Sau khi nhấn Enter, bạn kéo công thức xuống hết các dòng dữ liệu để xem Predicted Price:
Bước 7:Kiểm tra sai số khi Prediction, sử dụng Square Error, MSE, RMSE:
Cột Squared Error Tui mới bổ sung ở trên, đơn giản chỉ là lấy giá trị thực – giá trị prediction. Nó phục vụ để tính MSE và RMSE
MSE tính theo công thức: Trung bình cộng của Squared Error
RMSE tính theo công thức: Là căn bậc 2 của MSE
Như vậy tới đây Tui đã hoàn tất việc hướng dẫn phương trình hồi quy tuyến tính với mô hình Đa Biến. các công thức thực hiện trên Microsoft Excel
các bạn cần làm lại nhiều lần để hiểu rõ lý thuyết vì sao sử dụng các ma trận, các thao tác trên ma trận ở trên để có thể vận dụng thực hiện hồi quy đa biến vào dự báo giá nhà.
Bạn có thể tải file Excel chi tiết Tui thực hiện ở đây, trong Excel có link và youtube hướng dẫn tham khảo:
Trong bài 53 Tui đã trình bày chi tiết công thức hồi quy đơn biến, cũng như giải thích cách tính chi tiết các thông số của công thức, thực hiện trên Excel và lập trình bằng Python, trực quan hóa kết quả thực hiện hồi quy đơn biến.
Để củng cố thêm kiến thức cũng như kỹ năng lập trình và ứng dụng hồi quy đơn biến, trong bài này Tui tiếp tục cung cấp một ví dụ minh họa về dự báo giá nhà áp dụng hồi quy đơn biến. Đặc biệt Tui minh họa thêm cách huấn luyện mô hình hồi quy đơn biến bằng Sklearn-Python. Như vậy trong ví dụ thứ 2 này chúng ta sẽ thực hành bằng 3 cách để dự báo giá nhà:
Tập dữ liệu gồm có 2 biến x, y. Biến độc lập là x (diện tích nhà), biến phụ thuộc là y (Giá nhà – tỉ VNĐ)
Dùng hồi quy tuyến tính để tính y predicted giá nhà
Cách 1:Tính toán hồi quy đơn biên trong Microsoft Excel
Như đã đề cập về cách tính toán các thông số cho phương trình hồi quy:
Y = Β0 + Β1*X
Tính độ lệch chuẩn của biến độc lập x: Sử dụng Công thức STDEV
Tính độ lệch chuẩn của biến phụ thuộc y: Sử dụng Công thức STDEV
Tính trung bình của các biến độc lập x: Sử dụng Công thức AVERAGE
Tính trung bình của các biến phụ thuộc y: Sử dụng công thức AVERAGE
Tính độ tương quan giữa x và y: Sử dụng Công thức CORREL
Tính B1: Sử dụng Công thức
Độ Tương quan *( Độ lệch chuẩn của y / Độ lệch chuẩn của x)
7. Tính B0: Sử dụng công thức
Trung bình (Y) – B1 * Trung bình (X)
Ta bắt đầu lắp ráp công thức trong Excel để tính 7 dữ kiện cho công thức hồi quy đơn biến như dưới đây:
Hình trên là công thức để tính 7 dữ kiện liên quan tới hồi quy đơn biến. Ta quan sát kết quả đạt được:
Sau khi có 7 dữ kiện, ta bổ sung cột y_predicted để lắp ráp vào công thức:
Y = Β0 + Β1*X
Ta xem công thức Tui thực hiện cho phương trình:
Bạn chú ý các ô Cell tính toán công thức nó lệ thuộc vào dữ liệu bạn nhập, nếu nhập theo đúng cấu trúc mà Tui chụp hình ở trên thì các địa chỉ cell không cần đổi, còn nếu bạn nhập khác với cấu trúc file Excel mà Tui cung cấp thì cần tham chiếu địa chỉ ô cell cho đúng.
Ta xem kết quả thực hiện y-predicted:
Ta quan sát thấy kết quả dự báo rất sát với kết quả thực tế, ta xem thêm trực quan hóa đồ thị:
Các bạn có thể tải file Excel cùng với các công thức mà Tui đã thực hiện ở đây:
Cách 2: Lập trình Python để tính toán các dữ kiện nhằm thực hiện công thức hồi quy đơn biến
Ta thực hiện mã lệnh Python để tính toán dựa trên toán học ra các kết quả b1, b0 như dưới đây:
import matplotlib.pyplot as plt
import numpy as np
# area
x = np.array([[73.5,75.,76.5,79.,81.5,82.5,84.,85.,86.5,87.5,89.,90.,91.5]]).T
# price
y = np.array([[1.49,1.50,1.51,1.54,1.58,1.59,1.60,1.62,1.63,1.64,1.66,1.67,1.68]]).T
def calculateb1b0(x,y):
# tính trung bình
xbar = np.mean(x)
ybar = np.mean(y)
x2bar = np.mean(x ** 2)
xybar = np.mean(x * y)
# tính b0, b1
b1 = (xbar * ybar - xybar) / (xbar ** 2 - (x2bar))
b0 = ybar - b1 * xbar
return b1,b0
#calulate b1, b0
b1,b0=calculateb1b0(x,y)
print("b1=",b1)
print("b0=",b0)
y_predicted=b0+b1*x
print(y_predicted)
Thực hiện lệnh trên ta có kết quả:
Ta quan sát kết quả, rất giống với cách thực hiện trong Excel.
Tiếp tục viết mã lệnh để trực quan hóa kết quả, so sánh giá trị thực và giá trị predicted:
# Visualize data
def showGraph(x, y,y_predicted, title="", xlabel="", ylabel=""):
plt.figure(figsize=(14, 8))
plt.plot(x, y, 'r-o', label="price")
plt.plot(x, y_predicted, 'b-*', label="predicted value")
x_min = np.min(x)
x_max = np.max(x)
y_min = np.min(y)
y_max = np.max(y)
# mean y
ybar = np.mean(y)
plt.axhline(ybar, linestyle='--', linewidth=4, label="mean")
plt.axis([x_min*0.95, x_max*1.05, y_min*0.95, y_max*1.05])
plt.xlabel(xlabel, fontsize=16)
plt.ylabel(ylabel, fontsize=16)
plt.text(x_min, ybar*1.01, "mean", fontsize=16)
plt.legend(fontsize=15)
plt.title(title, fontsize=20)
plt.show()
showGraph(x, y,y_predicted,
title='Giá nhà theo diện tích',
xlabel='Diện tích (m2)',
ylabel='Giá nhà (tỷ VND)')
Thực thi lệnh trên ta có kết quả:
Nhìn vào kết quả trực quan hóa, ta thấy mô hình hồi quy đơn biến cho ra kết quả prediction khá tương đồng với giá trị thực, tức là mô hình chất lượng. Tuy nhiên đây chỉ là đơn biến, giá nhà nó không chỉ lệ thuộc vào diện tích mà nó còn lệ thuộc vào rất nhiều yếu tố khác như: số phòng, số tầng, mặt tiền, các tiện ích xung quanh….
Các bạn có thể tải Source code Python tính toán hồi quy đơn biến ở đây:
Cách 3: Huấn luyện mô hình máy học cho mô hình hồi quy tuyến tính đơn biến
Cách này chúng ta dùng linear_model trong thư viện sklearn để huấn luyện mô hình hồi quy. Chúng ta sẽ so sánh cách lập trình Python theo công thức toán học và theo thư viện sklearn.
import matplotlib.pyplot as plt
import numpy as np
from sklearn import linear_model
# area
x = np.array([[73.5,75.,76.5,79.,81.5,82.5,84.,85.,86.5,87.5,89.,90.,91.5]]).T
# price
y = np.array([[1.49,1.50,1.51,1.54,1.58,1.59,1.60,1.62,1.63,1.64,1.66,1.67,1.68]]).T
# input matrix X
X = np.concatenate([x], axis = 1)
def calculateb1b0(x,y):
# tính trung bình
xbar = np.mean(x)
ybar = np.mean(y)
x2bar = np.mean(x ** 2)
xybar = np.mean(x * y)
# tính b0, b1
b1 = (xbar * ybar - xybar) / (xbar ** 2 - (x2bar))
b0 = ybar - b1 * xbar
return b1,b0
#calulate b1, b0
b1,b0=calculateb1b0(x,y)
print("Lập trình Python theo công thức toán học:")
print("b1=",b1)
print("b0=",b0)
y_predicted=b0+b1*x
print(y_predicted)
# fit the model by Linear Regression
# fit_intercept = False for calculating the bias
regr = linear_model.LinearRegression(fit_intercept=True)
regr.fit(X, y)
print("Lập trình Python theo mô hình huấn luyện máy học LinearRegression:")
# Compare two results
print('Coefficient : ', regr.coef_)
print('Interception : ', regr.intercept_)
# Dự báo giá nhà ngay trên tập huấn luyện
ypred = regr.predict(X)
print(ypred)
Thực hiện lệnh trên ta có kết quả so sánh của 2 cách:
Quan sát các giá trị:
Theo công thức lập trình dựa trên tính toán toán học: b1, b0, y_predicted
Theo huấn luận mô hình máy học: Coefficient, Interception và ypred
Các giá trị này là tương đương nhau.
Như vậy rõ ràng, khi dùng thư viện huấn luyện mô hình máy học sẽ nhanh gọn lẹ hơn, vì ta chỉ cần học cách sử dụng thư viện (với 2 dòng lệnh là tính ra được b1(Coefficient), b0(Interception)).
Tiếp theo ta bổ sung mã lệnh để trực quan hóa kết quả:
Dưới đây là mã lệnh đầy đủ của huấn luyện mô hình máy học:
import matplotlib.pyplot as plt
import numpy as np
from sklearn import linear_model
# area
x = np.array([[73.5,75.,76.5,79.,81.5,82.5,84.,85.,86.5,87.5,89.,90.,91.5]]).T
# price
y = np.array([[1.49,1.50,1.51,1.54,1.58,1.59,1.60,1.62,1.63,1.64,1.66,1.67,1.68]]).T
# input matrix X
X = np.concatenate([x], axis = 1)
def calculateb1b0(x,y):
# tính trung bình
xbar = np.mean(x)
ybar = np.mean(y)
x2bar = np.mean(x ** 2)
xybar = np.mean(x * y)
# tính b0, b1
b1 = (xbar * ybar - xybar) / (xbar ** 2 - (x2bar))
b0 = ybar - b1 * xbar
return b1,b0
#calulate b1, b0
b1,b0=calculateb1b0(x,y)
print("Lập trình Python theo công thức toán học:")
print("b1=",b1)
print("b0=",b0)
y_predicted=b0+b1*x
print(y_predicted)
# fit the model by Linear Regression
# fit_intercept = False for calculating the bias
regr = linear_model.LinearRegression(fit_intercept=True)
regr.fit(X, y)
print("Lập trình Python theo mô hình huấn luyện máy học LinearRegression:")
# Compare two results
print('Coefficient : ', regr.coef_)
print('Interception : ', regr.intercept_)
# Dự báo giá nhà ngay trên tập huấn luyện
ypred = regr.predict(X)
print(ypred)
# Visualize data
def showGraph(x, y_act, y_pred, title="", xlabel="", ylabel=""):
plt.figure(figsize=(14, 8))
plt.plot(x, y_act, 'r-o', label="price actual")
plt.plot(x, y_pred, '--', label="price predict")
x_min = np.min(x)
x_max = np.max(x)
y_min = np.min(y_act)
y_max = np.max(y_act)
# mean price
ybar = np.mean(y_act)
plt.axhline(ybar, linestyle='--', linewidth=4, label="mean actual")
plt.axis([x_min*0.95, x_max*1.05, y_min*0.95, y_max*1.05])
plt.xlabel(xlabel, fontsize=16)
plt.ylabel(ylabel, fontsize=16)
plt.text(x_min, ybar*1.01, "mean actual", fontsize=16)
plt.legend(fontsize=15)
plt.title(title, fontsize=20)
plt.show()
showGraph(x, y,ypred,
title='Dự báo Giá nhà theo diện tích',
xlabel='Diện tích (m2)',
ylabel='Giá nhà (tỷ VND)')
Như vậy tới đây Tui đã hướng dẫn đầy đủ ví dụ thứ 2, với 3 cách: Vừa thực hiện trong Excel, vừa lập trình Python theo công thức toán học để tính ra b0, b1 và predicted. Và cuối cùng là huấn luyện mô hình máy học theo thư viện sklearn. Cả 3 cách này đều cho kết quả như nhau, do đó nền tảng toán học rất quan trọng, nếu hiểu toán học để áp dụng thì việc sử dụng thư viện sẽ thuận tiện hơn vì ta đã hiểu được bản chất.
Source code của huấn luyện mô hình các bạn tải ở đây:
Phương trình hồi qui tuyến tính có rất nhiều ứng dụng trong thực tiễn và là một trong những lớp mô hình đặc biệt quan trọng trong machine learning.
Ứng dụng vào việc dự báo về nhu cầu thị trường của một doanh nghiệp để chuẩn bị kế hoạch sản suất kinh doanh. Trong tài chính chúng ta có thể dự báo giá chứng khoán và các chỉ số tài chính dựa trên hồi qui tuyến tính. Hay ta có thể ứng dụng dự báo chỉ số lạm phát, tốc độ tăng trưởng GDP của quốc gia…
Hầu hết các bài toán dự báo liên quan tới biến mục tiêu liên tục thì đều có thể sử dụng hồi qui tuyến tính để dự báo.
Bài học hồi quy tuyến tính đơn biến – Simple Linear Regression sẽ được Tui viết thành 2 bài Blog với 2 ví dụ khác nhau. Các bài học đều thử nghiệm trên Excel và trong Python để các bạn dễ dàng hiểu lý thuyết cũng như ứng dụng trong thực tế.
Sau khi trình bày lý thuyết xong, Tui sẽ hướng dẫn các bạn cách tính Hồi quy đơn biến bằng các công thức trong Microsoft Excel và sau đó là lập trình bằng Python:
Ta có thể triển khai hồi quy tuyến tính(Linear Regression) bằng:
R linear regression.
MATLAB linear regression
Sklearn linear regression
Linear regression Python
Linear regression C#
Linear regression Java
Excel linear regression
Tự thực hiện theo các công thức toán học
Simple Linear Regression: Mô hình hồi quy tuyến tính đơn biến chỉ có một biến độc lập (input feature) mô tả mối quan hệ tuyến tính giữa biến phụ thuộc (output target) và biến độc lập. Công thức tổng quát:
Y = Β0 + Β1*X
Ý nghĩa của các biến và hằng số:
Y = Biến phụ thuộc
X = Biến độc lập
Β0 = Hằng số
Β1 = Hệ số mối quan hệ giữa X và Y
Một số đặc tính của hồi quy tuyến tính:
Đường hồi quy luôn luôn đi qua trung bình của biến độc lập x cũng như trung bình của biến phụ thuộc y
Đường hồi quy tối thiểu hóa tổng của “Diện tích các sai số“. Nên đôi khi được gọi là “Ordinary Least Square (OLS)“
Β1 giải thích sự thay đổi trong Y với sự thay đổi X bằng một đơn vị. Nói cách khác, nếu chúng ta tăng giá trị của X bởi một đơn vị thì nó sẽ là sự thay đổi giá trị của Y
Để tính được giá trị của phương trình hồi quy đơn biến ta cần tính được các dữ kiện sau:
Độ lệch chuẩn của biến độc lập x
Độ lệch chuẩn của biến phụ thuộc y
Trung bình của các biến độc lập x
Trung bình của các biến phụ thuộc y
Độ tương quan giữa biến độc lập x và biến phục thuộc y
Y = Β0 + Β1*X
Tính B1
Cuối cùng là tính B0
Bây giờ chúng ta bắt đầu thực hiện hồi quy đơn biến trong Microsoft Excel, giả sử chúng ta có tập dữ liệu trong Excel như dưới đây:
x
y
1
2
2
4
3
3
4
6
5
9
6
12
7
13
8
15
9
18
10
20
Tập dữ liệu gồm có 2 biến x và y. Trong đó biến độc lập là x, biến phụ thuộc là y Dùng hồi quy tuyến tính để tính y predicted
Để cho nhanh và dễ hiểu, chúng ta thực hiện trong Excel trước, sau đó chúng ta sẽ lập trình bằng Python
Như đã đề cập về cách tính toán các thông số cho phương trình hồi quy:
Y = Β0 + Β1*X
Tính độ lệch chuẩn của biến độc lập x: Sử dụng Công thức STDEV
Tính độ lệch chuẩn của biến phụ thuộc y: Sử dụng Công thức STDEV
Tính trung bình của các biến độc lập x: Sử dụng Công thức AVERAGE
Tính trung bình của các biến phụ thuộc y: Sử dụng công thức AVERAGE
Tính độ tương quan giữa x và y: Sử dụng Công thức CORREL
Tính B1: Sử dụng Công thức
Độ Tương quan *( Độ lệch chuẩn của y / Độ lệch chuẩn của x)
7. Tính B0: Sử dụng công thức
Trung bình (Y) – B1 * Trung bình (X)
Ta bắt đầu lắp ráp công thức trong Excel để tính 7 dữ kiện cho công thức hồi quy đơn biến như dưới đây:
Các bạn quan sát Tui nhập các công thức tính toán các thông số, các bạn nhập chính xác như trên. Lưu ý các địa chỉ ô Excel tính toán nó lệ thuộc vào dữ liệu bạn nhập trong Excel, nếu nhập như bài học Tui đã soạn thì không cần đổi địa chỉ ô Excel.
Kết quả thực hiện sẽ hiển thị ra như dưới đây:
Sau khi có các kết quả rồi, ta bổ sung thêm một cột Y-Predicted để lắp ráp tính toán công thức:
Y = Β0 + Β1*X
Kết quả thực hiện phương trình hồi quy đa biến sẽ hiển thị như dưới đây:
Dựa vào cột y và y-predicted mà ta so sánh được kết quả giữa giá trị thực và giá trị dự báo:
Đường màu xanh là giá trị thực (y), đường màu cam là giá trị dự báo (y-predicted). Ta thấy kết quả dự báo khá sát với thực tế.
Bây giờ ta thử lập trình hồi quy đơn biến với Python:
import matplotlib.pyplot as plt
import numpy as np
x = np.array([[1,2,3,4,5,6,7,8,9,10]]).T
y = np.array([[2,4,3,6,9,12,13,15,18,20]]).T
def calculateb1b0(x,y):
# tính trung bình
xbar = np.mean(x)
ybar = np.mean(y)
x2bar = np.mean(x ** 2)
xybar = np.mean(x * y)
# tính b0, b1
b1 = (xbar * ybar - xybar) / (xbar ** 2 - (x2bar))
b0 = ybar - b1 * xbar
return b1,b0
#calulate b1, b0
b1,b0=calculateb1b0(x,y)
print("b1=",b1)
print("b0=",b0)
y_predicted=b0+b1*x
print(y_predicted)
Thực thi mã lệnh Python ở trên ta tính được b1 và b0 giống như Excel mà ta đã tính ở bên trên:
Tính được b1 và b0 coi như đã hoàn thành được phương trình hồi quy đơn biến. Vì có b1 và b0 ta tính được:
y_predicted=b0+b1*x
So sánh kết quả y_predicted cũng giống như Excel đã tính.
Ta tiếp tục bổ sung mã lệnh để trực quan hóa dữ liệu:
# Visualize data
def showGraph(x, y,y_predicted, title="", xlabel="", ylabel=""):
plt.figure(figsize=(14, 8))
plt.plot(x, y, 'r-o', label="value sample")
plt.plot(x, y_predicted, 'b-*', label="predicted value")
x_min = np.min(x)
x_max = np.max(x)
y_min = np.min(y)
y_max = np.max(y)
# mean y
ybar = np.mean(y)
plt.axhline(ybar, linestyle='--', linewidth=4, label="mean")
plt.axis([x_min*0.95, x_max*1.05, y_min*0.95, y_max*1.05])
plt.xlabel(xlabel, fontsize=16)
plt.ylabel(ylabel, fontsize=16)
plt.text(x_min, ybar*1.01, "mean", fontsize=16)
plt.legend(fontsize=15)
plt.title(title, fontsize=20)
plt.show()
showGraph(x, y,y_predicted,
title='Giá trị Y theo X',
xlabel='Giá trị X',
ylabel='Giá trị Y')
Chạy mã lệnh trên ta có kết quả:
Như vậy cách tính hồi quy đơn biến trong Excel và trong Python là giống nhau.
Sourecode đầy đủ của hồi quy đơn biến đối với bài học này:
import matplotlib.pyplot as plt
import numpy as np
x = np.array([[1,2,3,4,5,6,7,8,9,10]]).T
y = np.array([[2,4,3,6,9,12,13,15,18,20]]).T
def calculateb1b0(x,y):
# tính trung bình
xbar = np.mean(x)
ybar = np.mean(y)
x2bar = np.mean(x ** 2)
xybar = np.mean(x * y)
# tính b0, b1
b1 = (xbar * ybar - xybar) / (xbar ** 2 - (x2bar))
b0 = ybar - b1 * xbar
return b1,b0
#calulate b1, b0
b1,b0=calculateb1b0(x,y)
print("b1=",b1)
print("b0=",b0)
y_predicted=b0+b1*x
print(y_predicted)
# Visualize data
def showGraph(x, y,y_predicted, title="", xlabel="", ylabel=""):
plt.figure(figsize=(14, 8))
plt.plot(x, y, 'r-o', label="value sample")
plt.plot(x, y_predicted, 'b-*', label="predicted value")
x_min = np.min(x)
x_max = np.max(x)
y_min = np.min(y)
y_max = np.max(y)
# mean y
ybar = np.mean(y)
plt.axhline(ybar, linestyle='--', linewidth=4, label="mean")
plt.axis([x_min*0.95, x_max*1.05, y_min*0.95, y_max*1.05])
plt.xlabel(xlabel, fontsize=16)
plt.ylabel(ylabel, fontsize=16)
plt.text(x_min, ybar*1.01, "mean", fontsize=16)
plt.legend(fontsize=15)
plt.title(title, fontsize=20)
plt.show()
showGraph(x, y,y_predicted,
title='Giá trị Y theo X',
xlabel='Giá trị X',
ylabel='Giá trị Y')
Như vậy tới đây Tui đã hướng dẫn xong lý thuyết về hồi quy đơn biến, giải thích chi tiết các thông số trong công thức hồi quy đơn biến, cũng như các dữ kiện cần phải tính toán, và minh họa được cách tính hồi quy đơn biến trong Excel và bằng Python code.
Các bạn lưu ý thực hiện bài này nhiều lần, bài học sau Tui tiếp tục trình bày thêm một ví dụ về hồi quy đơn biến để dự báo giá nhà, cũng thực hiện trong Excel và trong Python code.
Như vậy các bạn đã sử dụng thành thạo kỹ thuật ORM trong Python với Cơ sở dữ liệu MySQL Server thông qua bài 50 và bài 51. Toàn bộ các chức năng CRUD trên các bảng dữ liệu các bạn đã có thể xem, thêm, sửa xóa với ORM technique, cũng như xử lý dữ liệu dạng Master-Details với các mối quan hệ has_many và belongs_to.
Bài này Tui tiếp tục hướng dẫn các bạn cách sử dụng ORM để tương tác dữ liệu với giao diện người dùng trong cơ sở dữ liệu Sakila đã đề cập ở bài học trước, chúng ta sẽ sử dụng bảng Customer, bảng Address và bảng Rental. Sakila là cơ sở dữ liệu mẫu có nhiều relationship khi chúng ta cài đặt My SQL Server, nên các bạn có thể sử dụng trực tiếp từ máy của bạn. Giao diện tương tác được thiết kế như đưới đây:
Phần mềm trên bao gồm các chức năng sử dụng kỹ thuật ORM được liệt kê như dưới đây:
(1) Nạp danh sách Customer vào QTableWidget
(2) Xem chi tiết Customer, khi người dùng nhấn chọn Customer nào trong QTableWidget thì thông tin chi tiết của Customer sẽ hiển thị và các ô QLineEdit ở bên phải màn hình
(3) Xem danh sách Rentals của Customer, khi người dùng nhấn chọn Customer nào trong QTableWidget thì danh sách Rentals của Customer ngày sẽ được nạp vào QTableWidget ở bên dưới màn hình.
(4) Chức năng “Clear”: Khi người dùng nhấn vào nút lệnh này thì toàn bộ dữ liệu trong phần Customer Details sẽ được xóa rỗng để người sử dụng nhập mới dữ liệu Customer
(5) Chức năng “Insert“: Khi người dùng nhấn vào nút lệnh này thì Customer sẽ được thêm mới vào trong cơ sở dữ liệu Sakila.
(6) Chức năng “Update“: Khi người dùng nhấn vào nút lệnh này thì Customer sẽ được cập nhật dữ liệu vào cơ sở dữ liệu Sakila.
(7) Chức năng “Delete“: Khi người dùng nhấn vào nút lệnh này thì Customer sẽ được xóa khỏi cơ sở dữ liệu Sakila. Có xác nhận xóa hay không
(8) Chức năng “Exit”: Xác nhận thoát phần mềm hay không.
Dưới đây là 3 bảng Customer, Address và Rental trong cơ sở dữ liệu mẫu Sakila khi bạn cài MySQL Server:
Lưu ý các mã lệnh cấu hình chuỗi kết nối cơ sở dữ liệu nó lệ thuộc vào máy tính của bạn, và cũng tương tự như các bài học trước.
Ta tiến hành thực hiện các bước sau để hoàn tất dự án:
Bước 0: Tạo dự án “sakila_orm_gui” trong Pycharm có cấu trúc như dưới đây:
Mô tả cho các thư mục, tập tin của dự án như sau:
Thư mục “Classes“: Chứa các tập tin các mã lệnh để kết nối cơ sở dữ liệu (DatabaseConnection.py), tạo các classes mapping cho các bảng Customer, Address và Rental (ClassMapping.py).
Thư mục “Images“: Thư mục chứa hình ảnh, icon cho phần mềm
Thư mục “UI“: Thư mục chứa các giao diện thiết kế phần mềm (MainWindow.ui), code generate Python cho giao diện(MainWindow.py), code Python kế thừa để xử lý tương tác người dùng(MainWindowEx.py)
Tập tin “MyApp.py“: Tập tin chứa mã lệnh để thực thi chương trình
Bước 1: Viết mã lệnh “DatabaseConnection.py“
from orm import Table
#configuration JSON for connection MySQL
CONFIG={
'host': 'localhost',
'port': 3306,
'user': 'root',
'password': '@Obama123',
'database': 'sakila'
}
#connect to database from JSON configuration
Table.connect(config_dict=CONFIG)
Mã lệnh trên cấu hình chuỗi kết nối tới MySQL Server, tùy thuộc vào sự cài đặt và cấu hình phần mềm của bạn mà chuỗi kết nối này sẽ khác nhau.
Sau khi có chuỗi kết nối, ta gọi phương thức connect() của Table.
Bước 2: Tạo các classes mapping, chúng được khai báo và tạo các relationship trong “ClassMapping.py“:
from orm import Table, has_many, belongs_to
class Customer(Table):
table_name = 'customer'
relations = [
has_many(name='rentals', _class='Rental', foreign_key='customer_id'),
has_many(name='addresses', _class='Address', foreign_key='address_id')
]
class Rental(Table):
table_name = 'rental'
relations = [
belongs_to(name='customer', _class='Customer', foreign_key='customer_id',primary_key="customer_id")
]
class Address(Table):
table_name = 'address'
Bảng customer được khai báo Mapping thành lớp Customer, bảng address được khai báo Mapping thành lớp Address. Customer và Address có mối quan hệ: 1 Customer có nhiều Address theo thiết kế. Trong lớp Customer các bạn thấy Tui khai báo relations has_many:
name “addresses”: Đây chính là phương thức addresses() trả về danh sách Address
_class=”Address”: Tức là khi gọi hàm addresses() chương trình sẽ trả về danh sách đối tượng có kiểu Address
foreign_key=”address_id”: Dựa vào khóa ngoại này để ứng với 1 Customer sẽ truy suất ra được danh sách Address, và theo quan sát dữ liệu Sakila thì tuy thiết kế 1 Customer có nhiều Address nhưng dữ liệu chỉ có 1 Address, tức là mảng addresses() khi gọi hàm này thì ta lấy phần tử đầu tiên chính là 1 Address của Customer mà ta quan tâm.
Ngoài ra Customer cũng có mối quan hệ với Rental. 1 Customer có nhiều Rentals, và 1 Rental thuộc về một Customer.
Ta thấy relations khai báo trong Customer liên quan tới Rental:
name “customer”: Đây chính là phương thức customer() tả 1 một đối tượng Customer
_class=”Customer”: Tức là khi gọi hàm customer() chương trình sẽ trả về đối tượng có kiểu Customer
foreign_key=”customer_id”: Dựa vào khóa ngoại này để ứng với 1 Customer sẽ truy suất ra được danh sách Rentals.
primary_key=”customer_id”: Dựa vào khóa này, thì từ Rental sẽ suy ra Customer đang sở hữu Rental này.
Bước 3: Thiết kế giao diện tương tác người dùng MainWindow.ui, sử dụng chức năng tích hợp PyQt6, Qt Designer trong Pycharm để tạo giao diện MainWindow.ui trong thư mục UI này, cấu trúc như sau:
Các bạn thiết kế giao diện và đặt tên cho các Widget như hình minh họa ở trên.
Bước 4: Generate Python code MainWindow.py cho giao diện MainWindow.ui, Chức năng Generate đã được học ở những bài đầu tiên, bạn chưa biết thì nhớ xem lại từ đầu:
Bước 5: Viết mã lệnh kế thừa để xử lý sự kiện tương tác người dùng cho giao diện ở trên, đặt tên “MainWindowEx.py“:
Bước 5.1: Tạo MainWindowEx kế thừa từ Ui_MainWindow được generate ra ở bước trước, và Khai báo các thư viện:
from datetime import datetime
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QTableWidgetItem, QMessageBox
from UI.MainWindow import Ui_MainWindow
import Classes.DatabaseConnection
from Classes.ClassMapping import Customer, Rental, Address
class MainWindowEx(Ui_MainWindow):
def __init__(self):
pass
Các bạn quan sát các thư viện sử dụng ở trên, đặc biệt cần khai báo DatabaseConnection trước ClasssMapping. Vì cần kết nối Cơ sở dữ liệu trước khi tạo các mapping và mối quan hệ.
Bước 5.2: Khai báo hàm setupUi() để thiết lập giao diện cho phần mềm, đồng thời viết các signals và slots cho các Widget:
Bước 5.3: Viết hàm nạp toàn bộ Customer lên QTableWidget bằng hàm showAllCustomerOnQTableWidget(), khi khởi động phần mềm, chương trình sẽ dùng ORM để truy vấn và mapping hướng đối tượng Customer, sau đó chúng ta nạp lên giao diện:
Chạy các mã lệnh này lên ta sẽ có giao diện như dưới đây:
Hình trên minh họa, người sử dụng chọn Customer có ID là 18, thì dữ liệu chi tiết của Customer sẽ được hiển thị ở mục Customer Details, đồng thời danh sách Rentals của Customer này cũng được hiển thị vào mục List of Rental QTableWidget ở bên dưới màn hình.
Bước 5.5: Viết hàm xử lý sự kiện khi nhấn vào nút “Clear”
Sau khi người dùng nhập liệu và nhấn nút “Insert” thì dữ liệu Customer được lưu thành công và hiển thị lại lên giao diện.
Coding ở trên vì Sakila design store_id và address_id là not null, nên Tui tạm thời để là 1. Dưới đây là minh họa chức năng INSERT khi chạy phần mềm:
Vì Customer mới nên Rentals không có do đó QTableWidget List of Rental rỗng.
Và bạn cũng thấy Last Update đang None vì mới Insert chưa có Update.
Bước 5.7: Viết hàm xử lý sự kiện khi nhấn vào nút “Update“
def processUpdate(self):
customer_id = int(self.lineEditCustomerId.text())
# process for selected Customer details
customer = Customer.find(customer_id)
fname = self.lineEditFirstName.text()
lname = self.lineEditLastName.text()
email = self.lineEditEmail.text()
if self.checkBoxActive.isChecked():
active = 1
else:
active = 0
customer.update(first_name=fname,last_name=lname,email=email,active=active,last_update=datetime.now())
self.showAllCustomerOnQTableWidget()
Mã lệnh ở trên sẽ cập nhật dữ liệu Customer theo ORM. Khi thực hiện thành công chương trình sẽ cập nhật lại giao diện. Kết quả minh họa:
Ta thấy nếu dùng chức năng Update, thì Last Update sẽ có giá trị.
Bước 5.8: Viết hàm xử lý sự kiện khi nhấn vào nút “Delete“, chương trình sẽ xóa Customer theo primary customer_id:
def processDelete(self):
customer_id = int(self.lineEditCustomerId.text())
# process for selected Customer details
customer = Customer.find(customer_id)
dlg = QMessageBox(self.MainWindow)
dlg.setWindowTitle("Confirmation Deleting")
dlg.setIcon(QMessageBox.Icon.Critical)
dlg.setText("Are you sure you want to delete?")
buttons = QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
dlg.setStandardButtons(buttons)
button = dlg.exec()
if button == QMessageBox.StandardButton.Yes:
customer.destroy()
self.processClear()
self.showAllCustomerOnQTableWidget()
Chương trình yêu cầu xác nhận có muốn xóa hay không, nếu đồng ý thì sẽ xóa.
mã lệnh ở trên ta thực hiện:
Xóa Customer hiện tại đang chọn khỏi cơ sở dữ liệu
Xóa trống các dữ liệu của Customer vừa xóa ra khỏi giao diện Details
Nạp lại dánh sách Customer cho QTableWidget phần List Customers
Chạy thử nghiệm ta có:
Trường hợp 1: Nếu xóa Customer đã có Rental, chương trình sẽ báo lỗi, vì chúng ta cần xóa hết Rentals của Customer này đã
Trường hợp 2: Xóa Customer vừa thêm vào, sẽ thành công
Các bạn tự xử lý thêm mã lệnh
Cuối cùng ta vào chức năng thoát phần mềm:
Bước 5.9: Viết hàm xử lý sự kiện thoát phần mềm:
def processExit(self):
dlg = QMessageBox(self.MainWindow)
dlg.setWindowTitle("Confirmation Exit")
dlg.setText("Are you sure you want to Exit?")
dlg.setIcon(QMessageBox.Icon.Question)
buttons = QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
dlg.setStandardButtons(buttons)
button = dlg.exec()
if button == QMessageBox.StandardButton.Yes:
exit()
Chương trình sẽ xác nhận người dùng muốn thoát hay không, nếu đồng ý sẽ thoát:
Dưới đây là mã lệnh tổng hợp của MainWindowEx.py:
from datetime import datetime
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QTableWidgetItem, QMessageBox
from UI.MainWindow import Ui_MainWindow
import Classes.DatabaseConnection
from Classes.ClassMapping import Customer, Rental, Address
class MainWindowEx(Ui_MainWindow):
def __init__(self):
pass
def setupUi(self, MainWindow):
super().setupUi(MainWindow)
self.MainWindow=MainWindow
self.showAllCustomerOnQTableWidget()
self.tableWidgetCustomer.itemSelectionChanged.connect(self.processItemSelection)
self.pushButtonClear.clicked.connect(self.processClear)
self.pushButtonInsert.clicked.connect(self.processInsert)
self.pushButtonUpdate.clicked.connect(self.processUpdate)
self.pushButtonDelete.clicked.connect(self.processDelete)
self.pushButtonExit.clicked.connect(self.processExit)
def showWindow(self):
self.MainWindow.show()
def showAllCustomerOnQTableWidget(self):
customers = Customer.all()
self.tableWidgetCustomer.setRowCount(0)
row = 0
for cust in customers:
row = self.tableWidgetCustomer.rowCount()
self.tableWidgetCustomer.insertRow(row)
self.tableWidgetCustomer.setItem(row, 0, QTableWidgetItem(str(cust.customer_id)))
self.tableWidgetCustomer.setItem(row, 1, QTableWidgetItem(cust.first_name))
self.tableWidgetCustomer.setItem(row, 2, QTableWidgetItem(cust.last_name))
if cust.active==0:
self.tableWidgetCustomer.item(row,0).setBackground(Qt.GlobalColor.red)
self.tableWidgetCustomer.item(row, 1).setBackground(Qt.GlobalColor.red)
self.tableWidgetCustomer.item(row, 2).setBackground(Qt.GlobalColor.red)
def processItemSelection(self):
row = self.tableWidgetCustomer.currentRow()
if row == -1:
return
customer_id=int(self.tableWidgetCustomer.item(row,0).text())
#process for selected Customer details
customer = Customer.find(customer_id)
self.lineEditCustomerId.setText(str(customer.customer_id))
self.lineEditFirstName.setText(customer.first_name)
self.lineEditLastName.setText(customer.last_name)
self.lineEditEmail.setText(customer.email)
if customer.active==1:
self.checkBoxActive.setChecked(True)
else:
self.checkBoxActive.setChecked(False)
self.lineEditCreateDate.setText(str(customer.create_date))
self.lineEditLastUpdate.setText(str(customer.last_update))
#process for address table
addresses=customer.addresses()
address=addresses[0]
self.lineEditAddress.setText(address.address)
#process for rentals
self.showAllRentalOnQTableWidget(customer)
def showAllRentalOnQTableWidget(self,customer):
rentals = customer.rentals()
self.tableWidgetRental.setRowCount(0)
row = 0
for rental in rentals:
row = self.tableWidgetRental.rowCount()
self.tableWidgetRental.insertRow(row)
self.tableWidgetRental.setItem(row, 0, QTableWidgetItem(str(rental.rental_id)))
self.tableWidgetRental.setItem(row, 1, QTableWidgetItem(str(rental.rental_date)))
self.tableWidgetRental.setItem(row, 2, QTableWidgetItem(str(rental.inventory_id)))
self.tableWidgetRental.setItem(row, 3, QTableWidgetItem(str(rental.customer_id)))
self.tableWidgetRental.setItem(row, 4, QTableWidgetItem(str(rental.return_date)))
self.tableWidgetRental.setItem(row, 5, QTableWidgetItem(str(rental.staff_id)))
self.tableWidgetRental.setItem(row, 6, QTableWidgetItem(str(rental.last_update)))
def processClear(self):
self.lineEditCustomerId.setText("")
self.lineEditFirstName.setText("")
self.lineEditLastName.setText("")
self.lineEditEmail.setText("")
self.checkBoxActive.setChecked(False)
self.lineEditCreateDate.setText("")
self.lineEditLastUpdate.setText("")
self.lineEditAddress.setText("")
self.lineEditCustomerId.setFocus()
def processInsert(self):
# Insert new Student Object:
new_customer = Customer()
new_customer.first_name=self.lineEditFirstName.text()
new_customer.last_name = self.lineEditLastName.text()
new_customer.email = self.lineEditEmail.text()
if self.checkBoxActive.isChecked():
new_customer.active=1
else:
new_customer.active = 0
new_customer.store_id=1
new_customer.address_id=1
new_customer.save()
self.lineEditCustomerId.setText(str(new_customer.customer_id))
self.showAllCustomerOnQTableWidget()
def processUpdate(self):
customer_id = int(self.lineEditCustomerId.text())
# process for selected Customer details
customer = Customer.find(customer_id)
fname = self.lineEditFirstName.text()
lname = self.lineEditLastName.text()
email = self.lineEditEmail.text()
if self.checkBoxActive.isChecked():
active = 1
else:
active = 0
customer.update(first_name=fname,last_name=lname,email=email,active=active,last_update=datetime.now())
self.showAllCustomerOnQTableWidget()
def processDelete(self):
customer_id = int(self.lineEditCustomerId.text())
# process for selected Customer details
customer = Customer.find(customer_id)
dlg = QMessageBox(self.MainWindow)
dlg.setWindowTitle("Confirmation Deleting")
dlg.setIcon(QMessageBox.Icon.Critical)
dlg.setText("Are you sure you want to delete?")
buttons = QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
dlg.setStandardButtons(buttons)
button = dlg.exec()
if button == QMessageBox.StandardButton.Yes:
customer.destroy()
self.processClear()
self.showAllCustomerOnQTableWidget()
def processExit(self):
dlg = QMessageBox(self.MainWindow)
dlg.setWindowTitle("Confirmation Exit")
dlg.setText("Are you sure you want to Exit?")
dlg.setIcon(QMessageBox.Icon.Question)
buttons = QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
dlg.setStandardButtons(buttons)
button = dlg.exec()
if button == QMessageBox.StandardButton.Yes:
exit()
Bước 6: Cuối cùng ta khai báo MyApp.py để thực thi chương trình
from PyQt6.QtWidgets import QApplication, QMainWindow
from UI.MainWindowEx import MainWindowEx
qApplication=QApplication([])
qMainWindow=QMainWindow()
myWindow=MainWindowEx()
myWindow.setupUi(qMainWindow)
myWindow.showWindow()
qApplication.exec()
Thực thi mã lệnh trên, ta có phần mềm như mong muốn:
Như vậy, Tui đã hướng dẫn xong chi tiết cách sử dụng kỹ thuật ORM để lập trình xử lý giao diện tương tác người dùng cho cơ sở dữ liệu Sakila, làm việc trên 3 bảng có mối quan hệ: Customer, Address, Rental.
Đã minh họa đầy đủ các chức năng CRUD tương ứng với kỹ thuật ORM, các bạn chú ý làm lại nhiều lần để hiểu hơn về dự án.
Trong bài 50, chúng ta đã biết cách sử dụng kỹ thuật lập trình ORM để tương tác cơ sở dữ liệu MySQL Server bằng Python. Tuy nhiên chúng ta chưa đề cập tới vấn đề cả bảng dữ liệu có mối quan hệ dạng Master-Details.
Ví dụ như trong Quản lý Sản phẩm ta có Danh mục, ta có sản phẩm. 1 Danh mục có nhiều sản phẩm, và 1 sản phẩm thuộc về 1 danh mục. Vậy với trường hợp có relationship master-details như thế này thì chúng ta lập trình ORM ra sao? và trong thực tế chúng ta luôn luôn gặp các relationship master-details này.
Hay ngay trong quá trình cài đặt MySQL Server, chúng ta cũng được cung cấp một cơ sở dữ liệu mẫu “sakila“, là một cơ sở dữ liệu liên quan tới quản lý cho mượn Film, và cũng có nhiều relationship Master-Details, chúng ta sẽ lập trình ORM trên cơ sở dữ liệu mẫu này luôn (ta làm trên bảng Customer và bảng Rental):
Ta xem Relationship giữa bảng Customer và bảng Rental:
Một Customer có nhiều Rental (dựa vào khóa ngoại customer_id trong bảng rental, khi có 1 Customer ta sẽ biết được Customer này có bao nhiêu Rental)
Một Rental thuộc về một Customer (từ bảng rental ta có thuộc tính customer_id để suy ngược ra thông tin chi tiết Customer của Rental)
Đây chính là Relationship 1-many.
Ta tạo dự án “sakila_orm” trong Pycharm có cấu trúc như dưới đây:
Trong dự án này Tui chủ ý tách ra làm 3 file mã lệnh Python độc lập để có thể dễ dàng tái sử dụng:
File “DatabaseConnection.py“: Là file mã lệnh để kế nối Cơ sở dữ liệu, nó phải được thực hiện trước các câu lệnh khác trong dự án
File “ClassMapping.py“: Là file mã lệnh khai báo class mapping và relationship
File “TestRelationShipORM.py“: Là file mã lệnh để thử nghiệm kỹ thuật ORM với các bảng có Relationship
Bước 1:
Bây giờ ta xem mã lệnh của DatabaseConnection.py:
from orm import Table
#configuration JSON for connection MySQL
CONFIG={
'host': 'localhost',
'port': 3306,
'user': 'root',
'password': '@Obama123',
'database': 'sakila'
}
#connect to database from JSON configuration
Table.connect(config_dict=CONFIG)
Mã lệnh kết nối cơ sở dữ liệu được tách độc lập ra 1 file khác, khi có sự thay đổi về thông tin kết nối thì bạn chỉ cần vào file này chỉnh sửa sẽ không bị rối.
Bước 2:
Tiếp theo là mã lệnh File “ClasssMapping.py“:
Dựa vào cấu trúc bảng Customer và Rental, Ta có 1 Customer có nhiều Rental, nên ở đây trong lớp Customer ta dùng hàm has_many. Còn đối với lớp Rental thì ứng với 1 đối tượng Rental ta biết được Rental này của Customer nào nên ta dùng hàm belongs_to:
from orm import Table, has_many, belongs_to
class Customer(Table):
table_name = 'customer'
relations = [
has_many(name='rentals', _class='Rental', foreign_key='customer_id')
]
class Rental(Table):
table_name = 'rental'
relations = [
belongs_to(name='customer', _class='Customer', foreign_key='customer_id',primary_key="customer_id")
]
hàm has_many:
Hàm has_many cho biết đối tượng này chứa nhiều đối tượng khác.
+ name=’rentals’: Đây là phương thức rentals() khi gọi phương thức này nó sẽ trả về danh sách đối tượng có kiểu Rental được khai báo trong _class
+_class: Đây là tham số định nghĩa kiểu dữ liệu đối tượng trả về cho hàm “rentals”
+foreign_key=’customer_id’: Cho biết relationship giữa Customer và Rental, khi truy vấn thì dựa vào thuộc tính này chương trình sẽ lọc ra danh sách các Rental theo foreign_key
hàm belongs_to:
hàm belongs_to cho biết đối tượng này thuộc về đối tượng khác
+name=’customer’: Là là phương thức customer() trả về đối tượng Customer của Rental đang xét. Kiểu dữ liệu trả về được khai báo trong _class là ‘Customer’
+_class=’Customer’: Đầy là tham số định nghĩa kiểu dữ liệu đối tượng trả về cho hàm customer()
+foreign_key=’customer_id’: Cho biết relationship giữa Customer và Rental, khi truy vấn thì dựa vào thuộc tính này thì chương trình sẽ cho biết mã tham chiếu tới customer hiện tại của Rental là customer_id
+primary_key=’customer_id’: Khi gọi hàm customer() sẽ truy vấn ngược lại bảng Customer dựa vào primary_key customer_id này.
Bước 3:
Cuối cùng ta viết mã lệnh cho file “TestRelationshipORM.py“:
Đâu tiên chúng ta cần import DatabaseConnection, sau đó tới MappingClass:
import DatabaseConnection
from ClassMapping import Customer, Rental
Lưu ý cần theo đúng trình tự Import ở trên, vì chương trình cần kết nối cơ sở dữ liệu trước sau đó mới tới Mapping các Class cho Table.
Tiếp theo, để truy vấn toàn bộ Customer, ta viết nối tiếp mã lệnh như sau:
#Get all Customer
customers = Customer.all()
align='{0:<6} {1:<10} {2:<10} {3:<10}'
print(align.format('Id', 'First Name','Last Name',"Email"))
for cust in customers:
print(align.format(cust.customer_id,cust.first_name,cust.last_name,cust.email))
Chạy mã lệnh trên ta có kết quả:
Để truy vấn lấy thông tin chi tiết của Customer khi biết Primary Key:
Mã lệnh ở trên là truy vấn thông tin chi tiết của Rental khi biết rental_id=2
Thực thi lệnh trên ta có kết quả:
Bây giờ, để in thông tin chi tiết Customer của Rental này ta viết mã lệnh:
# Get Customer of this Rental:
customer=rental.customer()
print(f"Id={customer.customer_id}")
print(f"First Name={customer.first_name}")
print(f"Last Name={customer.last_name}")
print(f"Email={customer.email}")
Thực thi lệnh trên ta có kết quả:
Như vậy Tui đã minh họa xong kỹ thuật ORM để xử lý các bảng có Relationship. Đây là bài học rất quan trọng và cần thiết vì nó minh họa trường hợp trong thực tế mà ta thường gặp, hầu hết các bảng dữ liệu đều có các mối quan hệ này.
Dưới đây là mã lệnh đầy đủ của TestRelationshipORM.py:
import DatabaseConnection
from ClassMapping import Customer, Rental
#Get all Customer
customers = Customer.all()
align='{0:<6} {1:<10} {2:<10} {3:<10}'
print(align.format('Id', 'First Name','Last Name',"Email"))
for cust in customers:
print(align.format(cust.customer_id,cust.first_name,cust.last_name,cust.email))
#Query 1 Customer by primary key customer_id=1:
customer = Customer.find(1)
print(f"Id={customer.customer_id}")
print(f"First Name={customer.first_name}")
print(f"Last Name={customer.last_name}")
print(f"Email={customer.email}")
#Get all rentals for this customer:
rentals=customer.rentals()
align='{0:<10} {1:<20} {2:<20} {3:<10} {4:<10}'
print(align.format('Rental Id', 'Rental Date','Return Date',"Staff Id","Customer Id"))
for rental in rentals:
print(align.format(
rental.rental_id,
str(rental.rental_date),
str(rental.return_date),
rental.staff_id,
rental.customer_id))
#Query Rental detail by primary key rental_id=2
rental=Rental.find(2)
print(align.format('Rental Id', 'Rental Date','Return Date',"Staff Id","Customer Id"))
print(align.format(
rental.rental_id,
str(rental.rental_date),
str(rental.return_date),
rental.staff_id,
rental.customer_id))
# Get Customer of this Rental:
customer=rental.customer()
print(f"Id={customer.customer_id}")
print(f"First Name={customer.first_name}")
print(f"Last Name={customer.last_name}")
print(f"Email={customer.email}")
Ngoài ra bạn có thể tải mã nguồn của toàn bộ dự án ở đây:
Bài 48 và bài 49 các bạn đã lập trình thành thạo Python với MySQL Server, đã thực hiện đầy đủ các chức năng CRUD liên quan tới phần mềm quản lý.
Tuy nhiên, các cách lập trình ấy có một vài khuyết điểm như: Tốn thời gian viết các mã lệnh tương tác cơ sở dữ liệu, tốn thời gian mô hình hóa hướng đối tượng.
Các kỹ sư phần mềm họ đã phát triển một kỹ thuật ORM rất lợi hại để mô hình hóa đối tượng với các bảng dữ liệu một cách nhanh chóng.
ORM (Object Relational Mapping) là kỹ thuật trong lập trình phần mềm giúp ánh xạ dữ liệu từ cơ sở dữ liệu quan hệ vào các đối tượng trong mã nguồn. Điều này giúp giảm sự phụ thuộc vào cấu trúc của cơ sở dữ liệu, đồng thời tạo ra một cách tiếp cận linh hoạt hơn trong việc quản lý dữ liệu:
Tóm tắt một số Ưu điểm chính của ORM (hình nguồn medium):
ORM giúp người lập trình tập trung hơn vào việc lập trình hướng đối tượng
Cho phép truy cập vào code nghiệp vụ thay vì database
Hạn chế những lỗi ngữ pháp trong SQL
Quản lý Transaction và tạo key tự động
Đơn giản và dễ sử dụng
Ẩn chi tiết của những truy vấn SQL từ Object Oriented logic (OO-logic)
Đem lại năng suất cao hơn cho lập trình viên
Nâng cao tính độc lập
Năng suất hơn nhờ việc viết code ít hơn
Cho phép lập trình viên sử dụng lại code
ORM Framework cho phép truy xuất nhanh hơn bằng cách cache dữ liệu
Tự động thực hiện những thao tác với dữ liệu
Python cũng giống như các công ngữ lập trình C#, Java, nó cũng được cung cấp các thư viện khác nhau để thực hiện ORM:
Thông qua thư viện Python thì cơ sở dữ liệu sẽ được mapping hướng đối tượng theo nguyên tắc(hình nguồn medium) :
Tên bảng <-> Tên lớp
Các cột trong bảng<-> Thuộc tính của lớp
Từng dòng dữ liệu trong bảng <-> Các đối tượng của lớp
Ví dụ: Giả sử ta có bảng Person dưới đây, bảng này có 4 cột ID (Auto Increment), First_Name, Last_Name và Phone. Khi ORM ta có:
Tạo mới đối tượng ta không cần ID vì ID auto increment và tự động phát sinh giá trị khi chúng ta lưu thành công, khi truy vấn thì nó tự động có ID có giá trị được mapping lên.
Chú ý là bài học này ta vẫn sử dụng “Student Management” đã tạo trước đó. Nên để học tốt bài này thì các bạn cần học bài 47, bài 48 và bài 49 trước.
Chuỗi bài học này sẽ hướng dẫn các bạn sử dụng orm-mysql
Bước 0:Chuẩn bị cơ sở dữ liệu Student Management ở bài học trước:
Bước 1: cài đặt orm-mysql
pip install orm-mysql
Mở command line và chạy lệnh trên, ta có kết quả:
Bước 2: Tạo dự án “LearnMySQLORM” trong Pycharm, trong dự án này tạo file “TestORM.py” và tiến hành viết mã lệnh theo các bước sau:
Bước 3: Tạo file cấu hình kết nối cơ sở dữ liệu và tiến hành kết nối
from orm import Table, get_table
#configuration JSON for connection MySQL
CONFIG={
'host': 'localhost',
'port': 3306,
'user': 'root',
'password': '@Obama123',
'database': 'studentmanagement'
}
#connect to database from JSON configuration
Table.connect(config_dict=CONFIG)
Ở trên ta dùng phương thức connect của Table để tiến hành kết nối cơ sở dữ liệu
Bước 4: Kết nối và mapping bảng dữ liệu, ví dụ ta muốn ORM cho bảng student:
#Query all students:
students = Student.all()
align='{0:<3} {1:<6} {2:<18} {3:<10}'
print(align.format('ID', 'Code','Name',"Age"))
for stu in students:
print(align.format(stu.ID,stu.Code,stu.Name,stu.Age))
lệnh trên ta gọi hàm get_table(“student”), hàm này giúp ta mapping bảng student vào lớp đối tượng Student.
phương thức all() dùng để truy vấn toàn bộ dữ liệu Sinh viên
Kết quả thi khi thực hiện lệnh truy vấn toàn bộ Sinh viên:
Bước 6: Để tìm Student theo ID (tìm chi tiết, ta thường sử dụng)
#Find student detail by ID (eg: 2)
student = Student.find(2)
print("Id=",student.ID)
print("code=",student.Code)
print("name=",student.Name)
print("age=",student.Age)
Chạy lệnh trên ta có kết quả là chi tiết của Sinh viên có ID=2 được xuất ra màn hình:
Bước 7: Để thêm mới một Student:
#Insert new Student Object:
new_student = Student(Code="SV300",Name='Đông Tà',Age=25)
new_student.save()
Khi chạy mã lệnh trên, chương trình sẽ tự động thực hiện câu truy vấn:
cột nào không có giá trị nó sẽ tự động có giá trị NULL
Thực hiện lệnh trên thành công thì MySQL Server sẽ có dữ liệu mới với thông tin được cung cấp ở trên.
Bước 8: Để chỉnh sử dữ liệu cho đối tượng Student:
#Update student detail by ID 3
student = Student.find(3)
student.update(Name="Nguyễn Thị Lung Linh",Age=23)
Coding trên minh họa việc chỉnh tên và tuổi của Student có mã Id=3
Chạy lệnh trên ta có kết quả:
Bước 9: Để xóa Student ta viết:
#Remove student by ID 15
student = Student.find(15)
student.destroy()
Chạy mã lệnh, ta không còn thấy Sinh viên Đông Tà có mã 15 nữa.
Toàn bộ Coding ORM cho chức năng CRUD Tui tổng hợp ở đây:
from orm import Table, get_table
#configuration JSON for connection MySQL
CONFIG={
'host': 'localhost',
'port': 3306,
'user': 'root',
'password': '@Obama123',
'database': 'studentmanagement'
}
#connect to database from JSON configuration
Table.connect(config_dict=CONFIG)
#mapping student table
Student = get_table('student')
#Query all students:
students = Student.all()
align='{0:<3} {1:<6} {2:<18} {3:<10}'
print(align.format('ID', 'Code','Name',"Age"))
for stu in students:
print(align.format(stu.ID,stu.Code,stu.Name,stu.Age))
#Find student detail by ID (eg: 2)
student = Student.find(2)
print("Id=",student.ID)
print("code=",student.Code)
print("name=",student.Name)
print("age=",student.Age)
#Insert new Student Object:
new_student = Student(Code="SV300",Name='Đông Tà',Age=25)
new_student.save()
#Update student detail by ID 3
student = Student.find(3)
student.update(Name="Nguyễn Thị Lung Linh",Age=23)
#Remove student by ID 15
student = Student.find(15)
student.destroy()
Như vậy tới đây Tui đã trình bày xong ORM liên quan tới 1 bảng dữ liệu, đủ các chức năng CRUD. Các bạn chú ý thực hành nhiều lần để hiểu về nó, thành thạo về ORM.
Bài học sau Tui sẽ tiếp tục hướng dẫn ORM , tuy nhiên nâng cao hơn ở chỗ thực hiện ORM với các bảng có mối quan hệ, ví dụ Sinh viên – Môn học. Các bạn chú ý theo dõi, đây là các bài học quan trọng và thực tế
Trong bài 48 các bạn đã biết cách sử dụng ngôn ngữ lập trình Python để tương tác cơ sở dữ liệu MySQL Server “studentmanagement”, Các bạn đã lập trình nhuần nhuyễn CRUD để thêm mới dữ liệu, truy vấn dữ liệu, sắp xếp dữ liệu, cập nhật dữ liệu, xóa dữ liệu, phân trang dữ liệu….
Bài học này chúng ta sẽ ứng dụng để thiết kế giao diện tương tác người dùng, giao diện như dưới đây:
Phần mềm sử dụng cơ sở dữ liệu và các mã lệnh đã học ở các bài trước.
Phần mềm gồm có các chức năng sau:
(1) Tải toàn bộ dữ liệu từ bảng student lên QTableWidget
(2) “New”: Xóa dữ liệu trong các Widget để cho nhập mới dữ liệu
(3) “Insert”: Thêm mới student vào cơ sở dữ liệu MySQL
(4) “Update”: Chỉnh sữa dữ liệu student
(5) “Remove”: Xóa Student khỏi cơ sở dữ liệu MySQL
(6) “Avatar”/”Remove Avatar”: Chọn và xóa Avatar. Hình Ảnh sẽ được mã hóa thành base64string
(7) Xem chi tiết student: Nhấn chọn student trong QTableWidget, thông tin chi tiết của Student sẽ hiển thị vào các Widget cơ bản
Ta đi vào chi tiết:
Bước 1: Tạo cấu trúc thư mục và tập tin cho dự án “StudentManagement” như dưới đây:
images: Thư mục này lưu các hình ảnh, icon của phần mềm, tùy bạn lựa chọn
MainWindow.ui: Tập tin giao diện phần mềm
MainWindow.py: Tập tin python gencode của giao diện
MainWindowEx.py: Tập tin kết thừa lớp giao diện được tạo ra từ MainWindow.py
MyApp.py: Tập tin thực thi phần mềm
Bước 2: Bạn thiết kế giao diện như hình dưới đây:
Thiết kế giao diện, đặt tên các Widget như hình trên.