📊 Excel 文件处理
根据字段合并两个 Excel 文件
问题:使用 pandas 读取包含 计算公式 的 Excel 文件时,可能报错:
ValueError: Value does not match pattern ^[$]?([A-Za-z]{1,3})[$]?(\d+)
解决方案:使用 xlwings 库作为备用读取方法,避免计算公式导致的读取错误
参考:https://blog.csdn.net/weixin_43955820/article/details/130910057
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
import os
import pandas as pd
import xlwings as xw
import warnings
warnings.filterwarnings('ignore')
def read_excel_safe(filename):
"""安全读取 Excel 文件,处理含计算公式的情况"""
try:
# 尝试使用 pandas 直接读取
data = pd.read_excel(filename)
data.columns = [col.strip() for col in data.columns] # 清理列名空格
return data
except Exception as e:
print(f'Pandas 读取失败,使用 xlwings 模块: {e}')
# 使用 xlwings 作为备用方案
app = xw.App(visible=False, add_book=False)
app.display_alerts = False
app.screen_updating = False
wb = app.books.open(filename)
ws = wb.sheets[0]
info = ws.used_range
nrows = info.last_cell.row
ncolumns = info.last_cell.column
# 获取数据
ws_data = ws.range((1, 1), (nrows, ncolumns)).value
# 获取列名
data_columns = ws_data.pop(0)
data = pd.DataFrame(ws_data, columns=data_columns)
wb.close()
app.quit()
return data
# 主程序:合并多个 Excel 文件
def merge_excel_files(folder_path, output_file='合并后.xlsx'):
"""合并指定文件夹中的所有 Excel 文件"""
combined_data = pd.DataFrame()
file_list = os.listdir(folder_path)
print('开始合并文件...')
for index, filename in enumerate(file_list):
print(f'处理第 {index+1} 个文件: {filename}')
file_path = os.path.join(folder_path, filename)
data = read_excel_safe(file_path)
combined_data = pd.concat([combined_data, data], axis=0, ignore_index=True)
combined_data.to_excel(output_file, index=False)
print('文件合并完成 ✔')
return combined_data
# 使用示例
if __name__ == "__main__":
folder_path = r".\Excel文件路径" # 替换为实际路径
merge_excel_files(folder_path)
|
🌤️ 气象数据处理
二进制数据文件处理
https://blog.csdn.net/m0_51153866/article/details/120831623
读取数据
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import struct
def read_binary_data(filename, data_size=6720):
"""读取二进制气象数据文件"""
with open(filename, 'rb') as f:
# 'f' 表示浮点数,4*6720 表示读取 6720 个浮点数(每个4字节)
data = struct.unpack('f' * data_size, f.read(4 * data_size))
print(f'前20个数据点: {data[:20]}')
return data
# 使用示例
filename = './数据/xxx.dat'
meteorological_data = read_binary_data(filename)
|
数据处理
经度范围:起始经度为 121.88,步长为 5.65,共 32 个位置,故结束经度为 32*5.65+121.88=300.8
纬度取值:-18.0950 -12.3808 -6.6666 -0.9524 4.7618 10.4761 16.1902,共 7 个纬度值
zdef:1000,表示 1000hpa 高度场
时间:共 30 年
海温场气候场:每个站点的 30 年海温数据的平均值
均方差场:先求每个站点的海温数据的每年的距平值,然后再求每个站点均方差值
Nino3.4 区:5°S-5°N,170°W-120°W 海域的海温指数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
import numpy as np
import math
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.basemap import Basemap
# 显示中文和负号
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 初始化数组
h1000 = np.zeros((7, 32, 30)) # 原始数据 (纬度, 经度, 年份)
climatology = np.zeros((7, 32)) # 气候场
anomaly = np.zeros((7, 32, 30)) # 距平场
std_dev = np.zeros((7, 32)) # 均方差场
missing_records = [] # 缺失值记录
# 数据处理
data = np.array(meteorological_data)
for year in range(30): # 30年数据
for lat_idx in range(7): # 7个纬度
for lon_idx in range(32): # 32个经度
# 计算数据索引位置
index = lon_idx + 32 * lat_idx + 32 * 7 * year
h1000[lat_idx, lon_idx, year] = data[index]
# 处理缺失值(标记为9999)
if h1000[lat_idx, lon_idx, year] >= 9999:
h1000[lat_idx, lon_idx, year] = 0
missing_records.append((lat_idx, lon_idx, year))
# print(h1000[:,:,0][0]) # 表示第一年纬度为 -18.0950 下的 32 个经度的海温数据
# 提取特定年份数据
h1000_1982 = h1000[:, :, 4] # 1982年数据
h1000_1998 = h1000[:, :, 20] # 1998年数据
# 计算气候场(30年平均)
climatology = np.mean(h1000, axis=2)
# 填补缺失值
for lat_idx, lon_idx, year in missing_records:
h1000[lat_idx, lon_idx, year] = climatology[lat_idx, lon_idx]
# 计算距平场
for year in range(30):
anomaly[:, :, year] = h1000[:, :, year] - climatology
anomaly_1982 = anomaly[:, :, 4] # 1982年距平
anomaly_1998 = anomaly[:, :, 20] # 1998年距平
# 计算均方差场
std_dev = np.sqrt(np.mean(anomaly**2, axis=2))
# 经纬度坐标
longitude = np.arange(121.88, 300.8, 5.65) # 经度数组
latitude = np.array([-18.0950, -12.3808, -6.6666, -0.9524, 4.7618, 10.4761, 16.1902]) # 纬度数组
|
数据可视化
https://blog.csdn.net/weixin_44092702/article/details/99690821
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
def plot_meteorological_data(lon, lat, data, levels, title, colormap='PuBu'):
"""
绘制气象数据图
参数:
lon: 经度数组
lat: 纬度数组
data: 要绘制的数据(二维数组)
levels: 等高线密度/分级
title: 图像标题
colormap: 颜色映射,默认为'PuBu'
"""
# 创建Basemap对象
b_map = Basemap(
resolution='l',
area_thresh=10000,
projection='cyl',
llcrnrlon=min(lon),
urcrnrlon=max(lon),
llcrnrlat=min(lat),
urcrnrlat=max(lat)
)
fig = plt.figure(figsize=(18, 12))
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
# 创建网格数据
# https://zhuanlan.zhihu.com/p/33579211
lon_grid, lat_grid = np.meshgrid(lon, lat)
x, y = b_map(lon_grid, lat_grid)
# 绘制填充等高线
cs = b_map.contourf(x, y, data, levels=levels, cmap=plt.get_cmap(colormap))
b_map.colorbar(cs, label='数值')
# 添加地理信息, 关于海岸线和国家边界线的绘制设置
b_map.drawcoastlines(linewidth=1)
b_map.drawcountries(linewidth=1.5)
# 添加经纬度刻度
lon_dict = b_map.drawmeridians(
np.linspace(min(lon), max(lon), 5),
labels=[0, 0, 0, 1], # 四个值分别代表 [左,右,上,下],下为 1 即只标注在下
fontsize=12
)
lat_dict = b_map.drawparallels(
np.linspace(min(lat), max(lat), 5),
labels=[1, 0, 0, 0], # 左为 1 即只标注在左
fontsize=12
)
# 加上经纬刻度线
lon_list = []
lat_list = []
for lon_key in lon_dict.keys():
try:
# print(lon_dict[lon_key][1][0].get_position())
lon_list.append(lon_dict[lon_key][1][0].get_position()[0])
except Exception as e:
print(e)
continue
for lat_key in lat_dict.keys():
try:
# print(lat_dict[lat_key][1][0].get_position())
lat_list.append(lat_dict[lat_key][1][0].get_position()[1])
except Exception as e:
print(e)
continue
ax = plt.gca() #获取当前轴
# ax.xaxis.tick_top() #翻转 x轴
ax.set_xticks(lon_list)
ax.set_yticks(lat_list)
ax.tick_params(labelcolor='none') #设置隐藏原本的刻度值
plt.title(title, fontsize=20)
plt.show()
plt.close()
|
1
2
3
4
|
# 使用示例
title_1982 = '1982年海温数据'
levels_1982 = np.linspace(h1000_1982.min(), h1000_1982.max(), 30)
plot_meteorological_data(longitude, latitude, h1000_1982, levels_1982, title_1982)
|
NetCDF 文件数据可视化
安装依赖
1
2
3
|
pip install netCDF4
# 或指定特定版本
pip install netCDF4-1.5.8-cp39-cp39-win_amd64.whl
|
数据处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
import numpy as np
import datetime
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
from netCDF4 import Dataset
import seaborn as sns
# 读取NetCDF文件
nc_file = Dataset('air.mon.mean.nc')
# 查看变量信息
print("文件变量信息:")
for var_name in nc_file.variables.keys():
var_data = nc_file.variables[var_name]
print(f"{var_name}: {var_data.shape}")
# np.save(i + '.npy', var_data) # 保存数据
# 提取各变量数据
latitudes = nc_file.variables['lat'][:]
longitudes = nc_file.variables['lon'][:]
time_data = nc_file.variables['time'][:]
air_data = nc_file.variables['air'][:]
print(f"纬度范围: {latitudes.min()} 到 {latitudes.max()}")
print(f"经度范围: {longitudes.min()} 到 {longitudes.max()}")
print(f"时间点数量: {len(time_data)}")
print(f"空气数据维度: {air_data.shape}")
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
# 时间戳转换示例
def convert_netcdf_time(nc_time, index=0):
"""转换NetCDF时间戳为可读格式"""
base_date = datetime.datetime(1800, 1, 1, 0, 0, 0)
time_delta = datetime.timedelta(hours=float(nc_time[index]))
return base_date + time_delta
# 数据切片示例
# 获取特定站点的时序数据
station_timeseries = air_data[:, 1, 0] # 纬度索引1, 经度索引0的所有时间点
# 获取特定时间点的空间数据
spatial_data = air_data[0, :, :] # 第一个时间点的所有空间数据
# 中国区域数据提取
def extract_china_region(air_data, lons, lats):
"""
提取中国区域的数据
中国范围大约: 北纬 3~53°, 东经 73~135°
"""
# 查找中国范围内的索引
lat_mask = (lats >= 3) & (lats <= 53)
lon_mask = (lons >= 73) & (lons <= 135)
china_air = air_data[:, lat_mask, :]
china_air = china_air[:, :, lon_mask]
china_lats = lats[lat_mask]
china_lons = lons[lon_mask]
return china_air, china_lons, china_lats
# 提取中国区域数据
china_air, china_lons, china_lats = extract_china_region(air_data, longitudes, latitudes)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
# 可视化函数
def plot_netcdf_data(lons, lats, data, title, levels=20):
"""绘制NetCDF数据"""
b_map = Basemap(
resolution='l',
area_thresh=10000,
projection='cyl',
llcrnrlon=min(lons),
urcrnrlon=max(lons),
llcrnrlat=min(lats),
urcrnrlat=max(lats)
)
fig, ax = plt.subplots(figsize=(12, 8))
# 创建网格
lon_grid, lat_grid = np.meshgrid(lons, lats)
x, y = b_map(lon_grid, lat_grid)
# 绘制数据
contour = b_map.contourf(x, y, data, levels=levels, cmap='RdBu_r')
b_map.colorbar(contour, label='温度 (°C)')
# 添加地理要素
b_map.drawcoastlines()
b_map.drawcountries()
b_map.drawstates()
# 添加网格线
b_map.drawmeridians(
np.arange(np.floor(min(lons)), np.ceil(max(lons)), 10),
labels=[0, 0, 0, 1]
)
b_map.drawparallels(
np.arange(np.floor(min(lats)), np.ceil(max(lats)), 10),
labels=[1, 0, 0, 0]
)
plt.title(title, fontsize=16)
plt.tight_layout()
plt.show()
# 绘制前几个时间点的中国区域数据
for i in range(min(5, len(china_air))):
time_str = convert_netcdf_time(time_data, i).strftime("%Y-%m-%d")
plot_netcdf_data(
china_lons,
china_lats,
china_air[i, :, :],
f"中国区域气温 - {time_str}"
)
# 关闭NetCDF文件
nc_file.close()
|
恰同学少年模拟操作
通过修改 POST 请求直接操作
- 核心思想:通过分析网络请求,直接构造并发送修改后的数据包来模拟操作。
- 技术实现:使用 Python 的
requests 库发送 POST 请求,并通过 json 库动态构造和修改 JSON 数据。
- 关键函数:
process_json(uid, *args): 用于读取、修改并返回特定格式的 JSON 数据。
get_headers(): 返回模拟浏览器行为的请求头。
sent_post(uid): 发送最终的 POST 请求。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
import json
import random
import time
import requests
def process_json(uid, *args):
# 读取json格式数据,注意最后一项末尾不能有逗号
data = json.loads("""
{
"uuid": "1",
"appId": 42
}
""")
data['uuid'] = uid
# 返回字典
return json.dumps(data)
def get_headers():
headers = {
"Host": "",
"Connection": "keep-alive",
"Content-Length": "",
"sec-ch-ua" : """" Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100" """,
"sec-ch-ua-mobile": "?0",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36",
"sec-ch-ua-platform": "Windows",
"Content-Type":"application/x-www-form-urlencoded",
"Accept":"*/*",
"Origin" :"",
"Referer":"",
"Accept-Encoding":"gzip, deflate, br",
"Accept-Language":"zh-CN,zh;q=0.9 ",
}
return headers
def sent_post(uid):
url = "https://virtualcourse.zhihuishu.com/report/saveReport"
data = {
"jsonStr": process_json(uid),
"ticket": None
}
headers = get_headers()
response = requests.post(url, data=data, headers=headers)
print(response.status_code, response.text)
|
使用 Selenium 进行浏览器自动化
- 核心思想:模拟真实用户在浏览器中的操作(点击、输入、跳转等)。
- 技术栈:
selenium, pyautogui, ActionChains。
- 关键步骤:
- 初始化 WebDriver 并设置窗口。
- 访问登录页面,输入凭据。
- 使用显式等待 (
WebDriverWait) 确保元素加载完成后再进行操作。
- 使用
ActionChains 执行复杂的鼠标动作。
- 处理多窗口 (
window_handles, switch_to.window)。
- 使用
pyautogui 执行基于屏幕坐标的操作(如点击“跳过帮助”)。
- 模拟键盘操作 (
Keys.UP, Keys.RIGHT) 进行游戏内移动。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
|
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver import ActionChains
from time import sleep
import requests
import pyautogui
URL = 'https://passport.zhihuishu.com/login'
username = 'xxx'
password = 'xxx'
# 填写 msedgedriver 路径
browser = webdriver.Edge('C:\Program Files (x86)\Microsoft\Edge\Application\msedgedriver.exe')
# 最大化浏览器窗口
# browser.maximize_window()
# 设置浏览器窗口大小
browser.set_window_size(1280, 720)
# 创建等待时间对象
wait = WebDriverWait(browser, 300, 1)
# 向目标网站发送请求
browser.get(URL)
sleep(3)
# 某些内容需要等待网页加载完成才会出现,之后进行点击
username_loc = wait.until(
EC.presence_of_element_located(
(By.XPATH, "//input[@id='lUsername']")
)
)
# 填写手机号和秘密
username_loc = browser.find_element(
By.XPATH, "//input[@id='lUsername']"
)
username_loc.clear()
username_loc.send_keys(username)
sleep(1)
password_loc = browser.find_element(
By.XPATH, "//input[@id='lPassword']"
)
password_loc.clear()
password_loc.send_keys(password)
sleep(1)
# 模拟鼠标点击登录按钮
register_button = wait.until(
EC.presence_of_element_located(
(By.XPATH, "//span[@class='wall-sub-btn']")
)
)
# 将鼠标移动到按钮位置
ActionChains(browser) \
.move_to_element(register_button) \
.perform()
# 进行点击操作
register_button.click()
sleep(1)
# 模拟鼠标点击 “我的学堂”
xuetang_button = wait.until(
EC.presence_of_element_located(
(By.XPATH, "//a[@href='https://onlineweb.zhihuishu.com']")
)
)
ActionChains(browser) \
.move_to_element(xuetang_button) \
.perform()
xuetang_button.click()
sleep(5)
# 模拟鼠标点击进入虚拟仿真
courseName_button = wait.until(
EC.presence_of_element_located(
(By.XPATH, "//div[@class='courseName' and @data-v-5451e64c='']")
)
)
ActionChains(browser) \
.move_to_element(courseName_button) \
.perform()
courseName_button.click()
sleep(1)
# 获取当前窗口
# currentwindow = browser.current_window_handle
# print(currentwindow)
# sleep(1)
# 获取所有的窗口
all_window = browser.window_handles
print(all_window)
browser.switch_to.window(all_window[-1])
sleep(1)
# 模拟鼠标点击实验资源
resources_button = wait.until(
EC.presence_of_element_located(
(By.XPATH, "//span[@style='']")
)
)
ActionChains(browser) \
.move_to_element(resources_button) \
.perform()
resources_button.click()
sleep(1)
# 模拟鼠标点击开始实验
start_button = wait.until(
EC.presence_of_element_located(
(By.XPATH, "//button[@class='el-button btn el-button--primary']")
)
)
ActionChains(browser) \
.move_to_element(start_button) \
.perform()
start_button.click()
sleep(1)
all_window = browser.window_handles
print(all_window)
browser.switch_to.window(all_window[-1])
sleep(30)
waitload = wait.until(
EC.text_to_be_present_in_element(
(By.XPATH, "//div[@id='jindu_num']"),
"资源加载中:100%"
)
)
sleep(5)
# 点击跳过帮助,x和y是鼠标横纵坐标
pyautogui.click(x=900, y=930, )
# 开头的讲话
sleep(39)
pyautogui.moveTo(600, 500)
sleep(5)
# 按住鼠标右键并拖动,持续时间2秒
pyautogui.dragRel(300, 0, duration=2, button='right')
sleep(5)
# 通过控制键盘实现向前走动四步,抵达前言
for i in range(4):
ActionChains(browser) \
.send_keys(Keys.UP) \
.perform()
sleep(0.5)
sleep(100) #需要等待大概 100秒
# 向右走动十步
for i in range(10):
ActionChains(browser) \
.send_keys(Keys.RIGHT) \
.perform()
sleep(0.5)
# 关闭浏览器
browser.quit()
|
中文新闻事件检测 (NLP 任务)
目标:从非结构化的中文文本中抽取出结构化的事件信息
输入:一个包含事件信息的句子
输出:识别出的事件触发词及其事件类型(在预先定义的类型集合中)
触发词 (Trigger):标志事件发生的核心词或短语
事件类型 (Event Type):数据集中预先定义好的类别(如 Exhibit)
示例:
- 输入: “除了无人机,无人战斗车的爆发式展示也是本届珠海航展的最大亮点之一…”
- 输出(JSON 格式):
1
2
3
4
5
6
7
8
9
|
{
"event_mention": {
"event_type": "Exhibit",
"trigger": {
"text": "展示",
"offset": [12, 13]
}
}
}
|
使用 getopt 处理 Python 命令行参数
- 目的:使 Python 脚本能够接收和处理从命令行传入的参数。
- 方法:使用
getopt.getopt() 函数来解析 sys.argv 列表。
- 语法:
opts, args = getopt.getopt(argv, "短选项字母:", ["长选项="])
短选项:单个字母,后跟 : 表示该选项需要参数。
长选项:字符串列表,后跟 = 表示该选项需要参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
import sys
import getopt
def main(argv):
inputfile = ''
try:
opts, args = getopt.getopt(argv, "hi:o:", ["ifile=", "ofile="])
except getopt.GetoptError:
print('usage: test.py -i <inputfile> -o <outputfile>')
sys.exit(2)
for opt, arg in opts:
if opt == '-h':
print('test.py -i <inputfile> -o <outputfile>')
sys.exit()
elif opt in ("-i", "--ifile"):
inputfile = arg
elif opt in ("-o", "--ofile"):
outputfile = arg
print('Input file:', inputfile)
print('Output file:', outputfile)
if __name__ == "__main__":
main(sys.argv[1:])
|
运行示例:python script.py -i input.txt --ofile=output.txt
配置文件管理
为何需要配置文件?
可以将代码与配置数据(如常量、参数、数据库连接信息)分离,提高代码的整洁性和可维护性
主流配置文件格式有 ini、json、toml、yaml、xml 等等
复杂性递增排序:INI < JSON ≈ TOML < YAML
各种格式的示例与解析方法
-
db.ini
1
2
3
4
5
6
7
8
9
10
|
[DEFAULT]
path = /user/python
version = v1.
[localdb]
host = 127.0.0.1
user = root
password = 123456
port = 3306
database = mysql
|
Python 解析:
1
2
3
4
5
6
|
from configparser import ConfigParser
cfg = ConfigParser()
cfg.read('db.ini')
host = cfg.get('localdb', 'host') # 获取字符串
port = cfg.getint('localdb', 'port') # 获取并转换为整数
|
-
db.json
1
2
3
4
5
6
7
8
9
|
{
"localdb": {
"host": "127.0.0.1",
"user": "root",
"password": "123456",
"port": 3306,
"database": "mysql"
}
}
|
Python 解析:
1
2
3
4
5
|
import json
with open('db.json') as f:
cfg = json.load(f)
print(cfg['localdb']['host'])
|
-
db.toml
1
2
3
4
5
6
7
8
9
10
11
12
|
[mysql]
host = "127.0.0.1"
user = "root"
port = 3306
database = "test"
[mysql.parameters]
pool_size = 5
charset = "utf8"
[mysql.fields]
pandas_cols = [ "id", "name", "age", "date"]
|
Python 解析:
1
2
3
4
5
|
import toml
with open('db.toml', 'r') as f:
cfg = toml.load(f)
print(cfg['mysql']['parameters']['pool_size'])
|
-
db.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
mysql:
host: "127.0.0.1"
port: 3306
user: "root"
password: "123456"
database: "test"
parameter:
pool_size: 5
charset: "utf8"
fields:
pandas_cols:
- id
- name
- age
- date
|
Python 解析:
1
2
3
4
5
|
import yaml
with open('db.yaml', 'r') as f:
cfg = yaml.safe_load(f)
print(cfg['mysql']['parameters']['pool_size'])
|
动态修改并保存配置文件 (以 INI 为例)
修改并保存配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
import configparser
# 创建管理对象
conf = configparser.ConfigParser()
# 读取配置文件
conf.read('./GloVar.ini', encoding='utf-8')
print(conf.sections())
# 删除不需要的 section
if conf.has_section("CoarseAdjust"):
# 删除该 section 下的所有内容
conf.remove_section("CoarseAdjust")
# 添加 section 并向其中添加 key 和 value
conf.add_section("CoarseAdjust")
conf.set("CoarseAdjust", "xPerAngle", "{}".format(500))
conf.set("CoarseAdjust", "yPerAngle", "160")
items = conf.items('CoarseAdjust')
print(items)
# list 里面对象是元组
with open("GloVar.ini", "w+") as f:
conf.write(f)
|
中文文本情感分析 - 数据处理流程
- 数据集:某款手机评论数据集
- 特征:
Comment:评论的文本内容
Class:评论的情感类别标签
使用 jieba 库进行中文分词,将句子切分为词语的序列
中文分词处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
import jieba
import pandas as pd
import numpy as np
# 示例数据 (假设 'data' 是包含 'Comment' 列的 DataFrame)
data = pd.DataFrame({'Comment': ['这个手机非常好', '电池续航太差了'], 'Class': [1, -1]})
# 使用匿名函数进行分词
my_cut = lambda x: ' '.join(jieba.cut(x))
# 对每条评论进行分词处理
cutted = [my_cut(line) for line in data["Comment"]]
cutted_array = np.array(cutted)
# 创建分词后的新 DataFrame
data_cutted = pd.DataFrame({
'Comment': cutted_array,
'Class': data['Class']
})
data_cutted.index = range(0, len(data_cutted))
print(data_cutted)
|
停用词处理
去除文本中没有实际意义的词语(如"的"、“了"等),以提高分析效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
def stop_word_processing(stop_file, unstoped_texts):
"""
处理停用词的函数
参数:
stop_file: 停用词文件路径
unstoped_texts: 未处理停用词的文本列表
返回:
处理后的文本字符串
"""
with open(stop_file, 'r', encoding='utf8') as f:
stop_list = [line.strip('\n') for line in f.readlines()]
outstr = ''
for line in unstoped_texts:
for word in line.split(' '):
if word not in stop_list and word != '\t':
outstr += word + ' '
return outstr
|
词云可视化
使用 wordcloud 库生成词云图,直观展示高频词汇
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
from wordcloud import WordCloud
import matplotlib.pyplot as plt
a = stop_word_processing('stopwords.txt', data_cutted['Comment'][data_cutted['Class']==1])
# 生成词云
font_path = 'path/to/your/font.ttf' # 中文字体路径
wordcloud = WordCloud(
font_path = font, # 设置字体
max_words = 100, # 词云显示的最大词数
background_color = 'white', # 背景颜色
scale=32 # 按照比例进行放大画布,使得图片清晰
)
wordcloud.generate(a)
wordcloud.to_file('wordcloud.png')
# 显示词云
plt.figure(figsize = (20, 10)) # 设置画布大小
plt.imshow(wordcloud) # 画图
plt.axis('off') # 取消坐标轴
plt.show()
|
人脸识别与门禁系统
工作流程
- 人脸检测:使用 Dlib 检测视频帧中的人脸
- 特征点定位:定位人脸的 5 个关键特征点
- 特征提取:生成 128 维的人脸特征向量
- 数据库比对:与数据库中存储的特征向量进行相似度计算
- 身份验证:基于欧氏距离阈值判断身份
- 记录上传:将识别结果上传到后端系统
初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
import dlib
import cv2
print("GPU计算是否启用:", dlib.DLIB_USE_CUDA)
mysql_host = "s1.mc.fyi"
mysql_post = 11452
mysql_user = "root"
mysql_password = "##########"
mysql_db = "attendance_system"
login_url = "http://s1.mc.fyi:11453/login/"
login_header = {'Content-Type': 'application/json'}
login_username = "201635020501"
login_password = "##########"
upload_url = "http://s1.mc.fyi:11453/access_control/"
# 判断识别阈值,欧式距离小于0.4即可判断为相似,越小越严格
threshold = 0.4
# 初始化Dlib组件
# 创建正向人脸检测器
detector = dlib.get_frontal_face_detector()
# 创建人脸 5 特征点检测器
predictor = dlib.shape_predictor("shape_predictor_5_face_landmarks.dat")
# 导入 Dlib 预先学习的人脸识别模型
face_rec = dlib.face_recognition_model_v1("dlib_face_recognition_resnet_model_v1.dat")
# OpenCV 调用摄像头
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
# 设置视频参数
cap.set(3, 480)
|
数据处理
-
欧氏距离计算
用于衡量两个人脸特征向量之间的相似度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
import numpy as np
def return_euclidean_distance(feature_1, feature_2):
"""
计算两个128维特征向量间的欧氏距离
参数:
feature_1: 第一个特征向量
feature_2: 第二个特征向量
返回:
欧氏距离值
"""
feature_1 = np.array(feature_1)
feature_2 = np.array(feature_2)
dist = np.sqrt(np.sum(np.square(feature_1 - feature_2)))
return dist
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
from datetime import datetime, timedelta
import pymysql
def get_user_face_data(get_time, data):
"""
从数据库获取人脸数据(缓存五分钟)
参数:
get_time: 上次获取数据的时间
data: 上次获取的数据内容
返回:
更新时间, 数据内容
"""
if get_time == "" or get_time < (datetime.now() - timedelta(minutes=5)):
# 连接数据库
conn = pymysql.connect(
host=mysql_host,
port=mysql_post,
user=mysql_user,
password=mysql_password,
db=mysql_db,
charset='utf8'
)
# 创建游标
cur = conn.cursor()
sql = """
SELECT user_id, username, first_name, last_name, features
FROM users_userface
INNER JOIN users_user
ON users_userface.user_id = users_user.id;
"""
# 执行语句
cur.execute(sql)
result = cur.fetchall()
# 关闭连接池
cur.close()
conn.close()
return datetime.now(), result
return get_time, data
# 上传图片
def upload_image(save_name, user_id, euclidean_distance):
with open('temp/' + save_name, mode="rb")as f: # 打开文件
file = {
"file": (save_name, f.read()) # 引号的file是接口的字段,后面的是文件的名称、文件的内容
}
upload_post_result = requests.post(url=upload_url + '?user_id=' + str(user_id) + '&euclidean_distance=' + str(euclidean_distance),
headers={
'Authorization': 'JWT ' + login_token
},
files=file)
print("[BACKEND]", json.loads(upload_post_result.text)['detail'])
# 保存图片
def save_image(image):
save_name = datetime.now().strftime("%Y-%m-%d %H-%M-%S") + '.jpg'
cv2.imwrite('temp/' + save_name, image)
return save_name
|
检测流程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
# 初始化数组等
get_time = datetime.strptime("2020-01-01", "%Y-%m-%d")
calculate_time = datetime.strptime("2020-01-01", "%Y-%m-%d")
data = ""
face_descriptor_old = ""
two_sec_min_euclidean_distance_list = []
two_sec_min_user_info_list = []
two_sec_min_img_list = []
name = ""
color = (0, 0, 0)
# 摄像头循环
while cap.isOpened():
flag, img_read = cap.read()
kk = cv2.waitKey(1)
img_gray = cv2.cvtColor(img_read, cv2.COLOR_RGB2GRAY)
# 人脸数 faces
faces = detector(img_gray, 0)
# 使用的字体
font = cv2.FONT_HERSHEY_COMPLEX
if len(faces) != 0:
# 展示矩形框
for k, d in enumerate(faces):
# 判断是否超出边界
if d.right() > 640 or d.bottom() > 480 or d.left() < 0 or d.top() < 0:
name = "请保证人脸不要超出边界。"
color = (255, 0, 0)
# 判断是否多个人
elif len(faces) != 1:
name = "请保证摄像头范围内只存在一人。"
color = (255, 0, 0)
else:
# 采用 Dlib 的人脸5特征点检测器
shape = predictor(img_gray, d)
# 生成单张人脸图像的128D特征
face_descriptor = face_rec.compute_face_descriptor(img_read, shape)
# 从数据库获取人脸数据(缓存五分钟)
get_time, data = get_user_face_data(get_time, data)
# 计算欧氏距离
euclidean_distance_list = []
for one_data in data:
euclidean_distance_list.append(return_euclidean_distance(json.loads(one_data[4]), face_descriptor))
"""
if face_descriptor_old != "":
if return_euclidean_distance(face_descriptor_old, face_descriptor) > 0.4:
print("检测到摄像头前换人了。")
name = ""
two_sec_min_euclidean_distance_list = []
two_sec_min_user_info_list = []
two_sec_min_img_list = []
face_descriptor_old = face_descriptor
"""
# 每两秒保存一次
if calculate_time < (datetime.now() - timedelta(seconds=2)):
if two_sec_min_euclidean_distance_list:
if min(two_sec_min_euclidean_distance_list) < threshold:
index = two_sec_min_euclidean_distance_list.index(
min(two_sec_min_euclidean_distance_list)
)
print("[MESSAGE]识别成功,你可能是:",
two_sec_min_user_info_list[index][2],
two_sec_min_user_info_list[index][3],
",欧式距离:", min(two_sec_min_euclidean_distance_list), "。")
save_name = save_image(two_sec_min_img_list[index])
upload_image(save_name, two_sec_min_user_info_list[index][0], min(two_sec_min_euclidean_distance_list))
name = two_sec_min_user_info_list[index][2] + two_sec_min_user_info_list[index][3]
color = (0, 255, 0)
else:
print("[MESSAGE]识别失败,请完善自己账户人脸信息或联系管理员!")
name = "识别失败,请完善自己账户人脸信息或联系管理员!"
color = (255, 0, 0)
two_sec_min_euclidean_distance_list = []
two_sec_min_user_info_list = []
two_sec_min_img_list = []
calculate_time = datetime.now()
else:
two_sec_min_euclidean_distance_list.append(min(euclidean_distance_list))
two_sec_min_user_info_list.append(data[euclidean_distance_list.index(min(euclidean_distance_list))])
two_sec_min_img_list.append(img_read)
# 画边框
cv2.rectangle(
img_read,
tuple([d.left(), d.top()]),
tuple([d.right(), d.bottom()]),
(255, 255, 255), 2
)
# 文字位置
pos_namelist = tuple([d.left() + 5, d.bottom() - 25])
img_read = change_cv2_draw(img_read, name, pos_namelist, 13, color)
cv2.imshow("Dormitory Access Control System(Camera)", img_read)
|
目标检测
- 一、网络模型
- 1.1 VGG Backbone
- 1.2 Extra Layers
- 1.3 Multi-box Layers
- 1.4 SSD 模型类
- 二、先验框生成与匹配
- 三、损失函数
- 四、L2 正则化
- 五、训练处理
- 5.1 位置坐标转换
- 5.2 IOU 计算
- 5.3 位置编码和解码
- 5.4 先验框匹配
- 5.5 NMS 抑制
- 六、Detection 函数
参考资料
https://zhuanlan.zhihu.com/p/79854543
基本概念
1024x3x3 表示的是 1024 个 3x3 大小的卷积核
stride 是步幅,默认为 1
padding 是填充,默认为 0
特征图尺寸的计算公式为:
$$
\lfloor (输入图像大小-卷积核大小+2*padding)/stride+1 \rfloor
$$
Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
表示输入通道数为 3,输出通道数为 64,卷积核大小为 3x3,两个维度上的步幅均为 1,填充也为 1
1x1 卷积层相当于全连接层,通常用于调整网络层的 通道数量 和 控制模型复杂性
由上可知,在步幅为 1 的情况下
若要保证前后的特征图大小不变,则有公式
$$
padding=(卷积核大小-1)/2
$$
网络模型
整个网络是由三大部分组成:
1、VGG Backbone
2、Extra Layers
3、Multi-box Layer
先验框生成与匹配
匹配原则:
• 通常称与 gt 匹配的 prior 为正样本, 反之, 若某一个 prior 没有与任何一个 gt 匹配, 则为负样本
• 某个 gt 可以和多个 prior 匹配, 而每个 prior 只能和一个 gt 进行匹配
• 如果多个 gt 和某一个 prior 的 IOU 均大于阈值, 那么 prior 只与 IOU 最大的那个进行匹配
损失函数
两个损失函数加权求和:
• 位置损失函数 $L_{loc}$:smoothL1 损失函数
• 置信度损失函数 $L{conf}$:交叉熵损失函数
$$
L(x, c, l, g) = \dfrac{1}{N}(L_{conf}(x, c) + \alpha L_{loc}(x, l, g))
$$
其中:
$N$ 是先验框的正样本数量
$c$ 为类别置信度预测值
$l$ 为先验框的所对应边界框的位置预测值
$g$ 为 ground truth 的位置参数
位置损失函数:
针对所有的正样本,采用 Smooth L1 Loss,位置信息都是 encode 之后的位置信息
$$
smooth_{L_{1}}(x)=
\left{\begin{matrix}
0.5x^{2} &\text{if} \left|x\right|<1
\ \left|x\right|-0.5 &\text{otherwise}
\end{matrix}\right.
$$
置信度损失函数:
首先需要使用 hard negative mining 将正负样本按照 1:3 的比例把负样本抽样出来, 抽样的方法是:针对所有 batch 的 confidence, 按照置信度误差进行降序排列, 取出前 top_k 个负样本
编程:
• Reshape 所有 batch 中的 conf
1
|
batch_conf = conf_data.view(-1, self.num_classes)
|
• 置信度误差越大, 实际上就是预测背景的置信度越小
• 把所有 conf 进行 logsoftmax 处理(均为负值), 预测的置信度越小, 则 logsoftmax 越小, 取绝对值, 则 |logsoftmax│ 越大, 降序排列 -logsoftmax, 取前 top_k 的负样本
训练处理
Bounding Box 的位置表示方式有两种:
• A:$(x_{min}, y_{min}, x_{max}, y_{max})$
• B:$(x_{c}, y_{c}, w, h)$
预测和真实的边界框有一个转换关系
先验框位置:$ d = (d^{cx}, d^{cy}, d^{w}, d^{h}) $
真实框位置:$ g = (g^{cx}, g^{cy}, g^{w}, g^{h}) $
variance 用于调整检测值
$ \hat{g}^{cx}{j} = \dfrac{(g^{cx}{j}-d^{cx}{i})}{d^{w}{i} variance[0]} $
$ \hat{g}^{cy}{j} = \dfrac{(g^{cy}{j}-d^{cy}{i})}{d^{h}{i} variance[1]} $
$ \hat{g}^{w}{j} = log(\dfrac{g^{w}{j}}{d^{w}_{i}})/{variance[2]} $
$ \hat{g}^{h}{j} = log(\dfrac{g^{h}{j}}{d^{h}_{i}})/{variance[3]} $
测试处理
从预测值中得到边界框的真实值:
$ g^{cx}_{predict} = d^{w}*(variance[0]*l^{cx})+d^{cx} $
$ g^{cy}_{predict} = d^{h}*(variance[1]*l^{cy})+d^{cy} $
$ g^{w}_{predict} = d^{w}*exp(variance[2]*l^{w}) $
$ g^{h}_{predict} = d^{h}*exp(variance[3]*l^{h}) $
📂 数据分析 – 小象学院
Airbub 房屋出租回报
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_theme()
# ---------- 读取所需列 ----------
listingsdata = pd.read_csv(
'数据文件路径/listings.csv',
usecols=[
'property_type', 'room_type', 'accommodates',
'bedrooms', 'price']
)
listingsdata.head(5)
# ---------- 数据类型转换:将 price 从字符串转为数值 ----------
# 方法一:使用列表推导式
price_list = [
float(item.replace(',', '')[1:]) # 去掉千分位和美元符号
for item in listingsdata['price']
]
listingsdata['price'] = price_list
# 方法二:apply(大数据量时更高效)
def price_convert(x: str) -> float:
"""去除千分位和前缀符号后转为 float"""
return float(x.replace(',', '')[1:])
listingsdata['price'] = listingsdata['price'].apply(price_convert)
# ---------- 数据可视化 ----------
# 1. 绘制箱线图
plt.figure(figsize=(16, 9))
sns.boxplot(
x='property_type',
y='price',
data=listingsdata
)
# 将横坐标的刻度旋转 90°
plt.xticks(rotation=90)
plt.title('不同房屋类型的租金分布')
plt.show()
# 2. 分类数据绘图(默认 kind='strip',可改为 'box'、'boxen'、'violin' 等)
sns.catplot(
x='accommodates',
y='price',
data=listingsdata,
color='m',
estimator=np.median,
height=8,
aspect=1.35
)
plt.title('可容纳人数与租金的中位数关系')
plt.show()
# 3、绘制热力图
plt.figure(figsize=(16, 9))
sns.heatmap(
listingsdata.groupby(['property_type', 'bedrooms'])['price']
.mean()
.unstack(),
# 分组求和
# 先按照'property_type', 'bedrooms'列分组
# 再取出 price 求和
annot = True, #用于显示每个方形内部的数值
fmt = '.0f', #控制数值小数的位数
cmap='YlGnBu'
)
plt.title('房屋类型 / 卧室数 与平均租金热力图')
plt.show()
|
金融欺诈行为检测
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# 按类型和欺诈标志分组绘制条形图
ax = raw_data.groupby(['type', 'isFraud']).size().plot(kind='bar')
ax.set_title('欺诈交易分布')
ax.set_xlabel('交易类型与欺诈标志')
ax.set_ylabel('计数')
# 在条形上标注数字
for p in ax.patches:
ax.annotate(
# 设置金额格式,添加千分位符
str(format(int(p.get_height()), ',d')),
# 设置标注位置
(p.get_x(), p.get_height()*1.01)
)
|
多子图分析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
fig, axs = plt.subplots(2, 2, figsize=(10, 10))
transfer_data = raw_data[raw_data["type"] == "TRANSFER"]
# 左上子图:金额箱线图
a = sns.boxplot(x="isFlaggedFraud", y="amount", data=transfer_data, ax=axs[0][0])
axs[0][0].set_yscale("log") # 设置对数刻度(默认是linear)
# 右上子图:目标账户旧余额箱线图
b = sns.boxplot(
x="isFlaggedFraud", y="oldbalanceDest", data=transfer_data, ax=axs[0][1]
)
axs[0][1].set_ylim((0, 0.5e8))
# 左下子图:源账户旧余额箱线图
c = sns.boxplot(x="isFlaggedFraud", y="oldbalanceOrg", data=transfer_data, ax=axs[1][0])
axs[1][0].set_ylim((0, 3e7))
# 右下子图:散点图与回归线
d = sns.regplot(
x="oldbalanceOrg",
y="amount",
data=transfer_data[transfer_data["isFlaggedFraud"] == 1],
ax=axs[1][1],
)
plt.show()
|
特征编码与模型训练
one-hot 编码
1
2
3
4
5
6
|
from sklearn import preprocessing
# 对交易类型进行标签编码
type_label_encoder = preprocessing.LabelEncoder()
type_category = type_label_encoder.fit_transform(used_data["type"].values.copy())
print(type_label_encoder.classes_) # 显示编码映射:CASH_OUT=0, TRANSFER=1
|
训练验证模型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_curve, auc
# 划分训练集和测试集,测试集占比30%
X_train, X_test, y_train, y_test = train_test_split(
X_undersample, y_undersample, test_size=0.3, random_state=0
)
# 逻辑回归模型
lr_model = LogisticRegression()
# 训练模型
lr_model.fit(X_train, y_train)
# 对测试数据进行预测
y_pred_score = lr_model.predict_proba(X_test)
# 绘制ROC曲线,感兴趣详细可看:https://zhuanlan.zhihu.com/p/81202617
# ROC曲线横轴是fpr,纵轴是tpr
# fpr表示:模型预测为1但预测错误的个数,占所有实际label为0的样本数的比例
# tpr表示:模型预测为1且预测正确的个数,占所有实际label为1的样本数的比例
# 所以图像尽可能向左上角凸出比较好
fpr, tpr, thresholds = roc_curve(y_test, y_pred_score[:,1])
# 获取AUC的值
roc_auc = auc(fpr, tpr)
# AUC表示Roc曲线下的面积,介于0.1和1之间
# Auc的值越大表示模型分类效果越好
plt.title("Receiver Operating Characteristic")
plt.plot(fpr, tpr, "b", label="AUC = %0.2f" % roc_auc)
plt.legend(loc="lower right")
plt.plot([0, 1], [0, 1], "r--")
plt.xlim([-0.1, 1.0])
plt.ylim([-0.1, 1.01])
plt.xlabel("False Positive Rate")
plt.ylabel("True Positive Rate")
plt.show()
|
玻璃分类预测
多变量数据可视化
1
2
3
4
|
# 绘制特征间的散点图矩阵
plt.figure()
sns.pairplot(data[feature_names])
plt.show()
|
交叉验证选择超参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
# knn 模型需要确定 k 的值
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split, cross_val_score
# 通过交叉验证选择最优 K 值
k_range = range(1, 31)
cv_scores = []
print("交叉验证结果:")
for k in k_range:
knn = KNeighborsClassifier(n_neighbors=k)
scores = cross_val_score(knn, X_train, y_train, cv=7, scoring="accuracy")
score_mean = scores.mean()
cv_scores.append(score_mean)
print("%i: %.4f" % (k, score_mean))
# 选择最佳 K 值
best_k = np.argmax(cv_scores) + 1
print("最优 K 值:", best_k)
# 绘制准确率随 K 值变化曲线
plt.plot(k_range, cv_scores)
plt.xlabel("K")
plt.ylabel("Accuracy")
plt.show()
|
循环取得最优的 k,但是模型在训练集上的表现最优,测试集上则不一定
所以有时候需要 增加更多的数据,并使用其他模型验证结果
骨科疾病预测
类别标签编码
1
2
3
4
|
all_data['label'] = all_data['class'].map(
{'Abnormal':1, 'Normal':0}
) #将Normal映射为0,Abnormal映射为1
all_data.head(5)
|
效果等同于之前使用 sklearn.preprocessing.LabelEncoder()
模型持久化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
import pickle
# 保存模型
model_file = "./model_file/orthopedic_model.pkl"
with open(model_file, "wb") as f:
pickle.dump(best_model_name, f)
# 加载模型进行预测
with open(model_file, "rb") as f:
trained_model = pickle.load(f)
# 随机抽样预测
n = 5
random_sample_data = all_data.sample(n)
predictions = trained_model.predict(random_sample_data.iloc[:, :6].values)
|
流行美剧对婴儿姓名影响分析
统计所有姓名出现的次数
1
2
3
4
5
6
7
|
# 方法一
# 通过循环遍历数据,并利用字典统计(效率较低)
names_dict = {}
for index, row in data.iterrows():
name = row["Name"]
count = row["Count"]
names_dict[name] = names_dict.get(name, 0) + count
|
1
2
3
4
5
6
7
8
9
10
11
12
|
# 方法二
# 按姓名分组并求和
name_counts = data.groupby("Name")["Count"].sum().sort_values(ascending=False)
names_dict = dict(name_counts)
# 或者利用aggregate聚合函数,效果一样,速度都是比较快的
# data.groupby('Name')['Count'] \
# .aggregate(['sum']) \
# .sort_values(by='sum', ascending=False)
# 查看婴儿姓名为 Mary 的个数
names_dict.get("Mary")
|
使用 Collections 计数器
1
2
3
4
5
6
7
|
from collections import Counter
# 获取前10个最常见名字
top_10 = Counter(names_dict).most_common(10)
print("全美流行婴儿名字Top10:")
for name, count in top_10:
print(f"姓名:{name} -> 数量:{count:,}")
|
统计名字的平均长度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
# 计算名字长度
data["NameLen"] = data["Name"].apply(len)
# 按年份和性别分组计算平均长度
avg_lengths = data.groupby(["Year", "Gender"])["NameLen"].mean()
female_avg_length = avg_lengths[:, "F"].tolist() # 女性平均长度
male_avg_length = avg_lengths[:, "M"].tolist() # 男性平均长度
# 可视化
years = range(1880, 2015)
plt.figure(figsize=(10, 6))
plt.plot(years, female_avg_length, label="女性名字平均长度", color="r")
plt.plot(years, male_avg_length, label="男性名字平均长度", color="b")
plt.xlim(1880, 2014)
plt.xlabel("年份")
plt.ylabel("名字长度")
plt.title("名字平均长度变化趋势")
plt.legend(loc='best', frameon=True, borderpad=1, borderaxespad=1)
plt.show()
|
要研究影视作品对婴儿姓名的影响
可以结合影视作品的开播时间,查看与角色同名的婴儿的数量趋势来判断