У меня есть простой фрейм данных:
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
Ожидаемый результат
Я хотел бы скопировать числа в конце строки в столбце 1 и поместить их в столбец три или четыре соответственно, например:
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
Пытаться
Используя следующий код, я могу извлечь числа из строки:
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
Я хотел бы применить этот код ко всем значениям первого столбца:
library(tidyverse)
df %>% mutate(new = as.numeric(str_extract_all(df$test, "[0-9]+")[[1]])[1])
Однако это приводит к столбцу new, содержащему только число 1.
Что я делаю неправильно?
Основываясь на комментарии, который вы оставили к ответу, вы говорите, что вам нужно иметь возможность извлекать несколько чисел для каждого имени файла. Это важный тестовый пример, который вы должны описать в своем вопросе и включить в свой пример набора данных.





Мы можем использовать parse_number из readr
library(dplyr)
library(purrr)
library(stringr)
df %>%
mutate(new = readr::parse_number(as.character(test)))
Что касается проблемы с OP, он выбирает только первый элемент list ([[1]]) из str_extract_all (который возвращает list). Вместо этого лучше использовать str_extract, так как нам нужно извлечь только первый экземпляр одной или нескольких цифр (\\d+).
df %>%
mutate(new = as.numeric(str_extract(test, "[0-9]+")))
Если нам нужно получить вывод из str_extract_all (в случае), unlistlist в vector, а затем применить as.numeric к этому vector
df %>%
mutate(new = as.numeric(unlist(str_extract_all(test, "[0-9]+"))))
Если есть несколько экземпляров, сохраните его как list после преобразования в numeric, перебирая элементы list с помощью map
df %>%
mutate(new = map(str_extract_all(test, "[0-9]+"), as.numeric))
ПРИМЕЧАНИЕ. Решение на основе str_extract было впервые опубликовано здесь.
В base R мы можем использовать regexpr
df$new <- as.numeric(regmatches(df$test, regexpr("\\d+", df$test)))
В обновленном примере, если нам нужно получить два экземпляра чисел, первый из них можно извлечь с помощью str_extract, а последний (также можно использовать stri_extract_last - из stringi), предоставив поиск по регулярному выражению для проверки цифр, за которыми следует . и «текст»
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
Можно ли также использовать формат as.numeric(str_extract_all("test_A_3.txt", "[0-9]+")[[1]])[1]? В реальных данных у меня есть несколько чисел на строку, которую я хотел бы извлечь...
@user213544 user213544 Если строк несколько, лучше остаться list
Основываясь на комментарии @camille, я немного обновил вопрос. Настоящие файлы имеют такие имена, как test_a_1_1.txt и test_a_2_1.txt. Если я использую подмножество: df1 %>% mutate(new = map(str_extract_all(test, "[0-9]+"), as.numeric)[1]) проблема остается в том, что все значения равны 1, чего я не понимаю.
Учитывая, что они имеют фиксированную ширину, вы можете:
df$new <- substr(df$test, 8, 8) %>% as.integer
Я рекомендую использовать as.integer, а не as.numeric, потому что вы работаете с целыми числами, а не с плавающей запятой.
Немного изменив существующий код:
df %>%
mutate(new = as.integer(str_extract(test, "[0-9]+")))
Или просто
df$new <- as.integer(str_extract(df$test, "[0-9]+"))
Почему не базовое решение 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
Редактировать.
Следуя примеру пользователя @camille отвечать, где строки могут иметь разное количество цифр, вот решение с использованием пакета 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)
Увидев, как вы сказали, что у вас может быть несколько чисел в имени файла, я бы предложил использовать более подробный метод, но он будет масштабироваться для работы с более чем 1 или 2 числами. Таким образом, вы не будете жестко кодировать столбцы, такие как new1 и new2. Для иллюстрации я добавил третью цифру к одному из имен файлов.
Первоначальная проблема, с которой вы столкнулись, заключалась в том, что str_extract_all возвращает список, и затем вам нужно извлечь элементы из этого списка. Вы можете отменить вложенность этого списка, чтобы получить отдельные строки для каждого числа, добавить ключ, который упорядочивает числа каждого имени файла, а затем распространить на широкую форму, чтобы получить один столбец для каждого числа, с NA, где в имени файла нет числа.
library(dplyr)
library(stringr)
library(tidyr)
df <- 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))
df %>%
mutate(nums = str_extract_all(test, "\\d+")) %>%
unnest(nums) %>%
group_by(test) %>%
mutate(key = row_number()) %>%
spread(key, value = nums, sep = "")
#> # A tibble: 4 x 5
#> # Groups: test [4]
#> test value key1 key2 key3
#> <fct> <dbl> <chr> <chr> <chr>
#> 1 test_A_1_1.txt 0.51 1 1 <NA>
#> 2 test_A_2_1.txt 0.52 2 1 <NA>
#> 3 test_A_3_1.txt 0.56 3 1 <NA>
#> 4 test_A_4_2_1.txt 2 4 2 1
Мы также можем использовать sub или stringi::stri_extract_last_regex:
sapply(df1, function(x) sub('.*(\\d{1}).*', '\\1', x))
или
sapply(df1, function(x) stringi::stri_extract_last_regex(x, "\\d{1}"))
Извлечь число из строки во фрейме данных и поместить в новый столбец.
t$new<-substr(t[,1], 8,8)
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
Здесь вы извлекаете первый экземпляр чисел
[[1]][1]? В таком случае просто сделайтеstr_extract