在最后一个字符旁边分割字符串

时间:2014-08-15 09:51:42

标签: regex r

我有一个数字变量DATE,表示最后两个字符为MONTH且前一个或两个字符为DAY的日期。我想将列拆分为MONTHDAY的单独列。

我可以使用以下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

感谢您提出任何建议。

4 个答案:

答案 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"

您还可以尝试sprintfsubstr的组合。

在这里,我将这些包装成一个给出答案的函数:

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