Build an Automated E-commerce Analytics Dashboard with DuckDB
💰 Monetization idea: Package this system as a SaaS product for small e-commerce sellers at $15-40/month. 100 customers = $1,500-4,000 in recurring revenue.
Why DuckDB for E-commerce Analytics?
Many e-commerce sellers have massive order data (CSV/Excel) but lack automated analysis capabilities. They either manually process tens of thousands of rows in Excel (which crashes), or pay someone to make reports.
DuckDB’s core advantages shine here:
- Read CSV/Excel/Parquet directly, no database import needed — Sellers send you a file, you read and analyze it instantly
- SQL is the analysis — No complex Python data processing code needed, one SQL query handles all statistics
- Deploy in 10 minutes — One Python script + one cron job, fully automatic
The core value of this system: one set of SQL handles all e-commerce analytics, zero dependencies, 10-minute deployment.
Data Preparation: Simulated E-commerce Dataset
First, generate a simulated e-commerce dataset to model a real scenario:
import duckdb
import pandas as pd
import numpy as np
# Generate simulated e-commerce data
np.random.seed(42)
n = 50000
orders = pd.DataFrame({
'order_id': range(1, n+1),
'customer_id': np.random.randint(1, 5000, n),
'product_category': np.random.choice(['Electronics', 'Clothing', 'Food', 'Home', 'Beauty'], n),
'product_name': [f'Product{i%200+1}' for i in range(n)],
'quantity': np.random.randint(1, 10, n),
'unit_price': np.round(np.random.uniform(9.9, 999.9, n), 2),
'order_date': pd.date_range('2025-01-01', periods=n, freq='min'),
'province': np.random.choice(['Guangdong', 'Jiangsu', 'Zhejiang', 'Beijing', 'Shanghai', 'Sichuan', 'Hubei', 'Fujian'], n),
'payment_method': np.random.choice(['Alipay', 'WeChat', 'CreditCard', 'BankCard'], n),
'is_returned': np.random.choice([0, 1], n, p=[0.95, 0.05])
})
orders['total_amount'] = orders['quantity'] * orders['unit_price'] * (1 - orders['is_returned'])
# Save to CSV
orders.to_csv('ecommerce_orders.csv', index=False, encoding='utf-8-sig')
print(f"✅ Generated {len(orders)} order records")
The dataset contains 50,000 orders across 5 categories, 8 provinces, 4 payment methods, and a 5% return rate. Read with DuckDB:
import duckdb
con = duckdb.connect()
df = con.sql("SELECT * FROM read_csv_auto('ecommerce_orders.csv')").df()
print(df.shape) # (50000, 11)
One line of code, 50,000 rows loaded — no database configuration needed.
Core Analysis Modules
3.1 Revenue Overview Analysis — Daily Trends + Moving Average
revenue_sql = """
WITH daily_stats AS (
SELECT
DATE(order_date) AS order_date,
COUNT(*) AS order_count,
SUM(total_amount) AS daily_revenue,
AVG(total_amount) AS avg_order_value,
COUNT(DISTINCT customer_id) AS unique_customers,
SUM(is_returned) AS return_count,
ROUND(SUM(is_returned) * 1.0 / COUNT(*), 4) AS return_rate
FROM read_csv_auto('ecommerce_orders.csv')
GROUP BY DATE(order_date)
),
cumulative AS (
SELECT
order_date,
SUM(daily_revenue) OVER (ORDER BY order_date) AS cumulative_revenue,
SUM(order_count) OVER (ORDER BY order_date) AS cumulative_orders,
AVG(daily_revenue) OVER (ORDER BY order_date ROWS UNBOUNDED PRECEDING) AS rolling_avg_revenue
FROM daily_stats
)
SELECT * FROM cumulative
ORDER BY order_date;
"""
result = duckdb.sql(revenue_sql).df()
print(result.tail(7))
Key metrics explained:
| Metric | Meaning | Warning Threshold |
|---|---|---|
daily_revenue | Daily revenue | Alert if 3+ days declining |
rolling_avg_revenue | Rolling average revenue | Smooths short-term volatility |
return_rate | Return rate | Alert if > 10% |
unique_customers | Unique customers | Judge traffic health |
3.2 Category × Region Cross-Analysis
Find your “cash cow” combinations:
category_region_sql = """
SELECT
product_category AS category,
province AS region,
COUNT(*) AS order_count,
SUM(total_amount) AS revenue,
AVG(total_amount) AS avg_order_value,
SUM(is_returned) AS return_count,
ROUND(SUM(is_returned) * 1.0 / COUNT(*), 4) AS return_rate,
ROUND(SUM(total_amount) * 1.0 / SUM(SUM(total_amount)) OVER (), 4) AS share
FROM read_csv_auto('ecommerce_orders.csv')
GROUP BY product_category, province
HAVING SUM(total_amount) > 10000
ORDER BY revenue DESC
LIMIT 30;
"""
cat_result = duckdb.sql(category_region_sql).df()
print(cat_result.to_string(index=False))
This query helps you quickly identify:
- High-contribution category+region combinations: Focus retention and ad spend
- High-return-rate regions: May need logistics or product strategy adjustments
- Low-share but fast-growing: Potential opportunities to invest in early
3.3 Customer Value Segmentation (RFM Model)
Use DuckDB’s window functions for RFM customer segmentation:
rfm_sql = """
WITH customer_metrics AS (
SELECT
customer_id,
MAX(order_date) AS last_order_date,
MIN(order_date) AS first_order_date,
COUNT(*) AS frequency,
SUM(total_amount) AS monetary,
AVG(total_amount) AS avg_order_value
FROM read_csv_auto('ecommerce_orders.csv')
GROUP BY customer_id
),
rfm_scores AS (
SELECT
customer_id,
frequency,
monetary,
avg_order_value,
NTILE(5) OVER (ORDER BY frequency) AS freq_score,
NTILE(5) OVER (ORDER BY monetary) AS money_score,
NTILE(5) OVER (ORDER BY avg_order_value) AS avg_score,
CASE
WHEN frequency >= 10 AND monetary >= 5000 THEN 'High Value'
WHEN frequency >= 5 AND monetary >= 2000 THEN 'Potential'
WHEN frequency >= 3 AND monetary >= 500 THEN 'Regular'
WHEN frequency = 1 AND monetary < 200 THEN 'At Risk'
ELSE 'Other'
END AS customer_segment
FROM customer_metrics
)
SELECT
customer_segment,
COUNT(*) AS customer_count,
ROUND(AVG(monetary), 2) AS avg_spend,
ROUND(AVG(frequency), 2) AS avg_orders,
ROUND(SUM(monetary), 2) AS total_contribution
FROM rfm_scores
GROUP BY customer_segment
ORDER BY total_contribution DESC;
"""
rfm_result = duckdb.sql(rfm_sql).df()
print(rfm_result.to_string(index=False))
RFM Segmentation Strategy:
- High Value: VIP service, exclusive discounts, prevent churn
- Potential: Increase average order value, cross-sell recommendations
- Regular: Maintain contact, moderate marketing
- At Risk: Recall campaigns, limited-time offers
Automated Monthly Report Generation
Package the above analysis into an automated report generation function:
import pandas as pd
from datetime import datetime
def generate_monthly_report(data_path='ecommerce_orders.csv'):
"""Generate a complete monthly e-commerce analysis report"""
# 1. Monthly summary
monthly_sql = """
WITH base AS (
SELECT *,
DATE_FORMAT(order_date, '%Y-%m') AS month,
CASE
WHEN hour(order_date) < 12 THEN 'Morning'
WHEN hour(order_date) < 18 THEN 'Afternoon'
ELSE 'Evening'
END AS time_period
FROM read_csv_auto('$data_path')
),
monthly_summary AS (
SELECT
month,
COUNT(*) AS total_orders,
SUM(total_amount) AS total_revenue,
AVG(total_amount) AS avg_order_value,
COUNT(DISTINCT customer_id) AS unique_customers,
SUM(is_returned) AS total_returns,
ROUND(SUM(is_returned)*1.0/COUNT(*), 4) AS return_rate,
COUNT(DISTINCT product_category) AS category_count
FROM base
GROUP BY month
)
SELECT * FROM monthly_summary ORDER BY month DESC;
"""
summary = duckdb.sql(monthly_sql.replace('$data_path', data_path)).df()
# 2. Top 10 products
top_products_sql = """
SELECT
product_name,
product_category,
COUNT(*) AS sales_count,
ROUND(SUM(total_amount), 2) AS revenue
FROM read_csv_auto('$data_path')
GROUP BY product_name, product_category
ORDER BY revenue DESC
LIMIT 10;
"""
top_products = duckdb.sql(top_products_sql.replace('$data_path', data_path)).df()
# 3. Time-of-day distribution
traffic_sql = """
SELECT
CASE
WHEN hour(order_date) < 12 THEN 'Morning (0-12)'
WHEN hour(order_date) < 18 THEN 'Afternoon (12-18)'
ELSE 'Evening (18-24)'
END AS time_period,
COUNT(*) AS orders,
ROUND(SUM(total_amount), 2) AS revenue,
ROUND(SUM(total_amount)*1.0/SUM(SUM(total_amount))OVER(), 4) AS share
FROM read_csv_auto('$data_path')
GROUP BY time_period;
"""
traffic = duckdb.sql(traffic_sql).df()
return {
'monthly_summary': summary,
'top_products': top_products,
'traffic': traffic
}
# Execute
report = generate_monthly_report()
print("=== Monthly Summary ===")
print(report['monthly_summary'].to_string(index=False))
print("\n=== Top 10 Products ===")
print(report['top_products'].to_string(index=False))
print("\n=== Traffic by Time Period ===")
print(report['traffic'].to_string(index=False))
Comparison with Traditional Approaches
| Dimension | Excel | Pandas + Jupyter | DuckDB |
|---|---|---|---|
| Startup time | 30s+ (crashes with large files) | 5-10 seconds | < 1 second |
| 50K row CSV | Opens but slow filtering | Easy load | Millisecond queries |
| 5M row CSV | ❌ Cannot open | ⚠️ Needs optimization | Query directly, no import |
| SQL capability | Limited (pivot tables) | Needs extra libraries | Full SQL support |
| Deployment complexity | Manual operations | Needs Python environment | Single file, zero dependencies |
| Automation | Difficult | Needs scripts | cron + Python, one-liner |
DuckDB’s core advantage: bringing database capabilities into data analysis scenarios — no need to install PostgreSQL/MySQL, no ETL pipeline required, directly run complex queries on raw files.
Automated Deployment: Weekly Scheduled Reports
Use Python’s schedule library or system cron for fully automated weekly reports:
import schedule
import time
from datetime import datetime
def weekly_report():
report = generate_monthly_report()
# Generate HTML report
html = f"""
<h2>E-commerce Weekly Report - {datetime.now().strftime('%Y-%m-%d')}</h2>
<h3>Monthly Summary</h3>
<pre>{report['monthly_summary'].to_string(index=False)}</pre>
<h3>Top 10 Products</h3>
<pre>{report['top_products'].to_string(index=False)}</pre>
"""
with open(f'report_{datetime.now().strftime("%Y%m%d")}.html', 'w', encoding='utf-8') as f:
f.write(html)
print(f"✅ Report generated: report_{datetime.now().strftime('%Y%m%d')}.html")
# Run every Monday at 9 AM
schedule.every().monday.at("09:00").do(weekly_report)
while True:
schedule.run_pending()
time.sleep(3600)
Or use system cron (more production-friendly):
# Edit crontab
crontab -e
# Execute every Monday at 9 AM
0 9 * * 1 cd /home/user/report && python3 weekly_report.py
Monetization Suggestions
The commercialization path for this system is very clear:
- SaaS: Package the analysis logic as an API — sellers upload CSV, get automated reports → $15-40/month
- Custom services: Build custom dashboards for mid-large sellers, charge $500-2,000/month
- Training + toolkits: Package this methodology into a course, teach sellers to build it themselves → one-time knowledge payment
- Data products: Based on aggregated industry data, sell category trend reports to brands
Key moat: Your SQL templates and automation workflows are assets themselves. Others can copy your code, but copying your industry understanding is hard.
Want to learn more DuckDB实战 techniques and see the complete e-commerce analytics code repository and deployment tutorial → duckdblab.org
