用人力资源分析提高工作场所安全:教程
工作场所安全是世界各地雇主的首要任务。安全的工作场所不仅可以保护员工免受伤害,还可以节省公司与伤害相关的成本。在美国,工伤成本超过1700亿美元每年。这些事故大多是可以预防的。人力资源团队可以利用分析的力量来防止工作场所发生事故。
在本文中,我们将演示如何在工作场所安全上下文中使用无监督ML算法-关联规则。具体来说,我们将重点关注关联规则如何帮助合成大量安全数据,以确定干预点,以防止未来发生事故,以及可视化关联规则输出的新方法,以使解释,更重要的是,对结果的使用,更容易为人力资源领导者。
我们包含了一些用Python和r编写的实用代码示例,让我们开始吧!
内容
商业挑战
为什么要创建关联规则
工作流
1.摄取和准备数据
2.分析数据
3.可视化的结果
商业挑战
一家大型制造公司致力于为员工提供最安全的工作环境。他们采取了许多措施来跟踪和预防整个设施的伤害,但安全事故发生的频率仍然比他们想象的要高。他们为每个事件捕捉了非常详细的数据,在最近的一次会议上,制造副总裁问人力资源是否可以分析安全数据,以确定是否存在与事件相关的通常情况,如果解决了这些情况,可以防止未来发生事故。人力资源部门求助于他们的人力资源分析团队来分析安全数据并确定可预防的情况。
相关(免费)资源!继续往下读↓
人物分析资源库
下载我们的关键人力资源分析资源列表(90+),将帮助您提高您的专业知识和主动性。您的一站式人员分析!
注意:工作场所安全数据集并不容易获得,因此我们使用了英国车辆事故的数据集来演示如何进行这种分析。
为什么要创建关联规则
您可以使用多种方法来识别与安全事故相关的条件。然而,关联规则提供了一种快速识别频繁同时发生的条件的方法——安全和事故预防的关键优先级。
首先,让我们快速回顾一下关联规则的关键组件:
1.支持
支持度表示两个条件同时出现的概率(即,相对于数据集的大小,同时包含条件A和条件B的事件数量)。
支持度=条件A和条件B发生的概率
2.信心
置信度表示条件A发生时条件B发生的概率。
置信度=条件A和条件B的概率除以条件A的概率
3.电梯
升力表示如果条件A发生,我们预测条件B会发生的确定程度。一般认为,升力值大于1反映出更强的关联。
升力=条件A下条件B发生的概率除以条件B发生的概率
工作流
解决这一业务挑战的工作流程如下:
- 摄取和准备数据。我们不会花太多时间讨论所采取的步骤,而是集中讨论以下两个步骤:
- 分析数据。在正常情况下,在执行更复杂的分析(如关联规则)之前,我们将花费大量时间执行探索性数据分析。虽然执行了这种探索性分析,但本文将不讨论它,相反,我们将在本文中集中讨论关联规则的使用。
- 将结果可视化,以便采取预防措施。出于以下两个原因,我们提供了两种非传统的关联规则可视化方法:
- 关联规则的关键原则(即支持、信任和提升)并不总是与业务涉众产生共鸣,导致混乱和随后的缺乏行动。因此,我们寻求更直观的可视化形式,以更好地促进理解和行动的呼吁。
- 我们认为,解决可预防的事故情况不太可能产生不利影响,因此,在我们的解释和可视化方面,不像在其他情况下可以接受的那样严格。
工作流提供了R和Python的混合,反映了两位作者的背景。
如果你想提高你的人力资源分析技能,并学习如何在R或其他统计程序中执行数据分析,请查看我们的88必威 !
1.摄取和准备数据
首先,我们下载了两种编码语言所需的库。接下来,我们加载英国道路安全数据(可在Kaggle网站).有两个数据集,事故信息和车辆信息。这两个数据集都非常大,所以我们只集中分析了2015年的数据,然后才合并了两个数据集,将事故和车辆状况结合起来。
从mlxtend.frequent_patterns导入关联规则
"""将事故和车辆数据加载为熊猫数据帧,缩小到2015年数据和需要的列合并两个数据帧""" " #创建事故数据数据帧df=pd.read_csv('/content/drive/MyDrive/Kaggle/意外信息。csv',low_memory=False) #根据数据大小设置low_memory为False #创建车辆数据数据帧df2=pd.read_csv('/content/drive/MyDrive/Kaggle/Vehicle_Information.csv', low_memory=False, encoding='windows-1252') #设置low_memory为False;将dataframes缩窄到2015数据,只使用2015数据。意外数据= df[df['Year'] == 2015] vehicle_data = df2[df2['Year'] == 2015] #重置索引并删除使用旧索引创建的新列。意外数据=意外数据。reset_index(drop=True)drop(['1st_Road_Number', '2nd_Road_Number', 'Did_Police_Officer_Attend_Scene_of_Accident', 'Latitude', 'Location_Easting_OSGR','Location_Northing_OSGR',\ '经度',' LSOA_of_Accident_Location', '行人穿越-人类控制',' Special_Conditions_at_Site', 'Year', 'InScotland'], axis=1, inplace=True)drop(['Junction_Location', 'model', 'Vehicle_Location.]Restricted_Lane', 'Year'], axis=1, inplace=True) #使用' incident_index '列merged = pd合并两个数据帧。merge(incident_data, vehicle_data, how='inner', on=['意外索引','意外索引'])
使用Apriori算法来寻找不同事故条件之间的关联需要分类数据,因此我们在进行分析之前准备并保存了一些数据。
"""转换数据类型以准备对非分类的Bin数据进行分类清理未使用的列""" #从'Date'列合并[' month '] = pd.DatetimeIndex(merged['Date'])中提取月份。convert time to integer merged['Hour'] = pd.to_datetime(merged[' time ']).dt。set_index('意外索引')#bin time into time of day merged['Time_of_Day'] = pd. set_index('意外索引')cut(merged['Hour'], bins=[0,5,12,17,24], include_lowest=True, labels=['Early Morning','Morning','Afternoon','Evening']) #删除速度限制为0或10的行,因为分别只有2个和3个-剩下6个值,所以不需要合并。loc[merged['Speed_limit'] == 0.0]。index, inplace=True) merge .drop(合并。loc[merged['Speed_limit'] == 10.0]。index, inplace=True) #删除车龄超过50的行loc[merged['Age_of_Vehicle'] > 50.0]。index, inplace=True) #bin车龄合并['Grouped_Vehicle_Age'] = pd。cut(merged['Age_of_Vehicle'], bins=[1,10,20,30,40,50], include_lowest=True, labels=['0-10','10-21','21-30','31-40', '41-50'], precision=0) #删除我们不再需要的列并/或转换为合并的bins。drop(['Date', 'Time', 'Hour', 'Age_of_Vehicle'], axis=1, inplace=True) #将月份整数转换为月份名称导入日历merged[' month '] = merged[' month ']。应用(λx:calendar.month_abbr[x]) #创建一个数据框架,只包含我们想要使用的变量-删除不再需要的列事故=合并[['事故严重性',' Carriageway_Hazards', '周日',\ 'Junction_Control', 'Junction_Detail','Light_Conditions', 'Road_Type', 'Speed_limit', 'Urban_or_Rural_Area', 'Weather_Conditions', 'Age_Band_of_Driver',\ ' journey_destin_of_driver ',' Sex_of_Driver', ' vehicle_action',' Month',\ 'Time_of_Day','Grouped_Vehicle_Age']]].copy() #转换速度限制为字符串,这样我们就可以一个热编码事故['Speed_limit'] =事故['Speed_limit'].astype(str)
最后,我们将重点放在代表事故前条件的变量上。然后对这些变量进行one-hot编码,这种方法允许分析人员确定在每次事件中哪些条件存在,哪些条件不存在(分别表示为“1”和“0”)。
"""一个热编码所有变量删除为缺失值创建的列""" #获取所有列的假人(一个热编码)意外= pd.get_dummies(意外)#删除缺失值的已识别列意外。下降([Carriageway_Hazards_Data失踪或的范围,“Junction_Control_Data失踪或的范围”,\“Light_Conditions_Darkness照明未知”,“Road_Surface_Conditions_Data失踪或的范围”,\“Road_Type_Unknown”、“Weather_Conditions_Unknown”,“Age_Band_of_Driver_Data失踪或的范围”,\“Journey_Purpose_of_Driver_Data失踪或的范围”,“Journey_Purpose_of_Driver_Not已知”,“Sex_of_Driver_Not已知”,\“Sex_of_Driver_Data失踪或的范围”,' vehicle_机动数据丢失或超出范围'],axis=1, inplace=True)
2.分析数据
我们的目标是综合大型安全数据集,以快速识别经常导致不同事件严重程度结果(称为后果)的事件前条件(关联规则中称为前项)。因此,我们决定运行三次分析,分别代表事故严重程度(即轻微、严重和致命)。我们的理由有两个:
- 不同的事件严重程度结果发生的几率不同,因此需要应用不同的支持和结果参数来确定可预防的情况
- 可能使从分析中获得的结果的优先级。
协会轻微事件规则
描述性分析显示,大多数事件是“轻微的”。因此,我们决定采用更高的支持参数0.1,这意味着条件组合在100个事件中至少发生10次。由于我们选择使用的可视化方法,我们只选择了2个前项的组合,并将Lift参数设为1以识别更强的关联。
#Apriori最小支持轻微-最高支持min_support= 0.1 #Apriori n-grams的最大长度max_len= 2 frequent_items = Apriori (accidents, use_colnames=True, min_support=min_support, max_len=max_len + 1) rules = association_rules(frequent_items, metric='lift', min_threshold=1) target =' {\' incident_severity_轻微\'}' results_attrition_轻微= rules[rules[' results '].astype(str).str.contains(target)]。sort_values(by='confidence', ascending=False) #添加一个'length'列来捕获前提条件的长度,以便进一步过滤后面的results_attrition_轻微['length'] = results_attrition_轻微['antecedents']。Apply (lambda x: len(x)) print(results_attrition_light .shape) results_attrition_light .shape
先行词 | 顺向 | 前期的支持 | 后续支持 | 支持 | 信心 | 电梯 | 利用 | 信念 | 长度 | |
---|---|---|---|---|---|---|---|---|---|---|
909 | (Urban_or_Rural_Area_Urban Sex_of_Driver_Female) | (Accident_Severity_Slight) | 0.176877 | 0.856135 | 0.159933 | 0.904204 | 1.056147 | 0.008502 | 1.501790 | 2 |
852 | (Sex_of_Driver_Female Speed_limit_30.0) | (Accident_Severity_Slight) | 0.172028 | 0.856135 | 0.155425 | 0.903488 | 1.055311 | 0.008146 | 1.490651 | 2 |
551 | (Junction_Control_Give way or controlled, Se… | (Accident_Severity_Slight) | 0.145231 | 0.856135 | 0.129877 | 0.894281 | 1.044557 | 0.005540 | 1.360831 | 2 |
708 | (Sex_of_Driver_Female Light_Conditions_Daylight) | (Accident_Severity_Slight) | 0.221362 | 0.856135 | 0.196881 | 0.889407 | 1.038863 | 0.007365 | 1.300850 | 2 |
920 | (Urban_or_Rural_Area_Urban Time_of_Day_Morning) | (Accident_Severity_Slight) | 0.216056 | 0.856135 | 0.192160 | 0.889402 | 1.038857 | 0.007187 | 1.300789 | 2 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
814 | (道路类型:单行车道,Age_Band_of_Dri… | (Accident_Severity_Slight) | 0.123554 | 0.856135 | 0.105908 | 0.857179 | 1.001219 | 0.000129 | 1.007309 | 2 |
652 | (天气条件好,没有大风,光… | (Accident_Severity_Slight) | 0.140382 | 0.856135 | 0.120307 | 0.857001 | 1.001011 | 0.000122 | 1.006055 | 2 |
5 | (Carriageway_Hazards_None) | (Accident_Severity_Slight) | 0.984736 | 0.856135 | 0.843640 | 0.856717 | 1.000680 | 0.000574 | 1.004066 | 1 |
1010 | (车距,时间… | (Accident_Severity_Slight) | 0.159898 | 0.856135 | 0.136937 | 0.856405 | 1.000315 | 0.000043 | 1.001879 | 2 |
453 | (Carriageway_Hazards_None Grouped_Vehicle_Age…… | (Accident_Severity_Slight) | 0.204550 | 0.856135 | 0.175177 | 0.856403 | 1.000314 | 0.000055 | 1.001869 | 2 |
144行× 10列
分析确定了144个关联规则,或144个与轻微事件相关的两种情况的组合,在100个事件中至少有10次。这些规则如下所示。
严重事故协会
严重事故比轻微事故少。因此,我们决定将支持参数设置为0.05,这意味着确定的条件组合必须在整个数据集中至少出现100次中的5次。其余参数保持不变(即,前项组合为2,Lift为1)。
#严重事故的Apriori最小支持-中等支持min_support= 0.05 #Apriori n-grams的最大长度max_len= 2 frequent_items = Apriori (accidents, use_colnames=True, min_support=min_support, max_len=max_len + 1) rules = association_rules(frequent_items, metric='lift', min_threshold=1) target =' {\' incident_severity_serious \'}' results_attrition_serious = rules[rules[' consequences '].astype(str).str.contains(target)]。sort_values(by='confidence', ascending=False) #添加一个'length'列来捕获前提条件的长度,以便进一步过滤后面的results_attrition_serious['length'] = results_attrition_serious['antecedents']。Apply (lambda x: len(x)) print(results_attrition_serious.shape) results_attrition_serious.shape
22种关联,或与严重事故相关的22种条件组合,在100起事故中至少有5起(总共)被确定。这些关联如下图所示。
致命事故的关联
描述性分析显示,与严重和轻微的事故相比,致命事故要少得多。尽管支持参数较低,为0.01,但在致命事件的前因之间没有发现关联,这可能暗示了非常随机的事件。
3.可视化的结果
#Apriori对致命事故的最小支持-最低支持min_support= 0.01 #Apriori n-grams的最大长度max_len= 2 frequent_items = Apriori (accidents, use_colnames=True, min_support=min_support, max_len=max_len + 1) rules = association_rules(frequent_items, metric='lift', min_threshold=1) target ='{\'意外_severity_fatal \'}' results_attrition_fatal = rules[rules[' consequences '].astype(str).str.contains(target)]。sort_values(by='confidence', ascending=False) #添加一个'length'列来捕获前项的长度,以便进一步过滤后面的results_attrition_fatal['length'] = results_attrition_fatal['antecedents']。Apply (lambda x: len(x)) results_attrition_fatal
经过一些实验,我们选择了两种方法来可视化轻微和严重事件的关联规则。这些方法是:
- 扩大树木;而且
- Rosetype图。
下面是轻微和严重事件的展开树图。这些图从左侧的事件类型开始,然后可以扩展到右侧,显示起作用的条件。圆圈的大小基于所识别的完整关联规则集中条件出现的次数。扩展树的主要好处是非技术人员可以很容易地解释和理解它。
#库库(tidyverse) # #──附加包───────────────────────────────────────tidyverse 1.3.1──# #✓ggplot2 3.3.5✓purrr 0.3.4 # #✓宠物猫3.1.5✓dplyr 1.0.7 # #✓tidyr 1.1.4✓stringr 1.4.0 # #✓readr 2.0.1✓forcats 0.5.1 # #──冲突──────────────────────────────────────────tidyverse_conflicts()──# # x dplyr:过滤()面具统计数据::x dplyr过滤()# #::滞后()面具统计:滞后()库(collapsibleTree)库(echarts4r) #清洁的先成列输出aa_cleaning < -函数(x) {x = stringr: str_remove_all(字符串= x,模式=“frozenset”)x = stringr:: str_remove_all(字符串= x,模式 = "\\(|\\)|\\{|\\}|\\'") x = stringr:: str_replace_all(字符串= x,模式=“_”,替换 = " ") } # 干净的规则可折叠树可视化duplex_cleaning < -函数(df) {df % > % dplyr:变异(前身= aa_cleaning(祖先),顺向= aa_cleaning(结果))% > % tidyr::单独的(前身为= c(“antecedent_1”,“antecedent_2”),9月 = ",") %>% dplyr:: mutate_at (var(包含("先行词"))~ stringr:: str_trim())} #清洁的规则Rosetype可视化simplex_cleaning < -函数(df) {df % > % dplyr:变异(前身= aa_cleaning(祖先),顺向= aa_cleaning(结果))% > % tidyr:: separate_rows(前身,9月 = ",") %>% dplyr:: mutate_at (var(包含("先行词"))~ stringr:: str_trim())} #创建一个可折叠树图aa_collapsibleTree_vis < -函数(df,label_name){折叠树(df, hierarchy = c("antecedent_1", "antecedent_2"), width = 700, height = 700, nodeSize = "leafCount",折叠= TRUE, zoomable = FALSE, root = label_name)} #创建一个Rosetype可视化clean_vis_simple_conditions <- function(df){df %>% mutate(antecedents = str_to_title(antecedents),antecedents = str_trim(antecedents)) %>% group_by(antecedents) %>% summarise(counter = n()) %>% ungroup() %>% arrange(counter) %>% mutate(antecedents = forcats::as_factor(antecedents)) %>% echarts4r::e_chart(antecedents) %>% e_pie(counter, name = "事件条件和计数",roseType = "radius") %>% echarts4r::e_title("") %>% echarts4r::e_legend(FALSE) %>% echarts4r::e_tooltip()} #加载并可视化-严重伤害serious_results_tbl <- readr::read_csv(file = "../02_outputs/serious_results.csv") %>% janitor::clean_names() ##新名称:## * ' ' ->…1 # #行:22列:11 # #──列规范────────────────────────────────────────────────────────# #分隔符:"," # #杆(2):先行词,顺向# #双(9):……1、前因支持,后因支持,支持,信心,……## ##ℹ使用' spec() '检索该数据的完整列规范。##ℹ指定列类型或设置' show_col_types = FALSE '来关闭此消息。serious_cleaned_tbl <- duplex_cleaning(serious_results_tbl) ##警告:预计2个。在6行中填满“NA”的缺失片段[1,4,10,# # 11,16,21]。 serious_injuries_tree <- aa_collapsibleTree_vis(serious_cleaned_tbl, "Serious Injuries") serious_cleaned_simple_tbl <- simplex_cleaning(serious_results_tbl) serious_injuries_rose <- clean_vis_simple_conditions(serious_cleaned_simple_tbl) htmlwidgets::saveWidget(serious_injuries_tree, file = "serious_injuries_tree.html") htmlwidgets::saveWidget(serious_injuries_rose, file = "serious_injuries_rose.html") # Load and Visualise - Slight Injuries slight_results_tbl <- readr::read_csv(file = "../02_outputs/results_attrition_slight.csv") %>% janitor::clean_names() ## New names: ## * `` -> ...1 ## Rows: 144 Columns: 11 ## ── Column specification ──────────────────────────────────────────────────────── ## Delimiter: "," ## chr (2): antecedents, consequents ## dbl (9): ...1, antecedent support, consequent support, support, confidence, ... ## ## ℹ Use `spec()` to retrieve the full column specification for this data. ## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message. slight_cleaned_tbl <- duplex_cleaning(slight_results_tbl) ## Warning: Expected 2 pieces. Missing pieces filled with `NA` in 21 rows [14, ## 28, 33, 64, 68, 70, 71, 73, 79, 82, 99, 102, 105, 106, 117, 125, 126, 129, 135, ## 137, ...]. slight_injuries_tree <- aa_collapsibleTree_vis(slight_cleaned_tbl, "Slight Injuries") slight_cleaned_simple_tbl <- simplex_cleaning(slight_results_tbl) slight_injuries_rose <- clean_vis_simple_conditions(slight_cleaned_simple_tbl) htmlwidgets::saveWidget(slight_injuries_tree, file = "slight_injuries_tree.html") htmlwidgets::saveWidget(slight_injuries_rose, file = "slight_injuries_rose.html")
扩展树图的轻微损伤:
严重损伤扩展树图:
玫瑰型图表(部分echarts4r图书馆)是经典饼图的翻版。在我们的上下文中,它表示我们生成的关联规则的进一步简化。在每条规则中确定两个条件(即,假设数据集中有两列),然后每条规则被简化为一个列(即,一个放在另一个上面)。最后,计算每个独特事件条件的计数。事件条件的计数被可视化。
罗斯型图最明显的好处是您可以快速清晰地描述和理解潜在的规则。在短短几分钟内,我们就收集了超过25万件记录在案的事件,并自信地确定了与这些事件最常见的共同发生的情况。可以说,这是快速产生洞察力的有力例子。
轻微损伤如玫瑰型图:
严重损伤如玫瑰型图:
在展开树图和玫瑰型图的情况下,缺点是可视化是对确定的底层规则的简单解释,并且丢失了算法的支持、置信度和提升细节。考虑到这一现实,我们使用关联规则的核心指标来生成一个简短的规则列表,然后将其可视化。虽然这些可视化可能不会吸引纯粹的统计主义者,但它们有可能邀请非技术观众,特别是商业领袖,以最小的代价来了解底层算法的“真相”。
关闭
我们使用关联规则来分析一个大型道路交通事件数据集,以确定可预防的事件条件。我们发现这种方法有助于快速合成安全数据,以确定干预点,从而达到“告诉我我需要知道什么”的目的。当我们使用道路交通事故时,这种方法很容易适用于组织安全数据,其中一位作者已经使用它来分析军事伤害数据集,以提供预防。
此外,我们试图探索可视化关联规则输出的新方法。与传统方法相比,展开Tree 's图和Rosetype图都提供了对关联规则输出的清晰且更易于访问的解释。所使用的可视化方法具有容易被非技术观众解释的优点,这在将结果与安全和/或组织领导进行社会化时可能是有益的。然而,他们都忽略了关联规则生成的技术指标,这可能会剥夺统计纯粹主义者的权利。
如果您对使用关联规则感兴趣,该方法可以应用于其他人力资源用例,包括:
- 摩擦-确定导致员工留在或离开组织的条件。你可以找到一个这种分析的例子在这里.
- 性能- -确定导致低绩效或高绩效的条件;而且
- 学习-确定员工经常一起完成的培训课程/模块,以提供推荐引擎(即,完成这门课程的人也参加了X、Y和Z课程)。
你准备好迎接HR的未来了吗?
在线学习现代和相关的人力资源技能