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
薪資增長: 60117 → 88958 (增長48.0%)================================================
這個輸出結果展示了員工Georgi Facello(員工號10001)的完整職業發展數據,分析如下:
職業發展概況
1. 基本信息:
o 姓名:Georgi Facello
o 入職時間:1986年6月26日
o 當前狀態:仍在職(9999-01-01是數據庫表示當前的標準方式)
2. 部門信息:
o 所屬部門:Development (d005)
o 從1986年6月26日至今一直在同一部門
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,117 → 62,102 (+3.3%)
1987-1988: 62,102 → 66,074 (+6.4%)
1988-1989: 66,074 → 66,596 (+0.8%)
1989-1990: 66,596 → 66,961 (+0.5%)
1990-1991: 66,961 → 71,046 (+6.1%)
1991-1992: 71,046 → 74,333 (+4.6%)
1992-1993: 74,333 → 75,286 (+1.3%)
1993-1994: 75,286 → 75,994 (+0.9%)
1994-1995: 75,994 → 76,884 (+1.2%)
1995-1996: 76,884 → 80,013 (+4.1%)
1996-1997: 80,013 → 81,025 (+1.3%)
1997-1998: 81,025 → 81,097 (+0.1%)
1998-1999: 81,097 → 84,917 (+4.7%)
1999-2000: 84,917 → 85,112 (+0.2%)
2000-2001: 85,112 → 85,097 (-0.02%)
2001-2002: 85,097 → 88,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()
- 薪資年增長率分析(柱狀圖)
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()
- 職業發展路徑圖(甘特圖)
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()
- 薪資預測(線性回歸)
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人