使用pythonpandas或者R,整理日历数据
Use python pandas or R, organize calendar data
我有一个记录员工出勤历史的数据框,如下所示:
ID Sunday Monday Tuesday Wednesday Thursday Friday Saturday
1585 NA NA NA NA NA NA NA
1585 NA S S S S H NA
1585 NA H S S NA NA NA
1585 NA S S S NA NA NA
1597 NA S S NA S NA NA
1597 NA NA NA NA NA H NA
1597 NA H S S NA NA NA
1597 NA NA NA NA NA NA NA
上面的样本中,有两个人用ID唯一标识,后面7列是从2017年4月1日开始的星期六到星期日,有3种考勤行为:S
表示生病休假,H
代表假期,NA
代表这个人那天正在工作。
兴趣是重新整理病假缺勤记录。例如,个人 1585 从 2017 年 4 月 10 日星期一开始请病假,到 2017 年 4 月 19 日星期三结束,持续 10 天。注意这10天里,有两天是当地的节假日,但是会被认为是属于这个病假的。然后,此人于 2017 年 4 月 24 日星期一开始第二次病假,并于 4 月 26 日星期三结束。
我们还有关于 ID 为 1597 的第二个人的记录,同样从 2017 年 4 月 1 日开始(因此对于每个人,记录的开始和结束日期是相同的)。此人有三个缺席期:第一个开始于 2017 年 4 月 3 日星期一,结束于次日 4 月 4 日。第二个咒语只持续一天,从 4 月 6 日开始到结束。最后一个咒语从4月18日开始,到4月19日结束。
期望的输出是这样的:
ID Begin_date End_date Duration
1585 2017-04-10 2017-04-19 10
1585 2017-04-24 2017-04-26 3
1597 2017-04-03 2017-04-04 2
1597 2017-04-06 2017-04-06 1
1597 2017-04-18 2017-04-19 2
我面临的困难是如何识别连续的病假日期,而且在一个病假期间,可以有不同类型的出勤类型(节假日),但节假日仍然被认为属于那个单一的病假咒语。
您提到您最大的问题之一是识别病假,因为病假可能包括假期和周末。我突然想到病假咒语可以表示为正则表达式。这是一个利用这一点的潜在解决方案:
首先,将周末的所有 NA 替换为 'D',将工作日的所有 NA 替换为 'Y'(或其他一些 2 个字符)。在您的示例中,正常周末与正常工作日的处理方式不同,因为缺勤可以包含正常周末但不包含正常工作日,因此它们应该具有不同的值。
然后,对于每个ID,将数据展平,并将其连接成一个字符串。缺席拼写可以通过正则表达式识别为 "(S[SHD]*S)|S"
。对于每个找到的正则表达式字符串,在新的 table 中创建一行,其中开始日期、结束日期和缺席咒语的持续时间基于找到的字符串的开始位置、结束位置和长度。
为了让这个解决方案起作用,我们需要假设每个 ID 都有相同数量的行对应相同的日期,这样我们才能正确地将字符串的开始位置映射到日期。
基于@Cholts的思想,我写了一个R代码来生成想要的输出
#clean the workspace
rm(list=ls(all=TRUE))
require(tidyr)
library(dplyr)
library(lubridate)
library(stringr)
ID = c(rep(1585,4),rep(1597,4))
Sun = c(rep("D",8))
Sat = c(rep("D",8))
Mon = c("Y","S","H","S","S","Y","H","Y")
Tue = c("Y","S","S","S","S","Y","S","Y")
Wed = c("Y","S","S","S","Y","Y","S","Y")
Thur = c("Y","S","Y","Y","S","Y","Y","Y")
Fri = c("Y","H","Y","Y","Y","H","Y","Y")
id_u = unique(ID)
df = data.frame(Sun,Mon,Tue,Wed,Thur,Fri,Sat)
new_df = df %>% unite(new,Sun,Mon,Tue,Wed,Thur,Fri,Sat,remove=FALSE,sep="")
vstr = new_df$new
#===========================================================
idd = c()
begin_date = c()
end_date = c()
duration = c()
n = 2
start_date = ymd('2017-04-02')
for(i in 1:n){
ps = (i-1)*4 +1
pe = (i-1)*4 + 4
indstr = paste(vstr[ps:pe],collapse = "")
loca = str_locate_all(indstr,"S[SHD]*S|S")
rn = length(loca[[1]][,1])
for (j in 1:rn){
idd = append(idd,id_u[i])
begin_date = append(begin_date,ymd(start_date+loca[[1]][j,1]-1))
end_date = append(end_date,ymd(start_date+loca[[1]][j,2]-1))
duration = append(duration,loca[[1]][j,2]-loca[[1]][j,1]+1)
}
}
final_df = data.frame(idd,begin_date,end_date,duration)
输出是
> final_df
idd begin_date end_date duration
1 1585 2017-04-10 2017-04-19 10
2 1585 2017-04-24 2017-04-26 3
3 1597 2017-04-03 2017-04-04 2
4 1597 2017-04-06 2017-04-06 1
5 1597 2017-04-18 2017-04-19 2
我有一个记录员工出勤历史的数据框,如下所示:
ID Sunday Monday Tuesday Wednesday Thursday Friday Saturday
1585 NA NA NA NA NA NA NA
1585 NA S S S S H NA
1585 NA H S S NA NA NA
1585 NA S S S NA NA NA
1597 NA S S NA S NA NA
1597 NA NA NA NA NA H NA
1597 NA H S S NA NA NA
1597 NA NA NA NA NA NA NA
上面的样本中,有两个人用ID唯一标识,后面7列是从2017年4月1日开始的星期六到星期日,有3种考勤行为:S
表示生病休假,H
代表假期,NA
代表这个人那天正在工作。
兴趣是重新整理病假缺勤记录。例如,个人 1585 从 2017 年 4 月 10 日星期一开始请病假,到 2017 年 4 月 19 日星期三结束,持续 10 天。注意这10天里,有两天是当地的节假日,但是会被认为是属于这个病假的。然后,此人于 2017 年 4 月 24 日星期一开始第二次病假,并于 4 月 26 日星期三结束。
我们还有关于 ID 为 1597 的第二个人的记录,同样从 2017 年 4 月 1 日开始(因此对于每个人,记录的开始和结束日期是相同的)。此人有三个缺席期:第一个开始于 2017 年 4 月 3 日星期一,结束于次日 4 月 4 日。第二个咒语只持续一天,从 4 月 6 日开始到结束。最后一个咒语从4月18日开始,到4月19日结束。
期望的输出是这样的:
ID Begin_date End_date Duration
1585 2017-04-10 2017-04-19 10
1585 2017-04-24 2017-04-26 3
1597 2017-04-03 2017-04-04 2
1597 2017-04-06 2017-04-06 1
1597 2017-04-18 2017-04-19 2
我面临的困难是如何识别连续的病假日期,而且在一个病假期间,可以有不同类型的出勤类型(节假日),但节假日仍然被认为属于那个单一的病假咒语。
您提到您最大的问题之一是识别病假,因为病假可能包括假期和周末。我突然想到病假咒语可以表示为正则表达式。这是一个利用这一点的潜在解决方案:
首先,将周末的所有 NA 替换为 'D',将工作日的所有 NA 替换为 'Y'(或其他一些 2 个字符)。在您的示例中,正常周末与正常工作日的处理方式不同,因为缺勤可以包含正常周末但不包含正常工作日,因此它们应该具有不同的值。
然后,对于每个ID,将数据展平,并将其连接成一个字符串。缺席拼写可以通过正则表达式识别为 "(S[SHD]*S)|S"
。对于每个找到的正则表达式字符串,在新的 table 中创建一行,其中开始日期、结束日期和缺席咒语的持续时间基于找到的字符串的开始位置、结束位置和长度。
为了让这个解决方案起作用,我们需要假设每个 ID 都有相同数量的行对应相同的日期,这样我们才能正确地将字符串的开始位置映射到日期。
基于@Cholts的思想,我写了一个R代码来生成想要的输出
#clean the workspace
rm(list=ls(all=TRUE))
require(tidyr)
library(dplyr)
library(lubridate)
library(stringr)
ID = c(rep(1585,4),rep(1597,4))
Sun = c(rep("D",8))
Sat = c(rep("D",8))
Mon = c("Y","S","H","S","S","Y","H","Y")
Tue = c("Y","S","S","S","S","Y","S","Y")
Wed = c("Y","S","S","S","Y","Y","S","Y")
Thur = c("Y","S","Y","Y","S","Y","Y","Y")
Fri = c("Y","H","Y","Y","Y","H","Y","Y")
id_u = unique(ID)
df = data.frame(Sun,Mon,Tue,Wed,Thur,Fri,Sat)
new_df = df %>% unite(new,Sun,Mon,Tue,Wed,Thur,Fri,Sat,remove=FALSE,sep="")
vstr = new_df$new
#===========================================================
idd = c()
begin_date = c()
end_date = c()
duration = c()
n = 2
start_date = ymd('2017-04-02')
for(i in 1:n){
ps = (i-1)*4 +1
pe = (i-1)*4 + 4
indstr = paste(vstr[ps:pe],collapse = "")
loca = str_locate_all(indstr,"S[SHD]*S|S")
rn = length(loca[[1]][,1])
for (j in 1:rn){
idd = append(idd,id_u[i])
begin_date = append(begin_date,ymd(start_date+loca[[1]][j,1]-1))
end_date = append(end_date,ymd(start_date+loca[[1]][j,2]-1))
duration = append(duration,loca[[1]][j,2]-loca[[1]][j,1]+1)
}
}
final_df = data.frame(idd,begin_date,end_date,duration)
输出是
> final_df
idd begin_date end_date duration
1 1585 2017-04-10 2017-04-19 10
2 1585 2017-04-24 2017-04-26 3
3 1597 2017-04-03 2017-04-04 2
4 1597 2017-04-06 2017-04-06 1
5 1597 2017-04-18 2017-04-19 2