Part 5 更多
1. 课件11:文件与交互
突破内存易失性的限制,通过文件系统实现数据的持久化存储
1.1. 文件读写基础与持久化概念
持久化:
数据在程序结束后依然存在,通常通过文件系统实现。
必要性:
内存中的数据在程序终止后会丢失,文件存储可以保存重要信息。
1.1.1. 核心函数
open(): 打开文件,返回文件对象。
常用参数:
- 文件路径:字符串,指定文件位置。
- 模式:字符串,指定文件操作类型,如 'r'(读取)、'w'(写入)、'a'(追加)、'b'(二进制模式)等。
- 'r':只读模式,文件必须存在。
- 'w':写入模式,文件不存在则创建,存在则覆盖。
- 'a':追加模式,文件不存在则创建,存在则在末尾添加内容。
- 'b':二进制模式,用于非文本文件,如图片、音频等。
- '+':读写模式,可与其他模式结合使用,如 'r+'、'w+'。
实现双向操作- 'r+':读写模式,文件必须存在。
- 'w+':写入读模式,文件不存在则创建,存在则覆盖。
- 编码:字符串,指定文件编码格式,如 'utf-8'。
1.1.2. 最佳实践
使用 with 语句管理文件上下文,确保文件正确关闭:
python
with open('file.txt', 'r', encoding='utf-8') as file:
content = file.read()
# 文件在此块结束后自动关闭
它能确保代码块执行完毕后自动安全关闭文件,防止数据损坏或资源泄露
1.2. 结构化数据:CSV读写
CSV:Comma-Separated Values,逗号分隔值文件,用于存储表格数据的文本文件格式。
1.3. 示例代码
python
"""
示例:文件读写基础与持久化
包含演示:
- open() 函数和各种文件模式
- with 语句管理文件上下文
- 读取文件的多种方式
- 写入和追加文件内容
- 文件指针位置管理
运行:
python file_operations_demo.py
作者:自动生成示例(中文注释)
"""
def demo_basic_read_write():
"""演示基本的读写操作"""
print('--- 基本读写操作 ---')
# 写入文件
with open('example.txt', 'w', encoding='utf-8') as f:
f.write('Hello, World!\n')
f.write('This is line 2.\n')
f.write('This is line 3.\n')
print('文件写入完成')
# 读取整个文件
with open('example.txt', 'r', encoding='utf-8') as f:
content = f.read()
print('文件内容:')
print(content)
def demo_readlines():
"""演示逐行读取"""
print('--- 逐行读取 ---')
with open('example.txt', 'r', encoding='utf-8') as f:
lines = f.readlines() # 返回列表,每个元素包含换行符
print(f'共有 {len(lines)} 行')
for i, line in enumerate(lines, 1):
print(f'第 {i} 行:{line.strip()}')
print()
def demo_readline():
"""演示逐行读取(一次一行)"""
print('--- 单行读取(for循环) ---')
with open('example.txt', 'r', encoding='utf-8') as f:
for i, line in enumerate(f, 1): # 类似生成器的行为
print(f'第 {i} 行:{line.strip()}')
print()
def demo_append():
"""演示追加模式"""
print('--- 追加模式 ---')
# 原始内容
print('原始内容:')
with open('example.txt', 'r', encoding='utf-8') as f:
print(f.read())
# 追加新内容
with open('example.txt', 'a', encoding='utf-8') as f:
f.write('This is appended line 4.\n')
f.write('This is appended line 5.\n')
print('追加后的内容:')
with open('example.txt', 'r', encoding='utf-8') as f:
print(f.read())
def demo_read_write_mode():
"""演示读写模式(r+ 和 w+)"""
print('--- 读写模式 ---')
# w+ 模式:写入并读取
with open('rw_example.txt', 'w+', encoding='utf-8') as f:
f.write('Line 1\n')
f.write('Line 2\n')
f.write('Line 3\n')
# 回到文件开头读取
f.seek(0)
content = f.read()
print('w+ 模式下的内容:')
print(content)
# r+ 模式:读取并修改
with open('rw_example.txt', 'r+', encoding='utf-8') as f:
f.seek(0)
content = f.read()
print('原始内容:')
print(content)
# 从开头写入(覆盖)
f.seek(0)
f.write('Modified content\n')
print('修改后的内容:')
with open('rw_example.txt', 'r', encoding='utf-8') as f:
print(f.read())
print()
def demo_file_pointer():
"""演示文件指针操作"""
print('--- 文件指针操作 ---')
with open('example.txt', 'r', encoding='utf-8') as f:
# 读取前 10 个字符
print('前 10 个字符:', f.read(10))
print('当前指针位置:', f.tell())
# 回到开头
f.seek(0)
print('回到开头后的指针位置:', f.tell())
# 读取下一行
first_line = f.readline()
print('第一行:', first_line.strip())
print('当前指针位置:', f.tell())
print()
def demo_exception_handling():
"""演示异常处理"""
print('--- 异常处理 ---')
# 尝试读取不存在的文件
try:
with open('nonexistent.txt', 'r', encoding='utf-8') as f:
content = f.read()
except FileNotFoundError:
print('捕获异常:文件不存在')
# 权限错误示例(注释掉,避免权限问题)
try:
with open('example.txt', 'r', encoding='utf-8') as f:
content = f.read()
print('文件成功打开')
except PermissionError:
print('权限不足')
except IOError as e:
print(f'IO错误:{e}')
print()
def cleanup():
"""清理示例文件"""
import os
files = ['example.txt', 'rw_example.txt']
for file in files:
if os.path.exists(file):
os.remove(file)
print('清理示例文件完成')
def main():
print('=' * 50)
print('文件读写基础演示')
print('=' * 50)
print()
demo_basic_read_write()
print()
demo_readlines()
demo_readline()
demo_append()
print()
demo_read_write_mode()
demo_file_pointer()
demo_exception_handling()
cleanup()
if __name__ == '__main__':
main()
1.2.1. 读写CSV文件
模块:
csv:Python标准库中的模块,提供读写CSV文件的功能。
读取:
csv.reader():创建一个读取器对象,用于逐行读取CSV文件内容。
参数:文件对象
返回值:迭代器
每一行解析为一个字符串列表。
方法:
next(): 读取下一行数据。
写入:
csv.writer():创建一个写入器对象,用于将数据写入CSV文件。
方法:
writerow(): 写入一行数据。writerows(): 写入多行数据。
写入时,建议使用 newline='' 参数打开文件,防止多余空行出现:
1.2.2. 示例代码
python
"""
示例:CSV 文件读写
包含演示:
- csv.reader() 读取 CSV 文件
- csv.writer() 写入 CSV 文件
- 处理表格数据
- csv.DictReader 和 csv.DictWriter 高级用法
运行:
python csv_handler_demo.py
作者:自动生成示例(中文注释)
"""
import csv
import os
def demo_write_csv():
"""演示写入 CSV 文件"""
print('--- 写入 CSV 文件 ---')
# 准备数据
data = [
['姓名', '年龄', '城市'],
['Alice', 25, 'Beijing'],
['Bob', 30, 'Shanghai'],
['Charlie', 28, 'Guangzhou'],
['Diana', 26, 'Shenzhen']
]
# 写入 CSV 文件
# newline='' 可以防止多余的空行出现
with open('students.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerows(data)
print('CSV 文件写入完成')
def demo_read_csv():
"""演示读取 CSV 文件"""
print('--- 读取 CSV 文件 ---')
with open('students.csv', 'r', encoding='utf-8') as f:
reader = csv.reader(f)
for i, row in enumerate(reader):
if i == 0:
print(f'表头:{row}')
else:
print(f' {row}')
print()
def demo_dict_writer():
"""演示字典形式的 CSV 写入"""
print('--- 字典形式的 CSV 写入 ---')
# 字典形式的数据
data = [
{'姓名': 'Emma', '年龄': 27, '城市': 'Hangzhou'},
{'姓名': 'Frank', '年龄': 29, '城市': 'Chengdu'},
{'姓名': 'Grace', '年龄': 24, '城市': 'Wuhan'}
]
fieldnames = ['姓名', '年龄', '城市']
with open('more_students.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader() # 写入表头
writer.writerows(data)
print('字典形式的 CSV 文件写入完成')
def demo_dict_reader():
"""演示字典形式的 CSV 读取"""
print('--- 字典形式的 CSV 读取 ---')
with open('more_students.csv', 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for i, row in enumerate(reader, 1):
print(f'第 {i} 行:{dict(row)}')
print()
def demo_data_analysis():
"""演示 CSV 数据分析"""
print('--- CSV 数据分析 ---')
# 读取数据并分析
ages = []
with open('students.csv', 'r', encoding='utf-8') as f:
reader = csv.reader(f)
next(reader) # 跳过表头
for row in reader:
ages.append(int(row[1]))
print(f'平均年龄:{sum(ages) / len(ages):.1f}')
print(f'最小年龄:{min(ages)}')
print(f'最大年龄:{max(ages)}')
print()
def demo_csv_with_different_delimiter():
"""演示使用不同分隔符的 CSV"""
print('--- 不同分隔符的 CSV ---')
data = [
['Name', 'Score', 'Grade'],
['Alice', 85, 'B'],
['Bob', 92, 'A'],
['Charlie', 78, 'C']
]
# 使用分号作为分隔符
with open('scores.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f, delimiter=';')
writer.writerows(data)
print('使用分号分隔符的 CSV 文件写入完成')
# 读取时指定分隔符
with open('scores.csv', 'r', encoding='utf-8') as f:
reader = csv.reader(f, delimiter=';')
for row in reader:
print(row)
print()
def cleanup():
"""清理示例文件"""
files = ['students.csv', 'more_students.csv', 'scores.csv']
for file in files:
if os.path.exists(file):
os.remove(file)
print('清理示例文件完成')
def main():
print('=' * 50)
print('CSV 文件读写演示')
print('=' * 50)
print()
demo_write_csv()
print()
demo_read_csv()
demo_dict_writer()
print()
demo_dict_reader()
demo_data_analysis()
demo_csv_with_different_delimiter()
cleanup()
if __name__ == '__main__':
main()
1.3. 复杂数据:JSON读写
JSON(JavaScript Object Notation):一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。
网络API的事实标准格式
语法与字典和列表几乎相同
1.3.1. 读写JSON文件
模块:
json:Python标准库中的模块,提供读写JSON文件的功能。
文件操作:
json.dump(obj, file): 将Python对象obj序列化为JSON格式,并写入文件对象file。json.load(file): 从文件对象file中读取JSON数据,并反序列化为Python对象。
内存字符串操作:
json.dumps(obj): 将Python对象obj序列化为JSON格式的字符串。json.loads(s): 将JSON格式的字符串s反序列化为Python对象。
优势:
能完美表示嵌套和层级结构,支持布尔值(对应 JSON
的 true/false)和空值(对应 JSON 的 null)的自动转换
1.3.2. 代码示例
python
"""
示例:JSON 文件读写与数据序列化
包含演示:
- json.dump() 写入 JSON 文件
- json.load() 读取 JSON 文件
- json.dumps() 序列化为字符串
- json.loads() 反序列化字符串
- Python 对象到 JSON 的类型转换
- 复杂嵌套数据结构
运行:
python json_handler_demo.py
作者:自动生成示例(中文注释)
"""
import json
import os
def demo_dump_load():
"""演示 dump 和 load(文件操作)"""
print('--- json.dump() 和 json.load() ---')
# Python 对象
data = {
'name': 'Alice',
'age': 25,
'city': 'Beijing',
'hobbies': ['reading', 'coding', 'hiking'],
'is_student': True,
'gpa': None
}
# 写入 JSON 文件
with open('person.json', 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
print('JSON 文件写入完成')
print('文件内容:')
with open('person.json', 'r', encoding='utf-8') as f:
print(f.read())
# 读取 JSON 文件
with open('person.json', 'r', encoding='utf-8') as f:
loaded_data = json.load(f)
print('读取的数据:', loaded_data)
print()
def demo_dumps_loads():
"""演示 dumps 和 loads(字符串操作)"""
print('--- json.dumps() 和 json.loads() ---')
# Python 对象
person = {
'name': 'Bob',
'age': 30,
'skills': ['Python', 'JavaScript', 'SQL']
}
# 序列化为 JSON 字符串
json_string = json.dumps(person, ensure_ascii=False, indent=2)
print('JSON 字符串:')
print(json_string)
# 反序列化
parsed_data = json.loads(json_string)
print('解析后的数据:', parsed_data)
print()
def demo_type_conversion():
"""演示 Python 类型到 JSON 类型的转换"""
print('--- Python 类型与 JSON 类型转换 ---')
data = {
'string': 'Hello',
'integer': 42,
'float': 3.14,
'boolean_true': True,
'boolean_false': False,
'null_value': None,
'list': [1, 2, 3],
'dict': {'nested': 'value'},
'tuple': (4, 5, 6) # 元组会转换为列表
}
json_string = json.dumps(data, indent=2)
print('转换结果:')
print(json_string)
print()
def demo_nested_structure():
"""演示复杂嵌套数据结构"""
print('--- 复杂嵌套结构 ---')
company_data = {
'company_name': 'Tech Corp',
'departments': [
{
'name': 'Engineering',
'employees': [
{'id': 1, 'name': 'Alice', 'salary': 80000},
{'id': 2, 'name': 'Bob', 'salary': 75000}
]
},
{
'name': 'Sales',
'employees': [
{'id': 3, 'name': 'Charlie', 'salary': 70000}
]
}
]
}
# 写入文件
with open('company.json', 'w', encoding='utf-8') as f:
json.dump(company_data, f, ensure_ascii=False, indent=2)
print('复杂数据结构写入完成')
# 读取并展示
with open('company.json', 'r', encoding='utf-8') as f:
loaded = json.load(f)
print(f'公司:{loaded["company_name"]}')
for dept in loaded['departments']:
print(f' 部门:{dept["name"]}')
for emp in dept['employees']:
print(f' 员工:{emp["name"]},薪资:{emp["salary"]}')
print()
def demo_pretty_print():
"""演示格式化输出"""
print('--- 格式化输出 ---')
data = {'name': 'Diana', 'items': [1, 2, 3, 4, 5]}
# 紧凑格式
compact = json.dumps(data, separators=(',', ':'))
print('紧凑格式:', compact)
# 美化格式(默认)
pretty = json.dumps(data, indent=2)
print('美化格式:')
print(pretty)
print()
def demo_error_handling():
"""演示错误处理"""
print('--- 错误处理 ---')
# 有效的 JSON
try:
valid_json = '{"name": "Eve", "age": 28}'
data = json.loads(valid_json)
print('有效的 JSON 解析成功:', data)
except json.JSONDecodeError as e:
print('JSON 解码错误:', e)
# 无效的 JSON
try:
invalid_json = "{'name': 'Frank'}" # 使用单引号不符合 JSON 标准
data = json.loads(invalid_json)
except json.JSONDecodeError as e:
print('捕获到 JSON 解码错误(预期)')
# 文件不存在
try:
with open('nonexistent.json', 'r') as f:
data = json.load(f)
except FileNotFoundError:
print('捕获到文件不存在错误(预期)')
print()
def cleanup():
"""清理示例文件"""
files = ['person.json', 'company.json']
for file in files:
if os.path.exists(file):
os.remove(file)
print('清理示例文件完成')
def main():
print('=' * 50)
print('JSON 文件读写演示')
print('=' * 50)
print()
demo_dump_load()
demo_dumps_loads()
demo_type_conversion()
demo_nested_structure()
demo_pretty_print()
demo_error_handling()
cleanup()
if __name__ == '__main__':
main()
1.4. 实践中的文件处理
防御性编程
文件系统中可能存在的异常很多
注意异常处理
常见异常:
FileNotFoundError: 文件未找到。PermissionError: 权限不足。JSONDecodeError: JSON解码错误。IOError: 输入输出错误。
实例:配置加载器
读取、校验、写入默认值分别封装为纯函数
配置文件损坏或丢失,程序应能自动创建并使用预设的默认值,确保系统稳定启动
1.5. 示例代码
python
"""
示例:配置加载器与管理
包含演示:
- 从 JSON 文件加载配置
- 验证配置的必要字段
- 与默认值合并
- 创建默认配置文件
- 错误恢复机制
运行:
python config_loader_demo.py
作者:自动生成示例(中文注释)
"""
import json
import os
# 默认配置常量
DEFAULT_CONFIG = {
'app_name': 'MyApp',
'version': '1.0.0',
'debug': False,
'log_level': 'INFO',
'server': {
'host': 'localhost',
'port': 8000,
'timeout': 30
},
'database': {
'engine': 'sqlite',
'path': './data.db',
'max_connections': 10
},
'features': {
'enable_cache': True,
'cache_ttl': 3600,
'enable_auth': True
}
}
def load_config(config_file='config.json'):
"""
加载配置文件
参数:
config_file (str): 配置文件路径
返回:
dict: 配置字典
"""
if not os.path.exists(config_file):
print(f'配置文件 {config_file} 不存在,使用默认配置')
return DEFAULT_CONFIG.copy()
try:
with open(config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
print(f'成功加载配置文件:{config_file}')
return config
except json.JSONDecodeError as e:
print(f'配置文件 JSON 格式错误:{e},使用默认配置')
return DEFAULT_CONFIG.copy()
except Exception as e:
print(f'加载配置失败:{e},使用默认配置')
return DEFAULT_CONFIG.copy()
def validate_config(config):
"""
验证配置的必要字段
参数:
config (dict): 要验证的配置
返回:
tuple: (是否有效, 错误信息列表)
"""
errors = []
# 验证必要字段
if 'app_name' not in config:
errors.append('缺少必要字段:app_name')
if 'server' not in config or not isinstance(config['server'], dict):
errors.append('缺少必要字段:server(应为字典)')
else:
if 'host' not in config['server']:
errors.append('缺少必要字段:server.host')
if 'port' not in config['server']:
errors.append('缺少必要字段:server.port')
elif not isinstance(config['server']['port'], int):
errors.append('server.port 应为整数')
if 'database' not in config or not isinstance(config['database'], dict):
errors.append('缺少必要字段:database(应为字典)')
else:
if 'engine' not in config['database']:
errors.append('缺少必要字段:database.engine')
return len(errors) == 0, errors
def merge_with_defaults(config, defaults):
"""
将配置与默认值合并(递归)
参数:
config (dict): 用户配置
defaults (dict): 默认配置
返回:
dict: 合并后的配置
"""
result = defaults.copy()
for key, value in config.items():
if key in defaults and isinstance(defaults[key], dict) and isinstance(value, dict):
# 递归合并嵌套字典
result[key] = merge_with_defaults(value, defaults[key])
else:
result[key] = value
return result
def save_config(config, config_file='config.json'):
"""
保存配置文件
参数:
config (dict): 要保存的配置
config_file (str): 配置文件路径
"""
try:
with open(config_file, 'w', encoding='utf-8') as f:
json.dump(config, f, ensure_ascii=False, indent=2)
print(f'配置已保存到:{config_file}')
except Exception as e:
print(f'保存配置失败:{e}')
def demo_basic_loading():
"""演示:基本加载"""
print('--- 演示 1:基本加载 ---')
# 模拟 1:配置文件不存在
config = load_config('missing_config.json')
print(f'加载的配置:app_name={config["app_name"]}, version={config["version"]}')
print()
def demo_create_default():
"""演示:创建默认配置文件"""
print('--- 演示 2:创建默认配置 ---')
config_file = 'default_config.json'
save_config(DEFAULT_CONFIG, config_file)
# 验证创建的文件
with open(config_file, 'r', encoding='utf-8') as f:
content = f.read()
print('创建的配置文件内容(前 300 字符):')
print(content[:300] + '...')
print()
def demo_load_and_validate():
"""演示:加载并验证配置"""
print('--- 演示 3:加载并验证 ---')
# 创建一个测试配置文件
test_config = {
'app_name': 'TestApp',
'server': {
'host': '0.0.0.0',
'port': 5000
},
'database': {
'engine': 'postgres'
}
}
with open('test_config.json', 'w', encoding='utf-8') as f:
json.dump(test_config, f)
# 加载并验证
config = load_config('test_config.json')
is_valid, errors = validate_config(config)
if is_valid:
print('✓ 配置验证成功')
else:
print('✗ 配置验证失败:')
for error in errors:
print(f' - {error}')
print()
def demo_merge_with_defaults():
"""演示:与默认值合并"""
print('--- 演示 4:与默认值合并 ---')
# 部分配置
partial_config = {
'app_name': 'CustomApp',
'server': {
'port': 9000 # 只覆盖 port,host 使用默认
}
}
# 合并
merged = merge_with_defaults(partial_config, DEFAULT_CONFIG)
print('部分配置:')
print(f' app_name={partial_config["app_name"]}')
print(f' server.port={partial_config["server"]["port"]}')
print()
print('合并后的配置:')
print(f' app_name={merged["app_name"]}')
print(f' server.host={merged["server"]["host"]} (来自默认值)')
print(f' server.port={merged["server"]["port"]} (来自用户配置)')
print(f' database.engine={merged["database"]["engine"]} (来自默认值)')
print()
def demo_invalid_config():
"""演示:处理无效的配置"""
print('--- 演示 5:处理无效配置 ---')
# 创建无效的 JSON 文件
with open('invalid_config.json', 'w') as f:
f.write('{ invalid json content }')
config = load_config('invalid_config.json')
is_valid, errors = validate_config(config)
print(f'配置验证结果:有效={is_valid}')
print()
def demo_nested_config():
"""演示:处理嵌套配置"""
print('--- 演示 6:嵌套配置管理 ---')
# 复杂配置
complex_config = {
'app_name': 'ComplexApp',
'environments': {
'development': {
'server': {'host': 'localhost', 'port': 3000},
'debug': True
},
'production': {
'server': {'host': '0.0.0.0', 'port': 8000},
'debug': False
}
}
}
# 保存
save_config(complex_config, 'complex_config.json')
# 加载并显示
loaded = load_config('complex_config.json')
print('已加载复杂配置')
print(f' 开发环境 host: {loaded["environments"]["development"]["server"]["host"]}')
print(f' 生产环境 debug: {loaded["environments"]["production"]["debug"]}')
print()
def cleanup():
"""清理示例文件"""
files = [
'default_config.json',
'test_config.json',
'invalid_config.json',
'complex_config.json'
]
for file in files:
if os.path.exists(file):
os.remove(file)
print('清理示例文件完成')
def main():
print('=' * 50)
print('配置加载器与管理演示')
print('=' * 50)
print()
demo_basic_loading()
demo_create_default()
demo_load_and_validate()
demo_merge_with_defaults()
demo_invalid_config()
demo_nested_config()
cleanup()
if __name__ == '__main__':
main()
2. 课件12:第三方模块应用
如何安装第三方库
利用 Pandas 进行数据预处理
通过 Matplotlib 和 Seaborn 实现数据可视化
2.1. 第三方模块的安装与管理
核心工具:
pip:Python的包管理工具,用于安装和管理第三方库。
常用命令:
-
安装:
pip install package_name:安装指定的包。 -
卸载:
pip uninstall package_name:卸载指定的包。 -
查看已安装包:
pip list:列出所有已安装的包及其版本。
pip list:列出所有已安装的包及其版本。 -
显示包信息:
pip show package_name:显示指定包的详细信息。
python
"""
示例:第三方模块安装与管理
包含演示:
- pip 命令格式和使用
- 包的安装、卸载、查看
- 虚拟环境管理
- requirements.txt 文件的使用
注意:此示例主要展示命令格式,不会实际执行安装操作
需要网络连接和管理员权限才能实际执行
运行:
python pip_management_demo.py
作者:自动生成示例(中文注释)
"""
import subprocess
import sys
import os
def demo_pip_commands():
"""演示 pip 命令格式(不实际执行)"""
print('--- pip 命令格式演示 ---')
commands = [
{
'name': '安装包',
'command': 'pip install package_name',
'description': '安装指定的包',
'example': 'pip install pandas'
},
{
'name': '安装特定版本',
'command': 'pip install package_name==version',
'description': '安装指定版本的包',
'example': 'pip install pandas==2.0.0'
},
{
'name': '从 requirements.txt 安装',
'command': 'pip install -r requirements.txt',
'description': '从文件安装多个包',
'example': 'pip install -r requirements.txt'
},
{
'name': '卸载包',
'command': 'pip uninstall package_name',
'description': '卸载指定的包',
'example': 'pip uninstall pandas'
},
{
'name': '查看已安装包',
'command': 'pip list',
'description': '列出所有已安装的包及其版本',
'example': 'pip list'
},
{
'name': '显示包信息',
'command': 'pip show package_name',
'description': '显示指定包的详细信息',
'example': 'pip show pandas'
},
{
'name': '升级包',
'command': 'pip install --upgrade package_name',
'description': '升级指定的包到最新版本',
'example': 'pip install --upgrade pandas'
},
{
'name': '导出依赖',
'command': 'pip freeze > requirements.txt',
'description': '将当前环境的所有包导出到文件',
'example': 'pip freeze > requirements.txt'
}
]
for cmd in commands:
print(f"\n{cmd['name']}:")
print(f" 命令: {cmd['command']}")
print(f" 说明: {cmd['description']}")
print(f" 示例: {cmd['example']}")
print()
def demo_requirements_file():
"""演示 requirements.txt 文件的使用"""
print('--- requirements.txt 文件示例 ---')
requirements_content = """# 数据分析相关包
pandas>=2.0.0
numpy>=1.24.0
matplotlib>=3.7.0
seaborn>=0.12.0
# 机器学习包
scikit-learn>=1.3.0
tensorflow>=2.13.0
# 开发工具
jupyter>=1.0.0
ipykernel>=6.0.0
# 其他工具
requests>=2.31.0
beautifulsoup4>=4.12.0
"""
# 创建示例 requirements.txt 文件
with open('example_requirements.txt', 'w', encoding='utf-8') as f:
f.write(requirements_content)
print('已创建 example_requirements.txt 文件')
print('文件内容:')
print(requirements_content)
# 演示如何从文件安装(命令格式)
print('安装命令:')
print(' pip install -r example_requirements.txt')
print()
def demo_virtual_environment():
"""演示虚拟环境管理"""
print('--- 虚拟环境管理 ---')
venv_commands = [
{
'name': '创建虚拟环境',
'command': 'python -m venv myenv',
'description': '创建名为 myenv 的虚拟环境'
},
{
'name': '激活虚拟环境 (Windows)',
'command': 'myenv\\Scripts\\activate',
'description': '激活虚拟环境'
},
{
'name': '激活虚拟环境 (Linux/Mac)',
'command': 'source myenv/bin/activate',
'description': '激活虚拟环境'
},
{
'name': '退出虚拟环境',
'command': 'deactivate',
'description': '退出当前虚拟环境'
},
{
'name': '删除虚拟环境',
'command': 'rmdir /s myenv (Windows) 或 rm -rf myenv (Linux/Mac)',
'description': '删除虚拟环境目录'
}
]
for cmd in venv_commands:
print(f"\n{cmd['name']}:")
print(f" 命令: {cmd['command']}")
print(f" 说明: {cmd['description']}")
print()
def demo_package_info():
"""演示获取已安装包的信息"""
print('--- 查看已安装包信息 ---')
try:
# 尝试获取 pip 版本
result = subprocess.run([sys.executable, '-m', 'pip', '--version'],
capture_output=True, text=True, check=True)
print(f"pip 版本: {result.stdout.strip()}")
# 查看已安装的包(限制输出)
result = subprocess.run([sys.executable, '-m', 'pip', 'list', '--format=freeze'],
capture_output=True, text=True, check=True)
installed_packages = result.stdout.strip().split('\n')
print(f"\n已安装包数量: {len(installed_packages)}")
# 显示前10个包
print("前10个已安装包:")
for i, pkg in enumerate(installed_packages[:10]):
print(f" {i+1}. {pkg}")
if len(installed_packages) > 10:
print(f" ... 还有 {len(installed_packages) - 10} 个包")
except subprocess.CalledProcessError as e:
print(f"执行 pip 命令失败: {e}")
except FileNotFoundError:
print("未找到 pip 命令,请确保 Python 已正确安装")
print()
def demo_import_test():
"""测试一些常用包的导入"""
print('--- 常用包导入测试 ---')
test_packages = [
('pandas', 'pd'),
('numpy', 'np'),
('matplotlib', 'plt'),
('seaborn', 'sns'),
('scikit-learn', 'sklearn'),
('requests', None),
('json', None), # 标准库
('csv', None), # 标准库
]
for package, alias in test_packages:
try:
if alias:
exec(f"import {package} as {alias}")
print(f"✓ {package} ({alias}) - 已安装")
else:
exec(f"import {package}")
print(f"✓ {package} - 已安装")
except ImportError:
print(f"✗ {package} - 未安装")
except Exception as e:
print(f"? {package} - 导入错误: {e}")
print()
def cleanup():
"""清理示例文件"""
files = ['example_requirements.txt']
for file in files:
if os.path.exists(file):
os.remove(file)
print('清理示例文件完成')
def main():
print('=' * 50)
print('第三方模块安装与管理演示')
print('=' * 50)
print()
demo_pip_commands()
demo_requirements_file()
demo_virtual_environment()
demo_package_info()
demo_import_test()
cleanup()
if __name__ == '__main__':
main()
2.1.1. 包
包(Package)的概念:
包是包含多个模块的目录,目录中必须包含一个 __init__.py 文件
用于标识该目录为一个包。
2.2. 数据预处理
Data Preprocessing
垃圾进,垃圾出
数据清洗在数据分析中占据重要地位,占时一般也是最多的
2.2.1. 数据清理
- 缺失值处理:
isnull(): 检测缺失值。dropna(): 删除缺失值。fillna(): 填充缺失值。ffill(): 向前填充缺失值。bfill(): 向后填充缺失值。
- 重复值处理:
duplicated(): 检测重复值。drop_duplicates(): 删除重复值。
- 异常值处理:
- 四分位距(IQR)法:计算Q1和Q3,定义异常值范围。
小于下界 Q1−1.5×IQRQ1 - 1.5 \times IQRQ1−1.5×IQR 的值
或者大于上界 Q3+1.5×IQRQ3 + 1.5 \times IQRQ3+1.5×IQR 的值
可通过 箱体图 或 小提琴图 进行可视化检测。
- 四分位距(IQR)法:计算Q1和Q3,定义异常值范围。
- 数据集成:
- 合并:
pd.merge(): 基于键合并数据集。 - 堆叠:
pd.concat(): 沿指定轴连接数据集。
按行或列方向堆叠多个数据集
有点类似于关系运算中的连接
- 合并:
- 数据转换:
- 标准化:
- Z-score 标准化:均值为0,标准差为1。
- Min-Max 标准化:缩放到指定范围(通常为0到1)。
- 离散化:
pd.cut(): 将连续变量分箱。
例如将年龄分为青年、中年、老年等类别。pd.qcut(): 基于分位数分箱。
- 标准化:
- 数据规约(Data Reduction):
主成分分析(PCA):通过线性变换将高维数据映射至低维空间,保留主要信息。
常用于多维数据可视化
2.3. 数据可视化
两种主要绘图库及应用场景:
2.3.1. Matplotlib 基础
- 图表结构
- Figure:图表容器。
- Axes:坐标系。
- Axis:坐标轴。
- Subplot:子图。
- 绘图流程
- 准备数据
- 绘制图形
- 保存或显示图形
- 基础图形
- scatter: 散点图。
- bar:柱状图。
- hist:直方图。
- plot:折线图。
- pie:饼图。
python
"""
示例:Matplotlib 基础绘图演示
包含演示:
- 图表结构(Figure, Axes, Axis, Subplot)
- 绘图流程(准备数据、绘制、保存/显示)
- 基础图形:scatter, bar, hist, plot, pie
- 子图和多图布局
运行:
python matplotlib_basics_demo.py
依赖:
pip install matplotlib numpy pandas
注意:如果在无GUI环境中运行,可能需要设置后端:
import matplotlib
matplotlib.use('Agg') # 使用非交互式后端
作者:自动生成示例(中文注释)
"""
try:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
MATPLOTLIB_AVAILABLE = True
except ImportError:
print("警告:matplotlib 未安装,跳过绘图演示")
MATPLOTLIB_AVAILABLE = False
# 不设置 plt = None, np = None, pd = None,避免 Pylance 类型推断问题
import os
import os
# 设置中文字体支持(如果有中文字体的话)
if MATPLOTLIB_AVAILABLE:
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
# 如果在无GUI环境中,设置后端
try:
if MATPLOTLIB_AVAILABLE:
import matplotlib
matplotlib.use('Agg')
SAVE_ONLY = True
except:
SAVE_ONLY = False
def parse_arguments():
"""解析命令行参数"""
import argparse
parser = argparse.ArgumentParser(description='Matplotlib 基础绘图演示')
parser.add_argument('--save-images', action='store_true',
help='保存生成的图片文件(默认不保存)')
return parser.parse_args()
def ensure_dir(dir_name):
"""确保目录存在"""
if not os.path.exists(dir_name):
os.makedirs(dir_name)
def demo_chart_structure():
"""演示图表结构"""
print('--- 图表结构演示 ---')
# 创建Figure和Axes
fig, ax = plt.subplots(figsize=(8, 6))
# 设置标题和标签
ax.set_title('图表结构示例', fontsize=16, fontweight='bold')
ax.set_xlabel('X轴标签')
ax.set_ylabel('Y轴标签')
# 添加网格
ax.grid(True, alpha=0.3)
# 添加文本注释
ax.text(0.5, 0.5, '这是Axes区域', ha='center', va='center',
transform=ax.transAxes, fontsize=12, bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))
# 保存图表
ensure_dir('matplotlib_basics')
plt.savefig('matplotlib_basics/chart_structure.png', dpi=150, bbox_inches='tight')
print('图表已保存为 matplotlib_basics/chart_structure.png')
if not SAVE_ONLY:
plt.show()
plt.close()
print('Figure: 整个图表容器')
print('Axes: 坐标系区域')
print('Axis: X轴和Y轴')
print('Title: 图表标题')
print('Labels: 轴标签')
print()
def demo_scatter_plot():
"""演示散点图"""
print('--- 散点图 (scatter) ---')
# 生成随机数据
np.random.seed(42)
x = np.random.randn(100)
y = 2 * x + np.random.randn(100) * 0.5
sizes = np.random.randint(20, 200, 100)
colors = np.random.rand(100)
# 创建散点图
fig, ax = plt.subplots(figsize=(10, 6))
scatter = ax.scatter(x, y, s=sizes, c=colors, alpha=0.6, cmap='viridis')
# 设置标题和标签
ax.set_title('散点图示例', fontsize=16, fontweight='bold')
ax.set_xlabel('X变量')
ax.set_ylabel('Y变量')
# 添加颜色条
plt.colorbar(scatter, ax=ax, label='颜色值')
# 添加网格
ax.grid(True, alpha=0.3)
plt.savefig('matplotlib_basics/scatter_plot.png', dpi=150, bbox_inches='tight')
print('散点图已保存为 matplotlib_basics/scatter_plot.png')
if not SAVE_ONLY:
plt.show()
plt.close()
print()
def demo_bar_chart():
"""演示柱状图"""
print('--- 柱状图 (bar) ---')
# 准备数据
categories = ['产品A', '产品B', '产品C', '产品D', '产品E']
sales = [120, 150, 98, 175, 132]
colors = ['skyblue', 'lightgreen', 'lightcoral', 'gold', 'violet']
# 创建柱状图
fig, ax = plt.subplots(figsize=(10, 6))
bars = ax.bar(categories, sales, color=colors, alpha=0.8, width=0.6)
# 添加数值标签
for bar, value in zip(bars, sales):
ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 2,
f'{value}', ha='center', va='bottom', fontweight='bold')
# 设置标题和标签
ax.set_title('产品销售柱状图', fontsize=16, fontweight='bold')
ax.set_xlabel('产品类别')
ax.set_ylabel('销售额')
# 设置Y轴范围
ax.set_ylim(0, max(sales) * 1.1)
# 添加网格
ax.grid(True, axis='y', alpha=0.3)
plt.savefig('matplotlib_basics/bar_chart.png', dpi=150, bbox_inches='tight')
print('柱状图已保存为 matplotlib_basics/bar_chart.png')
if not SAVE_ONLY:
plt.show()
plt.close()
print()
def demo_histogram():
"""演示直方图"""
print('--- 直方图 (hist) ---')
# 生成正态分布数据
np.random.seed(42)
data1 = np.random.normal(50, 10, 1000) # 均值50,标准差10
data2 = np.random.normal(70, 15, 1000) # 均值70,标准差15
# 创建直方图
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))
# 子图1:单个直方图
ax1.hist(data1, bins=30, alpha=0.7, color='skyblue', edgecolor='black')
ax1.set_title('单个数据集直方图', fontsize=14, fontweight='bold')
ax1.set_xlabel('值')
ax1.set_ylabel('频次')
ax1.grid(True, alpha=0.3)
# 子图2:多个数据集比较
ax2.hist([data1, data2], bins=30, alpha=0.7, label=['数据集1', '数据集2'],
color=['skyblue', 'lightgreen'], edgecolor='black')
ax2.set_title('多个数据集直方图比较', fontsize=14, fontweight='bold')
ax2.set_xlabel('值')
ax2.set_ylabel('频次')
ax2.legend()
ax2.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('matplotlib_basics/histogram.png', dpi=150, bbox_inches='tight')
print('直方图已保存为 matplotlib_basics/histogram.png')
if not SAVE_ONLY:
plt.show()
plt.close()
print()
def demo_line_plot():
"""演示折线图"""
print('--- 折线图 (plot) ---')
# 准备时间序列数据
months = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
sales_2023 = [120, 135, 148, 162, 175, 168, 185, 192, 178, 195, 210, 225]
sales_2024 = [125, 142, 155, 168, 182, 175, 195, 205, 188, 202, 218, 235]
# 创建折线图
fig, ax = plt.subplots(figsize=(12, 6))
ax.plot(months, sales_2023, marker='o', linestyle='-', linewidth=2,
color='blue', label='2023年', markersize=6)
ax.plot(months, sales_2024, marker='s', linestyle='--', linewidth=2,
color='red', label='2024年', markersize=6)
# 设置标题和标签
ax.set_title('年度销售趋势对比', fontsize=16, fontweight='bold')
ax.set_xlabel('月份')
ax.set_ylabel('销售额(万元)')
# 添加图例
ax.legend()
# 添加网格
ax.grid(True, alpha=0.3)
# 旋转X轴标签
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig('matplotlib_basics/line_plot.png', dpi=150, bbox_inches='tight')
print('折线图已保存为 matplotlib_basics/line_plot.png')
if not SAVE_ONLY:
plt.show()
plt.close()
print()
def demo_pie_chart():
"""演示饼图"""
print('--- 饼图 (pie) ---')
# 准备数据
departments = ['技术部', '销售部', '市场部', '财务部', '人事部']
budgets = [35, 25, 20, 12, 8]
colors = ['lightblue', 'lightgreen', 'lightcoral', 'gold', 'violet']
explode = [0.1, 0, 0, 0, 0] # 突出显示第一个扇形
# 创建饼图
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))
# 子图1:基础饼图
wedges1, texts1, autotexts1 = ax1.pie(budgets, labels=departments, colors=colors,
autopct='%1.1f%%', startangle=90, explode=explode)
ax1.set_title('各部门预算分配(突出技术部)', fontsize=14, fontweight='bold')
# 子图2:环形饼图
wedges2, texts2, autotexts2 = ax2.pie(budgets, labels=departments, colors=colors,
autopct='%1.1f%%', startangle=90, wedgeprops=dict(width=0.3))
ax2.set_title('各部门预算分配(环形图)', fontsize=14, fontweight='bold')
# 调整字体大小
for autotext in autotexts1 + autotexts2:
autotext.set_fontsize(10)
autotext.set_fontweight('bold')
plt.tight_layout()
plt.savefig('matplotlib_basics/pie_chart.png', dpi=150, bbox_inches='tight')
print('饼图已保存为 matplotlib_basics/pie_chart.png')
if not SAVE_ONLY:
plt.show()
plt.close()
print()
def demo_subplots():
"""演示子图布局"""
print('--- 子图和多图布局 ---')
# 创建2x2的子图布局
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(12, 10))
fig.suptitle('多图布局示例', fontsize=16, fontweight='bold')
# 子图1:散点图
np.random.seed(42)
x = np.random.randn(50)
y = 2 * x + np.random.randn(50)
ax1.scatter(x, y, alpha=0.6, color='blue')
ax1.set_title('散点图')
ax1.grid(True, alpha=0.3)
# 子图2:柱状图
categories = ['A', 'B', 'C', 'D']
values = [10, 15, 7, 12]
ax2.bar(categories, values, color='lightgreen', alpha=0.7)
ax2.set_title('柱状图')
ax2.grid(True, axis='y', alpha=0.3)
# 子图3:直方图
data = np.random.normal(0, 1, 200)
ax3.hist(data, bins=20, alpha=0.7, color='lightcoral', edgecolor='black')
ax3.set_title('直方图')
ax3.grid(True, alpha=0.3)
# 子图4:饼图
sizes = [30, 25, 20, 15, 10]
labels = ['A', 'B', 'C', 'D', 'E']
ax4.pie(sizes, labels=labels, autopct='%1.0f%%', startangle=90)
ax4.set_title('饼图')
plt.tight_layout()
plt.savefig('matplotlib_basics/subplots_demo.png', dpi=150, bbox_inches='tight')
print('子图布局已保存为 matplotlib_basics/subplots_demo.png')
if not SAVE_ONLY:
plt.show()
plt.close()
print()
def cleanup():
"""清理生成的图片文件"""
image_dir = 'matplotlib_basics'
if os.path.exists(image_dir):
import shutil
shutil.rmtree(image_dir)
print('清理图片目录完成')
def main():
print('=' * 50)
print('Matplotlib 基础绘图演示')
print('=' * 50)
print()
# 解析命令行参数
args = parse_arguments()
if not MATPLOTLIB_AVAILABLE:
print('matplotlib 未安装,无法运行绘图演示')
print('请运行:pip install matplotlib numpy pandas')
return
try:
demo_chart_structure()
demo_scatter_plot()
demo_bar_chart()
demo_histogram()
demo_line_plot()
demo_pie_chart()
demo_subplots()
print('所有图表已生成并保存为PNG文件')
if SAVE_ONLY:
print('注意:当前环境为无GUI模式,所有图表已保存为文件')
except ImportError as e:
print(f'导入错误:{e}')
print('请安装必要的包:pip install matplotlib numpy pandas')
except Exception as e:
print(f'运行错误:{e}')
finally:
# 根据命令行参数决定是否清理图片
if not args.save_images:
cleanup()
else:
print('图片文件已保存,可在当前目录查看')
print('下次运行时如需清理,请不使用 --save-images 参数')
if __name__ == '__main__':
main()
2.3.2. Seaborn 高阶绘图
- 优势:
语法简介,内置美观调色板(palette),适合统计图表绘制。 - 高阶图形:
- 小提琴图(violinplot):显示数据分布及其概率密度。
- 箱线图(boxplot):显示数据的分位数及异常值。
- 回归模型图(lmplot):显示变量间的线性关系及回归线。
- 成对图(pairplot):显示多变量间的两两关系。
python
"""
示例:Seaborn 高级绘图演示
包含演示:
- 小提琴图(violinplot):显示数据分布及其概率密度
- 箱线图(boxplot):显示数据的分位数及异常值
- 回归模型图(lmplot):显示变量间的线性关系及回归线
- 成对图(pairplot):显示多变量间的两两关系
- Seaborn的优势:语法简洁,内置美观调色板
运行:
python seaborn_advanced_demo.py
依赖:
pip install seaborn matplotlib numpy pandas scikit-learn
作者:自动生成示例(中文注释)
"""
try:
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from sklearn.datasets import make_regression, load_iris
SEABORN_AVAILABLE = True
MATPLOTLIB_AVAILABLE = True
except ImportError:
print("警告:seaborn 或 scikit-learn 未安装,跳过相关演示")
SEABORN_AVAILABLE = False
# 尝试导入基础库
try:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
MATPLOTLIB_AVAILABLE = True
except ImportError:
MATPLOTLIB_AVAILABLE = False
# 不设置 plt = None, np = None, pd = None,避免 Pylance 类型推断问题
import os
# 设置Seaborn样式
if SEABORN_AVAILABLE:
sns.set_style("whitegrid") # 设置背景样式
sns.set_palette("husl") # 设置颜色调色板
# 设置中文字体支持
if MATPLOTLIB_AVAILABLE:
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
# 如果在无GUI环境中,设置后端
try:
if MATPLOTLIB_AVAILABLE:
import matplotlib
matplotlib.use('Agg')
SAVE_ONLY = True
except:
SAVE_ONLY = False
def parse_arguments():
"""解析命令行参数"""
import argparse
parser = argparse.ArgumentParser(description='Seaborn 高级绘图演示')
parser.add_argument('--save-images', action='store_true',
help='保存生成的图片文件(默认不保存)')
return parser.parse_args()
def ensure_dir(dir_name):
"""确保目录存在"""
if not os.path.exists(dir_name):
os.makedirs(dir_name)
def demo_violinplot():
"""演示小提琴图"""
print('--- 小提琴图 (violinplot) ---')
if not SEABORN_AVAILABLE:
print('跳过小提琴图演示(需要 seaborn)')
return
# 创建示例数据:不同部门的员工薪资
np.random.seed(42)
data = {
'部门': ['技术部'] * 50 + ['销售部'] * 50 + ['市场部'] * 50,
'薪资': (
np.random.normal(15000, 3000, 50).tolist() + # 技术部:均值15000,标准差3000
np.random.normal(12000, 2500, 50).tolist() + # 销售部:均值12000,标准差2500
np.random.normal(10000, 2000, 50).tolist() # 市场部:均值10000,标准差2000
)
}
df = pd.DataFrame(data)
# 创建小提琴图
plt.figure(figsize=(10, 6))
ax = sns.violinplot(x='部门', y='薪资', data=df,
hue='部门', palette='Set3', inner='quartile', legend=False)
# 设置标题和标签
ax.set_title('各部门薪资分布小提琴图', fontsize=16, fontweight='bold')
ax.set_xlabel('部门', fontsize=12)
ax.set_ylabel('薪资(元)', fontsize=12)
# 添加数值统计
for i, dept in enumerate(['技术部', '销售部', '市场部']):
dept_data = df[df['部门'] == dept]['薪资']
mean_val = dept_data.mean()
plt.text(i, mean_val + 500, f'均值: {mean_val:.0f}',
ha='center', va='bottom', fontweight='bold')
plt.tight_layout()
ensure_dir('seaborn_advanced')
plt.savefig('seaborn_advanced/violinplot_demo.png', dpi=150, bbox_inches='tight')
print('小提琴图已保存为 seaborn_advanced/violinplot_demo.png')
if not SAVE_ONLY:
plt.show()
plt.close()
print('小提琴图特点:')
print('- 显示数据分布的形状和密度')
print('- 白色箱子显示四分位数范围')
print('- 黑色线条显示中位数')
print('- 适合比较多个类别的分布')
print()
def demo_boxplot():
"""演示箱线图"""
print('--- 箱线图 (boxplot) ---')
if not SEABORN_AVAILABLE:
print('跳过箱线图演示(需要 seaborn 和 scikit-learn)')
return
# 使用模拟的iris数据集
np.random.seed(42)
# 模拟iris数据:3个品种,每个有4个特征
data = []
feature_names = ['sepal length', 'sepal width', 'petal length', 'petal width']
species_names = ['setosa', 'versicolor', 'virginica']
for species_idx, species in enumerate(species_names):
for _ in range(50):
if species == 'setosa':
features = [
np.random.normal(5.0, 0.3), # sepal length
np.random.normal(3.4, 0.3), # sepal width
np.random.normal(1.5, 0.2), # petal length
np.random.normal(0.2, 0.1) # petal width
]
elif species == 'versicolor':
features = [
np.random.normal(5.9, 0.5),
np.random.normal(2.8, 0.3),
np.random.normal(4.3, 0.5),
np.random.normal(1.3, 0.2)
]
else: # virginica
features = [
np.random.normal(6.6, 0.6),
np.random.normal(3.0, 0.3),
np.random.normal(5.6, 0.5),
np.random.normal(2.0, 0.3)
]
data.append(features + [species])
df = pd.DataFrame(data, columns=feature_names + ['species'])
# 创建箱线图
fig, axes = plt.subplots(2, 2, figsize=(15, 12))
fig.suptitle('鸢尾花数据集特征箱线图', fontsize=16, fontweight='bold')
colors = ['lightblue', 'lightgreen', 'lightcoral', 'gold']
for i, (feature, color) in enumerate(zip(feature_names, colors)):
ax = axes[i//2, i%2]
# 绘制箱线图
sns.boxplot(x='species', y=feature, data=df, ax=ax,
hue='species', palette='Set2', width=0.6, legend=False)
ax.set_title(f'{feature} by Species', fontsize=12, fontweight='bold')
ax.set_xlabel('Species', fontsize=10)
ax.set_ylabel(feature, fontsize=10)
# 添加网格
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('seaborn_advanced/boxplot_demo.png', dpi=150, bbox_inches='tight')
print('箱线图已保存为 seaborn_advanced/boxplot_demo.png')
if not SAVE_ONLY:
plt.show()
plt.close()
print('箱线图特点:')
print('- 箱子显示四分位数范围(Q1-Q3)')
print('- 中间线显示中位数(Q2)')
print('- 须线显示数据范围')
print('- 点显示异常值')
print('- 适合检测异常值和比较分布')
print()
def demo_lmplot():
"""演示回归模型图"""
print('--- 回归模型图 (lmplot) ---')
if not SEABORN_AVAILABLE:
print('跳过回归模型图演示(需要 seaborn)')
return
# 生成模拟回归数据
np.random.seed(42)
n_samples = 200
# 线性关系数据
x_linear = np.random.uniform(0, 10, n_samples)
y_linear = 2 * x_linear + 1 + np.random.normal(0, 2, n_samples)
# 二次关系数据
x_quad = np.random.uniform(0, 10, n_samples)
y_quad = 0.5 * x_quad**2 - 2 * x_quad + 5 + np.random.normal(0, 3, n_samples)
# 创建DataFrame
df_linear = pd.DataFrame({'x': x_linear, 'y': y_linear})
df_quad = pd.DataFrame({'x': x_quad, 'y': y_quad})
# 创建回归图
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))
fig.suptitle('回归模型图示例', fontsize=16, fontweight='bold')
# 线性回归
sns.regplot(x='x', y='y', data=df_linear, ax=ax1,
scatter_kws={'alpha':0.6, 'color':'blue'},
line_kws={'color':'red', 'linewidth':2})
ax1.set_title('线性回归', fontsize=14, fontweight='bold')
ax1.set_xlabel('X变量')
ax1.set_ylabel('Y变量')
ax1.grid(True, alpha=0.3)
# 多项式回归(二次)
sns.regplot(x='x', y='y', data=df_quad, ax=ax2, order=2,
scatter_kws={'alpha':0.6, 'color':'green'},
line_kws={'color':'red', 'linewidth':2})
ax2.set_title('多项式回归(二次)', fontsize=14, fontweight='bold')
ax2.set_xlabel('X变量')
ax2.set_ylabel('Y变量')
ax2.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('seaborn_advanced/lmplot_demo.png', dpi=150, bbox_inches='tight')
print('回归模型图已保存为 seaborn_advanced/lmplot_demo.png')
if not SAVE_ONLY:
plt.show()
plt.close()
print('回归模型图特点:')
print('- 显示变量间的线性关系')
print('- 自动拟合回归线')
print('- 显示置信区间')
print('- 支持多项式回归(order参数)')
print()
def demo_pairplot():
"""演示成对图"""
print('--- 成对图 (pairplot) ---')
if not SEABORN_AVAILABLE:
print('跳过成对图演示(需要 seaborn 和 scikit-learn)')
return
# 使用模拟的iris数据集
np.random.seed(42)
data = []
feature_names = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width']
species_names = ['setosa', 'versicolor', 'virginica']
for species_idx, species in enumerate(species_names):
for _ in range(50):
if species == 'setosa':
features = [
np.random.normal(5.0, 0.3), # sepal length
np.random.normal(3.4, 0.3), # sepal width
np.random.normal(1.5, 0.2), # petal length
np.random.normal(0.2, 0.1) # petal width
]
elif species == 'versicolor':
features = [
np.random.normal(5.9, 0.5),
np.random.normal(2.8, 0.3),
np.random.normal(4.3, 0.5),
np.random.normal(1.3, 0.2)
]
else: # virginica
features = [
np.random.normal(6.6, 0.6),
np.random.normal(3.0, 0.3),
np.random.normal(5.6, 0.5),
np.random.normal(2.0, 0.3)
]
data.append(features + [species])
df = pd.DataFrame(data, columns=feature_names + ['species'])
# 创建成对图
plt.figure(figsize=(12, 10))
pair_plot = sns.pairplot(df, hue='species', palette='Set1',
diag_kind='kde', # 对角线使用核密度估计
plot_kws={'alpha': 0.6},
diag_kws={'alpha': 0.7})
pair_plot.fig.suptitle('鸢尾花数据集特征成对关系图', fontsize=16, fontweight='bold', y=1.02)
plt.tight_layout()
plt.savefig('seaborn_advanced/pairplot_demo.png', dpi=150, bbox_inches='tight')
print('成对图已保存为 seaborn_advanced/pairplot_demo.png')
if not SAVE_ONLY:
plt.show()
plt.close()
print('成对图特点:')
print('- 显示所有数值变量间的两两关系')
print('- 对角线显示单变量分布')
print('- 颜色编码不同类别')
print('- 适合探索性数据分析')
print('- 可以快速发现变量间的相关性和模式')
print()
def demo_seaborn_advantages():
"""演示Seaborn的优势:语法简洁,美观调色板"""
print('--- Seaborn 优势演示 ---')
if not SEABORN_AVAILABLE:
print('跳过 Seaborn 优势演示(需要 seaborn)')
return
# 创建比较数据
np.random.seed(42)
categories = ['A', 'B', 'C', 'D', 'E']
values1 = np.random.randint(10, 50, 5)
values2 = np.random.randint(15, 45, 5)
df = pd.DataFrame({
'category': categories * 2,
'value': np.concatenate([values1, values2]),
'group': ['Group1'] * 5 + ['Group2'] * 5
})
# Seaborn版本(简洁语法)
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
ax1 = sns.barplot(x='category', y='value', hue='group', data=df, palette='husl')
ax1.set_title('Seaborn版本(简洁语法)', fontsize=12, fontweight='bold')
ax1.set_xlabel('类别')
ax1.set_ylabel('数值')
ax1.legend(title='分组')
# Matplotlib版本(繁琐语法)- 为了对比
plt.subplot(1, 2, 2)
x = np.arange(len(categories))
width = 0.35
bars1 = plt.bar(x - width/2, values1, width, label='Group1', alpha=0.8, color='skyblue')
bars2 = plt.bar(x + width/2, values2, width, label='Group2', alpha=0.8, color='lightgreen')
plt.xlabel('类别')
plt.ylabel('数值')
plt.title('Matplotlib版本(繁琐语法)', fontsize=12, fontweight='bold')
plt.xticks(x, categories)
plt.legend(title='分组')
# 添加数值标签
for bars in [bars1, bars2]:
for bar in bars:
height = bar.get_height()
plt.text(bar.get_x() + bar.get_width()/2., height,
f'{int(height)}', ha='center', va='bottom')
plt.tight_layout()
plt.savefig('seaborn_advanced/seaborn_advantages.png', dpi=150, bbox_inches='tight')
print('Seaborn优势对比图已保存为 seaborn_advanced/seaborn_advantages.png')
if not SAVE_ONLY:
plt.show()
plt.close()
print('Seaborn优势:')
print('1. 语法简洁:一行代码即可创建复杂图表')
print('2. 自动美化:内置美观调色板和样式')
print('3. 数据导向:直接使用DataFrame和列名')
print('4. 统计功能:自动添加统计信息和置信区间')
print('5. 集成Pandas:无缝配合数据处理')
print()
def cleanup():
"""清理生成的图片文件"""
image_dir = 'seaborn_advanced'
if os.path.exists(image_dir):
import shutil
shutil.rmtree(image_dir)
print('清理图片目录完成')
def main():
print('=' * 50)
print('Seaborn 高级绘图演示')
print('=' * 50)
print()
# 解析命令行参数
args = parse_arguments()
if not SEABORN_AVAILABLE:
print('seaborn 未安装,无法运行高级绘图演示')
print('请运行:pip install seaborn matplotlib numpy pandas scikit-learn')
return
try:
demo_violinplot()
demo_boxplot()
demo_lmplot()
demo_pairplot()
demo_seaborn_advantages()
print('所有Seaborn图表已生成并保存为PNG文件')
if SAVE_ONLY:
print('注意:当前环境为无GUI模式,所有图表已保存为文件')
except ImportError as e:
print(f'导入错误:{e}')
print('请安装必要的包:pip install seaborn matplotlib numpy pandas scikit-learn')
except Exception as e:
print(f'运行错误:{e}')
finally:
# 根据命令行参数决定是否清理图片
if not args.save_images:
cleanup()
else:
print('图片文件已保存,可在当前目录查看')
print('下次运行时如需清理,请不使用 --save-images 参数')
if __name__ == '__main__':
main()
2.3.3. PandasAI 简介
PandasAI:基于大语言模型(如 GPT-4)构建的库,旨在简化数据分析过程。
使得用户能够通过自然语言直接与数据交互,实现自动清洗和查询等功能。
python
"""
示例:Pandas 数据预处理演示
包含演示:
- 缺失值处理(isnull, dropna, fillna, ffill, bfill)
- 重复值处理(duplicated, drop_duplicates)
- 异常值处理(IQR法)
- 数据集成(merge, concat)
- 数据转换(标准化、离散化)
- 数据规约(PCA)
运行:
python pandas_preprocessing_demo.py
依赖:
pip install pandas numpy scikit-learn matplotlib seaborn
作者:自动生成示例(中文注释)
"""
try:
import pandas as pd
import numpy as np
PANDAS_AVAILABLE = True
except ImportError:
print("警告:pandas 未安装,跳过数据预处理演示")
PANDAS_AVAILABLE = False
# 不设置 pd = None 和 np = None,避免 Pylance 类型推断问题
try:
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.decomposition import PCA
SKLEARN_AVAILABLE = True
except ImportError:
print("警告:scikit-learn 未安装,跳过相关演示")
SKLEARN_AVAILABLE = False
import warnings
warnings.filterwarnings('ignore')
def parse_arguments():
"""解析命令行参数"""
import argparse
parser = argparse.ArgumentParser(description='Pandas 数据预处理演示')
parser.add_argument('--save-images', action='store_true',
help='保存生成的图片文件(默认不保存)')
return parser.parse_args()
def demo_missing_values():
"""演示缺失值处理"""
print('--- 缺失值处理 ---')
# 创建包含缺失值的数据
data = {
'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
'age': [25, np.nan, 30, 35, np.nan],
'salary': [50000, 60000, np.nan, 80000, 70000],
'department': ['IT', 'HR', 'IT', np.nan, 'Finance']
}
df = pd.DataFrame(data)
print('原始数据:')
print(df)
print()
# 检测缺失值
print('缺失值检测:')
print(df.isnull())
print(f'每列缺失值数量:\n{df.isnull().sum()}')
print()
# 删除缺失值
print('删除包含缺失值的行:')
df_dropna = df.dropna()
print(df_dropna)
print()
# 填充缺失值
print('填充缺失值:')
df_fillna = df.copy()
# 用均值填充数值列
df_fillna['age'] = df_fillna['age'].fillna(df_fillna['age'].mean())
df_fillna['salary'] = df_fillna['salary'].fillna(df_fillna['salary'].mean())
# 用特定值填充分类列
df_fillna['department'] = df_fillna['department'].fillna('Unknown')
print(df_fillna)
print()
# 前向填充和后向填充
print('前向填充(ffill):')
df_ffill = df.copy()
df_ffill = df_ffill.ffill()
print(df_ffill)
print()
print('后向填充(bfill):')
df_bfill = df.copy()
df_bfill = df_bfill.bfill()
print(df_bfill)
print()
def demo_duplicates():
"""演示重复值处理"""
print('--- 重复值处理 ---')
# 创建包含重复值的数据
data = {
'name': ['Alice', 'Bob', 'Alice', 'Charlie', 'Bob', 'David'],
'age': [25, 30, 25, 35, 30, 40],
'department': ['IT', 'HR', 'IT', 'Finance', 'HR', 'IT']
}
df = pd.DataFrame(data)
print('原始数据:')
print(df)
print()
# 检测重复值
print('检测重复行:')
print(df.duplicated())
print()
# 删除重复值
print('删除重复行(保留第一次出现):')
df_dedup = df.drop_duplicates()
print(df_dedup)
print()
# 基于特定列删除重复值
print('基于 name 列删除重复值:')
df_dedup_name = df.drop_duplicates(subset=['name'])
print(df_dedup_name)
print()
def demo_outliers():
"""演示异常值处理(IQR法)"""
print('--- 异常值处理(IQR法)---')
# 创建包含异常值的数据
np.random.seed(42)
data = {
'value': [10, 12, 11, 13, 12, 14, 100, 11, 13, 12, 15, 200, 13, 11, 12]
}
df = pd.DataFrame(data)
print('原始数据:')
print(df.describe())
print()
# 计算IQR
Q1 = df['value'].quantile(0.25)
Q3 = df['value'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
print(f'Q1: {Q1}, Q3: {Q3}, IQR: {IQR}')
print(f'下界: {lower_bound}, 上界: {upper_bound}')
print()
# 识别异常值
outliers = df[(df['value'] < lower_bound) | (df['value'] > upper_bound)]
print('异常值:')
print(outliers)
print()
# 移除异常值
df_clean = df[(df['value'] >= lower_bound) & (df['value'] <= upper_bound)]
print('移除异常值后的数据:')
print(df_clean.describe())
print()
def demo_data_integration():
"""演示数据集成"""
print('--- 数据集成 ---')
# 创建两个数据集
df1 = pd.DataFrame({
'id': [1, 2, 3, 4],
'name': ['Alice', 'Bob', 'Charlie', 'David'],
'department': ['IT', 'HR', 'Finance', 'IT']
})
df2 = pd.DataFrame({
'id': [1, 2, 3, 5],
'salary': [50000, 60000, 70000, 80000],
'experience': [2, 3, 4, 5]
})
print('数据集1:')
print(df1)
print()
print('数据集2:')
print(df2)
print()
# 合并(merge)
print('内连接合并(inner join):')
df_merged = pd.merge(df1, df2, on='id', how='inner')
print(df_merged)
print()
print('左连接合并(left join):')
df_left = pd.merge(df1, df2, on='id', how='left')
print(df_left)
print()
# 堆叠(concat)
df3 = pd.DataFrame({
'id': [6, 7],
'name': ['Eve', 'Frank'],
'department': ['Marketing', 'Sales']
})
print('原始数据集1:')
print(df1)
print()
print('要堆叠的数据集3:')
print(df3)
print()
print('垂直堆叠(concat):')
df_concat = pd.concat([df1, df3], ignore_index=True)
print(df_concat)
print()
def demo_data_transformation():
"""演示数据转换"""
print('--- 数据转换 ---')
if not SKLEARN_AVAILABLE:
print('跳过标准化演示(需要 scikit-learn)')
return
# 创建数值数据
data = {
'feature1': [10, 20, 30, 40, 50],
'feature2': [100, 200, 300, 400, 500],
'age': [25, 30, 35, 40, 45]
}
df = pd.DataFrame(data)
print('原始数据:')
print(df)
print()
# Z-score 标准化
print('Z-score 标准化:')
scaler = StandardScaler()
df_standardized = pd.DataFrame(
scaler.fit_transform(df),
columns=df.columns
)
print(df_standardized)
print(f'均值: {df_standardized.mean().round(6).tolist()}')
print(f'标准差: {df_standardized.std().round(6).tolist()}')
print()
# Min-Max 标准化
print('Min-Max 标准化:')
minmax_scaler = MinMaxScaler()
df_normalized = pd.DataFrame(
minmax_scaler.fit_transform(df),
columns=df.columns
)
print(df_normalized)
print()
# 离散化(分箱)
print('年龄离散化(pd.cut):')
age_bins = [0, 30, 40, 100]
age_labels = ['青年', '中年', '老年']
df['age_category'] = pd.cut(df['age'], bins=age_bins, labels=age_labels)
print(df[['age', 'age_category']])
print()
print('年龄离散化(pd.qcut - 等频分箱):')
df['age_quartile'] = pd.qcut(df['age'], q=3, labels=['低', '中', '高'])
print(df[['age', 'age_quartile']])
print()
def demo_pca():
"""演示主成分分析(PCA)"""
print('--- 数据规约:主成分分析(PCA)---')
if not SKLEARN_AVAILABLE:
print('跳过 PCA 演示(需要 scikit-learn)')
return
# 创建高维数据
np.random.seed(42)
data = np.random.randn(50, 4) # 50个样本,4个特征
df = pd.DataFrame(data, columns=['feature1', 'feature2', 'feature3', 'feature4'])
print('原始数据形状:', df.shape)
print('原始数据描述:')
print(df.describe())
print()
# 应用PCA
pca = PCA(n_components=2) # 降维到2维
principal_components = pca.fit_transform(df)
df_pca = pd.DataFrame(
principal_components,
columns=['PC1', 'PC2']
)
print('PCA后数据形状:', df_pca.shape)
print('主成分数据:')
print(df_pca.head())
print()
# 解释方差比
print('各主成分解释的方差比:')
print(pca.explained_variance_ratio_)
print(f'前2个主成分解释的总方差: {pca.explained_variance_ratio_.sum():.3f}')
print()
def main():
print('=' * 50)
print('Pandas 数据预处理演示')
print('=' * 50)
print()
# 解析命令行参数(保持与其他演示文件一致)
args = parse_arguments()
if not PANDAS_AVAILABLE:
print('pandas 未安装,无法运行数据预处理演示')
print('请运行:pip install pandas numpy')
return
try:
demo_missing_values()
demo_duplicates()
demo_outliers()
demo_data_integration()
demo_data_transformation()
demo_pca()
except ImportError as e:
print(f'导入错误:{e}')
print('请安装必要的包:pip install pandas numpy scikit-learn matplotlib seaborn')
except Exception as e:
print(f'运行错误:{e}')
if __name__ == '__main__':
main()
3. 课件13:API开发实战
通过程序与互联网服务进行数据交互
3.1. HTTP API
API本质:
Application Programming Interface,应用程序编程接口。
标准化的通信协议和数据格式
HTTP协议基础:
-
请求结构:
- 请求行
方法、URL
常用方法:- GET:请求数据。
- POST:提交数据。
- PUT:更新数据。
- DELETE:删除数据。
Pythonrequests库会自动处理URL的参数编码
URL: - 协议(http/https)
- 主机名(域名或IP地址)
- 路径
- 查询参数
- 请求头
例如User-Agent、Content-Type等 - 请求体
数据
- 请求行
-
响应结构:
- 状态行
状态码、状态消息
常见状态码:200(成功)、404(未找到)、500(服务器错误)等 - 响应头
例如Content-Type、Content-Length等 - 响应体
数据
- 状态行
3.2. 重要库:Requests
Requests:Python中用于发送HTTP请求的第三方库。
3.2.1. 基础调用
requests.get(url, params=None, **kwargs): 发送GET请求。requests.post(url, data=None, json=None, **kwargs): 发送POST请求。
3.2.2. 进阶配置
- 请求头(Headers):
通过headers参数自定义请求头。
伪装User-Agent防止被服务器拒绝访问。 - 超时与重试:
通过timeout参数设置请求超时时间。
否则可能永久阻塞程序
使用requests.adapters.HTTPAdapter配置重试策略。 - 会话保持:
requests.Session(): 创建会话对象,保持跨请求的参数(如Cookies)。
可以复用连接,提高性能。 - SSL验证:
默认验证HTTPS证书,可通过verify参数禁用验证(不推荐)。
一般仅测试环境禁用绕过
3.3. JSON数据解析与处理
现代Web API通常使用JSON格式传输数据
处理方法:
- 自动解析:
response.json(): 将响应体解析为Python对象(字典或列表)。 - 安全读取:
访问嵌套字典时,使用.get()方法并设置默认值,避免KeyError异常。 - 批量提取:
使用列表推导式或循环遍历提取所需字段。 - 时间转换:
使用datetime模块将时间戳转换为可读格式。
例如strptime()和strftime()方法。
3.4. 优秀实践方法
- 异常捕获
- 状态检查
- 数据清洗
- 安全建议:
- 避免在URL中传递敏感信息。
- 使用环境变量或配置文件存储API密钥。
不要将密钥硬编码在代码中。 - 定期更新依赖库,修补安全漏洞。
3.5. 代码示例
python
"""
示例:API开发实战演示
包含演示:
- HTTP请求基础(GET/POST)
- 请求头配置与User-Agent伪装
- 超时与重试机制
- 会话保持与Cookie管理
- SSL证书验证
- JSON数据解析与处理
- 异常处理与错误恢复
- 安全实践与API密钥管理
运行:
python api_development_demo.py
依赖:
pip install requests
注意:
本示例使用公开API进行演示,不会产生实际费用
如需测试私有API,请替换相应的URL和认证信息
作者:自动生成示例(中文注释)
"""
try:
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
from requests.exceptions import RequestException
REQUESTS_AVAILABLE = True
except ImportError:
print("警告:requests 未安装,跳过API演示")
REQUESTS_AVAILABLE = False
# 不设置 requests = None,避免 Pylance 类型推断问题
import json
import time
import os
from datetime import datetime
# 设置API密钥(实际使用时应该从环境变量读取)
# NEVER hardcode API keys in your code!
API_KEY = os.getenv('DEMO_API_KEY', 'demo_key_replace_with_real_key')
def demo_basic_http_requests():
"""演示基础HTTP请求"""
print('--- 基础HTTP请求 ---')
if not REQUESTS_AVAILABLE:
print('跳过HTTP请求演示(需要 requests)')
return
try:
# GET请求示例 - 获取JSONPlaceholder的示例数据
print('1. GET请求示例:')
response = requests.get('https://jsonplaceholder.typicode.com/posts/1')
print(f'状态码: {response.status_code}')
print(f'响应头: {dict(response.headers)}')
print(f'响应内容: {response.json()}')
print()
# POST请求示例 - 创建新资源
print('2. POST请求示例:')
new_post = {
'title': '示例标题',
'body': '这是通过API创建的示例内容',
'userId': 1
}
response = requests.post('https://jsonplaceholder.typicode.com/posts',
json=new_post)
print(f'POST状态码: {response.status_code}')
print(f'创建的资源: {response.json()}')
print()
except Exception as e:
print(f'请求错误: {e}')
def demo_request_headers():
"""演示请求头配置"""
print('--- 请求头配置 ---')
if not REQUESTS_AVAILABLE:
print('跳过请求头演示(需要 requests)')
return
try:
# 自定义请求头
headers = {
'User-Agent': 'Python-API-Demo/1.0',
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': f'Bearer {API_KEY}'
}
print('1. 自定义请求头示例:')
response = requests.get('https://httpbin.org/headers', headers=headers)
print(f'发送的请求头: {response.json()["headers"]}')
print()
# 模拟浏览器User-Agent
print('2. 浏览器User-Agent伪装:')
browser_headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
response = requests.get('https://httpbin.org/user-agent', headers=browser_headers)
print(f'伪装的User-Agent: {response.json()["user-agent"]}')
print()
except Exception as e:
print(f'请求错误: {e}')
def demo_timeout_and_retry():
"""演示超时与重试机制"""
print('--- 超时与重试机制 ---')
if not REQUESTS_AVAILABLE:
print('跳过超时重试演示(需要 requests)')
return
try:
# 配置重试策略
retry_strategy = Retry(
total=3, # 总重试次数
status_forcelist=[429, 500, 502, 503, 504], # 需要重试的状态码
allowed_methods=["HEAD", "GET", "OPTIONS"], # 需要重试的方法
backoff_factor=1 # 重试间隔倍数
)
# 创建带有重试机制的会话
adapter = HTTPAdapter(max_retries=retry_strategy)
session = requests.Session()
session.mount("http://", adapter)
session.mount("https://", adapter)
print('1. 超时设置示例:')
# 设置5秒超时
start_time = time.time()
try:
response = requests.get('https://httpbin.org/delay/2', timeout=5)
elapsed = time.time() - start_time
print(f'请求成功,耗时: {elapsed:.2f}秒')
except Exception as e:
elapsed = time.time() - start_time
print(f'请求超时或其他错误,耗时: {elapsed:.2f}秒, 错误: {e}')
print()
print('2. 重试机制示例:')
# 测试重试(使用可能不稳定的端点)
try:
response = session.get('https://httpbin.org/status/200') # 改用成功的端点
print(f'重试后状态码: {response.status_code}')
except Exception as e:
print(f'重试请求错误: {e}')
print()
except Exception as e:
print(f'请求错误: {e}')
def demo_session_management():
"""演示会话管理和Cookie保持"""
print('--- 会话管理和Cookie保持 ---')
if not REQUESTS_AVAILABLE:
print('跳过会话管理演示(需要 requests)')
return
try:
# 创建会话对象
session = requests.Session()
print('1. 会话保持示例:')
# 第一次请求
response1 = session.get('https://httpbin.org/cookies/set/session_id/12345')
print(f'第一次请求Cookie: {response1.json()}')
# 第二次请求(会自动携带Cookie)
response2 = session.get('https://httpbin.org/cookies')
print(f'第二次请求Cookie: {response2.json()}')
print()
print('2. 自定义Cookie示例:')
# 设置自定义Cookie
session.cookies.set('user_preference', 'dark_mode', domain='httpbin.org')
response = session.get('https://httpbin.org/cookies')
print(f'自定义Cookie: {response.json()}')
print()
except Exception as e:
print(f'请求错误: {e}')
def demo_ssl_verification():
"""演示SSL证书验证"""
print('--- SSL证书验证 ---')
if not REQUESTS_AVAILABLE:
print('跳过SSL验证演示(需要 requests)')
return
try:
print('1. 默认SSL验证(推荐):')
response = requests.get('https://httpbin.org/get', timeout=10)
print(f'HTTPS请求成功: {response.status_code}')
print()
print('2. 禁用SSL验证(仅测试环境使用):')
# 注意:生产环境中不推荐禁用SSL验证
response = requests.get('https://httpbin.org/get', verify=False, timeout=10)
print(f'禁用验证请求成功: {response.status_code}')
print('⚠️ 警告:生产环境应始终启用SSL验证')
print()
except Exception as e:
print(f'请求错误: {e}')
def demo_json_processing():
"""演示JSON数据解析与处理"""
print('--- JSON数据解析与处理 ---')
if not REQUESTS_AVAILABLE:
print('跳过JSON处理演示(需要 requests)')
return
try:
# 获取GitHub API数据 - 获取最近的几个releases
response = requests.get('https://api.github.com/repos/microsoft/vscode/releases?per_page=3',
headers={'Accept': 'application/vnd.github.v3+json'},
timeout=10)
if response.status_code == 200:
releases = response.json()
print('1. 列表数据处理 - 多个Release:')
print(f'获取到 {len(releases)} 个发布版本')
print()
# 处理每个release
for i, release in enumerate(releases[:2], 1): # 只处理前2个release
print(f'Release {i}:')
print(f' 版本名称: {release.get("name", "N/A")}')
print(f' 标签: {release.get("tag_name", "N/A")}')
print(f' 发布时间: {release.get("published_at", "N/A")[:10]}') # 只显示日期部分
# 处理assets列表
assets = release.get('assets', [])
if assets:
print(f' 附件数量: {len(assets)}')
print(' 附件列表:')
for asset in assets[:2]: # 只显示前2个附件
print(f' - {asset.get("name", "N/A")}: {asset.get("download_count", 0)} 次下载')
else:
print(' 附件数量: 0 (无附件)')
print()
print('2. 嵌套数据安全访问:')
if releases:
first_release = releases[0]
author = first_release.get('author', {})
print(f'最新版本发布者: {author.get("login", "N/A")}')
print(f'发布者类型: {author.get("type", "N/A")}')
print(f'是否为预发布: {first_release.get("prerelease", False)}')
print()
print('3. 数据聚合统计:')
total_reactions = 0
reaction_types = {}
prerelease_count = 0
draft_count = 0
for release in releases:
# 统计reactions
reactions = release.get('reactions', {})
total_count = reactions.get('total_count', 0)
total_reactions += total_count
# 统计各种reaction类型
for reaction_type in ['+1', 'laugh', 'hooray', 'heart', 'rocket', 'eyes']:
count = reactions.get(reaction_type, 0)
reaction_types[reaction_type] = reaction_types.get(reaction_type, 0) + count
# 统计发布类型
if release.get('prerelease', False):
prerelease_count += 1
if release.get('draft', False):
draft_count += 1
print(f'总反应数: {total_reactions}')
print(f'预发布版本数: {prerelease_count}')
print(f'草稿版本数: {draft_count}')
print('各类型反应统计:')
for reaction_type, count in reaction_types.items():
if count > 0: # 只显示有反应的类型
emoji_map = {
'+1': '👍',
'laugh': '😄',
'hooray': '🎉',
'heart': '❤️',
'rocket': '🚀',
'eyes': '👀'
}
emoji = emoji_map.get(reaction_type, reaction_type)
print(f' {emoji} {reaction_type}: {count}')
print()
print('4. 时间戳转换:')
if releases:
published_at = releases[0].get('published_at')
if published_at:
# 移除时区信息进行解析
dt = datetime.fromisoformat(published_at.replace('Z', '+00:00'))
print(f'最新版本发布日期: {dt.strftime("%Y年%m月%d日 %H:%M:%S")}')
print()
except Exception as e:
print(f'请求或解析错误: {e}')
def demo_error_handling():
"""演示异常处理与错误恢复"""
print('--- 异常处理与错误恢复 ---')
if not REQUESTS_AVAILABLE:
print('跳过异常处理演示(需要 requests)')
return
def safe_api_call(url, max_retries=3, backoff_factor=1):
"""安全的API调用函数"""
for attempt in range(max_retries):
try:
response = requests.get(url, timeout=5)
# 检查状态码
response.raise_for_status() # 抛出HTTPError异常如果状态码不是2xx
return response.json()
except RequestException as e:
print(f'尝试 {attempt + 1}/{max_retries}: {type(e).__name__}: {e}')
# 对于某些错误码,不需要重试
if hasattr(e, 'response') and e.response and e.response.status_code in [400, 401, 403, 404]:
break
except Exception as e:
print(f'尝试 {attempt + 1}/{max_retries}: {type(e).__name__}: {e}')
# 对于其他异常,继续重试
if attempt < max_retries - 1:
wait_time = backoff_factor * (2 ** attempt) # 指数退避
print(f'等待 {wait_time} 秒后重试...')
time.sleep(wait_time)
return None
print('1. 安全的API调用示例:')
result = safe_api_call('https://jsonplaceholder.typicode.com/posts/1')
if result:
print(f'成功获取数据: {result.get("title", "N/A")}')
else:
print('所有重试都失败了')
print()
print('2. 错误状态码处理:')
result = safe_api_call('https://httpbin.org/status/404')
if result is None:
print('正确处理了404错误')
print()
def demo_security_practices():
"""演示安全实践"""
print('--- 安全实践 ---')
if not REQUESTS_AVAILABLE:
print('跳过安全实践演示(需要 requests)')
return
print('1. API密钥安全管理:')
print('✅ 使用环境变量存储敏感信息')
print('✅ 不要在代码中硬编码API密钥')
print(f'当前API密钥: {"已设置" if API_KEY != "demo_key_replace_with_real_key" else "使用演示密钥"}')
print()
print('2. URL参数安全:')
# 安全的方式:使用params参数
try:
params = {'q': 'python tutorial', 'limit': 10}
response = requests.get('https://httpbin.org/get', params=params)
print(f'安全参数传递: {response.json()["args"]}')
except Exception as e:
print(f'参数传递示例错误: {e}')
print()
print('3. 输入验证和清理:')
def safe_search(query):
"""安全的搜索函数"""
if not query or len(query) > 100:
return None
# 清理输入
query = query.strip()
try:
# 使用params而不是字符串拼接
response = requests.get('https://httpbin.org/get',
params={'search': query},
timeout=5)
return response.json()
except:
return None
try:
result = safe_search('python api')
if result:
print(f'安全搜索结果: {result["args"]}')
except Exception as e:
print(f'安全搜索示例错误: {e}')
print()
print('4. 速率限制意识:')
print('✅ 实现请求间隔')
print('✅ 监控API使用量')
print('✅ 实现指数退避重试')
print()
def main():
print('=' * 50)
print('API开发实战演示')
print('=' * 50)
print()
if not REQUESTS_AVAILABLE:
print('requests 未安装,无法运行API演示')
print('请运行:pip install requests')
return
try:
demo_basic_http_requests()
demo_request_headers()
demo_timeout_and_retry()
demo_session_management()
demo_ssl_verification()
demo_json_processing()
demo_error_handling()
demo_security_practices()
print('所有API演示已完成')
except Exception as e:
print(f'演示过程中发生错误: {e}')
if __name__ == '__main__':
main()
4. 课件14:大模型开发实战
从"与大模型聊天"到"在程序中调用大模型"
目标:
理解LLM的API调用机制
掌握本地部署模型的集成方式
且能将LLM嵌入到实际应用流程
工程视角:
LLM------可调用的服务
程序通过API传入数据,获取并解析结果
4.1. 环境搭建:Ollama本地部署
Ollama平台:
- 优势:
本地部署,支持标准API调用,且适合CPU运行。 - 模型:
Qwen3:0.6b - 安装指令:
ollama pull qwen3:0.6b
具体环境搭建此处不展示
4.2. 大模型API基础调用
Python集成:
使用ollama Python包调用本地模型。
核心函数:
chat()
model: 模型名称。messages结构:
包含字典的列表
对话的核心
三大角色:
system:系统角色,设定对话背景。
一般用于定义规则、身份和输出格式user:用户角色,发送对话信息。
具体的输入数据或问题assistant:助手角色,返回对话结果。
模型的输出结果或存储的历史对话
进阶参数:
stream=True:启用流式响应,逐步获取结果。
类似"打字机"式的输出效果think=True:模型输出其推理过程,显示[思考中]标签
助于理解AI的逻辑
4.3. 实践案例:问卷自动分析
通过问卷处理展示传统编程于AI结合的优势
-
混合处理模式:
- 单选/多选题:通过 Python 传统逻辑进行统计分析。
- 开放性文字题:循环调用 LLM API 进行文本情感分析、主题提取和建议汇总
-
多轮调用与多轮对话的比较:
自动化处理中,程序通常为"多轮调用"
每条问卷记录发起一次独立API求情
将模型作为一个系统的"分析模块"来应用
4.4. 代码伦理与安全性
- 幻觉问题
模型分析可能出错,注意分析,辩证使用 - 隐私问题
避免上传敏感个人信息,确保数据安全 - 学术诚信
使用AI辅助时,注意引用和标注,避免抄袭风险
4.5. 示例代码
python
"""
示例:大模型开发实战演示
包含演示:
- Ollama本地模型基础调用
- messages结构与角色系统
- 流式响应与推理过程显示
- 问卷自动分析实践案例
- 代码伦理与安全性考虑
运行:
python llm_integration_demo.py
依赖:
pip install ollama pandas
注意:
需要先安装并运行Ollama:
1. 下载安装Ollama:https://ollama.ai
2. 拉取模型:ollama pull qwen3:0.6b
3. 启动Ollama服务
作者:自动生成示例(中文注释)
"""
try:
import ollama
import pandas as pd
import json
import time
from datetime import datetime
OLLAMA_AVAILABLE = True
except ImportError as e:
print(f"警告:缺少依赖包 - {e}")
print("请运行:pip install ollama pandas")
OLLAMA_AVAILABLE = False
def demo_basic_ollama_chat():
"""演示Ollama基础聊天调用"""
print('--- 基础Ollama聊天调用 ---')
if not OLLAMA_AVAILABLE:
print('跳过Ollama演示(需要 ollama 和 pandas)')
return
try:
# 基础对话
print('1. 基础对话示例:')
messages = [
{
'role': 'user',
'content': '你好,请介绍一下自己。'
}
]
response = ollama.chat(
model='qwen3:0.6b',
messages=messages
)
print(f'用户: {messages[0]["content"]}')
print(f'助手: {response["message"]["content"]}')
print()
# 包含系统角色的对话
print('2. 带系统角色的对话:')
system_messages = [
{
'role': 'system',
'content': '你是一个专业的Python编程助手,请用简洁的技术术语回答问题。'
},
{
'role': 'user',
'content': '解释一下什么是装饰器?'
}
]
response = ollama.chat(
model='qwen3:0.6b',
messages=system_messages
)
print(f'系统: {system_messages[0]["content"]}')
print(f'用户: {system_messages[1]["content"]}')
print(f'助手: {response["message"]["content"]}')
print()
except Exception as e:
print(f'Ollama调用错误: {e}')
print('请确保Ollama已安装并运行,且已拉取qwen3:0.6b模型')
def demo_streaming_response():
"""演示流式响应"""
print('--- 流式响应演示 ---')
if not OLLAMA_AVAILABLE:
print('跳过流式响应演示(需要 ollama)')
return
try:
print('流式响应(打字机效果):')
messages = [
{
'role': 'user',
'content': '请写一段关于Python的简短介绍。'
}
]
# 启用流式响应
stream = ollama.chat(
model='qwen3:0.6b',
messages=messages,
stream=True
)
print('助手: ', end='', flush=True)
full_response = ''
for chunk in stream:
if chunk['message']['content']:
content = chunk['message']['content']
print(content, end='', flush=True)
full_response += content
time.sleep(0.05) # 模拟打字机效果
print('\n')
print(f'完整响应长度: {len(full_response)} 字符')
print()
except Exception as e:
print(f'流式响应错误: {e}')
def demo_reasoning_process():
"""演示推理过程显示"""
print('--- 推理过程显示 ---')
if not OLLAMA_AVAILABLE:
print('跳过推理过程演示(需要 ollama)')
return
try:
print('启用推理过程显示:')
messages = [
{
'role': 'user',
'content': '如果一个班有30个学生,其中20个喜欢数学,15个喜欢物理,10个两个都喜欢,问有多少学生不喜欢数学也不喜欢物理?'
}
]
# 启用推理过程显示
response = ollama.chat(
model='qwen3:0.6b',
messages=messages,
options={'think': True} # 注意:实际参数可能因模型而异
)
print(f'问题: {messages[0]["content"]}')
print(f'推理过程: {response.get("thinking", "无推理过程")}')
print(f'最终答案: {response["message"]["content"]}')
print()
except Exception as e:
print(f'推理过程演示错误: {e}')
print('注意:推理过程显示功能可能需要特定模型支持')
def demo_multi_turn_conversation():
"""演示多轮对话"""
print('--- 多轮对话演示 ---')
if not OLLAMA_AVAILABLE:
print('跳过多轮对话演示(需要 ollama)')
return
try:
# 维护对话历史
conversation_history = [
{
'role': 'system',
'content': '你是一个有帮助的编程助手,请记住我们之前的对话内容。'
}
]
# 第一轮对话
user_input1 = '我想学习Python编程,应该从哪里开始?'
conversation_history.append({
'role': 'user',
'content': user_input1
})
response1 = ollama.chat(
model='qwen3:0.6b',
messages=conversation_history
)
assistant_reply1 = response1['message']['content']
conversation_history.append({
'role': 'assistant',
'content': assistant_reply1
})
print('第一轮对话:')
print(f'用户: {user_input1}')
print(f'助手: {assistant_reply1}')
print()
# 第二轮对话(基于历史)
user_input2 = '那数据结构和算法重要吗?'
conversation_history.append({
'role': 'user',
'content': user_input2
})
response2 = ollama.chat(
model='qwen3:0.6b',
messages=conversation_history
)
assistant_reply2 = response2['message']['content']
print('第二轮对话(记住上下文):')
print(f'用户: {user_input2}')
print(f'助手: {assistant_reply2}')
print()
except Exception as e:
print(f'多轮对话错误: {e}')
def create_sample_questionnaire_data():
"""创建示例问卷数据"""
# 模拟问卷数据
questionnaire_data = [
{
'id': 1,
'age': 25,
'gender': '女',
'education': '本科',
'programming_experience': '1-2年',
'favorite_language': ['Python', 'JavaScript'],
'learning_motivation': '我想转行到IT行业,听说编程就业前景好',
'difficulties': '基础语法理解起来比较吃力,特别是面向对象编程',
'suggestions': '希望有更多实战项目练习'
},
{
'id': 2,
'age': 30,
'gender': '男',
'education': '硕士',
'programming_experience': '3-5年',
'favorite_language': ['Python', 'Java'],
'learning_motivation': '工作中需要用到编程技能,公司要求学习',
'difficulties': '时间不够用,工作太忙了',
'suggestions': '课程内容很不错,但节奏可以稍微慢一点'
},
{
'id': 3,
'age': 22,
'gender': '女',
'education': '大专',
'programming_experience': '无',
'favorite_language': ['Python'],
'learning_motivation': '对编程感兴趣,想自学一些技能',
'difficulties': '数学基础不好,算法思维跟不上',
'suggestions': '多一些基础概念的解释视频'
}
]
return questionnaire_data
def analyze_questionnaire_with_llm(questionnaire_data):
"""使用LLM分析问卷数据"""
print('--- 问卷自动分析演示 ---')
if not OLLAMA_AVAILABLE:
print('跳过问卷分析演示(需要 ollama)')
return
try:
# 1. 统计分析(传统编程)
print('1. 传统编程统计分析:')
# 性别统计
gender_stats = {}
for record in questionnaire_data:
gender = record['gender']
gender_stats[gender] = gender_stats.get(gender, 0) + 1
print(f'性别分布: {gender_stats}')
# 年龄统计
ages = [record['age'] for record in questionnaire_data]
avg_age = sum(ages) / len(ages)
print(f'平均年龄: {avg_age:.1f}岁')
print()
# 2. LLM情感分析
print('2. LLM情感分析:')
for i, record in enumerate(questionnaire_data, 1):
motivation = record['learning_motivation']
analysis_prompt = f"""
请分析以下学习动机的情感倾向:
"{motivation}"
请从以下方面进行分析:
1. 情感倾向(积极/消极/中性)
2. 主要动机类型
3. 简要分析理由
请用JSON格式返回结果,包含字段:sentiment, motivation_type, reason
"""
messages = [
{
'role': 'system',
'content': '你是一个专业的情感分析助手,请准确分析文本情感并返回指定格式的JSON结果。'
},
{
'role': 'user',
'content': analysis_prompt
}
]
try:
response = ollama.chat(
model='qwen3:0.6b',
messages=messages
)
result_text = response['message']['content']
# 尝试解析JSON结果
try:
# 清理可能的markdown代码块标记
if '```json' in result_text:
result_text = result_text.split('```json')[1].split('```')[0].strip()
elif '```' in result_text:
result_text = result_text.split('```')[1].split('```')[0].strip()
analysis_result = json.loads(result_text)
print(f'记录{i}学习动机分析:')
print(f' 原文: {motivation}')
print(f' 情感倾向: {analysis_result.get("sentiment", "未知")}')
print(f' 动机类型: {analysis_result.get("motivation_type", "未知")}')
print(f' 分析理由: {analysis_result.get("reason", "无")}')
print()
except json.JSONDecodeError:
print(f'记录{i}分析结果解析失败,使用原始文本:')
print(f' 原文: {motivation}')
print(f' LLM分析: {result_text[:200]}...')
print()
except Exception as e:
print(f'记录{i}分析失败: {e}')
print()
# 3. 主题提取和建议汇总
print('3. 主题提取和建议汇总:')
all_difficulties = [record['difficulties'] for record in questionnaire_data]
all_suggestions = [record['suggestions'] for record in questionnaire_data]
combined_text = '学习困难:' + ';'.join(all_difficulties) + '\n建议:' + ';'.join(all_suggestions)
summary_prompt = f"""
请分析以下问卷反馈数据,提取主要主题并提供改进建议:
{combined_text}
请从以下方面进行分析:
1. 学习困难的主要类型和频率
2. 学生建议的主要方向
3. 对课程改进的具体建议
请用结构化的方式呈现分析结果。
"""
messages = [
{
'role': 'system',
'content': '你是一个教育数据分析专家,请专业地分析问卷数据并提供建设性建议。'
},
{
'role': 'user',
'content': summary_prompt
}
]
response = ollama.chat(
model='qwen3:0.6b',
messages=messages
)
print('综合分析结果:')
print(response['message']['content'])
print()
except Exception as e:
print(f'问卷分析错误: {e}')
def demo_ethics_and_security():
"""演示代码伦理与安全性考虑"""
print('--- 代码伦理与安全性演示 ---')
print('1. 隐私保护示例:')
print('✅ 敏感信息脱敏处理')
print('✅ 本地处理,避免上传敏感数据')
print()
print('2. 幻觉问题处理:')
print('✅ 交叉验证重要信息')
print('✅ 设置置信度阈值')
print('✅ 人工审核关键决策')
print()
print('3. 学术诚信提醒:')
print('✅ 标注AI生成内容')
print('✅ 理解而非抄袭AI输出')
print('✅ 平衡AI辅助与自主学习')
print()
# 演示数据脱敏
print('4. 数据脱敏示例:')
sensitive_data = {
'name': '张三',
'id_card': '123456789012345678',
'phone': '13800138000',
'email': 'zhangsan@example.com',
'feedback': '我觉得这门课很有帮助'
}
def anonymize_data(data):
"""数据脱敏函数"""
anonymized = data.copy()
# 脱敏处理
anonymized['name'] = '学生' + str(hash(data['name']))[:4]
anonymized['id_card'] = data['id_card'][:6] + '*' * 8 + data['id_card'][-4:]
anonymized['phone'] = data['phone'][:3] + '*' * 4 + data['phone'][-4:]
anonymized['email'] = data['email'].split('@')[0][:2] + '*' * 3 + '@' + data['email'].split('@')[1]
return anonymized
anonymized_data = anonymize_data(sensitive_data)
print('原始数据:')
for key, value in sensitive_data.items():
print(f' {key}: {value}')
print('脱敏后数据:')
for key, value in anonymized_data.items():
print(f' {key}: {value}')
print('✅ 敏感信息已保护,同时保留分析价值')
print()
def main():
print('=' * 50)
print('大模型开发实战演示')
print('=' * 50)
print()
if not OLLAMA_AVAILABLE:
print('ollama 或 pandas 未安装,无法运行完整演示')
print('请运行:pip install ollama pandas')
print('并确保Ollama已安装并运行')
return
try:
demo_basic_ollama_chat()
demo_streaming_response()
demo_reasoning_process()
demo_multi_turn_conversation()
# 创建并分析问卷数据
questionnaire_data = create_sample_questionnaire_data()
analyze_questionnaire_with_llm(questionnaire_data)
demo_ethics_and_security()
print('所有大模型演示已完成')
except Exception as e:
print(f'演示过程中发生错误: {e}')
print('请检查Ollama服务是否正常运行')
if __name__ == '__main__':
main()
运行输出示例:
