How to do ranges with string data type in R data.table?

问题描述 投票:0回答:3

下面是一个示例数据集(A列和B列),我想创建一个新的标志列C,将第3行到第8行分组到“91345-912350”范围内(在真实数据集中,我有多个范围分组到)。我无法将 A 列转换为整数,因为它会导致前导 0 的代码消失(例如 0450 将变为 450)。有什么方法可以识别字符串数据类型的范围吗?

请随意使用以下代码生成示例数据集:

DT = data.table(
  Code = c("0450", "912345", "912346","912347","912348","912349","912350","7860","X7960"),
  Description  = c("Desc A", "Desc B", "Desc C","Desc D","Desc E","Desc F","Desc G","Desc H","Desc I")
)    

附加信息:

(1) 代码不需要连续,因为范围是预定义的。只要代码在某个范围之间,它就会进入该范围组,无论是否有任何缺失的代码。 (例如,如果缺少 912347,则 912345、912346、91238、912349、912350 仍会进入 912345-912350 范围)

(2)预定义的范围都是没有字母的数字范围,如果代码中包含字母,代码组列将与原始代码列相同,就像单元格C10和单元格A10中所示。

(3) 并非所有代码都需要在一个范围内,我有一个预定义范围列表,我需要将代码分组到。在此示例数据集中,我的预定义范围是 912345-912350。可能有 7860、7861、7862 之类的代码,但我不需要将它们分组到 7860-7862 中,因为它们不在我的预定义范围内。如果代码没有范围组,代码组将返回与代码列相同的值,就像单元格 C9 和单元格 A9 中显示的一样。

r string data.table range leading-zero
3个回答
0
投票

这里的要求对我来说不是很清楚,但是你可以创建一个函数来将代码映射到一个范围。例如

mapRange <- function(code, min=912345, max=912350) { 
  return (sapply(code, function(c) {
    ic <- as.integer(c)
    if (is.na(ic)) return(c)
    if (ic >= min & ic <= max) return(paste(min, max, sep="-"))
    return(c)
  }))
}

然后

DT$`Code Range` <- mapRange(DT$Code)

或使用 tidyverse

DT <- DT %>% mutate(`Code Range` = mapRange(Code))

0
投票

我只能提供一个

dplyr
解决方案: 我已经使用了第 2 行到第 7 行。我们可以根据需要进行调整。

library(dplyr)

DT %>% 
  filter(row_number() >= 2 & row_number() <= 7) %>%
  type.convert(as.is = TRUE) %>% 
  mutate(Cod_Group = paste(Code, last(Code)-1 +row_number(), sep = " - ")) %>% 
  mutate(across(everything(), ~as.character(.))) %>% 
  bind_rows(DT) %>% 
  distinct(Code, .keep_all = TRUE) %>% 
  mutate(Cod_Group = coalesce(Code, Cod_Group)) %>% 
  arrange(Description)
     Code Description Cod_Group
1:   0450      Desc A      0450
2: 912345      Desc B    912345
3: 912346      Desc C    912346
4: 912347      Desc D    912347
5: 912348      Desc E    912348
6: 912349      Desc F    912349
7: 912350      Desc G    912350
8:   7860      Desc H      7860
9:  X7960      Desc I     X7960

0
投票

我做了更广泛的解决方案,涵盖了更多场景。请参阅我的扩展示例以及一些要测试的边缘案例。虽然我不允许范围内有间隙,但它支持具有前缀的 ID(包括填充零)。为了支持这一点,我们使用了一些辅助列并将所有结束数字视为数字(不包括填充零),并且在此之前我考虑了一个前缀(包括填充零)。这样我们就可以防止不同的 ID 前缀落在同一个组中。通过一些技巧,我们分配组 ID,然后获取每个组的第一个值和最后一个值并将它们粘贴在一起(如果组中有多个 id)。

解决方案

library(data.table)
library(stringr)

DT[, num := as.numeric(gsub("(.*)[^\\d](\\d*$)", "\\2", Code, perl = T))]
DT[, prefix := str_remove(Code, as.character(num))][prefix == "", prefix := NA_character_]
DT[, s := abs(num - shift(num, 1, "lag")), prefix][is.na(s) | s > 1, grp := .I][, s := NULL]
setnafill(DT, "locf", cols = "grp")
DT[, `Code Group` := fifelse(.SD[1] != .SD[.N], paste(.SD[1], .SD[.N], sep = " - "), unlist(.SD[1])), 
   by = grp, .SDcols = "Code"
]

辅助列的结果

DT

      Code Description    num prefix grp      Code Group
 1:      1      Desc A      1   <NA>   1           1 - 3
 2:      2      Desc B      2   <NA>   1           1 - 3
 3:      3      Desc C      3   <NA>   1           1 - 3
 4:    448      Desc D    448   <NA>   2             448
 5:   0449      Desc E    449      0   3     0449 - 0450
 6:   0450      Desc F    450      0   3     0449 - 0450
 7: 912345      Desc G 912345   <NA>   4 912345 - 912350
 8: 912346      Desc H 912346   <NA>   4 912345 - 912350
 9: 912347      Desc I 912347   <NA>   4 912345 - 912350
10: 912348      Desc J 912348   <NA>   4 912345 - 912350
11: 912349      Desc K 912349   <NA>   4 912345 - 912350
12: 912350      Desc L 912350   <NA>   4 912345 - 912350
13:   7860      Desc M   7860   <NA>   5            7860
14:  X7960      Desc N   7960      X   6   X7960 - X7961
15:  X7961      Desc O   7961      X   6   X7960 - X7961
16:  Y7962      Desc P   7962      Y   7           Y7962
17:  Z7963      Desc Q   7963      Z   8           Z7963

清理结果

DT[, .SD, .SDcols = c(1, 2, 6)]

      Code Description      Code Group
 1:      1      Desc A           1 - 3
 2:      2      Desc B           1 - 3
 3:      3      Desc C           1 - 3
 4:    448      Desc D             448
 5:   0449      Desc E     0449 - 0450
 6:   0450      Desc F     0449 - 0450
 7: 912345      Desc G 912345 - 912350
 8: 912346      Desc H 912345 - 912350
 9: 912347      Desc I 912345 - 912350
10: 912348      Desc J 912345 - 912350
11: 912349      Desc K 912345 - 912350
12: 912350      Desc L 912345 - 912350
13:   7860      Desc M            7860
14:  X7960      Desc N   X7960 - X7961
15:  X7961      Desc O   X7960 - X7961
16:  Y7962      Desc P           Y7962
17:  Z7963      Desc Q           Z7963

样本数据

codes <- c(1,2,3, "448", "0449", "0450", "912345", "912346", "912347", "912348", "912349", "912350", "7860", "X7960", "X7961", "Y7962", "Z7963")
DT = data.table(Code = codes, Description  = paste("Desc", LETTERS[1:length(codes)]))
© www.soinside.com 2019 - 2024. All rights reserved.