我有一个简单的数据框:
df <- data.frame(test = c("test_A_1_1.txt", "test_A_2_1.txt", "test_A_3_1.txt"), value = c(0.51, 0.52, 0.56))
test value
1 test_A_1_1.txt 0.51
2 test_A_2_1.txt 0.52
3 test_A_3_1.txt 0.56
Run Code Online (Sandbox Code Playgroud)
预期产量
我想将数字复制到第1列的字符串末尾,并将其分别放在第3列或第4列中,如下所示:
test value new new
1 test_A_1.txt 0.51 1 1
2 test_A_2.txt 0.52 2 1
3 test_A_3.txt 0.56 3 1
Run Code Online (Sandbox Code Playgroud)
尝试
使用以下代码,我可以从字符串中提取数字:
library(stringr)
as.numeric(str_extract_all("test_A_3.txt", "[0-9]+")[[1]])[1] # Extracts the first number
as.numeric(str_extract_all("test_A_3.txt", "[0-9]+")[[1]])[2] # Extracts the second number
Run Code Online (Sandbox Code Playgroud)
我想将此代码应用于第一列的所有值:
library(tidyverse)
df %>% mutate(new = as.numeric(str_extract_all(df$test, "[0-9]+")[[1]])[1])
Run Code Online (Sandbox Code Playgroud)
但是,这导致new只有数字的列1。我究竟做错了什么?
为什么不使用基础R解决方案?
df$new <- as.numeric(gsub("[^[:digit:]]+", "", df$test))
df
# test value new
#1 test_A_1.txt 0.51 1
#2 test_A_2.txt 0.52 2
#3 test_A_3.txt 0.56 3
Run Code Online (Sandbox Code Playgroud)
编辑。
按照用户@camille的答案中的示例,其中字符串可能具有不同数量的数字,这是使用package的解决方案stringr。
df1 <- data.frame(test = c("test_A_1.txt", "test_A_2.txt", "test_A_3.txt"), value = c(0.51, 0.52, 0.56))
df2 <- data.frame(test = c("test_A_1_1.txt", "test_A_2_1.txt", "test_A_3_1.txt"), value = c(0.51, 0.52, 0.56))
df3 <- data.frame(test = c("test_A_1_1.txt", "test_A_2_1.txt", "test_A_3_1.txt", "test_A_4_2_1.txt"), value = c(0.51, 0.52, 0.56, 2))
num2cols <- function(DF, col = "test"){
s <- stringr::str_extract_all(DF[[col]], "[[:digit:]]+")
Max <- max(sapply(s, length))
new <- do.call(rbind, lapply(s, function(x){
as.numeric(c(x, rep(NA, Max - length(x))))
}))
names_new <- paste0("new", seq.int(ncol(new)))
setNames(cbind(DF, new), c(names(DF), names_new))
}
num2cols(df1)
num2cols(df2)
num2cols(df3)
Run Code Online (Sandbox Code Playgroud)
我们可以使用parse_number从readr
library(dplyr)
library(purrr)
library(stringr)
df %>%
mutate(new = readr::parse_number(as.character(test)))
Run Code Online (Sandbox Code Playgroud)
关于OP的问题,它仅从中选择第一个list元素([[1]])str_extract_all(返回list)。相反,最好使用,str_extract因为我们只需要提取一个或多个数字(\\d+)的第一个实例
df %>%
mutate(new = as.numeric(str_extract(test, "[0-9]+")))
Run Code Online (Sandbox Code Playgroud)
如果我们需要从得到的输出str_extract_all(万一),unlist将list到vector,然后应用as.numeric上vector
df %>%
mutate(new = as.numeric(unlist(str_extract_all(test, "[0-9]+"))))
Run Code Online (Sandbox Code Playgroud)
如果有多个实例,则它保持为list转换成之后numeric通过经循环list与元件map
df %>%
mutate(new = map(str_extract_all(test, "[0-9]+"), as.numeric))
Run Code Online (Sandbox Code Playgroud)
注意:str_extract基于解决方案首先发布在这里。
在中base R,我们可以使用regexpr
df$new <- as.numeric(regmatches(df$test, regexpr("\\d+", df$test)))
Run Code Online (Sandbox Code Playgroud)
随着更新的例子,如果我们需要得到的数字的两个实例,第一个可以与提取str_extract,最后(stri_extract_last-从stringi可作为良好),通过提供一个正则表达式环视检查位数字加一个.和“TXT '
df %>%
mutate(new1 = as.numeric(str_extract(test, "\\d+")),
new2 = as.numeric(str_extract(test, "\\d+(?=\\.txt)")))
# test value new1 new2
#1 test_A_1_1.txt 0.51 1 1
#2 test_A_2_1.txt 0.52 2 1
#3 test_A_3_1.txt 0.56 3 1
Run Code Online (Sandbox Code Playgroud)
稍微修改您现有的代码:
df %>%
mutate(new = as.integer(str_extract(test, "[0-9]+")))
Run Code Online (Sandbox Code Playgroud)
或者简单地
df$new <- as.integer(str_extract(df$test, "[0-9]+"))
Run Code Online (Sandbox Code Playgroud)