У меня есть путаница относительно того, как работает мутация в tidyverse/dplyr. Я включил воспроизводимый пример здесь. Один использует мутацию, а другой использует цикл. Я ожидал бы, что оба дадут тот же результат, но это не так. Понятия не имею почему. Любая помощь будет оценена по достоинству.
library(tidyverse)
d <- data.frame(x = c('a,a,b,b,b','a,a','a,b,b,b,c,c,c'))
# Approach 1 (mutate)
d %>%
mutate(y = paste(unique(str_split(x, ',')[[1]]), collapse = ','))
d
# Approach 2 (loop)
for (i in 1:nrow(d))
{
d$y[i] <- paste(unique(str_split(d$x[i], ',')[[1]]), collapse = ',')
}
d
Я ожидаю, что результат будет одинаковым для обоих подходов, но это не так.
Проблема в том, что мы подмножаем только первый элемент list
с [[1]]
, а затем unique
находится только в этом элементе. Вместо этого нам нужно перебрать list
(из вывода str_split
)
library(tidyverse)
d %>%
mutate(y = str_split(x, ',') %>% # output is a list
map_chr(~ unique(.x) %>% # loop with map, get the unique elements
toString)) # paste the strings together
# x y
#1 a,a,b,b,b a, b
#2 a,a a
#3 a,b,b,b,c,c,c a, b, c
В цикле for
этого не было, потому что разбиение производилось по одному элементу за раз str_split(d$x[i]
Чтобы лучше понять, str_split
(strsplit
базовый R) is vectorized. They can take multiple strings and split into a
listof
вектор равен длине исходного вектора
str_split(d$x, ',') # list of length 3
#[[1]]
#[1] "a" "a" "b" "b" "b"
#[[2]]
#[1] "a" "a"
#[[3]]
#[1] "a" "b" "b" "b" "c" "c" "c"
Извлечение первого [[1]]
str_split(d$x, ',')[[1]]
#[1] "a" "a" "b" "b" "b"
В цикле for
мы разделяем элементы по отдельности и извлекаем элемент списка (длина 1).
str_split(d$x[1], ',')[[1]]
#[1] "a" "a" "b" "b" "b"
str_split(d$x[2], ',')[[1]]
#[1] "a" "a"
По этой причине нам нужно перебрать list
, а затем получить unique
из каждого элемента.
@PraGalaxy Это не имеет ничего общего с mutate
. Вы можете проверить, что str_split(d$x, ",")
возвращает list
из vectors
. Когда вы делаете [[1]]
, вы извлекаете только первый элемент list
Спасибо, кажется, теперь я понял. Кроме того, я провел некоторое исследование и выяснил, что «мутация» применяется к столбцу сразу. Он отлично работает с функциями, которые векторизованы. Но здесь str_split не векторизован и выводит список, отсюда и проблема. Это правильно?
@PraGalaxy str_split
векторизован, и именно поэтому он разбивает более 1 элемента за раз на list
из vector
s. В цикле for
вы разделяете один элемент/строку (d$x[i]
) за раз, и в этом разница. Но в обоих случаях strsplit
возвращает список. Разница в длине list
. Здесь это будет list
длины 3. Итак, если есть list
длины 1, извлеките его с помощью [[1]]
, он вернет элемент. Для списка длины 3 выполнение того же действия возвращает только первый
Означает ли это, что «мутация» не выполняет вычисление по одному элементу за раз? Итак, «x» в mutate — это весь столбец, а не одна ячейка за раз?