3.3链接表 结构 P79页

用一个类图来表示EmployeeNode类的结构,展示其属性和关系:
plaintext
+-----------------------------------------+
|             EmployeeNode                |
+-----------------------------------------+
| - emp_no: int                           |
| - birth_date: date                      |
| - first_name: str                       |
| - last_name: str                        |
| - gender: str                           |
| - hire_date: date                       |
| - departments: list                     |
| - manager_of: list                      |
| - salaries: list                        |
| - titles: list                          |
+-----------------------------------------+
| + __init__(emp_no, birth_date, ...)     |
| + __repr__(): str                       |
+-----------------------------------------+|              |             |v              v             v
+----------------+----------------+----------------+
|  departments   |   manager_of   |   salaries     |
|  (部门关系)    |  (管理关系)    |  (薪资历史)    |
+----------------+----------------+----------------+|v
+----------------+
|    titles      |
|  (职位历史)    |
+----------------+这个图表展示了:
EmployeeNode类的核心属性:
基本信息:员工编号 (emp_no)、出生日期 (birth_date)、姓名 (first_name/last_name)、性别 (gender)、入职日期 (hire_date)
关系属性:部门列表 (departments)、管理部门列表 (manager_of)、薪资历史 (salaries)、职位历史 (titles)
类的方法:
构造方法__init__用于初始化对象
__repr__方法用于返回对象的字符串表示
关系说明:
departments:存储员工所属的部门及任职时间信息
manager_of:存储员工管理的部门及管理时间信息
salaries:存储员工的薪资历史记录
titles:存储员工的职位变迁历史
这些属性共同构成了一个员工的完整档案,包括基本信息和各种职业相关的关系信息。
程序类关系总览图
plaintext
┌───────────────────┐       ┌───────────────────┐       ┌───────────────────┐
│   EmployeeNode    │       │   DepartmentNode  │       │    SalaryNode     │
├───────────────────┤       ├───────────────────┤       ├───────────────────┤
│ - emp_no          │       │ - dept_no         │       │ - emp_no          │
│ - birth_date      │       │ - dept_name       │       │ - salary          │
│ - first_name      │       │ - employees       │◄──────┤ - from_date       │
│ - last_name       │       │ - managers        │       │ - to_date         │
│ - gender          │       └───────────────────┘       └───────────────────┘
│ - hire_date       │                ▲                        ▲
│ - departments     │◄───────────────┘                        │
│ - manager_of      │                                         │
│ - salaries        │◄────────────────────────────────────────┘
│ - titles          │◄────────────────────────────────────────┐
└───────────────────┘                                         ││
┌───────────────────┐       ┌───────────────────┐             │
│    TitleNode      │       │   EmployeeGraph   │             │
├───────────────────┤       ├───────────────────┤             │
│ - emp_no          │       │ - connection      │             │
│ - title           │       │ - employees       │◄────────────┘
│ - from_date       │       │ - departments     │
│ - to_date         │       │ - employees_by_name│
└───────────────────┘       └───────────────────┘▲                         ▲│                         │└─────────────────────────┘(被管理/被构建)图表说明
1. 核心节点类(数据实体)
EmployeeNode(员工节点)
存储员工基本信息(编号、姓名、出生日期等)。
通过列表属性关联其他实体:
departments:关联所属的DepartmentNode及任职时间。
manager_of:关联管理的DepartmentNode及管理时间。
salaries:关联对应的SalaryNode(薪资历史)。
titles:关联对应的TitleNode(职位历史)。
DepartmentNode(部门节点)
存储部门基本信息(编号、名称)。
通过列表属性关联员工:
employees:关联属于该部门的EmployeeNode及任职时间。
managers:关联管理该部门的EmployeeNode及管理时间。
SalaryNode(薪资节点)
存储单条薪资记录(金额、生效 / 截止日期),通过emp_no关联EmployeeNode。
TitleNode(职位节点)
存储单条职位记录(职位名称、生效 / 截止日期),通过emp_no关联EmployeeNode。
2. 管理类(核心逻辑)
EmployeeGraph(员工关系图)
负责数据库连接、数据加载和关系构建。
存储所有节点的集合:
employees:以emp_no为键的EmployeeNode字典。
departments:以dept_no为键的DepartmentNode字典。
employees_by_name:以姓名为键的EmployeeNode字典(方便按姓名查询)。
核心方法:通过数据库查询,将分散的节点数据关联起来(如员工 - 部门、员工 - 薪资等关系)。
3. 关系总结
一对一关联:SalaryNode和TitleNode通过emp_no绑定到特定EmployeeNode。
一对多关联:
一个DepartmentNode对应多个EmployeeNode(员工)和多个EmployeeNode(经理)。
一个EmployeeNode对应多个DepartmentNode(多部门任职)、多个SalaryNode(薪资变动)、多个TitleNode(职位变动)。
管理关系:EmployeeGraph是整个程序的 “引擎”,负责初始化所有节点并建立它们之间的关联。
通过这个图表,可以直观理解程序的设计思路:用面向对象的方式将数据库中的表结构映射为节点类,再通过EmployeeGraph类构建节点间的关联,最终形成一个可查询的员工关系网络。
这段代码实现了一个员工信息管理系统,通过 MySQL 数据库构建了一个员工关系图,能够加载和展示员工、部门、薪资和职位等信息及其关系。下面我来逐句讲解:
python
运行
import mysql.connector
from mysql.connector import Error
from datetime import date
导入所需的库:mysql.connector用于连接 MySQL 数据库,Error用于捕获数据库错误,date用于处理日期数据
python
运行
# 首先定义所有节点类
class EmployeeNode:def __init__(self, emp_no, birth_date, first_name, last_name, gender, hire_date):self.emp_no = emp_noself.birth_date = birth_dateself.first_name = first_nameself.last_name = last_nameself.gender = genderself.hire_date = hire_dateself.departments = []  # 员工所属的部门列表self.manager_of = []   # 员工管理的部门列表self.salaries = []     # 员工的薪资历史self.titles = []       # 员工的职位历史
定义EmployeeNode类,用于表示员工节点
__init__方法初始化员工的基本信息
同时初始化了几个列表属性,用于存储与该员工相关的部门、管理关系、薪资和职位信息
python
运行def __repr__(self):return f"<Employee {self.emp_no}: {self.first_name} {self.last_name}>"
定义__repr__方法,用于返回员工对象的字符串表示,方便调试和打印
python
运行
class DepartmentNode:def __init__(self, dept_no, dept_name):self.dept_no = dept_noself.dept_name = dept_nameself.employees = []  # 部门中的员工列表self.managers = []   # 部门的经理列表
定义DepartmentNode类,用于表示部门节点
__init__方法初始化部门的基本信息
初始化了两个列表属性,用于存储部门中的员工和经理信息
python
运行def __repr__(self):return f"<Department {self.dept_no}: {self.dept_name}>"
定义__repr__方法,用于返回部门对象的字符串表示
python
运行
class SalaryNode:def __init__(self, emp_no, salary, from_date, to_date):self.emp_no = emp_noself.salary = salaryself.from_date = from_dateself.to_date = to_date
定义SalaryNode类,用于表示薪资节点
__init__方法初始化薪资信息,包括员工编号、薪资数额、生效日期和截止日期
python
运行def __repr__(self):return f"<Salary {self.salary} from {self.from_date} to {self.to_date}>"
定义__repr__方法,用于返回薪资对象的字符串表示
python
运行
class TitleNode:def __init__(self, emp_no, title, from_date, to_date):self.emp_no = emp_noself.title = titleself.from_date = from_dateself.to_date = to_date
定义TitleNode类,用于表示职位节点
__init__方法初始化职位信息,包括员工编号、职位名称、生效日期和截止日期
python
运行def __repr__(self):return f"<Title {self.title} from {self.from_date} to {self.to_date}>"
定义__repr__方法,用于返回职位对象的字符串表示
python
运行
# 然后定义EmployeeGraph类
class EmployeeGraph:def __init__(self, host, database, user, password):try:self.connection = mysql.connector.connect(host=host,database=database,user=user,password=password)self.employees = {}  # 存储员工对象,键为员工编号self.departments = {}  # 存储部门对象,键为部门编号self.employees_by_name = {}  # 存储员工对象,键为员工姓名(小写)except Error as e:print(f"Error connecting to MySQL: {e}")
定义EmployeeGraph类,用于构建和管理员工关系图
__init__方法接收数据库连接参数,尝试连接 MySQL 数据库
初始化了三个字典属性,用于存储员工、部门对象,以及按姓名索引的员工对象
使用try-except捕获数据库连接错误
python
运行def load_employees(self):query = "SELECT * FROM employees"cursor = self.connection.cursor(dictionary=True)cursor.execute(query)for row in cursor:emp = EmployeeNode(emp_no=row['emp_no'],birth_date=row['birth_date'],first_name=row['first_name'],last_name=row['last_name'],gender=row['gender'],hire_date=row['hire_date'])self.employees[row['emp_no']] = empname_key = f"{row['first_name']} {row['last_name']}".lower()self.employees_by_name[name_key] = empcursor.close()
定义load_employees方法,用于从数据库加载员工信息
执行 SQL 查询获取所有员工记录
为每条记录创建EmployeeNode对象,并存储到employees和employees_by_name字典中
最后关闭游标
python
运行def load_departments(self):query = "SELECT * FROM departments"cursor = self.connection.cursor(dictionary=True)cursor.execute(query)for row in cursor:dept = DepartmentNode(dept_no=row['dept_no'],dept_name=row['dept_name'])self.departments[row['dept_no']] = deptcursor.close()
定义load_departments方法,用于从数据库加载部门信息
执行 SQL 查询获取所有部门记录
为每条记录创建DepartmentNode对象,并存储到departments字典中
最后关闭游标
python
运行def build_dept_emp_relationships(self):query = """SELECT de.emp_no, de.dept_no, de.from_date, de.to_date, d.dept_name FROM dept_emp deJOIN departments d ON de.dept_no = d.dept_no"""cursor = self.connection.cursor(dictionary=True)cursor.execute(query)for row in cursor:emp = self.employees.get(row['emp_no'])dept = self.departments.get(row['dept_no'])if emp and dept:dept.employees.append({'employee': emp,'from_date': row['from_date'],'to_date': row['to_date']})emp.departments.append({'department': dept,'from_date': row['from_date'],'to_date': row['to_date']})cursor.close()
定义build_dept_emp_relationships方法,用于构建部门和员工之间的关系
执行 SQL 查询获取员工与部门的关联信息
对每条记录,获取对应的员工和部门对象
如果两者都存在,则在部门对象的employees列表和员工对象的departments列表中添加关联信息
最后关闭游标
python
运行def build_dept_manager_relationships(self):query = """SELECT dm.emp_no, dm.dept_no, dm.from_date, dm.to_date, d.dept_name FROM dept_manager dmJOIN departments d ON dm.dept_no = d.dept_no"""cursor = self.connection.cursor(dictionary=True)cursor.execute(query)for row in cursor:emp = self.employees.get(row['emp_no'])dept = self.departments.get(row['dept_no'])if emp and dept:dept.managers.append({'manager': emp,'from_date': row['from_date'],'to_date': row['to_date']})emp.manager_of.append({'department': dept,'from_date': row['from_date'],'to_date': row['to_date']})cursor.close()
定义build_dept_manager_relationships方法,用于构建部门和经理之间的关系
执行 SQL 查询获取经理与部门的关联信息
对每条记录,获取对应的员工 (经理) 和部门对象
如果两者都存在,则在部门对象的managers列表和员工对象的manager_of列表中添加关联信息
最后关闭游标
python
运行def load_salaries(self):query = "SELECT * FROM salaries"cursor = self.connection.cursor(dictionary=True)cursor.execute(query)for row in cursor:emp = self.employees.get(row['emp_no'])if emp:salary = SalaryNode(emp_no=row['emp_no'],salary=row['salary'],from_date=row['from_date'],to_date=row['to_date'])emp.salaries.append(salary)cursor.close()
定义load_salaries方法,用于加载员工的薪资信息
执行 SQL 查询获取所有薪资记录
对每条记录,获取对应的员工对象
如果员工存在,则创建SalaryNode对象,并添加到员工的salaries列表中
最后关闭游标
python
运行def load_titles(self):query = "SELECT * FROM titles"cursor = self.connection.cursor(dictionary=True)cursor.execute(query)for row in cursor:emp = self.employees.get(row['emp_no'])if emp:title = TitleNode(emp_no=row['emp_no'],title=row['title'],from_date=row['from_date'],to_date=row['to_date'])emp.titles.append(title)cursor.close()
定义load_titles方法,用于加载员工的职位信息
执行 SQL 查询获取所有职位记录
对每条记录,获取对应的员工对象
如果员工存在,则创建TitleNode对象,并添加到员工的titles列表中
最后关闭游标
python
运行def build_graph(self):self.load_employees()self.load_departments()self.build_dept_emp_relationships()self.build_dept_manager_relationships()self.load_salaries()self.load_titles()
定义build_graph方法,用于构建完整的员工关系图
依次调用前面定义的各个方法,加载和关联所有数据
python
运行def close(self):if hasattr(self, 'connection') and self.connection.is_connected():self.connection.close()
定义close方法,用于关闭数据库连接
检查连接是否存在且处于连接状态,如果是则关闭连接
python
运行
# 使用示例
if __name__ == "__main__":# 初始化并构建图graph = EmployeeGraph(host="localhost",database="employees",user="root",password="root")graph.build_graph()
当脚本作为主程序运行时的代码
创建EmployeeGraph对象,传入数据库连接参数
调用build_graph方法构建员工关系图
python
运行# 示例查询if 10001 in graph.employees:emp = graph.employees[10001]print(emp)print("所属部门:", emp.departments)print("管理的部门:", emp.manager_of)print("薪资历史:", emp.salaries)print("职位历史:", emp.titles)
示例查询代码,检查员工编号为 10001 的员工是否存在
如果存在,则打印该员工的基本信息、所属部门、管理的部门、薪资历史和职位历史
python
运行# 分析职业发展print(f"\n职业发展分析 - {emp.first_name} {emp.last_name}")print(f"入职时间: {emp.hire_date}")print(f"当前职位: {emp.titles[-1].title if emp.titles else '无记录'}")print(f"当前部门: {emp.departments[-1]['department'].dept_name if emp.departments else '无记录'}")if emp.salaries:growth = (emp.salaries[-1].salary / emp.salaries[0].salary) - 1print(f"薪资增长: {emp.salaries[0].salary}{emp.salaries[-1].salary} (增长{growth:.1%})")
对员工进行职业发展分析
打印入职时间、当前职位 (假设最后一个职位是当前职位)、当前部门 (假设最后一个部门是当前部门)
计算并打印薪资增长情况
python
运行# 关闭连接graph.close()最后调用close方法关闭数据库连接
这个程序通过面向对象的方式构建了一个员工关系图,将数据库中的员工、部门、薪资和职位信息及其关系映射为内存中的对象模型,方便进行各种查询和分析操作。
import mysql.connector
from mysql.connector import Error
from datetime import date
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd# 首先定义所有节点类
class EmployeeNode:def __init__(self, emp_no, birth_date, first_name, last_name, gender, hire_date):self.emp_no = emp_noself.birth_date = birth_dateself.first_name = first_nameself.last_name = last_nameself.gender = genderself.hire_date = hire_dateself.departments = []self.manager_of = []self.salaries = []self.titles = []def __repr__(self):return f"<Employee {self.emp_no}: {self.first_name} {self.last_name}>"class DepartmentNode:def __init__(self, dept_no, dept_name):self.dept_no = dept_noself.dept_name = dept_nameself.employees = []self.managers = []def __repr__(self):return f"<Department {self.dept_no}: {self.dept_name}>"class SalaryNode:def __init__(self, emp_no, salary, from_date, to_date):self.emp_no = emp_noself.salary = salaryself.from_date = from_dateself.to_date = to_datedef __repr__(self):return f"<Salary {self.salary} from {self.from_date} to {self.to_date}>"class TitleNode:def __init__(self, emp_no, title, from_date, to_date):self.emp_no = emp_noself.title = titleself.from_date = from_dateself.to_date = to_datedef __repr__(self):return f"<Title {self.title} from {self.from_date} to {self.to_date}>"# 然后定义EmployeeGraph类
class EmployeeGraph:def __init__(self, host, database, user, password):try:self.connection = mysql.connector.connect(host=host,database=database,user=user,password=password)self.employees = {}self.departments = {}self.employees_by_name = {}except Error as e:print(f"Error connecting to MySQL: {e}")def load_employees(self):query = "SELECT * FROM employees"cursor = self.connection.cursor(dictionary=True)cursor.execute(query)for row in cursor:emp = EmployeeNode(emp_no=row['emp_no'],birth_date=row['birth_date'],first_name=row['first_name'],last_name=row['last_name'],gender=row['gender'],hire_date=row['hire_date'])self.employees[row['emp_no']] = empname_key = f"{row['first_name']} {row['last_name']}".lower()self.employees_by_name[name_key] = empcursor.close()def load_departments(self):query = "SELECT * FROM departments"cursor = self.connection.cursor(dictionary=True)cursor.execute(query)for row in cursor:dept = DepartmentNode(dept_no=row['dept_no'],dept_name=row['dept_name'])self.departments[row['dept_no']] = deptcursor.close()def build_dept_emp_relationships(self):query = """SELECT de.emp_no, de.dept_no, de.from_date, de.to_date, d.dept_name FROM dept_emp deJOIN departments d ON de.dept_no = d.dept_no"""cursor = self.connection.cursor(dictionary=True)cursor.execute(query)for row in cursor:emp = self.employees.get(row['emp_no'])dept = self.departments.get(row['dept_no'])if emp and dept:dept.employees.append({'employee': emp,'from_date': row['from_date'],'to_date': row['to_date']})emp.departments.append({'department': dept,'from_date': row['from_date'],'to_date': row['to_date']})cursor.close()def build_dept_manager_relationships(self):query = """SELECT dm.emp_no, dm.dept_no, dm.from_date, dm.to_date, d.dept_name FROM dept_manager dmJOIN departments d ON dm.dept_no = d.dept_no"""cursor = self.connection.cursor(dictionary=True)cursor.execute(query)for row in cursor:emp = self.employees.get(row['emp_no'])dept = self.departments.get(row['dept_no'])if emp and dept:dept.managers.append({'manager': emp,'from_date': row['from_date'],'to_date': row['to_date']})emp.manager_of.append({'department': dept,'from_date': row['from_date'],'to_date': row['to_date']})cursor.close()def load_salaries(self):query = "SELECT * FROM salaries"cursor = self.connection.cursor(dictionary=True)cursor.execute(query)for row in cursor:emp = self.employees.get(row['emp_no'])if emp:salary = SalaryNode(emp_no=row['emp_no'],salary=row['salary'],from_date=row['from_date'],to_date=row['to_date'])emp.salaries.append(salary)cursor.close()def load_titles(self):query = "SELECT * FROM titles"cursor = self.connection.cursor(dictionary=True)cursor.execute(query)for row in cursor:emp = self.employees.get(row['emp_no'])if emp:title = TitleNode(emp_no=row['emp_no'],title=row['title'],from_date=row['from_date'],to_date=row['to_date'])emp.titles.append(title)cursor.close()def build_graph(self):self.load_employees()self.load_departments()self.build_dept_emp_relationships()self.build_dept_manager_relationships()self.load_salaries()self.load_titles()def close(self):if hasattr(self, 'connection') and self.connection.is_connected():self.connection.close()# 使用示例
if __name__ == "__main__":# 初始化并构建图graph = EmployeeGraph(host="localhost",database="employees",user="root",password="root")graph.build_graph()# 示例查询if 10001 in graph.employees:emp = graph.employees[10001]print(emp)print("所属部门:", emp.departments)print("管理的部门:", emp.manager_of)print("薪资历史:", emp.salaries)print("职位历史:", emp.titles)# 分析职业发展print(f"\n职业发展分析 - {emp.first_name} {emp.last_name}")print(f"入职时间: {emp.hire_date}")print(f"当前职位: {emp.titles[-1].title if emp.titles else '无记录'}")print(f"当前部门: {emp.departments[-1]['department'].dept_name if emp.departments else '无记录'}")if emp.salaries:growth = (emp.salaries[-1].salary / emp.salaries[0].salary) - 1print(f"薪资增长: {emp.salaries[0].salary}{emp.salaries[-1].salary} (增长{growth:.1%})")# 准备薪资数据
salaries = emp.salaries
salary_data = [(s.from_date.year, s.salary) for s in salaries]
df_salary = pd.DataFrame(salary_data, columns=['Year', 'Salary'])# 创建图表
plt.figure(figsize=(12, 6))
sns.lineplot(data=df_salary, x='Year', y='Salary', marker='o')# 添加标注
for i, row in df_salary.iterrows():plt.text(row['Year'], row['Salary'], f"{row['Salary']:,}", ha='center', va='bottom')plt.title(f"{emp.first_name} {emp.last_name} 薪资增长趋势 (1986-2002)")
plt.xlabel("年份")
plt.ylabel("薪资 ($)")
plt.grid(True)
plt.tight_layout()
plt.show()# 关闭连接graph.close()
===============================================
<Employee 10001: Georgi Facello>
所属部门: [{'department': <Department d005: Development>, 'from_date': datetime.date(1986, 6, 26), 'to_date': datetime.date(9999, 1, 1)}]
管理的部门: []
薪资历史: [<Salary 60117 from 1986-06-26 to 1987-06-26>, <Salary 62102 from 1987-06-26 to 1988-06-25>, <Salary 66074 from 1988-06-25 to 1989-06-25>, <Salary 66596 from 1989-06-25 to 1990-06-25>, <Salary 66961 from 1990-06-25 to 1991-06-25>, <Salary 71046 from 1991-06-25 to 1992-06-24>, <Salary 74333 from 1992-06-24 to 1993-06-24>, <Salary 75286 from 1993-06-24 to 1994-06-24>, <Salary 75994 from 1994-06-24 to 1995-06-24>, <Salary 76884 from 1995-06-24 to 1996-06-23>, <Salary 80013 from 1996-06-23 to 1997-06-23>, <Salary 81025 from 1997-06-23 to 1998-06-23>, <Salary 81097 from 1998-06-23 to 1999-06-23>, <Salary 84917 from 1999-06-23 to 2000-06-22>, <Salary 85112 from 2000-06-22 to 2001-06-22>, <Salary 85097 from 2001-06-22 to 2002-06-22>, <Salary 88958 from 2002-06-22 to 9999-01-01>]
职位历史: [<Title Senior Engineer from 1986-06-26 to 9999-01-01>]职业发展分析 - Georgi Facello
入职时间: 1986-06-26
当前职位: Senior Engineer
当前部门: Development
薪资增长: 6011788958 (增长48.0%)================================================
这个输出结果展示了员工Georgi Facello(员工号10001)的完整职业发展数据,分析如下:
职业发展概况
1.	基本信息:
o	姓名:Georgi Facello
o	入职时间:1986626日
o	当前状态:仍在职(9999-01-01是数据库表示当前的标准方式)
2.	部门信息:
o	所属部门:Development (d005)
o	从1986626日至今一直在同一部门
o	没有担任过管理职务(管理的部门为空列表)
3.	职位发展:
o	职位:Senior Engineer
o	从入职至今一直保持同一职位
o	没有职位变更记录
4.	薪资变化:
o	起薪:60,117 (1986)
o	当前薪资:88,958 (2002年调整后)
o	总增长幅度:48.0%
o	共经历17次薪资调整
o	平均每年增长约3.0%(按16年计算)
详细薪资调整分析
text
复制
下载
1986-1987: 60,11762,102 (+3.3%)
1987-1988: 62,10266,074 (+6.4%)
1988-1989: 66,07466,596 (+0.8%)
1989-1990: 66,59666,961 (+0.5%)
1990-1991: 66,96171,046 (+6.1%)
1991-1992: 71,04674,333 (+4.6%)
1992-1993: 74,33375,286 (+1.3%)
1993-1994: 75,28675,994 (+0.9%)
1994-1995: 75,99476,884 (+1.2%)
1995-1996: 76,88480,013 (+4.1%)
1996-1997: 80,01381,025 (+1.3%)
1997-1998: 81,02581,097 (+0.1%)
1998-1999: 81,09784,917 (+4.7%)
1999-2000: 84,91785,112 (+0.2%)
2000-2001: 85,11285,097 (-0.02%)
2001-2002: 85,09788,958 (+4.5%)
职业发展特点
1.	稳定性强:
o	36+任职同一部门
o	长期保持Senior Engineer职位
2.	薪资增长模式:
o	早期增长较快(前5年增长约18.5%)
o	中期增长平稳(1991-2002年平均约3.5%)
o	2000-2001年出现唯一一次微小降薪(可能是数据错误或特殊调整)
3.	职业发展建议:
o	可考虑横向发展(跨部门项目)
o	如寻求晋升,可能需要拓展管理技能
o	当前薪资增长率已趋于稳定,可能需要新的职业突破点
Python的matplotlib和seaborn库来创建专业图表。
1. 薪资增长趋势图(折线图)
python
复制
下载
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd# 准备薪资数据
salaries = emp.salaries
salary_data = [(s.from_date.year, s.salary) for s in salaries]
df_salary = pd.DataFrame(salary_data, columns=['Year', 'Salary'])# 创建图表
plt.figure(figsize=(12, 6))
sns.lineplot(data=df_salary, x='Year', y='Salary', marker='o')# 添加标注
for i, row in df_salary.iterrows():plt.text(row['Year'], row['Salary'], f"{row['Salary']:,}", ha='center', va='bottom')plt.title(f"{emp.first_name} {emp.last_name} 薪资增长趋势 (1986-2002)")
plt.xlabel("年份")
plt.ylabel("薪资 ($)")
plt.grid(True)
plt.tight_layout()
plt.show()

在这里插入图片描述

  1. 薪资年增长率分析(柱状图)
python
复制
下载
# 计算年增长率
df_salary['Growth'] = df_salary['Salary'].pct_change() * 100# 创建图表
plt.figure(figsize=(12, 6))
bars = plt.bar(df_salary['Year'][1:], df_salary['Growth'][1:], color=['green' if x >0 else 'red' for x in df_salary['Growth'][1:]])# 添加数值标签
for bar in bars:height = bar.get_height()plt.text(bar.get_x() + bar.get_width()/2., height,f'{height:.1f}%',ha='center', va='bottom')plt.title("年度薪资增长率 (%)")
plt.xlabel("年份")
plt.ylabel("增长率 (%)")
plt.axhline(0, color='black', linewidth=0.5)
plt.grid(axis='y')
plt.show()

在这里插入图片描述

3. 薪资分布箱线图(与全公司对比)
python
复制
下载
# 获取全公司同年入职员工薪资数据(示例)
# 实际应用中需要从数据库查询真实数据
company_salaries = {1986: [60117, 65000, 58000, 62000, 59000],2002: [85000, 92000, 88000, 89000, 91000]
}# 创建对比数据
compare_data = {'Year': [1986, 2002, 1986, 2002],'Salary': [emp.salaries[0].salary, emp.salaries[-1].salary, sum(company_salaries[1986])/len(company_salaries[1986]),sum(company_salaries[2002])/len(company_salaries[2002])],'Type': ['Georgi', 'Georgi', '公司平均', '公司平均']
}df_compare = pd.DataFrame(compare_data)# 创建图表
plt.figure(figsize=(10, 6))
sns.barplot(data=df_compare, x='Year', y='Salary', hue='Type', palette='Set2')plt.title("薪资与公司平均水平对比")
plt.xlabel("年份")
plt.ylabel("薪资 ($)")
plt.legend(title='')
plt.grid(axis='y')
plt.show()
  1. 职业发展路径图(甘特图)
python
复制
下载
from matplotlib.patches import Patch# 准备数据
events = [{"Type": "职位", "Start": emp.titles[0].from_date, "End": emp.titles[0].to_date, "Label": emp.titles[0].title},{"Type": "部门", "Start": emp.departments[0]['from_date'], "End": emp.departments[0]['to_date'], "Label": emp.departments[0]['department'].dept_name}
]# 转换为DataFrame
df_events = pd.DataFrame(events)# 创建图表
fig, ax = plt.subplots(figsize=(12, 4))colors = {'职位': 'tab:blue', '部门': 'tab:orange'}
for idx, row in df_events.iterrows():ax.barh(row['Type'], (row['End'] - row['Start']).days/365, left=row['Start'].year + (row['Start'].timetuple().tm_yday-1)/365,color=colors[row['Type']])ax.text((row['Start'].year + row['End'].year)/2, idx, row['Label'], ha='center', va='center', color='white')ax.set_xlim(1985, 2023)
ax.set_title("职业发展时间线")
ax.set_xlabel("年份")
ax.set_yticks(range(len(df_events)))
ax.set_yticklabels(df_events['Type'])
ax.grid(axis='x')# 添加图例
legend_elements = [Patch(facecolor=colors[k], label=k) for k in colors]
ax.legend(handles=legend_elements, loc='upper right')plt.tight_layout()
plt.show()
  1. 薪资预测(线性回归)
python
复制
下载
from sklearn.linear_model import LinearRegression
import numpy as np# 准备数据
X = np.array(df_salary['Year']).reshape(-1, 1)
y = np.array(df_salary['Salary'])# 训练模型
model = LinearRegression()
model.fit(X, y)# 预测未来
future_years = np.array([2005, 2010, 2015, 2020]).reshape(-1, 1)
predicted = model.predict(future_years)# 可视化
plt.figure(figsize=(12, 6))
plt.scatter(X, y, color='blue', label='实际薪资')
plt.plot(np.concatenate([X, future_years]), model.predict(np.concatenate([X, future_years]).reshape(-1, 1)),color='red', linestyle='--', label='预测趋势')plt.title("薪资增长趋势与预测")
plt.xlabel("年份")
plt.ylabel("薪资 ($)")
plt.legend()
plt.grid(True)# 标注预测值
for year, salary in zip(future_years.flatten(), predicted):plt.text(year, salary, f"{int(salary):,}", ha='center', va='bottom')plt.show()
使用建议
1.	安装必要库:
bash
复制
下载
pip install matplotlib seaborn scikit-learn pandas
2.	数据分析扩展:
o	可以添加更多员工数据进行对比分析
o	引入更多机器学习模型(如随机森林)提高预测准确性
o	添加交互功能(使用Plotly库)
3.	商业洞察:
o	识别高绩效员工成长模式
o	分析薪资增长与职位晋升的关系
o	预测未来人力成本

设计一个员工筛选生成器,可以找出同一年入职的员工。

功能说明
核心功能:get_employees_by_year(): 获取特定年份入职的所有员工analyze_hire_years(): 统计每年入职人数generate_report(): 生成指定年份区间的入职员工详细报告visualize_hire_trends(): 可视化每年入职人数趋势高级功能:支持按年份范围筛选自动将结果转换为Pandas DataFrame便于分析提供可视化图表展示入职趋势使用场景:python
# 查找所有1995年入职的员工
filter = EmployeeFilter(...)
employees_1995 = filter.get_employees_by_year(1995)# 分析1990-2000年入职情况
report = filter.generate_report(1990, 2000)
print(report.groupby('入职年份')['员工编号'].count())# 找出公司历史上入职人数最多的年份
stats = filter.analyze_hire_years()
peak_year = max(stats.items(), key=lambda x: x[1])
print(f"入职高峰年: {peak_year[0]}年, {peak_year[1]}人")
import mysql.connector
from mysql.connector import Error
from collections import defaultdict
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
import warnings# 1. 解决中文显示问题(无需外部字体文件)
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']  # 使用系统自带雅黑字体
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题# 2. 过滤警告信息
warnings.filterwarnings('ignore', category=UserWarning)class EmployeeFilter:def __init__(self, host, database, user, password, batch_size=50000):"""初始化数据库连接"""try:self.connection = mysql.connector.connect(host=host,database=database,user=user,password=password)self.employees_by_year = defaultdict(list)self.batch_size = batch_size  # 分批处理大小except Error as e:print(f"数据库连接错误: {e}")def load_employees_by_year_batch(self):"""分批按入职年份加载员工数据"""offset = 0while True:query = f"""SELECT emp_no, first_name, last_name, gender, hire_date, YEAR(hire_date) as hire_yearFROM employeesORDER BY hire_dateLIMIT {self.batch_size} OFFSET {offset}"""cursor = self.connection.cursor(dictionary=True)cursor.execute(query)rows = cursor.fetchall()cursor.close()if not rows:breakfor row in rows:hire_year = row['hire_year']self.employees_by_year[hire_year].append({'emp_no': row['emp_no'],'name': f"{row['first_name']} {row['last_name']}",'gender': row['gender'],'hire_date': row['hire_date']})offset += self.batch_sizedef get_employees_by_year(self, year=None, max_display=10):"""获取特定年份入职的员工(限制显示数量):param year: 年份:param max_display: 最大显示数量:return: 格式化字符串结果"""if not self.employees_by_year:self.load_employees_by_year_batch()if year:employees = self.employees_by_year.get(year, [])result = f"{year}年入职员工(共{len(employees)}人):\n"for emp in employees[:max_display]:result += f"{emp['emp_no']}: {emp['name']} ({emp['gender']}) - {emp['hire_date']}\n"if len(employees) > max_display:result += f"...(仅显示前{max_display}条,共{len(employees)}条)"return resultelse:return "请指定具体年份"def analyze_hire_years(self):"""分析每年入职人数统计"""if not self.employees_by_year:self.load_employees_by_year_batch()stats = {year: len(employees) for year, employees in self.employees_by_year.items()}return statsdef visualize_hire_trends(self, start_year=None, end_year=None):"""可视化每年入职人数趋势(优化大数据处理)"""stats = self.analyze_hire_years()# 筛选年份范围years = sorted(stats.keys())if start_year:years = [y for y in years if y >= start_year]if end_year:years = [y for y in years if y <= end_year]counts = [stats[y] for y in years]# 创建图表fig, ax = plt.subplots(figsize=(14, 6))# 使用条形图更适合离散年份数据bars = ax.bar(years, counts, color='#1f77b4', alpha=0.7)# 设置中文标题和标签ax.set_title("公司每年入职员工数量趋势", fontsize=14, pad=20)ax.set_xlabel("入职年份", fontsize=12)ax.set_ylabel("员工数量", fontsize=12)# 优化刻度显示ax.set_xticks(years)ax.set_xticklabels(years, rotation=45, ha='right')# 添加网格和数据标签ax.grid(axis='y', linestyle='--', alpha=0.6)for bar in bars:height = bar.get_height()ax.text(bar.get_x() + bar.get_width()/2., height,f'{height:,}', ha='center', va='bottom', fontsize=9)# 自动调整布局plt.tight_layout()return figdef generate_report(self, start_year=None, end_year=None, sample_size=5):"""生成精简版报告(避免大数据量输出)"""stats = self.analyze_hire_years()# 筛选年份范围years = sorted(stats.keys())if start_year:years = [y for y in years if y >= start_year]if end_year:years = [y for y in years if y <= end_year]result = "员工入职年份统计报告\n"result += "="*40 + "\n"for year in years:count = stats[year]employees = self.employees_by_year[year]result += f"\n{year}年: 共{count}人\n"result += "示例员工:\n"# 显示样本员工for emp in employees[:sample_size]:result += f"  - {emp['emp_no']}: {emp['name']} ({emp['gender']})\n"if count > sample_size:result += f"  ...(共{count}人)\n"return resultdef close(self):"""关闭数据库连接"""if hasattr(self, 'connection') and self.connection.is_connected():self.connection.close()# 使用示例
if __name__ == "__main__":# 初始化筛选器filter = EmployeeFilter(host="localhost",database="employees",user="root",password="root")try:# 示例1:获取特定年份入职员工(限制输出)print(filter.get_employees_by_year(1986))# 示例2:生成精简报告print("\n" + filter.generate_report(1985, 1990))# 示例3:可视化1985-2000年趋势fig = filter.visualize_hire_trends(1985, 2000)plt.show()# 示例4:统计信息stats = filter.analyze_hire_years()print("\n入职人数最多的年份:")peak_year = max(stats.items(), key=lambda x: x[1])print(f"{peak_year[0]}年: {peak_year[1]:,}人")finally:# 确保关闭连接filter.close()1986年入职员工(36150):
89812: Boriana Vingron (F) - 1986-01-01
90916: Eishiro Curless (M) - 1986-01-01
202218: Kristian Bergere (M) - 1986-01-01
213442: Vidar Tibblin (M) - 1986-01-01
214099: Holgard Prenel (F) - 1986-01-01
100711: Kwangyoen Rosca (M) - 1986-01-01
100812: Saniya Lanphier (F) - 1986-01-01
103944: Domenick Dehkordi (M) - 1986-01-01
104083: Basil Matteis (F) - 1986-01-01
104764: Juichirou Munke (M) - 1986-01-01
...(仅显示前10条,共36150)员工入职年份统计报告
========================================1985:35316人
示例员工:- 110022: Margareta Markovitch (M)- 110085: Ebru Alpin (M)- 110183: Shirish Ossenbruggen (F)- 110303: Krassimir Wegerle (F)- 110511: DeForest Hagimont (M)...(35316)1986:36150人
示例员工:- 89812: Boriana Vingron (F)- 90916: Eishiro Curless (M)- 202218: Kristian Bergere (M)- 213442: Vidar Tibblin (M)- 214099: Holgard Prenel (F)...(36150)1987:33501人
示例员工:- 417048: Subhada Bernardeschi (F)- 401034: Chikako Famili (F)- 422005: Avishai Stentiford (M)- 401863: Ghassan Wolniewicz (F)- 403068: Urs Matzat (M)...(33501)1988:31436人
示例员工:- 475755: Radhakrishnan Coombs (F)- 477365: Heng Siksek (M)- 478116: Debatosh Piveteau (F)- 478374: Hironoby Furudate (F)- 482005: Kwangyoen Dahlbom (M)...(31436)1989:28394人
示例员工:- 207103: Wonhee Byoun (F)- 211368: Alenka Zirintsis (F)- 211528: Kiam Schaar (M)- 100526: Huei Sewelson (M)- 213224: Godehard Demeyer (F)...(28394)1990:25610人
示例员工:- 418290: Remzi Demizu (F)- 419042: Insup Rosar (F)- 419834: Mohit Hiroyama (F)- 421168: Kshitij Falco (F)- 430862: Hugo Uchoa (M)...(25610)入职人数最多的年份:
1986: 36,150

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/92231.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/92231.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/92231.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度学习图像预处理:统一输入图像尺寸方案

在实际训练中&#xff0c;最常见也最简单的做法&#xff0c;就是在送入网络前把所有图片「变形」到同一个分辨率&#xff08;比如 256256 或 224224&#xff09;&#xff0c;或者先裁剪&#xff0f;填充成同样大小。具体而言&#xff0c;可以分成以下几类方案&#xff1a;一、图…

pytest-log

问题1&#xff1a;我们在运行测试用例的时候如何记录测试的log&#xff0c;如何使用&#xff1f;问题2&#xff1a;我写的函数&#xff0c;为了方便log记录&#xff0c;但是在pytest运行时&#xff0c;会兼容pytest且不会重复记录&#xff0c;怎么解决&#xff1f;1、pytest有内…

在安卓源码中添加自定义jar包给源码中某些模块使用

一、具体步骤 1. 准备目录与 Jar 包 在vendor下 创建新的模块目录&#xff0c;放入demo.jar 包&#xff1a; demojar/ # 模块目录 ├── Android.bp # 编译配置文件 └── demo.jar 2. 编写 Android.bp 配置 Android.bp 示例配置&#xff1a; java_import {…

buntu 22.04 上离线安装Docker 25.0.5(二)

以下有免费的4090云主机提供ubuntu22.04系统的其他入门实践操作 地址&#xff1a;星宇科技 | GPU服务器 高性能云主机 云服务器-登录 相关兑换码星宇社区---4090算力卡免费体验、共享开发社区-CSDN博客 兑换码要是过期了&#xff0c;可以私信我获取最新兑换码&#xff01;&a…

初探 Web 环境下的 LLM 安全:攻击原理与风险边界

文章目录前言1 什么是大型语言模型&#xff08;LLM&#xff09;&#xff1f;1.1 LLM的核心特征1.2 LLM在Web场景中的典型应用2 LLM攻击的核心手段&#xff1a;提示注入与权限滥用3 LLM与API集成的安全隐患&#xff1a;工作流中的漏洞节点3.1 LLM-API集成的典型工作流3.2 工作流…

【新手向】PyTorch常用Tensor shape变换方法

【新手向】PyTorch常用Tensor shape变换方法 前言 B站UP主科研水神大队长的视频中介绍了“缝合模块”大法&#xff0c;其中专门强调了“深度学习 玩的就是shape”。受此启发&#xff0c;专门整理能够调整tensor形状的几个内置函数&#xff0c;方便以后更好地调整PyTorch代码中的…

React 18 vs Vue3:状态管理方案深度对比

🔥 背景: React有Redux、Zustand、Jotai等方案 Vue有Pinia、Vuex 4.x 如何选择适合项目的方案? 🔍 核心对比: 维度 React (Redux Toolkit) Vue3 (Pinia) 类型安全 ✅ 需手动配置TS ✅ 自动类型推导 代码量 较多(需写action) 较少(类似Vuex 5) 响应式原理 不可变数据…

UE5网络联机函数

Find Sessions Create Session Join Session Destroy Session Steam是p2p直接联机 一、steam提供的测试用AppId AppId是steam为每一款游戏所设定的独有标识&#xff0c;每一款要上架steam的游戏都会拥有独一无二的AppId。不过为了方便开发者测试&#xff0c;steam提供了游…

Spring Boot 监控:AOP vs Filter vs Java Agent

01前言 在 高并发 微服务 中&#xff0c; 传统 手动埋点&#xff08;System.currentTimeMillis()&#xff09;就像用体温计量火箭速度——代码侵入、重复劳动、维护爆炸。 下文是无侵入、高精度、全链路 监控 API 耗时&#xff0c;全程不碰业务代码的方案&#xff01; 02实战&…

基于Android的电子记账本系统

博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业多年&#xff0c;熟悉各种主流语言&#xff0c;精通java、python、php、爬虫、web开发&#xff0c;已经做了多年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;没有什么华丽的语言&#xff0…

7月17日日记

结束了数学建模之后的这两天一直在紧张的复习&#xff0c;但是说实话效率有点低&#xff0c;因为可能觉得自己找到了两个小时速成课&#xff0c;觉得无所谓了&#xff0c;所以有点放松了。在宿舍杰哥和林雨城却一直在复习&#xff0c;感觉他们的微积分和线性代数复习的都比我好…

Linux下SPI设备驱动开发

一.SPI协议介绍1.硬件连接介绍引脚含义&#xff1a;DO(MOSI)&#xff1a;Master Output, Slave Input&#xff0c;SPI主控用来发出数据&#xff0c;SPI从设备用来接收数据。DI(MISO)&#xff1a;Master Input, Slave Output&#xff0c;SPI主控用来发出数据&#xff0c;SPI从设…

用Dify构建气象智能体:从0到1搭建AI工作流实战指南

作为一名Agent产品经理,我最近在负责气象智能体的建设项目。传统气象服务面临三大痛点:数据孤岛严重(气象局API、卫星云图、地面观测站等多源数据格式不一)、响应链路长(从数据采集到预警发布需人工介入多个环节)、交互体验单一(用户只能被动接收标准化预警,无法个性化…

Android NDK ffmpeg 音视频开发实战

文章目录接入FFmpeg1.下载FFmpeg 源码2.编译FFmpeg.so库异常处理3.自定义FFmpeg交互so库创建4.配置CMakeLists.txt5.CMakeLists.txt 环境配置6.Native与Java层调用解码器准备接入FFmpeg 1.下载FFmpeg 源码 FFmpeg官网地址 2.编译FFmpeg.so库 移动 FFmpeg 源码文件夹至 Andr…

使用 go-redis-entraid 实现 Entra ID 无密钥认证

1、依赖与安装 步骤命令说明安装&#xff08;或升级&#xff09; go-redis v9.9go get github.com/redis/go-redis/v9latestentraid 必须 ≥ 9.9.0安装 go-redis-entraidgo get github.com/redis/go-redis-entraid自动拉取 transit 依赖 2、认证方式一览 方式说明创建 Stream…

window上docker安装RabbitMQ

1、要进http://localhost:15672管理页面需要安装management版本2、搜索镜像并pull3、启动镜像时将端口映射出来4、启动成功&#xff0c;点击可查看日志详情&#xff0c;浏览器访问5、直接使用guest/guest登录会报错User can only log in via localhost解决办法有两个&#xff1…

异世界历险之数据结构世界(排序(插入,希尔,堆排))

前言 介绍 插入排序 基本知识&#xff1a; 直接插入排序是一种简单的插入排序法&#xff0c;其基本思想是&#xff1a; 把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中&#xff0c;直到所有的记录插入完为止&#xff0c;得到一个新的有序序列 直接插入…

oracle 数据库中,将几张表的数据按指定日期范围实时同步至同一个数据库的备份表中。

以下是一个Oracle数据库中实现表数据按指定日期范围实时同步至备份表的解决方案。这个方案使用存储过程和触发器组合实现&#xff1a; 1. 创建备份表结构 首先需要为每张需要备份的表创建对应的备份表&#xff0c;结构与原表相同&#xff1a; -- 为原表创建备份表&#xff08;示…

电脑网络连接正常,微信、QQ能正常使用,但无法访问网页?DNS问题的解决方案和背后原理。

文章目录1. 问题背景2. 解决方案2.1 手动刷新DNS2.1.1 Windows版本2.1.2 Mac版本2.2 手动设置DNS服务器2.2.1 Windows版2.2.2 Mac版2.3 其他解决方案3. DNS是什么&#xff1f;3.1 详细解释DNS3.1.1 A distributed, hierarchical database&#xff08;一个分布式和分层数据库结构…

【HTML】图片比例和外部div比例不一致,最大程度占满

图片比例和外部div比例不一致&#xff0c;最大程度占满&#xff0c;并且图片比例不变 其中1.jpg,2.jpg,1.html在同一目录 |-----|- 1.jpg|- 2.jpg|- 1.html1.jpg2.jpg<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /&g…