#r #dataframe #parallel-processing #future
Вопрос:
Мне просто интересно, является ли это серьезным компромиссом, который следует учитывать. Допустим, у вас есть фрейм данных в R и вы хотите выполнить операцию над каждым наблюдением (строкой). Я знаю, что это уже деликатный вопрос для перебора строк, поэтому мне просто было интересно, какой из трех вариантов:
- Нормально для цикла над каждой строкой
- Разделите фрейм данных на список
nrow
элементов, примените операцию к каждому элементу и свяжите результат вместе - Сделайте то же самое, что и выше, параллельно
Без какого-либо сравнительного анализа или около того, это в основном то, о чем я спрашиваю в псевдокоде:
library(future.apply)
n = 1000000
x = 1:n
y = x rnorm(n, mean=50, sd=50)
df = data.frame(
x = x,
y = y
)
# 1)
# iterating over each row with normal for loop
for(r in 1:nrow(df)){
row = df[r, ]
r = f(row)
df[r, ] = row
}
# 2)
# create a list of length nrow(df) and apply do something to each list element
# and rowbind it together
res = df %>% split(., .$x) %>% lapply(., function(x){
r = f(x)
})
bind_rows(res, .id="x")
# 3)
# create a list of length nrow(df) and apply do something to each list element in parallel
# and rowbind it together
res = df %>% split(., .$x) %>% future_lapply(., function(x){
r = f(x)
})
bind_rows(res, .id="x")
Вероятно, ни один из вышеперечисленных вариантов не является лучшим, поэтому я был бы рад высказать любые мысли по этому поводу. Извините, если это очень наивный вопрос. Я только начинаю с Р.
Комментарии:
1.
split
это почти наверняка неправильный подход. Исходя из вашего желания делать по одной строке за раз в afuture
, подумайтеfuture.apply::future_Map
о столбцах, которые вам нужно передать в свойf(.)
.2. Вам не нужно использовать
split()
, просто зацикливайтесь на индексах. Или, если у вас есть функция, которая фактически извлекает значения из столбцов, вы можете использоватьfurrr::future_pmap_dfr()
.
Ответ №1:
Я очень часто использую эту схему tibble %>% nest %>% mutate(map) %>% unnest
. Взгляните на приведенный ниже пример.
library(tidyverse)
n = 10000
f = function(data) sqrt(data$x^2 data$y^2 data$z^2)
tibble(
x = 1:n,
y = x rnorm(n, mean=50, sd=50),
z = x y rnorm(n, mean=50, sd=50)
) %>% nest(data = c(x:z)) %>%
mutate(l = map(data, f)) %>%
unnest(c(data, l))
выход
# A tibble: 10,000 x 4
x y z l
<int> <dbl> <dbl> <dbl>
1 1 67.1 136. 151.
2 2 75.4 127. 148.
3 3 -11.1 38.9 40.6
4 4 58.1 106. 121.
5 5 23.5 126. 128.
6 6 73.4 179. 193.
7 7 44.5 121. 129.
8 8 106. 131. 169.
9 9 32.5 140. 144.
10 10 -27.7 82.7 87.8
# ... with 9,990 more rows
Лично для меня это очень понятно и элегантно. Но вы можете с этим не согласиться.
Обновление 1
Честно говоря, ваш вопрос также заинтриговал меня с точки зрения производительности. Поэтому я решил проверить это. Вот код:
library(tidyverse)
library(microbenchmark)
n = 1000
df = tibble(
x = 1:n,
y = x rnorm(n, mean=50, sd=50),
z = x y rnorm(n, mean=50, sd=50)
)
f = function(data) sqrt(data$x^2 data$y^2 data$z^2)
f1 = function(df){
df %>% nest(data = c(x:z)) %>%
mutate(l = map(data, f)) %>%
unnest(c(data, l))
}
f1(df)
f2 = function(df){
df = df %>% mutate(l=NA)
for(r in 1:nrow(df)){
row = df[r, ]
df$l[r] = f(row)
}
df
}
f2(df)
f3 = function(df){
res = df %>%
split(., .$x) %>%
lapply(., f)
df %>% bind_cols(l = unlist(res))
}
f3(df)
ggplot2::autoplot(microbenchmark(f1(df), f2(df), f3(df), times=100))
Вот результат:
Должен ли я добавить что-нибудь еще и объяснить, почему схема tibble%>% nest%>% mutate (map)%>% unnest
такая классная?
Комментарии:
1. Большое спасибо:) Это выглядит действительно хорошо, мне просто нужно некоторое время, чтобы пройти через это и посмотреть, как я могу применить это к своей проблеме. Но почему это происходит намного быстрее?
2.Почему это решение работает быстрее? Я не знаю наверняка, но, похоже, это связано с внутренней оптимизацией
dplyr
пакета. Но я знаю, что почти всегдаfor
цикл в R-плохая идея. И правда в том, что я нахожу все меньше и меньше причин использовать его. С другой стороны, схемаtibble%>% nest%>% mutate (map)%>% unnest
мне очень ясна. Здесь я мог бы сослаться на множество примеров на этом сайте, где я показал другим, как его применять. Однако лучше проконсультироваться с настоящим гуру. Я предлагаю вам прочитать главу 25 R для науки о данных r4ds.had.co.nz/many-models.html осторожно3. большое вам спасибо!:) Высоко ценится
4. В случае переполнения стека благодарность выражается, когда вы принимаете ответ.