Как извлечь число из строки в фрейме данных и поместить его в новый столбец?

У меня есть простой фрейм данных:

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. Что я делаю неправильно?

Здесь вы извлекаете первый экземпляр чисел [[1]][1]? В таком случае просто сделайте str_extract

akrun 04.07.2019 17:52

Основываясь на комментарии, который вы оставили к ответу, вы говорите, что вам нужно иметь возможность извлекать несколько чисел для каждого имени файла. Это важный тестовый пример, который вы должны описать в своем вопросе и включить в свой пример набора данных.

camille 04.07.2019 17:55
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
2
8 773
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

Ответ принят как подходящий

Мы можем использовать 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 04.07.2019 17:45

@user213544 user213544 Если строк несколько, лучше остаться list

akrun 04.07.2019 17:49

Основываясь на комментарии @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, чего я не понимаю.

user213544 04.07.2019 18:13

Учитывая, что они имеют фиксированную ширину, вы можете:

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

Другие вопросы по теме