r dataframe

¿Cómo extraer un número de una cadena en un marco de datos en R y colocarlo en una nueva columna?



dataframe (6)

¿Por qué no una solución base 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

Editar.

Siguiendo el ejemplo de la answer user @ camille donde las cadenas pueden tener diferentes números de números, aquí hay una solución que usa el paquete 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)

Tengo un marco de datos simple:

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

Rendimiento esperado

Me gustaría copiar los números al final de la cadena en la columna 1 y colocarlos en la columna tres o cuatro respectivamente, así:

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

Intento

Usando el siguiente código, puedo extraer los números de la cadena:

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

Me gustaría aplicar este código en todos los valores de la primera columna:

library(tidyverse) df %>% mutate(new = as.numeric(str_extract_all(df$test, "[0-9]+")[[1]])[1])

Sin embargo, esto lleva a una columna new , con solo el número 1 . ¿Qué estoy haciendo mal?


Al ver que usted dijo que podría tener varios números en el nombre de un archivo, sugeriría usar un método más detallado pero que se pueda escalar para trabajar con más de 1 o 2 números. De esa manera, no se están codificando columnas como new2 y new2 . Para ilustrar, agregué un tercer número a uno de los nombres de archivo.

El problema original con el que se encontró fue que str_extract_all devuelve una lista, y luego necesita extraer elementos de esa lista. Puede anular la lista para obtener filas individuales para cada número, agregar una clave que secuencia sobre los números de cada nombre de archivo, luego expandirse a una forma amplia para obtener una columna por número, con NA donde no existe un número en el nombre del archivo.

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


Dado que son de ancho fijo, puedes:

df$new <- substr(df$test, 8, 8) %>% as.integer

Recomiendo usar as.integer lugar de as.numeric porque está trabajando con enteros, no con flotantes.


Modificando ligeramente su código existente:

df %>% mutate(new = as.integer(str_extract(test, "[0-9]+")))

O simplemente

df$new <- as.integer(str_extract(df$test, "[0-9]+"))


Podemos usar parse_number desde readr

library(dplyr) library(purrr) library(stringr) df %>% mutate(new = readr::parse_number(as.character(test)))

Con respecto al problema del OP, está seleccionando solo el primer elemento de la list ( [[1]] ) del str_extract_all (que devuelve una list ). En su lugar, es mejor usar str_extract ya que necesitamos extraer solo la primera instancia de uno o más dígitos ( //d+ )

df %>% mutate(new = as.numeric(str_extract(test, "[0-9]+")))

Si necesitamos obtener la salida de str_extract_all (en el caso), unlist la list en un vector y luego aplique el as.numeric en ese vector

df %>% mutate(new = as.numeric(unlist(str_extract_all(test, "[0-9]+"))))

Si hay varias instancias, entonces manténgala como una list después de convertirla en numeric haciendo un bucle a través de los elementos de la list con map

df %>% mutate(new = map(str_extract_all(test, "[0-9]+"), as.numeric))

NOTA: La solución basada en str_extract se publicó por primera vez aquí.

En la base R , podemos usar regexpr

df$new <- as.numeric(regmatches(df$test, regexpr("//d+", df$test)))

Actualizar

Con el ejemplo actualizado, si necesitamos obtener dos instancias de números, el primero se puede extraer con str_extract y el último ( stri_extract_last - from stringi se puede usar), proporcionando un aspecto de expresión regular para verificar los dígitos seguidos por un . y ''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


También podemos usar sub o stringi::stri_extract_last_regex :

sapply(df1, function(x) sub(''.*(//d{1}).*'', ''//1'', x))

o

sapply(df1, function(x) stringi::stri_extract_last_regex(x, "//d{1}"))