使用R解决人员分析中的常见挑战
在人员分析工作中,您经常被要求讲述公司人数和公司如何发展到今天的故事。我经常看到这以瀑布图的形式展示,这很好,但是在向非技术人员分享年度变化时会变得混乱。
为了满足这个需求,我创建了迭代绘图,突出了每一年的一些额外上下文。然后,可以将这些绘图添加到幻灯片中,逐年进行演示,或者将它们一起制作成gif。让我们一起制作它!
挑战:讲述我们的人数如何年度变化到现在。
步骤:1. 加载必要的包和数据 2. 计算每月人数 3. 为每一年添加相关上下文 4. 创建绘图 5. 设置自动创建每年的绘图 6. 调整主题和绘图格式
1. 加载必要的包和数据
为了完成这个挑战,我们需要以下包:- tidyverse – hrbrthemes(用于使我们的绘图更美观)
为了创建我们的可视化效果,我们需要一个包含唯一标识符(例如员工ID)、入职日期和离职日期的文件。在这个示例中,我将使用模拟数据(在底部我已经包含了用于制作模拟数据的代码,如果您想一步一步跟随)。
# 加载包library(tidyverse)library(hrbrthemes)# 加载数据employee_data <- mock_data # 或者您可以使用类似 employee_data <- read.csv("input.csv")
顺便说一句,我通常将原始读入的数据分配给一个变量,然后创建一个新的变量用于未来的操作。这并不总是必要的,但是当使用大型数据集时,可以加快速度,这样每次需要更改代码时就不必重新加载数据。
为了使计算正确工作,我们需要确保R知道入职日期和离职日期实际上是日期。一般来说,在R中处理日期可能会很麻烦,但是为了完成这个挑战,我们需要将日期列格式化为日期,并且没有NA值。
df <- employee_data %>% mutate(Hire.Date = as.Date(Hire.Date, format = "%m/%d/%Y"), Termination.Date = as.Date(Termination.Date, format = "%m/%d/%Y"))
在我的输入文件中,仍在工作的员工的离职日期为空白,因为当然,他们还没有离职。如果日期列中有空白,R会变得混乱,因此我们将添加一行代码,为未来指定一个远期日期。
df <- employee_data %>% mutate(Hire.Date = as.Date(Hire.Date, format = "%m/%d/%Y"), Termination.Date = as.Date(Termination.Date, format = "%m/%d/%Y")) %>% mutate(Termination.Date = if_else(is.na(Termination.Date), as.Date("2100-12-31"), Termination.Date))
最后一行代码表示在终止日期列中有NA/空白的任何位置,都指定一个远期日期。在这种情况下,是2100年12月31日。希望我们都不要那时还在工作。
2. 计算每月员工人数
希望这一步看起来很简单,但是我在弄清楚它的过程中遇到了一些困难,所以请对自己有耐心。
首先,我们将创建一个序列,每个月都有一个日期,然后设置一个数据框作为我们每月员工人数的占位符,最后我们将使用sapply函数来计算每个月的员工人数。我们开始吧!
创建一个包含每个月日期的序列(例如,1/1/2023,1/2/2023等):
month_seq <- seq(from = min(df$hire_date), to = max(df$hire_date), by = "1 month")
这意味着从最早的入职日期开始,到最晚的入职日期结束,按月份进行排序。这样我们的数据中每个月都会有一个值。以下是它的样子:
现在我们要使用该序列创建一个起始数据框,然后在其中添加员工人数。
headcount_data <- data.frame(Date = month_seq)
好了,现在是棘手的部分。我们要计算headcount_data数据框中每个日期上活跃员工的数量。也就是说,在2020年1月1日、2020年2月2日等日期上有多少在职员工。
假设我们想计算2020年1月1日的情况。我们需要找出入职日期小于或等于2020年1月1日且离职日期大于2020年1月1日的员工数量。换句话说,已经入职但尚未离职的员工数量。
然后我们使用sapply在headcount_data的每个日期上进行计算。
headcount_data <- headcount_data %>% mutate(Active.Employees = sapply(Date, function(x) { sum(x >= df$hire_date & (is.na(df$termination_date) | x < df$termination_date)) }))
还跟上来了吗?如果你已经顺利完成到这一步,请给自己一个大大的鼓励!如果你遇到了问题,也给自己一个大大的鼓励,因为你已经走到这一步了,可以前往这里查看完整代码,看看你的代码是否有任何不一致之处。
3. 添加相关背景
这是故事叙述的部分。根据你对组织的熟悉程度和经验,你可能需要采访一些主题专家或资深员工。基本上,你想要添加有助于解释员工人数增加或减少的重要背景。
我想为每一年(也可以是每个月)添加背景,所以我要在headcount_data中添加一个年份列。
headcount_data <- headcount_data %>% mutate(year = as.integer(year(Date)))
这将为每个日期添加一个年份列:
现在,我们要为每年添加背景。假设我们想为2020年添加背景“COVID-19”,并希望在2020年的每个月都显示该背景。
为此,我们将使用case_when根据年份添加一个名为“context”的列。
headcount_data <- headcount_data %>% mutate(context = case_when( year == 2018 ~ "2018年的背景", year == 2019 ~ "2019年的背景", year == 2020 ~ "COVID-19", TRUE ~ "没有其他背景"))
在上面的代码中,我们指定了当年份为2018的每一行中,我们希望上下文列的值为”2018的上下文”。您可以为感兴趣的每一年添加上下文,在TRUE子句中,您可以指定未在上面指定的任何年份的上下文应该是什么。
到目前为止,您的headcount_data应该是这样的:
现在是有趣的部分!我们开始绘图。
4. 创建图表
首先,我们将使用ggplot创建一个包含所有数据的基本区域图。我们将在x轴上使用Date,在y轴上使用Active.Employees,以便我们可以看到随时间的人数变化。
headcount_data %>% ggplot(aes(x = Date, y = Active.Employees)) + geom_area()
这将给您一个基本的图表:
现在我们开始一些基本的改进,然后再进行一些高级的改进:1. 添加注释 2. 添加标题和副标题
我们将添加以年份结尾的注释(当我们为每年制作图表时,这将变得更加相关)。让我们从将它们分配给变量开始,以便于每年更新:
# 注释annotation_ending_year <- max(headcount_data$year)annotation_ending_headcount <- max(headcount_data$Active.Employees)# 标题labels_title <- "我们的人数故事"labels_subtitle <- last(headcount_data$context)
现在我们将把它们添加到我们的基本图表中:
headcount_data %>% ggplot(aes(x = Date, y = Active.Employees)) + geom_area() + labs(title = labels_title, subtitle = labels_subtitle) + annotate("text", x = max(headcount_data$Date), y = max(headcount_data$Active.Employees), label = annotation_ending_headcount, hjust = -.25)
这将为我们提供带有一些额外上下文的基本图表:
现在我们已经创建了基本图表,我们希望自动为每年创建一个加法图表。因此,会有从2018年到2018年末、从2018年到2019年末、从2018年到2020年末等等的图表。
5. 自动为每年创建一个图表
我们将使用for循环为数据集中的每一年创建一个图表。
基本上,我们将在名为”years”的向量中获取数据集中的每个唯一年份。然后对于”years”中的每个年份,我们将创建数据的子集,然后创建该子集的图表。这可能听起来令人困惑,但看看代码可能会更有意义。
首先进行一些设置:
# 创建一个唯一年份的向量years <- unique(headcount_data$year)# 用于存放图表的空列表plots <- list()
现在是循环的部分!它可能看起来很庞大,但只需一步一步来:
# 遍历years中的每个年份,并创建图表for (i in 2:length(years)) { # 逐渐添加一年的子集 subset_df <- headcount_data %>% filter(year <= years[i]) # 注释的计算 annotation_ending_year <- max(subset_df$Date) annotation_ending_active <- subset_df %>% filter(Date == ending_year) %>% select(Active.Employees) %>% as.numeric() # 使用子集创建图表(p) p <- subset_df %>% ggplot(aes(x = Date, y = Active.Employees)) + geom_area() + labs(title = labels_title, subtitle = labels_subtitle) + annotate("text", x = max(subset_df$Date), y = max(subset_df$Active.Employees), label = ending_active, hjust = -.25) # 保存每个图表 ggsave(p, file = paste("example_plot_", years[i], ".png"), height = 6, width = 8, units = "in")}
您现在应该在工作目录中保存了每年的一个图表,命名为“example_plot_year”。我喜欢每年都有一个单独的图表,这样我就可以将每个图表放在幻灯片中,并在人们有问题时暂停。或者,您可以将这些图表动画在一起,创建一个gif,或者使用像ScreenToGif这样的屏幕录制软件,得到类似以下的效果:
我们做到了!!!唯一剩下的就是添加一些样式,使图表更符合您的品牌,并添加一个矩形突出显示最近的年份。
6. 调整主题和图表格式
我想要做的第一件事是添加一个矩形,突出显示最近的年份。这将帮助观众知道要关注的地方,并且每个图表中都会更新,因此我们可以一次查看一年的数据,而不会混淆整体情况。
我们可以通过添加另一个注释层“rect”来实现,大致如下:
annotate("rect", xmin = , xmax = , ymin = , ymax = )
这是我花了一些时间才达到自己想要的效果的另一个方面,关键在于:
X轴:我希望矩形从给定年份的第一个(即最早)日期(即数据子集中的最大年份)开始,并在给定年份的最后(即最晚)日期(即数据子集中的最大年份)结束。因此,对于2019年的图表,我们希望矩形从1/1/2019开始,到12/1/2019结束。
annotate("rect", xmin = floor_date(max(subset_df$Date), "year"), xmax = ceiling_date(max(subset_df$Date), "year")
Y轴:我希望矩形从y轴开始,并在该年份的最终人数上方结束,这样就更容易阅读,而不会拥挤。再次看看2019年,我希望矩形直接从y轴开始,到最终人数的上方(+300)结束,最终人数为240。
annotate("rect", xmin = floor_date(max(subset_df$Date), "year"), xmax = ceiling_date(max(subset_df$Date), "year"), ymin = -Inf, ymax = ending_active + 300)
样式:最后,我将使矩形变为灰色,并将alpha值设置为0.1,使其相对透明,您可以看到底部的面积图:
annotate("rect", xmin = floor_date(max(subset_df$Date), "year"), xmax = ceiling_date(max(subset_df$Date), "year"), ymin = -Inf, ymax = ending_active + 300, alpha = .1, color = "gray", fill = "gray")
限制坐标轴:为了使过渡更平滑,我将对x轴和y轴设置限制,使每个图表的比例相同。
scale_x_date(breaks = "1 year", date_labels = "%Y", expand = c(.1,.1), limits = c(min(headcount_data$Date), max(headcount_data$Date)))
哇呜!我们离成功很近了,现在我要对主题进行一些更改,然后给自己倒一杯酒。现在是时候将自己的风格加入其中了,我的最终效果看起来可能是这样的:
这是我最终的for循环代码:
# 遍历每年的循环,创建图表for (i in 2:length(years)) { # 逐年创建子集 subset_df <- headcount_data %>% filter(year <= years[i]) # 注释计算 ending_year <- max(subset_df$Date) ending_active <- subset_df %>% filter(Date == ending_year) %>% select(Active.Employees) %>% as.numeric() # 使用子集创建图表(p) p <- subset_df %>% ggplot(aes(x = Date, y = Active.Employees)) + geom_area(fill = "#457b9d") + labs(title = "我们的人数变化故事", subtitle = paste(years[i],":", last(subset_df$context)), x = "", y = "") + scale_x_date(breaks = "1 year", date_labels = "%Y", expand = c(.1,.1), limits = c(min(headcount_data$Date), max(headcount_data$Date))) + theme_classic(base_family = "Arial") + theme(plot.title = element_text(size = 24, face = "bold", color = "#457b9d"), plot.subtitle = element_text(size = 18), panel.grid.major = element_blank(), panel.grid.minor = element_blank(), axis.ticks.y = element_blank(), axis.text.y = element_blank(), axis.line.y = element_blank()) + annotate("text", x = ending_year, y = ending_active, label = ending_active, vjust = -1.25, hjust = -.25, color = "#457b9d") + annotate("rect", xmin = floor_date(max(subset_df$Date), "year"), xmax = ceiling_date(max(subset_df$Date), "year"), ymin = -Inf, ymax = ending_active + 300, alpha = .1, color = "gray", fill = "gray") # 保存每个图表 ggsave(p, file = paste("example_plot_final", years[i], ".png"), height = 6, width = 8, units = "in") }
ALLLLLL DOOOOONNNNEEEE!
我们现在可以动态地查看我们的人数随时间的变化,副标题中还提供了一些背景信息。一些未来的改进想法:使用gganimate制作图表,为每年的人数变化添加百分比,如果人数增加或减少,改变图表的颜色,添加增长趋势线的预测等等,可能性无限!
你试过制作一个吗?如果是的话,我很想看看你的成果!
Github上的完整代码在这里。
想要更多关于人力资源分析的资源吗?
12个免费资源帮助你入门人力资源分析
我推荐给那些想要开始从事人力资源分析职业的人的免费资源。
jeagleson.medium.com
如果你想要更多这样的资源并且可以访问本站的所有精彩内容,你可以使用我的链接每月支付5美元(我将获得一小笔佣金,您不需要额外付费)。
使用我的推荐链接加入VoAGI – Jenna Eagleson
阅读Jenna Eagleson(以及VoAGI上数千名其他作者的所有故事)。您的会员费直接支持…
jeagleson.medium.com
Jenna Eagleson 我的背景是工业与组织心理学,我在人力资源分析领域找到了我的归属。数据可视化使我的工作更有生命力。我喜欢学习和使用Power BI、R、Tableau等工具进行开发。我很想听听你的故事!请在Linkedin或Twitter上联系我。