📈 Phân tích dữ liệu kết hợp: Phân tích dữ liệu bán hàng từ SQL Server bằng Python và trực quan hóa với Matplotlib

Khả năng kết hợp sức mạnh của SQL Server để lưu trữ dữ liệu và Python để phân tích là một kỹ năng rất có giá trị trong lĩnh vực phân tích dữ liệu. Bài viết này sẽ hướng dẫn chi tiết quy trình phân tích dữ liệu bán hàng từ SQL Server bằng Python và cách trực quan hóa kết quả với thư viện Matplotlib.
Tại sao kết hợp SQL Server và Python?

Kết hợp SQL Server và Python mang lại nhiều lợi ích:
- Quản lý dữ liệu hiệu quả: SQL Server là hệ quản trị cơ sở dữ liệu mạnh mẽ, có khả năng xử lý lượng dữ liệu lớn
- Phân tích linh hoạt: Python với các thư viện như Pandas, NumPy, SciPy cung cấp công cụ phân tích dữ liệu linh hoạt
- Trực quan hóa đa dạng: Matplotlib, Seaborn, Plotly cho phép tạo các biểu đồ chuyên nghiệp
- Mở rộng đến ML/AI: Dễ dàng mở rộng từ phân tích cơ bản đến machine learning với scikit-learn, TensorFlow, PyTorch
- Quy trình ETL hoàn chỉnh: Xử lý toàn bộ quy trình Extract-Transform-Load
Chuẩn bị môi trường
Trước khi bắt đầu, chúng ta cần cài đặt các thư viện Python cần thiết:
pip install pyodbc pandas numpy matplotlib seaborn
Kết nối đến SQL Server và truy vấn dữ liệu
Đầu tiên, chúng ta cần thiết lập kết nối đến SQL Server và truy vấn dữ liệu bán hàng:
import pyodbc
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import os
from datetime import datetime, timedelta
# Cấu hình biểu đồ với phong cách đẹp
plt.style.use('ggplot')
sns.set(style="whitegrid")
# Thiết lập kết nối đến SQL Server
def get_connection():
server = 'your_server_name'
database = 'your_database'
username = 'your_username'
password = 'your_password'
driver = '{ODBC Driver 17 for SQL Server}'
connection_string = f'DRIVER={driver};SERVER={server};DATABASE={database};UID={username};PWD={password}'
try:
conn = pyodbc.connect(connection_string)
print("Kết nối thành công đến SQL Server!")
return conn
except Exception as e:
print(f"Lỗi kết nối: {e}")
return None
# Lấy dữ liệu bán hàng
def get_sales_data(conn, start_date=None, end_date=None):
if not start_date:
# Mặc định lấy dữ liệu 6 tháng gần nhất
start_date = (datetime.now() - timedelta(days=180)).strftime('%Y-%m-%d')
if not end_date:
end_date = datetime.now().strftime('%Y-%m-%d')
query = f"""
SELECT
s.OrderDate,
p.ProductName,
c.CategoryName,
r.RegionName,
s.Quantity,
s.UnitPrice,
s.Quantity * s.UnitPrice AS SalesAmount
FROM
Sales s
JOIN Products p ON s.ProductID = p.ProductID
JOIN Categories c ON p.CategoryID = c.CategoryID
JOIN Regions r ON s.RegionID = r.RegionID
WHERE
s.OrderDate BETWEEN '{start_date}' AND '{end_date}'
ORDER BY
s.OrderDate
"""
try:
df = pd.read_sql(query, conn)
print(f"Đã truy vấn {len(df)} bản ghi dữ liệu bán hàng.")
return df
except Exception as e:
print(f"Lỗi khi truy vấn dữ liệu: {e}")
return None
# Kết nối và lấy dữ liệu
conn = get_connection()
if conn:
sales_df = get_sales_data(conn)
# Đóng kết nối sau khi lấy dữ liệu
conn.close()
else:
# Tạo dữ liệu mẫu cho mục đích thử nghiệm
print("Sử dụng dữ liệu mẫu do không thể kết nối đến SQL Server")
# Tạo dữ liệu mẫu với 200 bản ghi
np.random.seed(42)
dates = pd.date_range(start='2023-01-01', end='2023-06-30')
products = ['Laptop', 'Smartphone', 'Tablet', 'Headphones', 'Monitor', 'Keyboard', 'Mouse', 'Printer']
categories = ['Electronics', 'Computers', 'Accessories', 'Office Equipment']
regions = ['North', 'South', 'East', 'West', 'Central']
sales_df = pd.DataFrame({
'OrderDate': np.random.choice(dates, 200),
'ProductName': np.random.choice(products, 200),
'CategoryName': np.random.choice(categories, 200),
'RegionName': np.random.choice(regions, 200),
'Quantity': np.random.randint(1, 10, 200),
'UnitPrice': np.random.uniform(50, 1000, 200).round(2)
})
sales_df['SalesAmount'] = sales_df['Quantity'] * sales_df['UnitPrice']
sales_df = sales_df.sort_values('OrderDate')
Phân tích khám phá dữ liệu (EDA)
Sau khi có dữ liệu, chúng ta sẽ thực hiện phân tích khám phá dữ liệu:
# Hiển thị thông tin tổng quan về dữ liệu
print("Thông tin dữ liệu:")
print(sales_df.info())
print("\nThống kê mô tả:")
print(sales_df.describe())
# Kiểm tra giá trị thiếu
print("\nGiá trị thiếu trong từng cột:")
print(sales_df.isnull().sum())
# Chuyển đổi OrderDate thành datetime nếu chưa phải
if not pd.api.types.is_datetime64_any_dtype(sales_df['OrderDate']):
sales_df['OrderDate'] = pd.to_datetime(sales_df['OrderDate'])
# Tạo các cột thời gian hữu ích
sales_df['Year'] = sales_df['OrderDate'].dt.year
sales_df['Month'] = sales_df['OrderDate'].dt.month
sales_df['MonthName'] = sales_df['OrderDate'].dt.strftime('%b')
sales_df['Day'] = sales_df['OrderDate'].dt.day
sales_df['WeekDay'] = sales_df['OrderDate'].dt.weekday
sales_df['WeekDayName'] = sales_df['OrderDate'].dt.strftime('%a')
Phân tích doanh số bán hàng theo thời gian
Bây giờ chúng ta sẽ phân tích doanh số bán hàng theo thời gian:
# 1. Doanh số theo tháng
monthly_sales = sales_df.groupby(['Year', 'Month', 'MonthName'])['SalesAmount'].sum().reset_index()
monthly_sales = monthly_sales.sort_values(['Year', 'Month'])
plt.figure(figsize=(12, 6))
plt.bar(monthly_sales['MonthName'], monthly_sales['SalesAmount'], color='skyblue')
plt.title('Doanh số bán hàng theo tháng', fontsize=16)
plt.xlabel('Tháng', fontsize=12)
plt.ylabel('Doanh số (VND)', fontsize=12)
plt.xticks(rotation=45)
plt.grid(axis='y', alpha=0.3)
# Thêm giá trị doanh số lên cột
for i, v in enumerate(monthly_sales['SalesAmount']):
plt.text(i, v + 0.1, f'{v:,.0f}', ha='center', fontsize=9)
plt.tight_layout()
plt.savefig('sales_by_month.png')
plt.show()
# 2. Doanh số theo ngày trong tuần
weekday_sales = sales_df.groupby(['WeekDay', 'WeekDayName'])['SalesAmount'].sum().reset_index()
weekday_sales = weekday_sales.sort_values('WeekDay')
plt.figure(figsize=(10, 6))
ax = sns.barplot(x='WeekDayName', y='SalesAmount', data=weekday_sales, palette='viridis')
plt.title('Doanh số bán hàng theo ngày trong tuần', fontsize=16)
plt.xlabel('Ngày trong tuần', fontsize=12)
plt.ylabel('Doanh số (VND)', fontsize=12)
plt.grid(axis='y', alpha=0.3)
# Thêm giá trị doanh số lên cột
for i, p in enumerate(ax.patches):
ax.annotate(f'{p.get_height():,.0f}',
(p.get_x() + p.get_width() / 2., p.get_height()),
ha='center', va='center', fontsize=9, color='black',
xytext=(0, 10), textcoords='offset points')
plt.tight_layout()
plt.savefig('sales_by_weekday.png')
plt.show()
# 3. Xu hướng doanh số theo thời gian
daily_sales = sales_df.groupby('OrderDate')['SalesAmount'].sum().reset_index()
plt.figure(figsize=(14, 7))
plt.plot(daily_sales['OrderDate'], daily_sales['SalesAmount'], marker='o', linestyle='-', color='blue', alpha=0.7)
plt.title('Xu hướng doanh số bán hàng theo thời gian', fontsize=16)
plt.xlabel('Ngày', fontsize=12)
plt.ylabel('Doanh số (VND)', fontsize=12)
plt.grid(True, alpha=0.3)
plt.xticks(rotation=45)
# Thêm đường xu hướng
z = np.polyfit(range(len(daily_sales)), daily_sales['SalesAmount'], 1)
p = np.poly1d(z)
plt.plot(daily_sales['OrderDate'], p(range(len(daily_sales))), "r--", linewidth=2, label="Xu hướng")
plt.legend()
plt.tight_layout()
plt.savefig('sales_trend.png')
plt.show()
Phân tích doanh số theo danh mục sản phẩm
Tiếp theo, chúng ta phân tích doanh số theo danh mục sản phẩm:
# 1. Doanh số theo danh mục
category_sales = sales_df.groupby('CategoryName')['SalesAmount'].sum().reset_index()
category_sales = category_sales.sort_values('SalesAmount', ascending=False)
plt.figure(figsize=(12, 7))
ax = sns.barplot(x='CategoryName', y='SalesAmount', data=category_sales, palette='Blues_d')
plt.title('Doanh số bán hàng theo danh mục sản phẩm', fontsize=16)
plt.xlabel('Danh mục', fontsize=12)
plt.ylabel('Doanh số (VND)', fontsize=12)
plt.xticks(rotation=0)
plt.grid(axis='y', alpha=0.3)
# Thêm giá trị doanh số lên cột
for i, p in enumerate(ax.patches):
ax.annotate(f'{p.get_height():,.0f}',
(p.get_x() + p.get_width() / 2., p.get_height()),
ha='center', va='center', fontsize=10, color='black',
xytext=(0, 10), textcoords='offset points')
plt.tight_layout()
plt.savefig('sales_by_category.png')
plt.show()
# 2. Tỷ lệ doanh số theo danh mục (pie chart)
plt.figure(figsize=(10, 8))
plt.pie(category_sales['SalesAmount'], labels=category_sales['CategoryName'],
autopct='%1.1f%%', startangle=90, shadow=True,
wedgeprops={'edgecolor': 'white'}, textprops={'fontsize': 12})
plt.title('Tỷ lệ doanh số theo danh mục sản phẩm', fontsize=16)
plt.axis('equal') # Equal aspect ratio ensures that pie is drawn as a circle
plt.tight_layout()
plt.savefig('sales_by_category_pie.png')
plt.show()
# 3. Top 10 sản phẩm bán chạy nhất
top_products = sales_df.groupby('ProductName')['SalesAmount'].sum().reset_index()
top_products = top_products.sort_values('SalesAmount', ascending=False).head(10)
plt.figure(figsize=(12, 8))
ax = sns.barplot(x='SalesAmount', y='ProductName', data=top_products, palette='YlOrRd_r')
plt.title('Top 10 sản phẩm bán chạy nhất', fontsize=16)
plt.xlabel('Doanh số (VND)', fontsize=12)
plt.ylabel('Sản phẩm', fontsize=12)
plt.grid(axis='x', alpha=0.3)
# Thêm giá trị doanh số lên cột
for i, p in enumerate(ax.patches):
ax.annotate(f'{p.get_width():,.0f}',
(p.get_width(), p.get_y() + p.get_height() / 2),
ha='left', va='center', fontsize=10, color='black',
xytext=(10, 0), textcoords='offset points')
plt.tight_layout()
plt.savefig('top_products.png')
plt.show()
Phân tích doanh số theo khu vực
Bây giờ chúng ta sẽ phân tích doanh số theo khu vực:
# 1. Doanh số theo khu vực
region_sales = sales_df.groupby('RegionName')['SalesAmount'].sum().reset_index()
region_sales = region_sales.sort_values('SalesAmount', ascending=False)
plt.figure(figsize=(12, 7))
ax = sns.barplot(x='RegionName', y='SalesAmount', data=region_sales, palette='viridis')
plt.title('Doanh số bán hàng theo khu vực', fontsize=16)
plt.xlabel('Khu vực', fontsize=12)
plt.ylabel('Doanh số (VND)', fontsize=12)
plt.grid(axis='y', alpha=0.3)
# Thêm giá trị doanh số lên cột
for i, p in enumerate(ax.patches):
ax.annotate(f'{p.get_height():,.0f}',
(p.get_x() + p.get_width() / 2., p.get_height()),
ha='center', va='center', fontsize=10, color='black',
xytext=(0, 10), textcoords='offset points')
plt.tight_layout()
plt.savefig('sales_by_region.png')
plt.show()
# 2. Heatmap thể hiện doanh số theo khu vực và danh mục
region_category = sales_df.pivot_table(
values='SalesAmount',
index='RegionName',
columns='CategoryName',
aggfunc='sum')
plt.figure(figsize=(14, 8))
sns.heatmap(region_category, annot=True, fmt=',', cmap='YlGnBu', linewidths=.5)
plt.title('Phân tích doanh số theo khu vực và danh mục sản phẩm', fontsize=16)
plt.xlabel('Danh mục sản phẩm', fontsize=12)
plt.ylabel('Khu vực', fontsize=12)
plt.tight_layout()
plt.savefig('region_category_heatmap.png')
plt.show()
# 3. Biểu đồ đường thể hiện xu hướng doanh số theo khu vực
region_time_sales = sales_df.groupby(['OrderDate', 'RegionName'])['SalesAmount'].sum().reset_index()
plt.figure(figsize=(14, 8))
for region in region_time_sales['RegionName'].unique():
region_data = region_time_sales[region_time_sales['RegionName'] == region]
plt.plot(region_data['OrderDate'], region_data['SalesAmount'], marker='o', linestyle='-', label=region)
plt.title('Xu hướng doanh số theo khu vực', fontsize=16)
plt.xlabel('Ngày', fontsize=12)
plt.ylabel('Doanh số (VND)', fontsize=12)
plt.grid(True, alpha=0.3)
plt.xticks(rotation=45)
plt.legend()
plt.tight_layout()
plt.savefig('region_sales_trend.png')
plt.show()
Phân tích tương quan và thống kê nâng cao
Tiếp tục với phân tích tương quan và thống kê nâng cao:
# 1. Phân tích tương quan giữa số lượng và giá bán
plt.figure(figsize=(10, 8))
sns.scatterplot(x='Quantity', y='UnitPrice', data=sales_df,
hue='CategoryName', size='SalesAmount', sizes=(50, 500), alpha=0.7)
plt.title('Tương quan giữa số lượng và giá bán theo danh mục', fontsize=16)
plt.xlabel('Số lượng', fontsize=12)
plt.ylabel('Giá đơn vị (VND)', fontsize=12)
plt.grid(True, alpha=0.3)
plt.legend(title='Danh mục sản phẩm', fontsize=10)
plt.tight_layout()
plt.savefig('quantity_price_correlation.png')
plt.show()
# 2. Boxplot phân phối doanh số theo danh mục
plt.figure(figsize=(14, 8))
sns.boxplot(x='CategoryName', y='SalesAmount', data=sales_df, palette='Set3')
plt.title('Phân phối doanh số theo danh mục sản phẩm', fontsize=16)
plt.xlabel('Danh mục sản phẩm', fontsize=12)
plt.ylabel('Doanh số (VND)', fontsize=12)
plt.grid(axis='y', alpha=0.3)
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig('sales_distribution_by_category.png')
plt.show()
# 3. Histogram phân phối số lượng sản phẩm bán ra
plt.figure(figsize=(12, 7))
sns.histplot(data=sales_df, x='Quantity', bins=15, kde=True)
plt.title('Phân phối số lượng sản phẩm trong mỗi đơn hàng', fontsize=16)
plt.xlabel('Số lượng', fontsize=12)
plt.ylabel('Tần suất', fontsize=12)
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('quantity_distribution.png')
plt.show()
# 4. Phân tích tương quan giữa các biến số
# Tạo một DataFrame mới chỉ với các cột số
numeric_df = sales_df[['Quantity', 'UnitPrice', 'SalesAmount']]
# Tính ma trận tương quan
corr_matrix = numeric_df.corr()
plt.figure(figsize=(10, 8))
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm', vmin=-1, vmax=1, linewidths=0.5)
plt.title('Ma trận tương quan giữa các biến số', fontsize=16)
plt.tight_layout()
plt.savefig('correlation_matrix.png')
plt.show()
Tạo dashboard tổng hợp
Cuối cùng, chúng ta sẽ tạo một dashboard tổng hợp các phân tích:
def create_dashboard():
plt.figure(figsize=(20, 22))
# 1. Doanh số theo tháng
plt.subplot(3, 2, 1)
monthly_sales = sales_df.groupby(['Year', 'Month', 'MonthName'])['SalesAmount'].sum().reset_index()
monthly_sales = monthly_sales.sort_values(['Year', 'Month'])
plt.bar(monthly_sales['MonthName'], monthly_sales['SalesAmount'], color='skyblue')
plt.title('Doanh số bán hàng theo tháng', fontsize=14)
plt.xlabel('Tháng', fontsize=10)
plt.ylabel('Doanh số (VND)', fontsize=10)
plt.xticks(rotation=45)
plt.grid(axis='y', alpha=0.3)
# 2. Tỷ lệ doanh số theo danh mục
plt.subplot(3, 2, 2)
category_sales = sales_df.groupby('CategoryName')['SalesAmount'].sum().reset_index()
plt.pie(category_sales['SalesAmount'], labels=category_sales['CategoryName'],
autopct='%1.1f%%', startangle=90, wedgeprops={'edgecolor': 'white'})
plt.title('Tỷ lệ doanh số theo danh mục sản phẩm', fontsize=14)
plt.axis('equal')
# 3. Top 5 sản phẩm bán chạy nhất
plt.subplot(3, 2, 3)
top_products = sales_df.groupby('ProductName')['SalesAmount'].sum().reset_index()
top_products = top_products.sort_values('SalesAmount', ascending=False).head(5)
sns.barplot(x='SalesAmount', y='ProductName', data=top_products, palette='YlOrRd_r')
plt.title('Top 5 sản phẩm bán chạy nhất', fontsize=14)
plt.xlabel('Doanh số (VND)', fontsize=10)
plt.ylabel('Sản phẩm', fontsize=10)
plt.grid(axis='x', alpha=0.3)
# 4. Doanh số theo khu vực
plt.subplot(3, 2, 4)
region_sales = sales_df.groupby('RegionName')['SalesAmount'].sum().reset_index()
region_sales = region_sales.sort_values('SalesAmount', ascending=False)
sns.barplot(x='RegionName', y='SalesAmount', data=region_sales, palette='viridis')
plt.title('Doanh số bán hàng theo khu vực', fontsize=14)
plt.xlabel('Khu vực', fontsize=10)
plt.ylabel('Doanh số (VND)', fontsize=10)
plt.grid(axis='y', alpha=0.3)
# 5. Xu hướng doanh số theo thời gian
plt.subplot(3, 2, 5)
daily_sales = sales_df.groupby('OrderDate')['SalesAmount'].sum().reset_index()
plt.plot(daily_sales['OrderDate'], daily_sales['SalesAmount'], marker='o', linestyle='-', color='blue', alpha=0.7)
plt.title('Xu hướng doanh số bán hàng', fontsize=14)
plt.xlabel('Ngày', fontsize=10)
plt.ylabel('Doanh số (VND)', fontsize=10)
plt.grid(True, alpha=0.3)
plt.xticks(rotation=45)
# 6. Tương quan giữa số lượng và giá bán
plt.subplot(3, 2, 6)
sns.scatterplot(x='Quantity', y='UnitPrice', data=sales_df,
hue='CategoryName', size='SalesAmount', sizes=(20, 200), alpha=0.7)
plt.title('Tương quan giữa số lượng và giá bán', fontsize=14)
plt.xlabel('Số lượng', fontsize=10)
plt.ylabel('Giá đơn vị (VND)', fontsize=10)
plt.grid(True, alpha=0.3)
plt.legend(fontsize=8)
plt.suptitle('DASHBOARD PHÂN TÍCH DỮ LIỆU BÁN HÀNG', fontsize=20, y=0.98)
plt.tight_layout(rect=[0, 0, 1, 0.96])
plt.savefig('sales_dashboard.png', dpi=300, bbox_inches='tight')
plt.show()
# Tạo dashboard
create_dashboard()
Phân tích RFM (Recency, Frequency, Monetary)
Cuối cùng, chúng ta thực hiện phân tích RFM để phân khúc khách hàng:
def rfm_analysis(sales_df):
# Giả sử có thêm cột CustomerID trong dữ liệu
if 'CustomerID' not in sales_df.columns:
print("Không có dữ liệu khách hàng để thực hiện phân tích RFM")
return
# Lấy ngày cuối cùng trong dữ liệu
latest_date = sales_df['OrderDate'].max()
# Tính toán các giá trị RFM
rfm_data = sales_df.groupby('CustomerID').agg({
'OrderDate': lambda x: (latest_date - x.max()).days, # Recency
'OrderID': 'count', # Frequency (giả sử có cột OrderID)
'SalesAmount': 'sum' # Monetary
}).reset_index()
# Đổi tên cột
rfm_data.columns = ['CustomerID', 'Recency', 'Frequency', 'Monetary']
# Phân hạng RFM (1-5, 5 là tốt nhất)
rfm_data['R_Score'] = pd.qcut(rfm_data['Recency'], 5, labels=[5, 4, 3, 2, 1])
rfm_data['F_Score'] = pd.qcut(rfm_data['Frequency'], 5, labels=[1, 2, 3, 4, 5])
rfm_data['M_Score'] = pd.qcut(rfm_data['Monetary'], 5, labels=[1, 2, 3, 4, 5])
# Tính điểm RFM tổng hợp
rfm_data['RFM_Score'] = rfm_data['R_Score'].astype(str) + rfm_data['F_Score'].astype(str) + rfm_data['M_Score'].astype(str)
# Phân khúc khách hàng
def segment_customer(row):
if row['RFM_Score'] in ['555', '554', '544', '545', '454', '455', '445']:
return 'Champions'
elif row['RFM_Score'] in ['543', '444', '435', '355', '354', '345', '344', '335']:
return 'Loyal Customers'
elif row['RFM_Score'] in ['553', '551', '552', '541', '542', '533', '532', '531', '452', '451', '442', '441', '431', '453', '433', '432', '423', '353', '352', '351', '342', '341', '333', '323']:
return 'Potential Loyalists'
elif row['RFM_Score'] in ['512', '511', '422', '421', '412', '411', '311']:
return 'New Customers'
elif row['RFM_Score'] in ['525', '524', '523', '522', '521', '515', '514', '513', '425', '424', '413', '414', '415', '315', '314', '313']:
return 'Promising'
elif row['RFM_Score'] in ['535', '534', '443', '434', '343', '334', '325', '324']:
return 'Need Attention'
elif row['RFM_Score'] in ['331', '321', '312', '221', '213', '231', '241', '251']:
return 'About To Sleep'
elif row['RFM_Score'] in ['255', '254', '245', '244', '253', '252', '243', '242', '235', '234', '225', '224', '153', '152', '145', '143', '142', '135', '134', '133', '125', '124']:
return 'At Risk'
elif row['RFM_Score'] in ['155', '154', '144', '214', '215', '115', '114', '113']:
return 'Cannot Lose Them'
else:
return 'Lost'
rfm_data['Segment'] = rfm_data.apply(segment_customer, axis=1)
# Trực quan hóa phân khúc khách hàng
plt.figure(figsize=(12, 8))
segment_counts = rfm_data['Segment'].value_counts()
ax = sns.barplot(x=segment_counts.index, y=segment_counts.values, palette='viridis')
plt.title('Phân khúc khách hàng theo RFM', fontsize=16)
plt.xlabel('Phân khúc', fontsize=12)
plt.ylabel('Số lượng khách hàng', fontsize=12)
plt.xticks(rotation=45)
plt.grid(axis='y', alpha=0.3)
for i, p in enumerate(ax.patches):
ax.annotate(f'{p.get_height():,.0f}',
(p.get_x() + p.get_width() / 2., p.get_height()),
ha='center', va='center', fontsize=10, color='black',
xytext=(0, 10), textcoords='offset points')
plt.tight_layout()
plt.savefig('customer_segments.png')
plt.show()
return rfm_data
# Thực hiện phân tích RFM nếu có dữ liệu khách hàng
rfm_data = rfm_analysis(sales_df)
Lưu kết quả phân tích vào SQL Server
Cuối cùng, chúng ta lưu kết quả phân tích trở lại SQL Server:
def save_analysis_to_sql(sales_summary, rfm_data=None):
# Kết nối đến SQL Server
conn = get_connection()
if not conn:
print("Không thể kết nối đến SQL Server để lưu kết quả phân tích")
return
try:
# Lưu bảng tổng hợp doanh số
sales_summary.to_sql('SalesSummary', conn, if_exists='replace', index=False)
print("Đã lưu bảng tổng hợp doanh số vào SQL Server")
# Lưu kết quả phân tích RFM nếu có
if rfm_data is not None:
rfm_data.to_sql('CustomerRFM', conn, if_exists='replace', index=False)
print("Đã lưu kết quả phân tích RFM vào SQL Server")
# Đóng kết nối
conn.close()
except Exception as e:
print(f"Lỗi khi lưu kết quả phân tích: {e}")
# Tạo bảng tổng hợp doanh số
sales_summary = pd.DataFrame({
'TotalSales': [sales_df['SalesAmount'].sum()],
'AverageOrderValue': [sales_df['SalesAmount'].mean()],
'TotalOrders': [sales_df.shape[0]],
'TopCategory': [sales_df.groupby('CategoryName')['SalesAmount'].sum().idxmax()],
'TopProduct': [sales_df.groupby('ProductName')['SalesAmount'].sum().idxmax()],
'TopRegion': [sales_df.groupby('RegionName')['SalesAmount'].sum().idxmax()],
'AnalysisDate': [datetime.now()]
})
# Lưu kết quả phân tích
save_analysis_to_sql(sales_summary, rfm_data)
Kết luận
Trong bài viết này, chúng ta đã tìm hiểu cách kết hợp sức mạnh của SQL Server và Python để phân tích dữ liệu bán hàng. Chúng ta đã thực hiện:
- Kết nối đến SQL Server và truy vấn dữ liệu bán hàng
- Phân tích khám phá dữ liệu (EDA)
- Phân tích doanh số theo thời gian, danh mục sản phẩm, và khu vực
- Phân tích tương quan và thống kê nâng cao
- Tạo dashboard tổng hợp
- Phân tích RFM để phân khúc khách hàng
- Lưu kết quả phân tích trở lại SQL Server
Kết hợp SQL Server và Python là một cách tiếp cận mạnh mẽ cho phân tích dữ liệu doanh nghiệp. SQL Server cung cấp khả năng lưu trữ và quản lý dữ liệu hiệu quả, trong khi Python với các thư viện như Pandas, Matplotlib, và Seaborn cung cấp công cụ phân tích và trực quan hóa linh hoạt. Kỹ năng này rất có giá trị cho các nhà phân tích dữ liệu và kỹ sư dữ liệu trong môi trường doanh nghiệp.
Bạn đã có kinh nghiệm phân tích dữ liệu từ SQL Server bằng Python chưa? Bạn đã sử dụng những kỹ thuật nào khác? Hãy chia sẻ trong phần bình luận nhé!
Bài viết liên quan
Tự động dự đoán doanh thu từ dữ liệu trong SQL Server bằng mô hình Python
Hướng dẫn xây dựng hệ thống dự đoán doanh thu tự động bằng cách kết hợp SQL Server và mô hình học máy Python.
Tạo dashboard tương tác với Power BI và SQL Server
Hướng dẫn chi tiết cách tạo dashboard tương tác sử dụng Power BI kết nối với SQL Server để phân tích dữ liệu kinh doanh.