#r #parallel-processing #apply #lm
#r #параллельная обработка #применить #lm
Вопрос:
Сначала я создаю пару примеров фреймов данных:
df = data.frame("sample1" = runif(10), "sample2" = runif(10), "sample3" = runif(10), "sample4" = runif(10))
traits = data.frame("var1" = c(rep("group1", 2), rep("group2", 2)))
rownames(traits) = colnames(df)
Если я создаю формулу в виде текстовой строки, я могу подключить ее прямо в lm ()
> row = t(df[1,])
> ModString = "row ~ traits$var1"
> Mod = lm(as.formula(ModString))
> Mod
Call:
lm(formula = as.formula(ModString))
Coefficients:
(Intercept) traits$var1group2
0.7799 0.1788
Но если я попытаюсь сделать то же самое с parLapply, я получу сообщение об ошибке, указывающее, что аргумент «черты» работал не так, как ожидалось:
> num_cores <- detectCores() - 1
> cl <- makeCluster(num_cores)
> results <- parLapply(cl = cl, seq(1:10), function(i, df, traits){
row = df[i,]
ModString = "vector ~ traits$factor1"
Mod = lm(ModString)
return(Mod)
}, df = df, traits = traits)
Error in checkForRemoteErrors(val) :
9 nodes produced errors; first error: object 'traits' not found
Но что странно, так это то, что аргумент «черты» попадает в parLapply, который я использую, похоже, проблема в том, как работает lm(). Я могу вводить и возвращать «черты» просто отлично:
> cl <- makeCluster(num_cores)
> results <- parLapply(cl = cl, seq(1:10), function(i, df, traits){
row = df[i,]
traits2 = traits
ModString = "vector ~ traits$factor1"
return(list(traits2, row, ModString))
}, df = df, traits = traits)
> results
[[1]]
[[1]][[1]]
var1
sample1 group1
sample2 group1
sample3 group2
sample4 group2
[[1]][[2]]
sample1 sample2 sample3 sample4
1 0.6941108 0.8656177 0.9807334 0.936609
[[1]][[3]]
[1] "vector ~ traits$factor1"
[[2]]
[[2]][[1]]
var1
sample1 group1
sample2 group1
sample3 group2
sample4 group2
[[2]][[2]]
sample1 sample2 sample3 sample4
2 0.1007983 0.5599374 0.0208095 0.8082196
[[2]][[3]]
[1] "vector ~ traits$factor1"
[[3]]
[[3]][[1]]
var1
sample1 group1
sample2 group1
sample3 group2
sample4 group2
[[3]][[2]]
sample1 sample2 sample3 sample4
3 0.9633059 0.7564143 0.913617 0.4179525
[[3]][[3]]
[1] "vector ~ traits$factor1"
[[4]]
[[4]][[1]]
var1
sample1 group1
sample2 group1
sample3 group2
sample4 group2
[[4]][[2]]
sample1 sample2 sample3 sample4
4 0.06625104 0.390351 0.511572 0.8386714
[[4]][[3]]
[1] "vector ~ traits$factor1"
[[5]]
[[5]][[1]]
var1
sample1 group1
sample2 group1
sample3 group2
sample4 group2
[[5]][[2]]
sample1 sample2 sample3 sample4
5 0.6135228 0.4926991 0.08513074 0.105647
[[5]][[3]]
[1] "vector ~ traits$factor1"
[[6]]
[[6]][[1]]
var1
sample1 group1
sample2 group1
sample3 group2
sample4 group2
[[6]][[2]]
sample1 sample2 sample3 sample4
6 0.7121677 0.6554129 0.6409468 0.4906039
[[6]][[3]]
[1] "vector ~ traits$factor1"
[[7]]
[[7]][[1]]
var1
sample1 group1
sample2 group1
sample3 group2
sample4 group2
[[7]][[2]]
sample1 sample2 sample3 sample4
7 0.4651641 0.546514 0.4039608 0.1758802
[[7]][[3]]
[1] "vector ~ traits$factor1"
[[8]]
[[8]][[1]]
var1
sample1 group1
sample2 group1
sample3 group2
sample4 group2
[[8]][[2]]
sample1 sample2 sample3 sample4
8 0.5121237 0.4950444 0.9662431 0.6851582
[[8]][[3]]
[1] "vector ~ traits$factor1"
[[9]]
[[9]][[1]]
var1
sample1 group1
sample2 group1
sample3 group2
sample4 group2
[[9]][[2]]
sample1 sample2 sample3 sample4
9 0.2486208 0.135422 0.2128657 0.7332921
[[9]][[3]]
[1] "vector ~ traits$factor1"
[[10]]
[[10]][[1]]
var1
sample1 group1
sample2 group1
sample3 group2
sample4 group2
[[10]][[2]]
sample1 sample2 sample3 sample4
10 0.06203028 0.7916495 0.3528376 0.2259685
[[10]][[3]]
[1] "vector ~ traits$factor1"
Какую смущающе тривиальную деталь я здесь упускаю?
Комментарии:
1. У меня очень мало опыта работы с
parLapply
, но я могу сказать вам, что вам вообще не следует создавать формулы с$
помощью. Формула должна содержать имена переменных столбцов вdata
аргументе. например,lm(var1 ~ var2 var3,data = my_data)
гдеvar1
и т.д. находятся все столбцы вmy_data
.2. то же самое происходит, если я индексирую по имени
3. Да, проблема в вашем фундаментальном подходе и в целом в том, как организованы ваши данные. Незначительные изменения при сохранении данных в двух разных фреймах данных, один из которых содержит столбцы в виде строк, будут настолько сложными в управлении, что вы столкнетесь с множеством проблем.
4. К сожалению, в моей ситуации этого нельзя избежать, это был всего лишь простейший пример, который я мог привести.
5. Это полностью выполнимо и намного чище… Я опубликую ответ.
Ответ №1:
Я бы сделал это таким образом; обратите внимание на радикально отличающуюся организацию данных:
library(dplyr)
library(tidyr)
library(tibble)
library(parallel)
#You seem to have rows of data that should be columns,
# this puts things in a form more suitable for work in R
df_new <- df %>%
mutate(row = 1:n()) %>%
gather(key = sample,value = val,sample1:sample4) %>%
arrange(row,sample)
#Data in rownames is not terribly useful
traits_new <- rownames_to_column(traits,"sample")
#Now we can put it all in *one* data frame
df_new <- left_join(df_new,
traits_new,
by = "sample")
#...and split it into a list representing each of the df's you
# want a lm() fit on
df_new_split <- split(df_new,df_new$row)
#Wrapper for lm with the only formula we need
fit_lm <- function(x){
lm(val ~ var1,data = x)
}
num_cores <- detectCores() - 1
cl <- makeCluster(num_cores)
results <- parLapply(cl = cl,df_new_split,fit_lm)
Комментарии:
1. Спасибо, но мне действительно нужно, чтобы данные были организованы таким образом из-за того, что я делаю с ними ниже по потоку. Мой реальный вопрос заключается в том, почему строка интерпретируется по-разному в двух контекстах.
2. @Thoughtcraft Причина в том, что bc lm () не предназначен для использования таким образом, как вы. Возможность передавать имена переменных в формулах, которые просматриваются в среде вызова (как работает первая попытка), предназначена для простой интерактивной работы, а не для программирования. Все преобразования данных, которые я показываю, могут быть отменены позже.
3. Конечно, но тогда почему lm работает с одной и той же строкой и данными вне parLapply? Это то, что я хочу выяснить.
4. @Thoughtcraft Потому что, когда вы не разбиваете вещи на несколько процессов, в R есть система для поиска имен в последовательных средах. Даже тогда все может пойти совсем не так, если вы делаете это внутри вложенных функций. Но при использовании
parLapply
у меня не было бы предположений о том, к чему каждый процесс может получить доступ из вашей глобальной среды.5. @Thoughtcraft Иметь доступ к и знать, что нужно искать, — это две разные вещи. Когда вы выполняете присваивание, подобное
traits2 <- traits
, в R есть процесс поискаtraits
, который включает проверку аргумента функции, внутри которой вы находитесь. Но помните, что как только вы вызываетеlm()
, вы спускаетесь на целый уровень области видимости. Все, что происходит там, происходит в другой совершенно новой среде. И способ работы lm заключается в том, что он проверяетdata
аргумент, а затем проверяет вызывающую среду; простая передача аргумента не создает фактический аргумент в этой среде.
Ответ №2:
Хорошо, я чувствую себя действительно глупо, но я собираюсь оставить вопрос открытым, потому что это отличный пример того, как легко запутаться при копировании-вставке и редактировании нескольких версий кода. Я не всегда использовал as.formula
в своем parLapply
, а также забыл изменить имя переменной vector на row и перенести его.
Итак. Следующее работает просто великолепно:
require(parallel)
num_cores <- detectCores() - 1
cl <- makeCluster(num_cores)
results <- parLapply(cl = cl, seq(1:10), function(i, df, traits){
row = t(df[i,])
ModString = "row ~ traits[,"var1"]"
Mod = lm(as.formula(ModString))
return(Mod)
}, df = df, traits = traits)
Комментарии:
1. Не понимаю, почему этот ответ бесполезен. Это причина моей проблемы. Хотя я не буду спорить ни с кем, кто отрицательно оценивает вопрос, поскольку он был довольно плохо рассмотрен.