我有一个数字变量DATE
,表示最后两个字符为MONTH
且前一个或两个字符为DAY
的日期。我想将列拆分为MONTH
和DAY
的单独列。
我可以使用以下R
代码执行此操作。虽然我希望有一个更简单的regex
解决方案。
my.data <- read.table(text = '
ID DATE VARX
A111 104 0
A111 204 1
A111 1004 4
A111 2004 4
B111 3004 2
C111 3004 3
C111 105 4
C111 1005 4
', header = TRUE, stringsAsFactors = FALSE)
# remove the last two characters of a string
my.data$DAY <- ifelse(nchar(my.data$DATE) == 3,
substr(my.data$DATE, nchar(my.data$DATE) - (nchar(my.data$DATE)-1), nchar(my.data$DATE) - (nchar(my.data$DATE)-1)),
substr(my.data$DATE, nchar(my.data$DATE) - (nchar(my.data$DATE)-1), nchar(my.data$DATE) - (nchar(my.data$DATE)-2)))
# keep the last two characters of a string
my.data$MONTH <- substr(my.data$DATE, (nchar(my.data$DATE)-1), nchar(my.data$DATE))
ID DATE VARX DAY MONTH
1 A111 104 0 1 04
2 A111 204 1 2 04
3 A111 1004 4 10 04
4 A111 2004 4 20 04
5 B111 3004 2 30 04
6 C111 3004 3 30 04
7 C111 105 4 1 05
8 C111 1005 4 10 05
感谢您提出任何建议。
答案 0 :(得分:5)
以下是一些替代方案。第一个是最简洁的。前两个只使用基数R.
1)数字操作
transform(my.data, MONTH = DATE %% 100, DAY = DATE %/% 100)
,并提供:
ID DATE VARX MONTH DAY
1 A111 104 0 4 1
2 A111 204 1 4 2
3 A111 1004 4 4 10
4 A111 2004 4 4 20
5 B111 3004 2 4 30
6 C111 3004 3 4 30
7 C111 105 4 5 1
8 C111 1005 4 5 10
2)sub 这与(1)中的结果相同。
spl <- function(x, replace) as.numeric(sub("(.*)(..)", replace, x))
transform(my.data, MONTH = spl(DATE, "\\2"), DAY = spl(DATE, "\\1"))
3)strapply 将as.numeric
应用于括号中的匹配部分并返回它。这给出了与(1)中相同的结果。
library(gsubfn)
spl <- function(x, rx) strapply(x, rx, as.numeric, simplify = TRUE)
transform(my.data, MONTH = spl(DATE, ".*(..)"), DAY = spl(DATE, "(.*).."))
注意它们都会返回看起来更合适的数字列,但是如果您想在(1)中更改as.character(...)
或适当的sprintf
,请忽略{{1}在(2)中或用as.numeric
替换(3)中的as.numeric
。
更新已添加2和3并进行了一些改进。
答案 1 :(得分:3)
您可以使用sprintf
,然后使用substr
my.data <- structure(list(ID = c("A111", "A111", "A111", "A111", "B111",
"C111", "C111", "C111"), DATE = c(104L, 204L, 1004L, 2004L, 3004L,
3004L, 105L, 1005L), VARX = c(0L, 1L, 4L, 4L, 2L, 3L, 4L, 4L)), .Names = c("ID",
"DATE", "VARX"), class = "data.frame", row.names = c(NA, -8L))
val <- sprintf("%04d", my.data$DATE)
my.data$DAY <- as.numeric(substr(val,1,2))
my.data$MONTH <- substr(val,3,4)
my.data
# ID DATE VARX DAY MONTH
#1 A111 104 0 1 04
#2 A111 204 1 2 04
#3 A111 1004 4 10 04
#4 A111 2004 4 20 04
#5 B111 3004 2 30 04
#6 C111 3004 3 30 04
#7 C111 105 4 1 05
#8 C111 1005 4 10 05
或者,您可以尝试:
library(stringr)
my.data1 <- cbind(my.data, do.call(rbind,str_extract_all(my.data$DATE, perl('\\d+(?=[0-9]{2}$)|(?<=[0-9])\\d{2}'))))
colnames(my.data1)[4:5] <- c("DAY", "MONTH")
my.data1
# ID DATE VARX DAY MONTH
#1 A111 104 0 1 04
#2 A111 204 1 2 04
#3 A111 1004 4 10 04
#4 A111 2004 4 20 04
#5 B111 3004 2 30 04
#6 C111 3004 3 30 04
#7 C111 105 4 1 05
#8 C111 1005 4 10 05
答案 2 :(得分:2)
要使用正则表达式,您可以尝试:
dat <- c(104, 204, 1004, 2004, 3004, 3004, 105, 1005)
day <- gsub("(.*?)(..)", "\\1", dat)
day
[1] "1" "2" "10" "20" "30" "30" "1" "10"
mth <- gsub("(.*?)(..)", "\\2", dat)
mth
[1] "04" "04" "04" "04" "04" "04" "05" "05"
您还可以尝试sprintf
和substr
的组合。
在这里,我将这些包装成一个给出答案的函数:
foo <- function(x){
dat <- sprintf("%04d", x)
cbind(day=substr(dat, 1, 2), month=substr(dat, 3, 4))
}
foo(dat)
foo(dat)
day month
[1,] "01" "04"
[2,] "02" "04"
[3,] "10" "04"
[4,] "20" "04"
[5,] "30" "04"
[6,] "30" "04"
[7,] "01" "05"
[8,] "10" "05"
答案 3 :(得分:1)
我真的不明白为什么使用正则表达式会使问题复杂化,因为您已经知道最后2位数对应于月份而剩余的数字对应于当天。但是,如果你真的想使用正则表达式怎么样
my.data$DAY <- gsub("^([0-9])?([0-9])([0-9])([0-9])$", "\\1\\2", my.data$DATE)
my.data$MONTH <- gsub("^([0-9])?([0-9])([0-9])([0-9])$", "\\3\\4", my.data$DATE)
my.data
# ID DATE VARX DAY MONTH
# A111 104 0 1 04
# A111 204 1 2 04
# A111 1004 4 10 04
# A111 2004 4 20 04
# B111 3004 2 30 04
# C111 3004 3 30 04
# C111 105 4 1 05
# C111 1005 4 10 05