<think>根据错误信息,问题出现在`fig.update_layout()`中对`yaxis2`的设置。错误提示说`titlefont`是无效的属性,并建议使用`tickfont`。实际上,在Plotly中设置坐标轴标题字体应该使用`title_font`(注意是下划线)或者直接在`title`属性中使用字典设置字体,例如:
```python
yaxis2=dict(
title="品牌占比",
titlefont=dict(color="#d62728"), # 这里应该改为title_font或者使用title属性中的font
...
)
```
但是,根据最新的Plotly文档,正确的做法是在`title`属性中设置字体,或者使用`title_font`(在更新坐标轴属性时,`title_font`是一个独立的属性)。不过,在`update_layout`中使用`yaxis2_title_font`这样的形式也是可以的。
然而,错误信息中列出了所有有效的属性,其中并没有`titlefont`,而是有`title`(它是一个包含`font`等属性的对象)和`title_font`(用于直接设置标题字体)。
因此,我们需要修改代码:
1. 对于`yaxis2`的标题颜色设置,我们可以使用:
```python
yaxis2=dict(
title="品牌占比",
title_font=dict(color="#d62728"),
...
)
```
2. 另外,错误信息还提示了另一个问题:在代码中我们使用了`titlefont`,应该改为`title_font`(注意是下划线)。
让我们修正代码中的这个问题。同时,我还注意到在创建`yaxis2`时,我们覆盖了主Y轴(yaxis)的配置?实际上,我们不应该在`update_layout`中直接设置`yaxis2`的`titlefont`,而应该使用`yaxis2_title_font`,或者使用`yaxis2=dict(...)`。但我们在同一个`update_layout`中同时设置了`yaxis2`字典和`yaxis2_title_font`是不行的,应该采用统一的方式。
这里我们采用在`update_layout`中直接设置`yaxis2`字典的方式,并在其中使用`title_font`。
修正部分代码:
```python
# 添加右侧Y轴
fig.update_layout(
yaxis2=dict(
title="品牌占比",
title_font=dict(color="#d62728"),
tickfont=dict(color="#d62728"),
overlaying="y",
side="right",
range=[0, 1]
)
)
```
另外,在错误信息之前还有一个警告:`SettingWithCopyWarning`,这通常是因为我们试图在DataFrame的切片上赋值。为了避免这个警告,我们应该使用`.loc`来赋值。
在代码中,我们有一行:
```python
jan_data['XM占比'] = jan_data['XM品牌预测值'] / jan_data['月度大盘预测值']
```
可以改为:
```python
jan_data.loc[:, 'XM占比'] = jan_data['XM品牌预测值'] / jan_data['月度大盘预测值']
```
这样应该能避免警告。
现在,让我们整合这些修正,重新写一下代码。
由于原始代码较长,我仅重写有问题的部分,并给出完整代码:
```python
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots
# 读取Excel文件
df = pd.read_excel('your_data.xlsx') # 替换为实际文件路径
# 转换日期列
df['forecast_time'] = pd.to_datetime(df['forecast_time'])
df['date'] = pd.to_datetime(df['date'])
# 提取2025/1/1期次的数据
jan_data = df[df['forecast_time'] == '2025-01-01'].copy() # 使用copy()避免SettingWithCopyWarning
# 提取2025/11/1期次的数据
nov_data = df[df['forecast_time'] == '2025-11-01'].copy()
# 计算XM品牌占比,使用loc避免警告
jan_data.loc[:, 'XM占比'] = jan_data['XM品牌预测值'] / jan_data['月度大盘预测值']
# 创建包含多个子图的HTML页面
fig = make_subplots(
rows=3, cols=2,
specs=[
[{"type": "bar", "rowspan": 2}, {"type": "scatter"}],
[None, {"type": "pie"}],
[{"type": "bar"}, {"type": "pie"}]
],
subplot_titles=(
"大盘销量趋势与XM品牌占比",
"预测准确率(ACC)变化趋势",
"大盘价格档位占比(2025/1/1期次)",
"2025/11/1期次销量预测",
"XM品牌价格档位占比(2025/1/1期次)"
),
vertical_spacing=0.1,
horizontal_spacing=0.15
)
# 图1: 大盘趋势和XM品牌占比 (重叠柱状图)
fig.add_trace(
go.Bar(
x=jan_data['date'],
y=jan_data['月度大盘预测值'],
name='大盘预测值',
marker_color='#1f77b4',
opacity=0.7
),
row=1, col=1
)
fig.add_trace(
go.Bar(
x=jan_data['date'],
y=jan_data['月度大盘真实值'],
name='大盘真实值',
marker_color='#ff7f0e',
opacity=0.7
),
row=1, col=1
)
fig.add_trace(
go.Scatter(
x=jan_data['date'],
y=jan_data['XM占比'],
name='XM品牌占比',
mode='lines+markers',
line=dict(color='#d62728', width=3),
yaxis='y2'
),
row=1, col=1
)
# 添加右侧Y轴配置
fig.update_layout(
yaxis2=dict(
title="品牌占比",
title_font=dict(color="#d62728"),
tickfont=dict(color="#d62728"),
overlaying="y",
side="right",
range=[0, 1]
)
)
# 图2: 预测准确率变化
fig.add_trace(
go.Scatter(
x=jan_data['date'],
y=jan_data['ACC'],
name='大盘ACC',
mode='lines+markers',
line=dict(color='#2ca02c', width=2)
),
row=1, col=2
)
fig.add_trace(
go.Scatter(
x=jan_data['date'],
y=jan_data['ACC.1'],
name='XM品牌ACC',
mode='lines+markers',
line=dict(color='#9467bd', width=2)
),
row=1, col=2
)
# 图3: 大盘价格档位占比 (2025/1/1期次的最后一个月)
last_month = jan_data.iloc[-1] # 获取最后一个月的数据
price_bins = ['0-1000', '1000-2000', '2000-3000', '3000-4000', '4000-6000', '6000+']
pred_values = [
last_month['月度0-1000预测'],
last_month['月度1000-2000预测'],
last_month['月度2000-3000预测'],
last_month['月度3000-4000预测'],
last_month['月度4000-6000预测'],
last_month['月度6000-99999预测']
]
fig.add_trace(
go.Pie(
labels=price_bins,
values=pred_values,
name='大盘价格档位',
hole=0.4,
marker_colors=['#8dd3c7','#ffffb3','#bebada','#fb8072','#80b1d3','#fdb462']
),
row=2, col=2
)
# 图4: 2025/11/1期次销量预测
# 注意:这里我们使用nov_data,并且取每个价格档位的预测值的平均值(因为nov_data可能有多个月份?)
# 但根据问题描述,每个期次的数据包含从预测时间到2027-12-01的月份,所以nov_data中也有多个月份
# 我们这里展示的是nov_data中所有月份的平均值?还是只展示某一特定月份?
# 根据需求,是“2025/11/1期次的数据”,并且要表现预测销量和占比。这里我们可能想展示该期次预测的最后一个月份(即2027-12-01)?
# 或者展示该期次预测的所有月份的平均?但原需求没有明确,这里暂时按原代码取平均。
# 但原需求是“最后再用2025/11/1的期次的数据,表现一下预测的数据的销量和之间的占比,以及各个档位的信息”
# 这里我们展示该期次预测的最后一个月份(即2027-12-01)的各档位预测值
# 修改:从nov_data中取最后一个月份
last_month_nov = nov_data.iloc[-1]
fig.add_trace(
go.Bar(
x=price_bins,
y=[
last_month_nov['月度0-1000预测'],
last_month_nov['月度1000-2000预测'],
last_month_nov['月度2000-3000预测'],
last_month_nov['月度3000-4000预测'],
last_month_nov['月度4000-6000预测'],
last_month_nov['月度6000-99999预测']
],
name='2025/11/1期次预测(2027-12)',
marker_color='#17becf'
),
row=3, col=1
)
# 图5: XM品牌价格档位占比 (2025/1/1期次的最后一个月)
xm_pred_values = [
last_month['XM0-1000预测'],
last_month['XM1000-2000预测'],
last_month['XM2000-3000预测'],
last_month['XM3000-4000预测'],
last_month['XM4000-6000预测'],
last_month['XM6000-99999预测']
]
fig.add_trace(
go.Pie(
labels=price_bins,
values=xm_pred_values,
name='XM品牌价格档位',
hole=0.4,
marker_colors=['#8dd3c7','#ffffb3','#bebada','#fb8072','#80b1d3','#fdb462']
),
row=3, col=2
)
# 更新整体布局
fig.update_layout(
title_text='销售预测数据分析报告 (2025/1/1 和 2025/11/1 期次)',
height=1200,
width=1400,
showlegend=True,
template='plotly_white'
)
# 更新子图布局
fig.update_xaxes(title_text="日期", row=1, col=1)
fig.update_yaxes(title_text="销量", row=1, col=1)
fig.update_xaxes(title_text="日期", row=1, col=2)
fig.update_yaxes(title_text="ACC值", row=1, col=2)
fig.update_xaxes(title_text="价格档位", row=3, col=1)
fig.update_yaxes(title_text="预测销量", row=3, col=1)
# 保存为HTML文件
fig.write_html('sales_forecast_analysis.html', auto_open=True)
```
主要修改点:
1. 使用`.copy()`避免`SettingWithCopyWarning`,并在计算占比时使用`.loc`。
2. 修正了`yaxis2`的配置,将`titlefont`改为`title_font`(并放在`yaxis2`字典内)。
3. 对于2025/11/1期次的柱状图,之前是取平均值,现在改为取该期次最后一个月份(2027-12-01)的数据。这样更符合需求,因为需求是“表现一下预测的数据的销量和之间的占比,以及各个档位的信息”。当然,如果需求是展示该期次所有月份的平均,那么可以改回平均值。这里根据需求描述,我理解为展示该期次预测的最后一个时间点(即2027-12-01)的情况。
注意:如果nov_data中没有数据(即没有2027-12-01这个月份),那么需要调整。根据问题描述,所有预测时间的截至都是2027/12/1,所以每个期次的数据应该都包含到2027-12-01。
如果还有问题,请根据实际情况调整。
§§相关问题§§:
1. 在Plotly中如何避免SettingWithCopyWarning?
2. 在Plotly中设置双Y轴时,如何正确配置第二个Y轴的属性?
3. 如何处理时间序列数据在Plotly中的展示,特别是当数据点很多时?
4. 在Plotly中,如何动态更新图表以展示不同期次的数据?
</think>
这个错误是由于在Plotly中使用了无效的属性名`titlefont`导致的。根据错误信息,Plotly建议使用`tickfont`,但实际上我们需要的是设置坐标轴标题的字体颜色,正确的方式是使用`title_font`属性(注意是下划线分隔)。
### 修正后的代码
以下是修正后的完整代码,解决了属性名错误问题:
```python
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots
# 读取Excel文件
df = pd.read_excel('your_data.xlsx') # 替换为实际文件路径
# 转换日期列
df['forecast_time'] = pd.to_datetime(df['forecast_time'])
df['date'] = pd.to_datetime(df['date'])
# 提取2025/1/1期次的数据
jan_data = df[df['forecast_time'] == '2025-01-01'].copy() # 使用copy避免SettingWithCopyWarning
# 提取2025/11/1期次的数据
nov_data = df[df['forecast_time'] == '2025-11-01'].copy()
# 创建包含多个子图的HTML页面
fig = make_subplots(
rows=3, cols=2,
specs=[
[{"type": "bar", "rowspan": 2}, {"type": "scatter"}],
[None, {"type": "pie"}],
[{"type": "bar"}, {"type": "pie"}]
],
subplot_titles=(
"大盘销量趋势与XM品牌占比",
"预测准确率(ACC)变化趋势",
"大盘价格档位占比(2025/1/1期次)",
"2025/11/1期次销量预测",
"XM品牌价格档位占比(2025/1/1期次)"
),
vertical_spacing=0.1,
horizontal_spacing=0.15
)
# 图1: 大盘趋势和XM品牌占比 (重叠柱状图)
fig.add_trace(
go.Bar(
x=jan_data['date'],
y=jan_data['月度大盘预测值'],
name='大盘预测值',
marker_color='#1f77b4',
opacity=0.7
),
row=1, col=1
)
fig.add_trace(
go.Bar(
x=jan_data['date'],
y=jan_data['月度大盘真实值'],
name='大盘真实值',
marker_color='#ff7f0e',
opacity=0.7
),
row=1, col=1
)
# 计算XM品牌占比
jan_data.loc[:, 'XM占比'] = jan_data['XM品牌预测值'] / jan_data['月度大盘预测值'] # 使用loc避免警告
fig.add_trace(
go.Scatter(
x=jan_data['date'],
y=jan_data['XM占比'],
name='XM品牌占比',
mode='lines+markers',
line=dict(color='#d62728', width=3),
yaxis='y2'
),
row=1, col=1
)
# 添加右侧Y轴 - 修正属性名错误
fig.update_layout(
yaxis2=dict(
title="品牌占比",
title_font=dict(color="#d62728"), # 修正:使用title_font代替titlefont
tickfont=dict(color="#d62728"),
overlaying="y",
side="right",
range=[0, 1]
)
)
# 图2: 预测准确率变化
fig.add_trace(
go.Scatter(
x=jan_data['date'],
y=jan_data['ACC'],
name='大盘ACC',
mode='lines+markers',
line=dict(color='#2ca02c', width=2)
),
row=1, col=2
)
fig.add_trace(
go.Scatter(
x=jan_data['date'],
y=jan_data['ACC.1'],
name='XM品牌ACC',
mode='lines+markers',
line=dict(color='#9467bd', width=2)
),
row=1, col=2
)
# 图3: 大盘价格档位占比 (2025/1/1期次的最后一个月)
last_month = jan_data.iloc[-1] # 获取最后一个月的数据
price_bins = ['0-1000', '1000-2000', '2000-3000', '3000-4000', '4000-6000', '6000+']
pred_values = [
last_month['月度0-1000预测'],
last_month['月度1000-2000预测'],
last_month['月度2000-3000预测'],
last_month['月度3000-4000预测'],
last_month['月度4000-6000预测'],
last_month['月度6000-99999预测']
]
fig.add_trace(
go.Pie(
labels=price_bins,
values=pred_values,
name='大盘价格档位',
hole=0.4,
marker_colors=['#8dd3c7','#ffffb3','#bebada','#fb8072','#80b1d3','#fdb462']
),
row=2, col=2
)
# 图4: 2025/11/1期次销量预测
fig.add_trace(
go.Bar(
x=price_bins,
y=[
nov_data['月度0-1000预测'].mean(),
nov_data['月度1000-2000预测'].mean(),
nov_data['月度2000-3000预测'].mean(),
nov_data['月度3000-4000预测'].mean(),
nov_data['月度4000-6000预测'].mean(),
nov_data['月度6000-99999预测'].mean()
],
name='2025/11/1期次预测',
marker_color='#17becf'
),
row=3, col=1
)
# 图5: XM品牌价格档位占比 (2025/1/1期次的最后一个月)
xm_pred_values = [
last_month['XM0-1000预测'],
last_month['XM1000-2000预测'],
last_month['XM2000-3000预测'],
last_month['XM3000-4000预测'],
last_month['XM4000-6000预测'],
last_month['XM6000-99999预测']
]
fig.add_trace(
go.Pie(
labels=price_bins,
values=xm_pred_values,
name='XM品牌价格档位',
hole=0.4,
marker_colors=['#8dd3c7','#ffffb3','#bebada','#fb8072','#80b1d3','#fdb462']
),
row=3, col=2
)
# 更新整体布局
fig.update_layout(
title_text='销售预测数据分析报告 (2025/1/1 和 2025/11/1 期次)',
height=1200,
width=1400,
showlegend=True,
template='plotly_white'
)
# 更新子图布局
fig.update_xaxes(title_text="日期", row=1, col=1)
fig.update_yaxes(title_text="销量", row=1, col=1)
fig.update_xaxes(title_text="日期", row=1, col=2)
fig.update_yaxes(title_text="ACC值", row=1, col=2)
fig.update_xaxes(title_text="价格档位", row=3, col=1)
fig.update_yaxes(title_text="平均销量", row=3, col=1)
# 保存为HTML文件
fig.write_html('sales_forecast_analysis.html', auto_open=True)
```
### 主要修正点:
1. **属性名修正**:
- 将错误的`titlefont`改为正确的`title_font`
- 使用`title_font=dict(color="#d62728")`设置坐标轴标题字体颜色
2. **避免SettingWithCopyWarning**:
- 使用`.copy()`创建数据子集副本
- 使用`jan_data.loc[:, 'XM占比'] = ...`语法避免警告
3. **其他改进**:
- 保持图表布局和功能不变
- 添加了注释说明关键修改点
### 运行前准备:
1. 确保安装依赖:`pip install pandas plotly openpyxl`
2. 将代码中的`'your_data.xlsx'`替换为实际Excel文件路径
这个修正版本应该能解决之前的属性名错误问题,并避免产生SettingWithCopyWarning。