在R中的字符串后提取日期
Extract date after string in R
我正在尝试使用 tidyr 的提取功能从 Notes
列中提取日期。我正在处理的数据如下所示:
dates <- data.frame(col1 = c("customer", "customer2", "customer3"),
Notes = c("DOB: 12/10/62
START: 09/01/2019
END: 09/01/2020", "
S/DATE: 28/08/19
R/DATE: 27/08/20", "DOB: 13/01/1980
Start:04/12/2018"),
End_date = NA,
Start_Date = NA )
我试过像这样在字符串“S/DATE”之后提取日期:
extract <- extract(
dates,
col = "Notes",
into = "Start_date",
regex = "(?<=(S\/DATE:)).*" # Using regex lookahead
)
但是,这只会提取字符串“S/DATE:”,而不是其后的日期。当我在 regex101.com 上尝试这个时,它按预期工作。
谢谢。易卜拉欣
您可以在此处使用 sub
作为基础 R 选项:
s_date <- ifelse(grepl("S/DATE", dates$Notes),
sub("^.*\bS/DATE: (\S+).*$", "\1", dates$Notes), NA)
s_date
[1] NA "28/08/19" NA
请注意,此处需要调用上面的 grepl
,因为默认情况下 sub
将 return 整个输入字符串(在本例中为完整的 Notes
)如果 S/DATE
是 而不是 在文本中找到。
一种方法也可以像这样。 (假设您需要 S/DATE
或 START
之一,因为您预期的新列名称是 Start_date)。但是,如果不需要所有这些值,您可以轻松修改此语法。
解释-
- 最里面的 expr
Notes
列已被这些分隔符 :
或 \n
. 拆分为列表
- 在此列表中,空格被删除
- 在修改后的列表中,
Start
或 S/Date
旁边的项目使用 sapply
提取,这将列表简化为向量(如果可能)
- 最后
lubridate::dmy
用在最外层表达式
sapply(strsplit(dates$Notes,
"[: | \n]"),
function(x) subset(x, x != "")[1 + which(toupper(subset(x, x != "")) %in% c("S/DATE", "START"))])
[1] "09/01/2019" "28/08/19" "04/12/2018"
如果您将以上内容包含在 lubridate::dmy
中,日期的格式也会正确
dmy(sapply(strsplit(dates$Notes,
"[: | \n]"),
function(x) subset(x, x != "")[1 + which(toupper(subset(x, x != "")) %in% c("S/DATE", "START"))]))
[1] "2019-01-09" "2019-08-28" "2018-12-04"
此外,这可以传递到 dplyr 管道中,以便同时在您的 dates
中创建一个新列
dates %>% mutate(Start_Date = dmy(sapply(strsplit(Notes,
"[: | \n]"),
function(x) subset(x, x != "")[1 + which(toupper(subset(x, x != "")) %in% c("S/DATE", "START"))])))
col1 Notes End_date Start_Date
1 customer DOB: 12/10/62\nSTART: 09/01/2019\nEND: 09/01/2020 NA 2019-01-09
2 customer2 \nS/DATE: 28/08/19\nR/DATE: 27/08/20 NA 2019-08-28
3 customer3 DOB: 13/01/1980\nStart:04/12/2018 NA 2018-12-04
另一种方法是拆分文本并处理更小的块。
一步一步的插图,一行数据
# Split the text on newlines, yielding dates with labels
dates$Notes %>% head(1) %>% strsplit("\n")
[[1]]
[1] "DOB: 12/10/62" "START: 09/01/2019" "END: 09/01/2020"
深入到下一个层次
# Split each name/value pair on colons
dates$Notes %>% head(1) %>% strsplit("\n") %>%
unlist() %>% strsplit(":\s*")
[[1]]
[1] "DOB" "12/10/62"
[[2]]
[1] "START" "09/01/2019"
[[3]]
[1] "END" "09/01/2020"
提取单个值
# extract a vector of name labels
dates$Notes %>% head(1) %>% strsplit("\n") %>%
unlist() %>% strsplit(":\s*") %>%
sapply(function(x) x[1])
[1] "DOB" "START" "END"
# extract a vector of associated values
dates$Notes %>% head(1) %>% strsplit("\n") %>%
unlist() %>% strsplit(":\s*") %>%
sapply(function(x) x[2])
[1] "12/10/62" "09/01/2019" "09/01/2020"
通过一些巧妙的dplyr
用法,您将得到一个数据框
dates %>%
group_by(col1) %>%
# summarize can collapse many rows into one or expand one into many
summarize(
name = Notes %>% strsplit("\n") %>%
unlist() %>% strsplit(":\s*") %>%
sapply(function(x) x[1]),
value = Notes %>% strsplit("\n") %>%
unlist() %>% strsplit(":\s*") %>%
sapply(function(x) x[2])
) %>%
ungroup()
结果,所有值都已分离并准备好进行进一步处理
# A tibble: 8 x 3
col1 name value
<chr> <chr> <chr>
1 customer DOB 12/10/62
2 customer START 09/01/2019
3 customer END 09/01/2020
4 customer2 NA NA
5 customer2 S/DATE 28/08/19
6 customer2 R/DATE 27/08/20
7 customer3 DOB 13/01/1980
8 customer3 Start 04/12/2018
我会合并 stringr
和 lubridate
:
dates %>%
mutate(
Start_Date =
sub("\ns/date:", "\nstart:", tolower(Notes)) %>%
str_remove_all("(.*\nstart:)|(\n.*)") %>%
trimws() %>%
lubridate::dmy()
)
# col1 Notes End_date Start_Date
# 1 customer DOB: 12/10/62\nSTART: 09/01/2019\nEND: 09/01/2020 NA 2019-01-09
# 2 customer2 \nS/DATE: 28/08/19\nR/DATE: 27/08/20 NA 2019-08-28
# 3 customer3 DOB: 13/01/1980\nStart:04/12/2018 NA 2018-12-04
答案没有那么简洁,但我觉得很直观,也很容易遵循这些步骤。
首先,我将一个 start
模式替换为另一个 (sub
),其中我使用 tolower
来制作所有小写字母。然后我删除开始日期之前的所有内容,以及行更改 str_remove_all
之后的所有内容。最后我trim空格(trimws
)转成日期(lubridate::dmy
).
我正在尝试使用 tidyr 的提取功能从 Notes
列中提取日期。我正在处理的数据如下所示:
dates <- data.frame(col1 = c("customer", "customer2", "customer3"),
Notes = c("DOB: 12/10/62
START: 09/01/2019
END: 09/01/2020", "
S/DATE: 28/08/19
R/DATE: 27/08/20", "DOB: 13/01/1980
Start:04/12/2018"),
End_date = NA,
Start_Date = NA )
我试过像这样在字符串“S/DATE”之后提取日期:
extract <- extract(
dates,
col = "Notes",
into = "Start_date",
regex = "(?<=(S\/DATE:)).*" # Using regex lookahead
)
但是,这只会提取字符串“S/DATE:”,而不是其后的日期。当我在 regex101.com 上尝试这个时,它按预期工作。
谢谢。易卜拉欣
您可以在此处使用 sub
作为基础 R 选项:
s_date <- ifelse(grepl("S/DATE", dates$Notes),
sub("^.*\bS/DATE: (\S+).*$", "\1", dates$Notes), NA)
s_date
[1] NA "28/08/19" NA
请注意,此处需要调用上面的 grepl
,因为默认情况下 sub
将 return 整个输入字符串(在本例中为完整的 Notes
)如果 S/DATE
是 而不是 在文本中找到。
一种方法也可以像这样。 (假设您需要 S/DATE
或 START
之一,因为您预期的新列名称是 Start_date)。但是,如果不需要所有这些值,您可以轻松修改此语法。
解释-
- 最里面的 expr
Notes
列已被这些分隔符:
或\n
. 拆分为列表
- 在此列表中,空格被删除
- 在修改后的列表中,
Start
或S/Date
旁边的项目使用sapply
提取,这将列表简化为向量(如果可能) - 最后
lubridate::dmy
用在最外层表达式
sapply(strsplit(dates$Notes,
"[: | \n]"),
function(x) subset(x, x != "")[1 + which(toupper(subset(x, x != "")) %in% c("S/DATE", "START"))])
[1] "09/01/2019" "28/08/19" "04/12/2018"
如果您将以上内容包含在 lubridate::dmy
中,日期的格式也会正确
dmy(sapply(strsplit(dates$Notes,
"[: | \n]"),
function(x) subset(x, x != "")[1 + which(toupper(subset(x, x != "")) %in% c("S/DATE", "START"))]))
[1] "2019-01-09" "2019-08-28" "2018-12-04"
此外,这可以传递到 dplyr 管道中,以便同时在您的 dates
dates %>% mutate(Start_Date = dmy(sapply(strsplit(Notes,
"[: | \n]"),
function(x) subset(x, x != "")[1 + which(toupper(subset(x, x != "")) %in% c("S/DATE", "START"))])))
col1 Notes End_date Start_Date
1 customer DOB: 12/10/62\nSTART: 09/01/2019\nEND: 09/01/2020 NA 2019-01-09
2 customer2 \nS/DATE: 28/08/19\nR/DATE: 27/08/20 NA 2019-08-28
3 customer3 DOB: 13/01/1980\nStart:04/12/2018 NA 2018-12-04
另一种方法是拆分文本并处理更小的块。
一步一步的插图,一行数据
# Split the text on newlines, yielding dates with labels
dates$Notes %>% head(1) %>% strsplit("\n")
[[1]]
[1] "DOB: 12/10/62" "START: 09/01/2019" "END: 09/01/2020"
深入到下一个层次
# Split each name/value pair on colons
dates$Notes %>% head(1) %>% strsplit("\n") %>%
unlist() %>% strsplit(":\s*")
[[1]]
[1] "DOB" "12/10/62"
[[2]]
[1] "START" "09/01/2019"
[[3]]
[1] "END" "09/01/2020"
提取单个值
# extract a vector of name labels
dates$Notes %>% head(1) %>% strsplit("\n") %>%
unlist() %>% strsplit(":\s*") %>%
sapply(function(x) x[1])
[1] "DOB" "START" "END"
# extract a vector of associated values
dates$Notes %>% head(1) %>% strsplit("\n") %>%
unlist() %>% strsplit(":\s*") %>%
sapply(function(x) x[2])
[1] "12/10/62" "09/01/2019" "09/01/2020"
通过一些巧妙的dplyr
用法,您将得到一个数据框
dates %>%
group_by(col1) %>%
# summarize can collapse many rows into one or expand one into many
summarize(
name = Notes %>% strsplit("\n") %>%
unlist() %>% strsplit(":\s*") %>%
sapply(function(x) x[1]),
value = Notes %>% strsplit("\n") %>%
unlist() %>% strsplit(":\s*") %>%
sapply(function(x) x[2])
) %>%
ungroup()
结果,所有值都已分离并准备好进行进一步处理
# A tibble: 8 x 3
col1 name value
<chr> <chr> <chr>
1 customer DOB 12/10/62
2 customer START 09/01/2019
3 customer END 09/01/2020
4 customer2 NA NA
5 customer2 S/DATE 28/08/19
6 customer2 R/DATE 27/08/20
7 customer3 DOB 13/01/1980
8 customer3 Start 04/12/2018
我会合并 stringr
和 lubridate
:
dates %>%
mutate(
Start_Date =
sub("\ns/date:", "\nstart:", tolower(Notes)) %>%
str_remove_all("(.*\nstart:)|(\n.*)") %>%
trimws() %>%
lubridate::dmy()
)
# col1 Notes End_date Start_Date
# 1 customer DOB: 12/10/62\nSTART: 09/01/2019\nEND: 09/01/2020 NA 2019-01-09
# 2 customer2 \nS/DATE: 28/08/19\nR/DATE: 27/08/20 NA 2019-08-28
# 3 customer3 DOB: 13/01/1980\nStart:04/12/2018 NA 2018-12-04
答案没有那么简洁,但我觉得很直观,也很容易遵循这些步骤。
首先,我将一个 start
模式替换为另一个 (sub
),其中我使用 tolower
来制作所有小写字母。然后我删除开始日期之前的所有内容,以及行更改 str_remove_all
之后的所有内容。最后我trim空格(trimws
)转成日期(lubridate::dmy
).