【问题标题】:How to calculate the duration of employment spells如何计算工作期限
【发布时间】:2021-08-18 04:24:17
【问题描述】:

我拥有为期 1 年的每月员工就业状况数据。数据框包括 4 个变量:人员 ID、国家/地区、月份和该特定月份的主要活动(就业、失业、非活动、其他)。我在这里举了一个例子:

ID <- c(1:10, 1:10)
country <- c("AT", "BE", "CH", "CZ", "HR", "SO", "SV", "RU", "GR", "GE", "AT", "BE", "CH", "CZ", "HR", 
             "SO", "SV", "RU", "GR", "GE")
month <- c("Jan", "Feb", "Mar", "Apr", "May", "Aug", "Dec", "Nov", "Sep", "Jan", "Jun", "Jul", "Oct",
           "Jan", "Feb", "Mar", "Apr", "May", "Aug", "Dec")
act <- c("Unemployed", "Employed", "Other", "Other", "Inactive", "Unemployed", "Employed", 
         "Employed", "Employed", "Unemployed", "Other", "Unemployed", "Unemployed", "Unemployed", 
         "Other", "Other", "Employed", "Other", "Other", "NA")
df <- data.frame(ID, country, month, act)
df[order(ID),]

   ID country month        act
1   1      AT   Jan Unemployed
11  1      AT   Jun      Other
21  1      AT   Nov Unemployed
2   2      BE   Feb   Employed
12  2      BE   Jul Unemployed
22  2      BE   Sep Unemployed
3   3      CH   Mar      Other
13  3      CH   Oct Unemployed
23  3      CH   Jan         NA
4   4      CZ   Apr      Other
14  4      CZ   Jan Unemployed
24  4      CZ   Jun Unemployed
5   5      HR   May   Inactive
15  5      HR   Feb      Other
25  5      HR   Jul      Other
6   6      SO   Aug Unemployed
16  6      SO   Mar      Other
26  6      SO   Oct   Employed
7   7      SV   Dec   Employed
17  7      SV   Apr   Employed
27  7      SV   Nov   Employed
8   8      RU   Nov   Employed
18  8      RU   May      Other
28  8      RU   Jan         NA
9   9      GR   Sep   Employed
19  9      GR   Aug      Other
29  9      GR   Jun   Inactive
10 10      GE   Jan Unemployed
20 10      GE   Dec         NA
30 10      GE   Aug Unemployed

我的目标是创建一个新的数据框,其中每一行代表一个就业期但条件是就业期之前和之后必须是失业期。这样我就能够只包括人们从失业到就业再回到失业的就业咒语,并计算这些咒语的持续时间。理想情况下,最后会有 4 个变量:PersID、国家、拼写持续时间、开始月份、结束月份。它应该是这样的:

   ID country spell_duration starting ending
1   1      AT              5      Jan    May
11  1      AT              5      Jun    Oct
2   2      BE              7      Feb    Aug
12  2      BE              6      Jul    Dec
3   3      CH             10      Mar    Dec
13  3      CH              1      Oct    Oct
4   4      CZ              8      Apr    Nov
14  4      CZ              5      Jan    May
5   5      HR              5      May    Sep
15  5      HR              4      Feb    May
6   6      SO              2      Aug    Sep
16  6      SO              6      Mar    Aug
7   7      SV              1      Dec    Dec
17  7      SV              9      Apr    Dec
8   8      RU              8      Nov    Dec
18  8      RU              7      May    Nov
9   9      GR              3      Sep    Nov
19  9      GR              2      Aug    Sep
10 10      GE              8      Jan    Aug
20 10      GE              1      Dec    Dec

我已经找到了 Maria (How to calculate number and duration of categorical spells by ID in R) 的解决方案,但她的问题不同。我不想要工作的总时长,我也不需要咒语的数量

【问题讨论】:

  • 如何计算列startingending
  • 这是我的问题的一部分。抱歉,如果在帖子中不够清楚!
  • 更好地展示这两列是如何实现的逻辑。
  • 我不知道如何实现这两列,这是我的问题的一部分。我想要实现的是就业期的开始月份和结束月份以及持续时间。

标签: r


【解决方案1】:

我使用data.table 包进行操作,我猜循环正在工作。

编辑: 剩下一个额外的“}”,我对其进行了编辑。我试过了,效果很好。

EDIT2:我也添加了“setDT(df)”。

library(data.table)

 df <- fread(paste("ID country month        act
    1      AT   Jan Unemployed
    1      AT   Jun      Other
    1      AT   Nov Unemployed
    2      BE   Feb   Employed
    2      BE   Jul Unemployed
    2      BE   Sep Unemployed
    3      CH   Mar      Other
    3      CH   Oct Unemployed
    3      CH   Jan         NA
    4      CZ   Apr      Other
    4      CZ   Jan Unemployed
    4      CZ   Jun Unemployed
    5      HR   May   Inactive
    5      HR   Feb      Other
    5      HR   Jul      Other
    6      SO   Aug Unemployed
    6      SO   Mar      Other
    6      SO   Oct   Employed
    7      SV   Dec   Employed
    7      SV   Apr   Employed
    7      SV   Nov   Employed
    8      RU   Nov   Employed
    8      RU   May      Other
    8      RU   Jan         NA
    9      GR   Sep   Employed
    9      GR   Aug      Other
    9      GR   Jun   Inactive
    10      GE   Jan Unemployed
    10      GE   Dec         NA
    10      GE   Aug Unemployed", collapse = '\n'))
setDT(df)
df[, monthInt := match(month, month.abb)]

df <- df[order(ID,monthInt)]

finalDt <- data.table()
for (i in unique(df[, ID])) {
  
  tempT <- df[ID == i]
  
  for (tim in 1:(nrow(tempT)-1)) {
    timT <- data.table(ID = tempT[tim,ID],
                       country = tempT[tim, country],
                       spell_duration = tempT[tim+1, monthInt] - tempT[tim, monthInt],
                       starting = month.abb[tempT[tim, monthInt]],
                       ending = month.abb[tempT[tim+1, monthInt]-1])
    
    finalDt <- rbind(finalDt,timT)
    
  }
}

【讨论】:

  • 我用我的数据和 data.table 包进行了尝试,但出现“:=”错误。它说“检查 is.data.table(DT) == TRUE。否则,:= 和 :=(...) 被定义用于 j,仅一次且以特定方式使用。”我真的不明白为什么,因为当我使用您发布的示例数据集进行尝试时,它可以工作。
  • 剩下一个额外的“}”,我对其进行了编辑。我试过了,效果很好。
  • 最后我仍然得到一个错误:“bmerge 中的错误(i, x, leftcols, rightcols, roll, rollends, nomatch, mult, : Incompatible join types: x.ID is type integer64 but i.ID 是 double 类型,包含分数”。ID-Variable 不包含任何逗号,它是最多 7 位的整数。
  • 我的代码中没有任何合并,不明白。也许问题是您的数据。只需检查变量类并确保 ID 列是整数。在 data.table 包中,您可以这样做: df[, ID := as.integer(ID)]
【解决方案2】:

没想太多,第一时间就想到了。不过非常麻烦。我确信有更优雅的解决方案,但这不需要任何额外的包。

data <- df
Empl_spells <- data.frame(ID = c(), Start = c(), End = c())

for(user in unique(data$ID)){
  # subset per user
  user_dat <- data[data$ID == user,]
  # initiate a list to store where changes occur and a counter for
  # entries to this list
  if(nrow(user_dat) > 2){
    Changes_data <- list()
    entry <- 1
    # for every row, check if it switches from employed to unemployed
    # or the opposite. Mark with "break" if some other entry interrupts
    for(i in 2:nrow(user_dat)){
      if(user_dat$act[i] == "Employed" &
         user_dat$act[i-1] == "Unemployed"){
        Changes_data[[entry]] <- c("Start", i)
        entry <- entry + 1
      }else if(user_dat$act[i] == "Unemployed" &
               user_dat$act[i-1] == "Employed"){
        Changes_data[[entry]] <- c("End", i)
        entry <- entry + 1
      }else if(user_dat$act[i] != "Employed" &
               user_dat$act[i] != "Unemployed"){
        Changes_data[[entry]] <- c("Break", i)
        entry <- entry + 1
      }
    }
    # see where to an "End" follows a "Start" immediately in the new list
    Changes_df <- do.call(rbind.data.frame, Changes_data)
    EmplToUnempl <- which(Changes_df[-nrow(Changes_df), 1] == "Start" & Changes_df[-1, 1] == "End")
    if(length(EmplToUnempl) >= 1){
      append <- data.frame(ID = user,
                           Start = user_dat$month[as.numeric(Changes_df[EmplToUnempl, 2])],
                           End = user_dat$month[as.numeric(Changes_df[EmplToUnempl + 1, 2])-1])
      # append the data to the data.frame for all of the people
      Empl_spells <- rbind(Empl_spells, append)
    }
  }
}

由于我没有您的数据,因此我没有对此进行测试。这是你想要的吗?

编辑(矢量化;可能会更快):

data <- df

users <- unique(data$ID)
calculate <- function(user){
  # subset per user
  user_dat <- data[data$ID == user,]
  # initiate a list to store where changes occur and a counter for
  # entries to this list
  if(nrow(user_dat) > 2){
    Changes_data <- list()
    entry <- 1
    # for every row, check if it switches from employed to unemployed
    # or the opposite. Mark with "break" if some other entry interrupts
    for(i in 2:nrow(user_dat)){
      if(user_dat$act[i] == "Employed" &
         user_dat$act[i-1] == "Unemployed"){
        Changes_data[[entry]] <- c("Start", i)
        entry <- entry + 1
      }else if(user_dat$act[i] == "Unemployed" &
               user_dat$act[i-1] == "Employed"){
        Changes_data[[entry]] <- c("End", i)
        entry <- entry + 1
      }else if(user_dat$act[i] != "Employed" &
               user_dat$act[i] != "Unemployed"){
        Changes_data[[entry]] <- c("Break", i)
        entry <- entry + 1
      }
    }
    # see where to an "End" follows a "Start" immediately in the new list
    Changes_df <- do.call(rbind.data.frame, Changes_data)
    EmplToUnempl <- which(Changes_df[-nrow(Changes_df), 1] == "Start" & Changes_df[-1, 1] == "End")
    if(length(EmplToUnempl) >= 1){
      append <- data.frame(ID = user,
                           Start = user_dat$month[as.numeric(Changes_df[EmplToUnempl, 2])],
                           End = user_dat$month[as.numeric(Changes_df[EmplToUnempl + 1, 2])-1])
      # append the data to the data.frame for all of the people
      return(append)
    }
  }
}

empl_spells <- lapply(users, FUN = calculate)
Empl_spells <- do.call(rbind.data.frame, empl_spells)

编辑#2(计算持续时间):

MonthToNumeric <- function(x){
  which(c("Jan", "Feb", "Mar", "Apr", "May", "Jun",
          "Jul", "Aug", "Sep", "Oct", "Nov", "Dec") == x)
}

calcDuration <- function(Start, End){
  return(MonthToNumeric(End) - MonthToNumeric(Start) + 1)
}

Empl_spells$Duration <- mapply(FUN = calcDuration, Start = Empl_spells[, 2], End = Empl_spells[, 3])

【讨论】:

  • 抱歉回复晚了。我用我的数据进行了尝试,我收到一个名为“xi[[j]] 中的错误:'closure' 类型的对象不是子集”的错误。 (另外我认为}else if((user_dat$act[i] == "Unemployed" &amp; 中的括号太多了)
  • 现在我有了一些数据,我改变了一些东西。但是我猜它变得更加麻烦了......
  • 我认为它正在工作,因为没有错误。但这需要很长时间,20分钟后才得到560个就业法术。有什么方法可以加快这个过程,还是因为我正在使用的大数据集(>6.000.000 个观察值)?
  • 哦,也许您也知道如何以简单的方式计算每个就业法术的持续时间并将其添加到最终数据框中?
  • for 在 R 中很慢,通常使用 apply 时计算时间会更短。对于我的解决方案,lapply 适用于外部for 循环,其中代码适用于列表中的每个人。我在 Edit 中做了一些小改动。我还添加了第二个 Edit#2 函数,该函数将月份缩写替换为数值,并在结果中添加“持续时间”列。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-20
  • 2011-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多