Я работаю с набором данных, в котором некоторые данные располагаются способами, которые не очень полезны для дальнейшей работы. Например:
ID Group timestamp location
1 2 12 secs c(50,120)
2 1 3 secs c(20,45)
3 1 7 secs c(12,30)
4 2 18 secs c(45,100)
5 3 4 secs c(0,80)
Я хочу разделить столбец местоположения на два числовых столбца и сделать столбец временной метки числовым, чтобы работать с ними как таковыми.
Пытался удалить символы и использовать as.numeric
, но при запуске любого mutate
орка со столбцами получаю ошибку non-numeric argument to binary operator
.
data= data %>%
mutate(timestamp = gsub("\\secs", "", timestamp)) %>%
mutate(location = gsub("\\c()", "", location)) %>%
separate(location, c("location.x", "location.y"), sep = ",") %>%
drop_na(timestamp,
location.y)
as.numeric(data$timestamp)
as.numeric(data&location.y)
data = data %>%
group_by(Group) %>%
mutate(av_location.y = mean(location.y),
av_time = max(timestamp) - min(timestamp))
Если кто-нибудь знает, как я могу обойти эту проблему с вектором символов, я буду признателен.
Предполагая, что вы действительно имеете дело с символьными столбцами:
library(dplyr, warn.conflicts = FALSE)
data <- tribble(
~ID, ~Group, ~timestamp, ~location,
1, 2, "12 secs", "c(50,120)",
2, 1, "3 secs" , "c(20,45)",
3, 1, "7 secs" , "c(12,30)",
4, 2, "18 secs", "c(45,100)",
5, 3, "4 secs" , "c(0,80)")
data |>
mutate(timestamp = readr::parse_number(timestamp),
location = purrr::map(location, \(loc) textConnection(loc) |> dget())) |>
tidyr::unnest_wider(location, names_sep = ".")
#> # A tibble: 5 × 5
#> ID Group timestamp location.1 location.2
#> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 1 2 12 50 120
#> 2 2 1 3 20 45
#> 3 3 1 7 12 30
#> 4 4 2 18 45 100
#> 5 5 3 4 0 80
Выражения типа as.numeric(data$timestamp)
сами по себе не сохраняют никаких изменений, вам нужно будет присвоить этот результат, т.е.
data$timestamp <- as.numeric(data$timestamp)
Мы предполагаем, что данные воспроизводимо показаны в примечании в конце. Это либо выглядит как data
, где столбец location
представляет собой символ, либо как data2
, где столбец location
представляет собой список числовых векторов. Код обрабатывает оба варианта, но если это вектор символов, то строку {...} можно при желании опустить, ничего не меняя.
Извлеките временную метку, используя separate
. Это также создаст ненужный столбец, который мы устраним, используя показанную NA. convert=TRUE
приводит к преобразованию номеров символов в числовые.
Следующая строка проверяет, является ли location
столбцом списка, и если да, то преобразует его в символьный столбец. Эту строку можно было бы опустить, если бы мы знали, что location
— это символ.
Наконец, снова используйте separate
на location
.
library(dplyr)
library(tidyr)
data %>%
separate(timestamp, c("timestamp", NA), convert = TRUE) %>%
{ if (is.list(.$location)) mutate(., location = paste(location)) else . } %>%
separate(location, c(NA,"location1", "location2", NA), convert = TRUE)
предоставление
ID Group timestamp location1 location2
1 1 2 12 50 120
2 2 1 3 20 45
3 3 1 7 12 30
4 4 2 18 45 100
5 5 3 4 0 80
data <- data.frame(
ID = 1:5,
Group = c(2L, 1L, 1L, 2L, 3L),
timestamp = c("12 secs", "3 secs", "7 secs", "18 secs", "4 secs"),
location = c("c(50,120)", "c(20,45)", "c(12,30)", "c(45,100)", "c(0,80)")
data2 <- data %>%
mutate(location = lapply(location, \(x) eval(parse(text = x))))
На всякий случай, если кому-то интересно, вот как я бы это сделал, используя только базовые функции R. Было бы немного странно использовать базу R для работы с тибблом, но это работает, и, возможно, это поможет кому-то еще с аналогичным вопросом.
Я использую gsub()
для удаления нечисловых символов, strsplit()
для отделения двух местоположений друг от друга и lapply()
для получения только первого или только второго элемента каждого элемента списка. Смотрите комментарии в коде!
Спасибо пользователю margusl (из другого ответа) за код для создания данных.
library(dplyr)
## stack overflow user margusl's code to create your data:
data <- tribble(
~ID, ~Group, ~timestamp, ~location,
1, 2, "12 secs", "c(50,120)",
2, 1, "3 secs" , "c(20,45)",
3, 1, "7 secs" , "c(12,30)",
4, 2, "18 secs", "c(45,100)",
5, 3, "4 secs" , "c(0,80)")
## Make a new column that removes non-numeric characters from the
## timestamps and converts the type to numeric
data$time <- as.numeric(gsub("\\D", "", data$timestamp))
## Split the strings containing the two locations by the comma so we
## have a list of vectors each of length 2 where the first element has
## the first location and the second has the second
split_by_comma <- strsplit(data$location, ',')
## Then get the first element from each list element
data$loc1 <- lapply(split_by_comma, '[', 1)
## And remove all non-numeric characters and convert to numeric type
data$loc1 <- as.numeric(gsub("\\D", "", data$loc1))
## Repeat for the second element of each list element
data$loc2 <- lapply(split_by_comma, '[', 2)
data$loc2 <- as.numeric(gsub("\\D", "", data$loc2))
Неоптимизированный базовый подход R,
lapply(df0[sapply(df0, is.character)], \(a) {
lapply(regmatches(a, gregexpr("[[:digit:]]+", a)) , strtoi) |>
list2DF() |> t() }) |> do.call(what = "cbind") |>
`colnames<-`(c("timestamp", "location.1" ,"location.2"))
который не включает проверки (длины), дает
timestamp location.1 location.2
12 50 120
3 20 45
7 12 30
18 45 100
4 0 80
Другим вариантом может быть
cbind(strtoi(sub("\\D+", "", df0$timestamp)),
t(vapply(df0$location, \(i) eval(str2expression(i)), numeric(2L), USE.NAMES=FALSE))) |>
`colnames<-`(c("timestamp", "location.1" ,"location.2"))
(где , USE.NAMES=FALSE))) |> `colnames<-`(c("timestamp", "location.1" ,"location.2"))
— косметика и может быть заменена одним закрывающимся )
.)
Это очень редко. Я подозреваю, что мы имеем дело с xy-проблемой . Откуда берется location = c("c(50,120)", "c(20,45)", ...)
?