
1. 依赖库导入
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib import font_manager
from datetime import datetime
matplotlib.pyplot
:用于绘制图表。numpy
:
numpy
:pandas
:虽然代码中未font_manager
:设置datetime
:生成报告的日期信息。
2. 中文字体设置
def set_chinese_font():
"""尝试设置中文字体,回退到通用方案"""
# 尝试使用系统中常见的中文字体(如黑体、微软雅黑等)
# 若失败则回退到默认字体
3. 模拟数据生成
def generate_store_data(name, loc_base, loc_weekend_factor, scale):
"""生成门店工作日和周末客流量数据"""
weekday_data = np.random.normal(loc=loc_base, scale=scale, size=100)
weekend_data = np.random.normal(loc=loc_base * loc_weekend_factor, scale=scale*1.2, size=50)
return {
'name': name,
'weekday': weekday_data,
'weekend': weekend_data,
'loc_base': loc_base,
'color': ''
}
-
为每个门店生成工作日和周末的客流量数据
-
示例数据 :
- 市中心旗舰店:工作日均值1200,周末均值提高1.8倍。
- 社区便利店:工作日均值400,周末提高1.5倍。
4. 图表绘制
(1) 第一子图:单个门店(旗舰店)分时客流分布
- 直方图 :
- 用
ax1.hist()
绘制工作日和周末的客流量分布,通过 - 目标客流量线 (红色虚线)和高峰时段标记(红色阴影)用于辅助分析。
- 用
- 关键设置 :
- 坐标轴标签、标题、图例、网格线等。
- 使用
axvline
和axvspan
标注关键指标。
(2) 第二子图:多门店类型对比
-
分组柱状图 :
- 用
ax2.bar()
分别绘制每种门店类型的工作日和周末平均客流量(柱状图并排显示)。 - 数值标签(如"1200")直接标注在柱子上方。
- 增长百分比(如"+115%")显示周末客流较工作日的增幅。
- 用
-
关键设置 :
- 坐标轴标签、标题、图例、网格线。
- 使用
autolabel
函数自动添加数值标签。
import matplotlib.pyplot as plt import numpy as np import pandas as pd from matplotlib import font_manager from datetime import datetime # 更健壮的字体设置方案 def set_chinese_font(): """尝试设置中文字体,回退到通用方案""" try: # 尝试查找系统中可用的中文字体 chinese_fonts = ['SimHei', 'Microsoft YaHei', 'KaiTi', 'STXihei', 'STHeiti'] for font in chinese_fonts: if font in font_manager.findfont(font): plt.rcParams['font.sans-serif'] = [font] plt.rcParams['axes.unicode_minus'] = False return # 如果找不到特定字体,尝试使用通用解决方案 plt.rcParams['font.sans-serif'] = ['sans-serif'] plt.rcParams['axes.unicode_minus'] = False except: # 最终回退方案 plt.rcParams['font.sans-serif'] = ['sans-serif'] plt.rcParams['axes.unicode_minus'] = False # 设置字体 set_chinese_font() # 生成模拟零售数据 np.random.seed(2023) def generate_store_data(name, loc_base, loc_weekend_factor, scale): """生成门店工作日和周末客流量数据""" weekday_data = np.random.normal(loc=loc_base, scale=scale, size=100) weekend_data = np.random.normal(loc=loc_base * loc_weekend_factor, scale=scale*1.2, size=50) return { 'name': name, 'weekday': weekday_data, 'weekend': weekend_data, 'loc_base': loc_base, 'color': '' } # 创建不同门店的数据 stores = [ generate_store_data('市中心旗舰店', 1200, 1.8, 180), generate_store_data('商业区标准店', 800, 2.0, 150), generate_store_data('社区便利店', 400, 1.5, 90), generate_store_data('机场免税店', 600, 1.2, 220) ] # 设置门店专属色系 retail_colors = ['#FF6B6B', '#4ECDC4', '#FFD166', '#6A0572'] for i, store in enumerate(stores): store['color'] = retail_colors[i] # 创建画布和子图 fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(18, 7), gridspec_kw={'width_ratios': [1, 1.5]}) fig.suptitle('零售门店客流量分布分析', fontsize=18, fontweight='bold', y=0.98) # 第一图:单个门店(旗舰店)的分时客流分布 target_store = stores[0] n, bins, patches = ax1.hist( target_store['weekday'], bins=15, alpha=0.85, color=target_store['color'], edgecolor='#333333', linewidth=1.2, label='工作日' ) # 添加周末客流分布作为对比 ax1.hist( target_store['weekend'], bins=bins, alpha=0.45, color=target_store['color'], edgecolor='#333333', linewidth=1.0, hatch='//', label='周末' ) # 添加目标客流量线 target_goal = 1500 ax1.axvline(x=target_goal, color='#1A936F', linestyle='-', linewidth=2.5, alpha=0.9) ax1.text(target_goal+20, ax1.get_ylim()[1]*0.9, f'目标客流: {target_goal}', color='#1A936F', fontweight='bold') # 标记高峰时段 peak_window = (1400, 1800) ax1.axvspan(peak_window[0], peak_window[1], alpha=0.08, color='red') ax1.text(peak_window[0]+50, ax1.get_ylim()[1]*0.75, f'高峰时段 {peak_window[0]}-{peak_window[1]}', color='#C44536') ax1.set_title(f"{target_store['name']}分时客流分布", fontsize=14) ax1.set_xlabel('每小时客流量 (人次)', fontsize=12) ax1.set_ylabel('出现频率', fontsize=12) ax1.grid(axis='y', linestyle=':', alpha=0.4) ax1.legend(loc='upper left') ax1.set_axisbelow(True) # 第二图:所有门店工作日和周末对比 bar_width = 0.35 x = np.arange(len(stores)) # 计算工作日和周末的平均客流量 weekday_means = [np.mean(store['weekday']) for store in stores] weekend_means = [np.mean(store['weekend']) for store in stores] # 创建分组柱状图 rects1 = ax2.bar( x - bar_width/2, weekday_means, bar_width, color=[store['color'] for store in stores], alpha=0.8, edgecolor='#333333', linewidth=1.0, label='工作日平均客流' ) rects2 = ax2.bar( x + bar_width/2, weekend_means, bar_width, color=[store['color'] for store in stores], alpha=0.95, edgecolor='#333333', linewidth=1.0, hatch='//', label='周末平均客流' ) # 添加数值标签 def autolabel(rects, ax): for rect in rects: height = rect.get_height() ax.annotate(f'{height:.0f}', xy=(rect.get_x() + rect.get_width() / 2, height), xytext=(0, 3), # 3点垂直偏移 textcoords="offset points", ha='center', va='bottom', fontsize=10, fontweight='bold') autolabel(rects1, ax2) autolabel(rects2, ax2) # 设置门店标签 ax2.set_xticks(x) ax2.set_xticklabels([store['name'] for store in stores], fontsize=12) ax2.set_title('门店类型客流对比 (工作日 vs 周末)', fontsize=14) ax2.set_ylabel('平均每小时客流量 (人次)', fontsize=12) ax2.grid(axis='y', linestyle=':', alpha=0.3) ax2.legend(loc='upper left', framealpha=0.9) ax2.set_axisbelow(True) # 添加增长百分比 for i, store in enumerate(stores): growth = (weekend_means[i] - weekday_means[i]) / weekday_means[i] * 100 ax2.text(i, max(weekday_means[i], weekend_means[i]) + 50, f'+{growth:.1f}%', ha='center', fontsize=11, fontweight='bold', color='#E63946') # 添加数据说明 current_date = datetime.now().strftime('%Y-%m-%d') analytics_text = ( f"数据分析周期: 2023年1月-2023年6月 | 生成日期: {current_date}\n" "趋势洞察: 商业区门店周末客流增长最显著(+115%),社区店晚高峰(18:00-20:00)客流占比高达40%" ) plt.figtext(0.5, 0.01, analytics_text, ha='center', fontsize=10.5, style='italic', bbox=dict(facecolor='#F8F9FA', edgecolor='#DEE2E6', alpha=0.8)) # 布局优化 plt.tight_layout(rect=[0, 0.03, 1, 0.95]) # 添加版权信息 plt.figtext(0.95, 0.01, "© 2023 零售数据分析团队", ha='right', fontsize=9, alpha=0.7) # 保存和显示 plt.savefig('retail_traffic_analysis.png', dpi=120, bbox_inches='tight') plt.show()