<think>我们遇到了一个严重的缩进错误(IndentationError)。在Python中,缩进是语法的一部分,错误的缩进会导致程序无法运行。根据错误信息,问题出现在第19行附近,但实际上错误信息显示的是在文件中的多个位置出现了意外的缩进。
从错误信息来看,似乎整个代码块的缩进都出现了问题。这可能是因为在复制粘贴代码时,代码被错误地缩进,或者在编辑过程中混合了空格和制表符。
为了解决这个问题,我们需要确保整个代码的缩进是一致的。通常,我们使用4个空格作为一级缩进。下面我将提供一个完整的、重新调整了缩进的代码版本。同时,为了避免混合制表符和空格,我们使用空格进行缩进。
此外,我注意到之前的错误信息中提到了“SyntaxError: expected 'except' or 'finally' block”,这通常是因为try块后面没有跟随except或finally块。在下面的代码中,我将确保每个try块都有对应的except块。
由于整个代码较长,我将重新整理整个代码,确保缩进正确,并修复之前提到的异常处理问题。
注意:由于在错误信息中看到很多“IndentationError: unexpected indent”,我们需要将代码的每一层缩进都调整为4个空格,并且保持一致。
以下是完整的、修正了缩进和异常处理结构的代码:
</think>
## 解决 Python 缩进错误(IndentationError)
您遇到的 `IndentationError: unexpected indent` 错误表明您的 Python 代码中存在不正确的缩进。Python 使用缩进来定义代码块(如函数、循环和条件语句),因此缩进错误会阻止代码执行。以下是完整的解决方案:
```python
import pandas as pd
import numpy as np
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
import os
import sys
import traceback
import logging
import time
def setup_logging():
log_file = r'C:\Users\DELL\Desktop\PCA_analysis.log'
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(log_file, encoding='utf-8'),
logging.StreamHandler(sys.stdout)
]
)
return logging.getLogger('PCA_Analysis')
def get_excel_sheet_names(file_path):
"""获取Excel文件中所有工作表名称"""
try:
xl = pd.ExcelFile(file_path)
return xl.sheet_names
except Exception as e:
logging.error(f"获取工作表名称失败: {str(e)}")
return []
def setup_chinese_font(logger):
"""设置中文字体支持"""
try:
# 尝试多种常见中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'KaiTi', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
logger.info("中文字体设置成功")
return True
except Exception as e:
logger.error(f"字体设置错误: {e}")
try:
font_path = 'C:/Windows/Fonts/simhei.ttf'
if os.path.exists(font_path):
font_prop = fm.FontProperties(fname=font_path)
plt.rcParams['font.family'] = font_prop.get_name()
plt.rcParams['axes.unicode_minus'] = False
logger.info(f"已手动设置字体: {font_path}")
return True
else:
logger.warning("未找到simhei.ttf字体文件")
return False
except Exception as font_error:
logger.error(f"手动字体设置失败: {font_error}")
return False
def perform_pca_analysis(data, period_name, output_dir, logger):
"""执行PCA分析并生成所有结果"""
result = {'success': False}
try:
logger.info(f"开始分析时间段: {period_name}")
if data.empty:
logger.warning(f"{period_name}时间段数据为空,跳过")
return result
if data.isnull().any().any():
logger.warning("数据包含缺失值,将使用列均值填充")
data = data.fillna(data.mean())
scaler = StandardScaler()
scaled_data = scaler.fit_transform(data.T)
pca = PCA(n_components=2)
principal_components = pca.fit_transform(scaled_data)
# 创建得分图DataFrame
score_df = pd.DataFrame(
principal_components,
columns=['主成分1', '主成分2'],
index=data.columns
)
# 创建载荷图DataFrame
loadings_df = pd.DataFrame(
pca.components_.T,
columns=['PC1', 'PC2'],
index=data.index
)
explained_variance = pca.explained_variance_ratio_
# 生成得分图
plt.figure(figsize=(10, 8))
plt.scatter(score_df['主成分1'], score_df['主成分2'], s=50, alpha=0.7)
for year, row in score_df.iterrows():
plt.annotate(str(year),
(row['主成分1'] + 0.02, row['主成分2'] + 0.02),
fontsize=9)
plt.title(f'PCA分析 - {period_name}时间段', fontsize=16)
plt.xlabel(f'主成分1 (方差贡献率: {explained_variance[0]:.2%})', fontsize=12)
plt.ylabel(f'主成分2 (方差贡献率: {explained_variance[1]:.2%})', fontsize=12)
plt.grid(True, linestyle='--', alpha=0.6)
score_path = os.path.join(output_dir, f'PCA_得分图_{period_name}.png')
plt.savefig(score_path, dpi=300, bbox_inches='tight')
plt.close()
# 生成载荷图
plt.figure(figsize=(12, 10))
ax = plt.gca()
for i, indicator in enumerate(loadings_df.index):
x = loadings_df.loc[indicator, 'PC1']
y = loadings_df.loc[indicator, 'PC2']
ax.arrow(0, 0, x, y,
head_width=0.05, head_length=0.05,
fc='red', ec='red', alpha=0.7)
ax.text(x * 1.15, y * 1.15, indicator,
color='blue', fontsize=10,
bbox=dict(facecolor='white', alpha=0.5, edgecolor='none'))
circle = plt.Circle((0,0), 1.0, color='gray', fill=False, linestyle='--', alpha=0.3)
ax.add_artist(circle)
plt.axhline(y=0, color='k', linestyle='--', alpha=0.3)
plt.axvline(x=0, color='k', linestyle='--', alpha=0.3)
max_val = max(loadings_df.abs().max().max() * 1.3, 1.0)
plt.xlim(-max_val, max_val)
plt.ylim(-max_val, max_val)
plt.title(f'指标因子载荷图 - {period_name}时间段', fontsize=16)
plt.xlabel(f'主成分1 (方差贡献率: {explained_variance[0]:.2%})', fontsize=12)
plt.ylabel(f'主成分2 (方差贡献率: {explained_variance[1]:.2%})', fontsize=12)
plt.grid(True, linestyle='--', alpha=0.4)
loading_path = os.path.join(output_dir, f'PCA_载荷图_{period_name}.png')
plt.savefig(loading_path, dpi=300, bbox_inches='tight')
plt.close()
# 保存数据结果
result_path = os.path.join(output_dir, f'PCA_结果_{period_name}.xlsx')
with pd.ExcelWriter(result_path) as writer:
score_df.to_excel(writer, sheet_name='得分矩阵')
loadings_df.to_excel(writer, sheet_name='载荷矩阵')
variance_df = pd.DataFrame({
'主成分': ['PC1', 'PC2'],
'方差贡献率': explained_variance,
'累计贡献率': [explained_variance[0], sum(explained_variance[:2])]
})
variance_df.to_excel(writer, sheet_name='方差解释', index=False)
result.update({
'success': True,
'score_path': score_path,
'loading_path': loading_path,
'result_path': result_path,
'explained_variance': explained_variance
})
logger.info(f"时间段 {period_name} 分析成功!")
return result
except Exception as e:
logger.error(f"分析时间段 {period_name} 时出错: {str(e)}")
logger.error(traceback.format_exc())
return result
def main():
logger = setup_logging()
try:
logger.info("="*50)
logger.info("PCA分析程序启动")
logger.info("="*50)
# 设置中文字体支持
setup_chinese_font(logger)
# 创建输出目录
output_dir = r'C:\Users\DELL\Desktop\PCA_Results'
try:
os.makedirs(output_dir, exist_ok=True)
logger.info(f"输出目录已创建: {output_dir}")
except Exception as e:
logger.error(f"创建输出目录失败: {e}")
return
# 安全读取Excel文件
file_path = r'C:\Users\DELL\Desktop\underdata05.xlsx'
try:
logger.info(f"正在读取Excel文件: {file_path}")
if not os.path.exists(file_path):
raise FileNotFoundError(f"文件不存在: {file_path}")
# 获取所有工作表名称
sheet_names = get_excel_sheet_names(file_path)
logger.info(f"Excel文件中包含的工作表: {sheet_names}")
# 检查目标工作表是否存在
target_sheet = 'underdata05'
if target_sheet not in sheet_names:
# 尝试自动匹配工作表(不区分大小写)
matched_sheets = [name for name in sheet_names if name.lower() == target_sheet.lower()]
if matched_sheets:
logger.warning(f"工作表 '{target_sheet}' 未找到,但发现相似工作表: {matched_sheets[0]}")
target_sheet = matched_sheets[0]
else:
# 尝试默认使用第一个工作表
logger.warning(f"工作表 '{target_sheet}' 未找到,将使用第一个工作表: {sheet_names[0]}")
target_sheet = sheet_names[0]
logger.info(f"最终使用的工作表: {target_sheet}")
# 读取Excel文件
df = pd.read_excel(file_path, sheet_name=target_sheet, header=0, index_col=0)
if df.shape[0] == 0 or df.shape[1] == 0:
raise ValueError("数据为空或格式不正确")
logger.info(f"数据读取成功! 形状: {df.shape}")
logger.info(f"前5个指标: {df.index.tolist()[:5]}")
logger.info(f"前5个年份: {df.columns.tolist()[:5]}")
except Exception as e:
logger.error(f"文件读取失败: {str(e)}")
logger.error(traceback.format_exc())
return
# 定义时间段划分
time_periods = {
"1952-1977": (1952, 1977),
"1978-1991": (1978, 1991),
"1992-2012": (1992, 2012),
"2013-2025": (2013, 2025)
}
# 分时段执行PCA
all_results = {}
for name, (start_year, end_year) in time_periods.items():
try:
logger.info(f"\n{'='*50}")
logger.info(f"处理时间段: {name} ({start_year}-{end_year})")
period_cols = [col for col in df.columns if start_year <= col <= end_year]
if not period_cols:
logger.warning(f"警告: {name}时间段无数据,跳过")
continue
period_data = df[period_cols]
result = perform_pca_analysis(period_data, name, output_dir, logger)
all_results[name] = result
except Exception as e:
logger.error(f"处理时间段 {name} 时出错: {str(e)}")
logger.error(traceback.format_exc())
# 生成汇总报告
try:
summary_path = os.path.join(output_dir, 'PCA_分析汇总报告.txt')
with open(summary_path, 'w', encoding='utf-8') as f:
f.write("PCA分析结果汇总报告\n")
f.write("="*50 + "\n\n")
f.write(f"分析时间: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(f"源文件: {file_path}\n")
f.write(f"工作表名称: {target_sheet}\n")
f.write(f"输出目录: {output_dir}\n\n")
for name, result in all_results.items():
f.write(f"时间段: {name}\n")
if result.get('success', False):
f.write(f"状态: 成功\n")
f.write(f"主成分1方差贡献率: {result['explained_variance'][0]:.2%}\n")
f.write(f"主成分2方差贡献率: {result['explained_variance'][1]:.2%}\n")
f.write(f"累计方差贡献率: {sum(result['explained_variance'][:2]):.2%}\n")
f.write(f"得分图路径: {result['score_path']}\n")
f.write(f"载荷图路径: {result['loading_path']}\n")
f.write(f"详细结果路径: {result['result_path']}\n")
else:
f.write(f"状态: 失败\n")
f.write("-"*50 + "\n\n")
f.write("\n生成文件列表:\n")
files = os.listdir(output_dir)
for file in files:
f.write(f"- {file}\n")
logger.info(f"汇总报告已保存: {summary_path}")
except Exception as e:
logger.error(f"生成汇总报告时出错: {str(e)}")
logger.error(traceback.format_exc())
logger.info("\n" + "="*50)
logger.info(f"PCA分析完成! 结果保存至: {output_dir}")
logger.info("="*50)
except Exception as e:
logger.critical(f"程序发生未捕获的异常: {str(e)}")
logger.critical(traceback.format_exc())
finally:
input("按Enter键退出程序...")
if __name__ == "__main__":
main()
```
### Python 缩进规则详解
Python 使用缩进来定义代码块结构,而不是像其他语言那样使用花括号。以下是关键规则:
1. **一致的缩进级别**:
```python
if condition:
# 缩进4个空格
print("条件成立")
# 同一代码块保持相同缩进
else:
# 另一个代码块
print("条件不成立")
```
2. **缩进单位**:
- 推荐使用4个空格(PEP 8标准)
- 避免混合使用空格和制表符(Tab)
- 大多数IDE会自动将Tab转换为空格
3. **多行语句的缩进**:
```python
# 括号内的多行语句
result = some_function(
arg1,
arg2,
arg3
)
# 反斜杠显式续行
long_string = "这是一个非常长的字符串,需要" \
"分成多行编写"
```
### 常见缩进错误及修复方法
| 错误类型 | 示例 | 修复方法 |
|----------|------|----------|
| 意外的缩进 | `print("Hello")`<br>` print("World")` | 移除多余缩进 |
| 缺少缩进 | `if True:`<br>`print("应缩进")` | 添加4个空格缩进 |
| 缩进不一致 | `if True:`<br>` print("缩进4")`<br>` print("缩进2")` | 统一使用4个空格 |
| 空行缩进 | ` # 空行有空格` | 删除空行中的空格 |
### 避免缩进错误的技巧
1. **使用专业的代码编辑器**:
- VS Code、PyCharm等编辑器会显示缩进指南
- 启用"显示空白字符"功能(查看不可见字符)
2. **配置编辑器设置**:
- 设置Tab大小为4个空格
- 启用"自动将Tab转换为空格"
- 在VS Code中配置:
```json
{
"editor.tabSize": 4,
"editor.insertSpaces": true,
"editor.renderWhitespace": "all"
}
```
3. **使用代码格式化工具**:
- 安装Black:`pip install black`
- 运行格式化:`black your_script.py`
- 这将自动修复所有缩进问题
4. **检查混合空格和制表符**:
```python
# 在Python脚本开头添加
if True:
print("检查混合缩进")
# ^ 这里可能有制表符和空格混合
```
- 使用`python -tt your_script.py`检查混合缩进
- 错误消息会显示:`TabError: inconsistent use of tabs and spaces in indentation`
5. **复制代码时的注意事项**:
- 粘贴代码后重新调整缩进
- 使用编辑器的"重新缩进"功能(在VS Code中是Ctrl+Shift+P > "重新缩进行")
### 如何修复现有代码
1. **手动修复**:
- 逐行检查缩进
- 确保所有代码块使用一致的4空格缩进
- 删除行尾多余空格
2. **使用自动格式化**:
```bash
# 安装autopep8
pip install autopep8
# 修复缩进问题
autopep8 --in-place --aggressive --aggressive your_script.py
```
3. **检查特定行**:
- 错误消息通常包含行号:`IndentationError: unexpected indent (line 19)`
- 直接跳转到指定行检查