diff --git a/.idea/misc.xml b/.idea/misc.xml index fccf0cb..de0b43c 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 9d36c46..73ee83b 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,7 +2,7 @@ - + \ No newline at end of file diff --git a/.idea/hire.iml b/.idea/salary02.iml similarity index 76% rename from .idea/hire.iml rename to .idea/salary02.iml index 8180d6b..16b41b0 100644 --- a/.idea/hire.iml +++ b/.idea/salary02.iml @@ -4,7 +4,7 @@ - + \ No newline at end of file diff --git a/data_analysis.py b/data_analysis.py index 57b4b7c..8a68813 100644 --- a/data_analysis.py +++ b/data_analysis.py @@ -1,10 +1,11 @@ import numpy as np import pandas as pd +import matplotlib.pyplot as plt num_time_step = 200 num_iter = 10 -env_data = pd.DataFrame(pd.read_excel('env_data.xlsx', engine='openpyxl')) +env_data = pd.DataFrame(pd.read_excel('env_data.xlsx', engine='openpyxl', sheet_name=0)) assert env_data.shape[0] == num_iter * (num_time_step + 1) @@ -12,16 +13,16 @@ lst_df = [] for i in range(num_iter): df_tmp = env_data.iloc[i * (num_time_step + 1): (i + 1) * (num_time_step + 1), 1:] lst_df.append(df_tmp) -# df_tmp = env_data.iloc[1: 21] -# lst_df.append(df_tmp) +lst_column = lst_df[0].columns[1:] -x = np.array(env_data[['t']]) -y = np.array(env_data[['out_w_avg_salary']]) - -import matplotlib.pyplot as plt - -plt.xlabel('t') -plt.ylabel('out_w_avg_salary') -plt.plot(x, y) -plt.show() +for str_col in lst_column: + x = np.arange(num_time_step+1) + for df in lst_df: + y = np.array(df[str_col]).flatten() + plt.xlabel('t') + plt.ylabel(str_col) + plt.plot(x, y) + # plt.show() + # plt.close() + plt.savefig(f'{str_col}.pdf', bbox_inches="tight") diff --git a/env.py b/env.py index 7c0ae1e..f099617 100644 --- a/env.py +++ b/env.py @@ -3,27 +3,26 @@ import numpy as np from random import uniform import math -from traits.trait_types import self - from worker import WorkerAgent from firm import FirmAgent # plt.ion() + class Env(ap.Model): float_market_size: float - percent_rh: float + # percent_rh: float percent_search: float n_worker: int n_firm: int e_revenue: float - a_lst_worker: ap.AgentList - a_lst_firm: ap.AgentList + a_lst_worker: ap.AgentList[WorkerAgent] + a_lst_firm: ap.AgentList[FirmAgent] """ Worker: Mean(s), Gini(s) - Firm: Mean(($$\pi_{j,t}$$)), Gini($$\pi_{j,t}$$) + Firm: Mean((pi_{j,t})), Gini(pi_{j,t}) Env: Percent(IsHired) """ out_w_avg_salary: float @@ -44,7 +43,7 @@ class Env(ap.Model): # 在工人列表中添加工人 for i in range(self.n_worker): - # 初始化 workeragent,并把alpha属性传过去 + # 初始化 worker agent,并把alpha属性传过去 w = WorkerAgent(self, self.p.alpha) self.a_lst_worker.append(w) @@ -74,13 +73,15 @@ class Env(ap.Model): self.create_and_destroy_bankrupt_firms() + self.a_lst_worker.update_wd_by_is_hired() + # self.picture_out() self.update() if self.t == 200: self.stop() # self.picture_out() - pass + # pass def update(self): lst_salary = [] @@ -94,11 +95,11 @@ class Env(ap.Model): self.out_w_gini_salary = self.gini(lst_salary) lst_profit = [] - n_w_firm = 0 + # n_w_firm = 0 for f in self.a_lst_firm: lst_profit.append(f.s_profit) - if f.s_profit > 0: - n_w_firm += 1 + # if f.s_profit > 0: + # n_w_firm += 1 n_firms = len(lst_profit) self.out_f_avg_profit = sum(lst_profit) / n_firms self.out_f_gini_profit = self.gini(lst_profit) @@ -111,54 +112,37 @@ class Env(ap.Model): self.record('out_w_percent_hired') def create_and_destroy_bankrupt_firms(self): - n_bankrupt_firms = 0 for f in self.a_lst_firm: if f.s_value < 0: - # 直接淘汰企业(此处设定为清空企业员工,清空企业利润值和价值,相当于重新添加了一个新的企业?) - n_bankrupt_firms += 1 - for work in f.l_senior_workers: - work.s_is_hired = False - for work in f.l_junior_workers: - work.s_is_hired = False - f.s_profit = 0 - f.s_value = 0 + # 直接淘汰企业,并新增一个企业 + for worker in f.l_senior_workers: + worker.s_is_hired = False + worker.working_firm = None + for worker in f.l_junior_workers: + worker.s_is_hired = False + worker.working_firm = None + self.a_lst_firm.remove(f) + del f + new_f = FirmAgent(self, self.p.is_RH_ratio >= uniform(0, 1), self.p.is_FH_ratio >= uniform(0, 1)) + self.a_lst_firm.append(new_f) else: if f.s_profit < 0: - if f.s_IsFH: - # 第一种方式,末位淘汰制,淘汰所有员工中生产价值最低的 - f.l_junior_workers.sort(key=lambda x: x['s_yield'], reverse=True) - for work in f.l_junior_workers: - # f.l_junior_workers.sort(key=lambda x: x['s_yield'], reverse=True) - if work == f.l_junior_workers[-1]: - work.s_is_hired = False - else: - # 第二种方式,末位淘汰制,淘汰所有员工中单位产值(产值/工资)价值最低的 - f.l_all_w = f.l_junior_workers + f.l_senior_workers - for work in f.l_all_w: - work.unit_yield_salary = work.s_yield * 10000 / work.s_salary - f.l_all_w.sort(key=lambda x: x['unit_yield_salary'], reverse=True) - if work == f.l_all_w[-1]: - work.s_is_hired = False - # for f in self.a_lst_firm: - # if f.s_profit < 0: # TODO - # n_bankrupt_firms += 1 - # for work in f.l_senior_workers: - # work.s_is_hired = False - # for work in f.l_junior_workers: - # work.s_is_hired = False - # self.a_lst_firm.remove(f) - # del f - # if n_bankrupt_firms > 0: - # for _ in range(n_bankrupt_firms): - # f = FirmAgent(self, self.p.is_RH_ratio >= uniform(0, 1)) - # self.a_lst_firm.append(f) + f.l_all_w = f.l_junior_workers + f.l_senior_workers + min_unit_yield_salary, worst_worker = float('inf'), None + for worker in f.l_all_w: + unit_yield_salary = worker.s_yield if f.s_IsFH else worker.s_yield * 10000 / worker.s_salary + if unit_yield_salary < min_unit_yield_salary: + min_unit_yield_salary = unit_yield_salary + worst_worker = worker + worst_worker.s_is_hired = False + worst_worker.working_firm = None assert len(self.a_lst_firm) == self.n_firm, \ f'current num firm {len(self.a_lst_firm)} != expected num firm {self.n_firm}' - def provide_lst_random_firms(self, the_worker: WorkerAgent): - '''选择企业数量 = 企业总数*百分比 + """ + 选择企业数量 = 企业总数*百分比 选择企业的列表 = 随机选择的企业的个数 如果员工处于被雇佣的状态: 如果员工工作的企业在随机选定的企业列表中 @@ -167,7 +151,7 @@ class Env(ap.Model): 返回值:移除后,再重新选择随机选择企业 否则: 返回值:选择企业列表 - ''' + """ n_select_firms = int(self.percent_search * self.n_firm) a_lst_select_firms = self.a_lst_firm.random(n_select_firms) if the_worker.s_is_hired: @@ -194,6 +178,7 @@ class Env(ap.Model): @staticmethod def gini(x): + x = [ele for ele in x if ele > 0] if len(x) == 0: return 0 if sum(x) == 0: @@ -223,9 +208,8 @@ if __name__ == '__main__': parameters = { 'n_worker': 1000, 'n_firm': 100, - 'percent_search': 0.2, 'alpha': 0.5, - # 'alpha': ap.Range(0, 1, 0.5), + 'percent_search': 0.2, 'is_RH_ratio': 0.5, 'is_FH_ratio': 0.5, } @@ -235,4 +219,3 @@ if __name__ == '__main__': exp = ap.Experiment(Env, sample, iterations=10, record=True) results = exp.run() results['variables']['Env'].to_excel('env_data.xlsx', engine='openpyxl') - diff --git a/env_data.xlsx b/env_data.xlsx index f6a454f..b6a9cb6 100644 Binary files a/env_data.xlsx and b/env_data.xlsx differ diff --git a/firm.py b/firm.py index ec9d2a9..baeb0db 100644 --- a/firm.py +++ b/firm.py @@ -1,46 +1,42 @@ import math -from typing import Union, Any +# from typing import Union, Any import agentpy as ap from random import uniform, randint -from typing import TYPE_CHECKING +# from typing import TYPE_CHECKING -import numpy as np +# import numpy as np -if TYPE_CHECKING: - from worker import WorkerAgent +# if TYPE_CHECKING: +# from worker import WorkerAgent class FirmAgent(ap.Agent): c_incentive: float s_IsRH: bool s_IsFH: bool - s_avg_senior_yield: float - s_avg_junior_yield: float - s_a_yield: float + s_avg_senior_yield: float # updated in self.update_yields + s_avg_junior_yield: float # updated in self.update_yields + s_a_yield: float # updated in self.update_yields # s_salary: float initial_f_salary: float - s_revenue: float - s_profit: float - s_value: float - firing_worker: 'WorkerAgent' - l_senior_workers: list - l_junior_workers: list - l_applied_workers: list + s_revenue: float # updated in env.provide_logit_share + s_profit: float # updated in self.update_s_profit + s_value: float # updated in self.update_s_profit + # firing_worker: 'WorkerAgent' + l_senior_workers: list # updated in self.update_two_worker_list + l_junior_workers: list # updated in self.update_two_worker_list + l_applied_workers: list # updated in self.apply, self.empty_apply - # def __init__(self, model, *args, **kwargs): - # super().__init__(model, args, kwargs) - # self.l_all_workers = None - - def setup(self, is_RH, is_FH): + def setup(self, is_rh, is_fh): self.c_incentive = uniform(0, 1) # self.s_profit = randint(10, 20) self.l_senior_workers, self.l_junior_workers = [], [] - self.l_all_workers = [] + # self.l_all_workers = [] self.l_applied_workers = [] - self.s_IsRH = is_RH - self.s_IsFH = is_FH + self.s_IsRH = is_rh + self.s_IsFH = is_fh self.initial_f_salary = randint(8000, 10000) self.s_profit = 0 self.s_value = 0 @@ -52,13 +48,13 @@ class FirmAgent(ap.Agent): self.l_applied_workers = [] def select_worker(self): - ''' + """ 企业找到最想招聘的员工 :return: - ''' + """ n_workers = len(self.l_applied_workers) if n_workers > 0: - selected_worker = None + # selected_worker = None if n_workers > 1: # 对员工产出进行排队和比较 # 先判断是什么选择方式 @@ -70,35 +66,21 @@ class FirmAgent(ap.Agent): best_worker = the_worker selected_worker = best_worker else: - # selected_worker = self.l_applied_workers[0] # TODO + # selected_worker = self.l_applied_workers[0] # 加入worker进入到某个企业,企业更新了自己的利润,利润差值作为该名员工能带来的单位利润,排序利润 # 但是代码中对于员工产出是(0,1)值,需要先更新员工所在列表,然后更新企业产出、利润、工资总额,再用假设利润-上期利润 # 问题是,当期的估算不能考虑到有员工会在下一期离指的问题,或许考虑换成?max_单位产出成本? - max_unit_yield_salary, p_salary, best_worker= 0.0,0.0, None + max_unit_yield_salary, p_salary, best_worker = 0.0, 0.0, None for the_worker in self.l_applied_workers: if the_worker.s_salary == 0: - the_worker.p_salary = self.initial_f_salary + p_salary = self.initial_f_salary else: - the_worker.p_salary = the_worker.s_salary*(1+self.c_incentive) - the_worker.unit_yield_salary = the_worker.s_yield * 10000 / the_worker.p_salary - if the_worker.unit_yield_salary > max_unit_yield_salary: - max_unit_yield_salary = the_worker.unit_yield_salary + p_salary = the_worker.s_salary * (1 + self.c_incentive) + unit_yield_salary = the_worker.s_yield * 10000 / p_salary + if unit_yield_salary > max_unit_yield_salary: + max_unit_yield_salary = unit_yield_salary best_worker = the_worker selected_worker = best_worker - # print(f'{self}: my best firm is {best_firm} from {n_firms} firms with utility {max_utility}') - # return best_worker - # 当企业是想要利用产出最高的员工时,从申请的员工中选出产出最高的员工 - # if self.s_IsRH : - # # 计算该名员工的工资水平: 原有工资水平* (1+incentive) - # # bw_salary = self.best_worker.salary() - # - # # 将该名员工的产出与公司原有员工的产出进行对比,如果高于senior列表中的最后一名的产出,就进入senior_list, 否则进入junior_list - # self.l_senior_workers.append(best_worker) - # else: - # self.l_junior_workers.append(best_worker) - # # best_worker.apply(self) - # else: - # # 计算由于改名员工下一期的薪资总数(根据薪资函数更新)以及企业下一期的利润的差值,并选出最大值 else: selected_worker = self.l_applied_workers[0] selected_worker.update_working_firm_is_hired(self) @@ -106,7 +88,7 @@ class FirmAgent(ap.Agent): def update_two_worker_list(self, new_worker): lst_all_worker = self.l_senior_workers + self.l_junior_workers + [new_worker] - lst_all_worker.sort(key=lambda x: x['s_yield'], reverse=True) # from highest yield to lowest + lst_all_worker.sort(key=lambda x: x['s_yield'], reverse=True) # from the highest yield to lowest n_all_worker = len(lst_all_worker) # 向上取整数值 n_senior_worker = math.ceil(n_all_worker * 0.2) @@ -142,9 +124,9 @@ class FirmAgent(ap.Agent): self.s_a_yield = 0.8 * self.s_avg_senior_yield + 0.2 * self.s_avg_junior_yield def get_sum_salary(self): - ''' + """ 计算某公司整体的薪金水平 - ''' + """ l_all_workers = self.l_senior_workers + self.l_junior_workers sum_salary = 0.0 for the_worker in l_all_workers: @@ -155,5 +137,5 @@ class FirmAgent(ap.Agent): self.s_profit = self.s_revenue - self.get_sum_salary() self.s_value += self.s_profit - def step(self): - pass + # def step(self): + # pass diff --git a/main.py b/main.py deleted file mode 100644 index bf8b9e5..0000000 --- a/main.py +++ /dev/null @@ -1,16 +0,0 @@ -# This is a sample Python script. - -# Press Shift+F10 to execute it or replace it with your code. -# Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings. - - -def print_hi(name): - # Use a breakpoint in the code line below to debug your script. - print(f'Hi, {name}') # Press Ctrl+F8 to toggle the breakpoint. - - -# Press the green button in the gutter to run the script. -if __name__ == '__main__': - print_hi('PyCharm') - -# See PyCharm help at https://www.jetbrains.com/help/pycharm/ diff --git a/oa25.txt b/oa25.txt new file mode 100644 index 0000000..99a1ab8 --- /dev/null +++ b/oa25.txt @@ -0,0 +1,25 @@ +000000 +011234 +022341 +033412 +044123 +101111 +112403 +124032 +130324 +143240 +202222 +214310 +223104 +231043 +240431 +303333 +310142 +321420 +334201 +342014 +404444 +413021 +420213 +432130 +441302 \ No newline at end of file diff --git a/out_f_avg_profit.pdf b/out_f_avg_profit.pdf new file mode 100644 index 0000000..ef20771 Binary files /dev/null and b/out_f_avg_profit.pdf differ diff --git a/out_f_gini_profit.pdf b/out_f_gini_profit.pdf new file mode 100644 index 0000000..c50d90a Binary files /dev/null and b/out_f_gini_profit.pdf differ diff --git a/out_w_avg_salary.pdf b/out_w_avg_salary.pdf new file mode 100644 index 0000000..5946d3c Binary files /dev/null and b/out_w_avg_salary.pdf differ diff --git a/out_w_gini_salary.pdf b/out_w_gini_salary.pdf new file mode 100644 index 0000000..6bcc439 Binary files /dev/null and b/out_w_gini_salary.pdf differ diff --git a/out_w_percent_hired.pdf b/out_w_percent_hired.pdf new file mode 100644 index 0000000..43ec28c Binary files /dev/null and b/out_w_percent_hired.pdf differ diff --git a/worker.py b/worker.py index 1e68677..fc079e7 100644 --- a/worker.py +++ b/worker.py @@ -12,43 +12,33 @@ if TYPE_CHECKING: class WorkerAgent(ap.Agent): - select_firm: object - c_effort: float - s_is_hired: bool + # select_firm: object + c_effort: float # used in self.update_yield + s_is_hired: bool # updated in self.update_working_firm_is_hired, env.create_and_destroy_bankrupt_firms # c_work_months: int - s_work_duration: int - working_firm: 'FirmAgent' - s_salary: float - s_yield: float + s_work_duration: int # updated in self.update_wd_by_is_hired + working_firm: 'FirmAgent' # updated in self.update_working_firm_is_hired, env.create_and_destroy_bankrupt_firms + s_salary: float # updated in self.update_salary + s_yield: float # updated in self.update_yield # s_w_applied: bool c_alpha: float def setup(self, alpha): - # super().__init__(unique_id, model) - # self.num_workers = 10000 self.c_effort = uniform(0, 1) - # self.c_work_months = randint(0, 60) self.s_is_hired = False - self.s_work_duration = randint(0, 60) + self.s_work_duration = randint(30, 60) self.c_alpha = alpha self.update_yield() self.s_salary = 0 - # return - - # def A_Utility(self, c_w_weight=0.5): - # a = np.exp(FirmAgent.c_f_incentive, c_w_weight) - # b = np.exp(FirmAgent.s_f_profit/max(FirmAgent.s_f_profit), 1-c_w_weight) - # self.select_firm = a * b - def select_firm(self): - ''' + """ 挑选出来的企业列表 数量:列表的长度 - ''' + """ lst_firms = self.model.provide_lst_random_firms(self) - n_firms = len(lst_firms) + # n_firms = len(lst_firms) # find the max incentive and profit among all firms max_incentive, max_value = 0, 0 for f in lst_firms: @@ -61,22 +51,14 @@ class WorkerAgent(ap.Agent): return random.choice(lst_firms) max_utility, best_firm = 0, None for f in lst_firms: - if self.s_salary < f.s_profit * 1 / 10: + if self.s_salary < f.s_profit / 10: u = math.pow(f.c_incentive / max_incentive, self.c_alpha) * math.pow(f.s_value / max_value, - 1 - self.c_alpha) + 1 - self.c_alpha) if u > max_utility: max_utility = u best_firm = f - # if self.s_salary < best_firm.s_profit * 5 / 100: best_firm.apply(self) - - # print(f'{self}: my best firm is {best_firm} from {n_firms} firms with utility {max_utility}') - # 选出能够给自己带来最好的效用的企业,并输出/返回 - # best_firm.apply(self) - # return best_firm - # self.update_wd_by_is_hired() - def update_wd_by_is_hired(self): if self.s_is_hired: self.s_work_duration += 1 @@ -84,22 +66,10 @@ class WorkerAgent(ap.Agent): self.update_salary(self.working_firm) def update_salary(self, the_firm: 'FirmAgent'): - if self.s_salary == 0: self.s_salary = the_firm.initial_f_salary - pass else: - if self.s_salary <= the_firm.s_profit*1/10: - if self.s_salary * (1 + the_firm.c_incentive) <= the_firm.s_profit*1/10: - self.s_salary = self.s_salary * (1 + the_firm.c_incentive) - pass - else: - self.s_salary = the_firm.s_profit*1/10 - pass - pass - else: - self.s_salary = the_firm.s_profit*1/10 - + self.s_salary = max(min(self.s_salary * (1 + the_firm.c_incentive), the_firm.s_profit / 10), self.s_salary) def update_yield(self): self.s_yield = 2 / (1 + math.exp(-0.01 * self.s_work_duration * self.c_effort)) - 1 @@ -108,9 +78,3 @@ class WorkerAgent(ap.Agent): self.s_is_hired = True self.working_firm = f self.update_wd_by_is_hired() - - def step(self): - self.update_wd_by_is_hired() - ''' - 是否更换公司成功的状态转换? - '''