MB
HƯỚNG NGHIỆP DỮ LIỆU NOCODE
HỌC LÀ CÓ VIỆC LÀM

📈 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

Đăng ngày 05/05/202520 phút đọcDanh mục: Phân tích dữ liệu
Phân tích dữ liệu bán hàng từ SQL Server bằng Python

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?

Phân tích dữ liệu bán hàng từ SQL Server bằng Python

Kết hợp SQL Server và Python mang lại nhiều lợi ích:

  1. 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
  2. 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
  3. Trực quan hóa đa dạng: Matplotlib, Seaborn, Plotly cho phép tạo các biểu đồ chuyên nghiệp
  4. 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
  5. 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()
Xu hướng doanh số bán hàng theo thời gian

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()
Tỷ lệ doanh số theo danh mục sản phẩm

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 doanh số theo khu vực và danh mục sản phẩm

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()
Ma trận tương quan giữa các biến số

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()
Dashboard phân tích dữ liệu bán hàng

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)
Phân khúc khách hàng theo RFM

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:

  1. Kết nối đến SQL Server và truy vấn dữ liệu bán hàng
  2. Phân tích khám phá dữ liệu (EDA)
  3. Phân tích doanh số theo thời gian, danh mục sản phẩm, và khu vực
  4. Phân tích tương quan và thống kê nâng cao
  5. Tạo dashboard tổng hợp
  6. Phân tích RFM để phân khúc khách hàng
  7. 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.

Bạn muốn học thêm về phân tích dữ liệu?

Đăng ký khóa học SQL Server và Python của chúng tôi để nâng cao kỹ năng phân tích dữ liệu và trực quan hóa của bạn.