Как назначить функцию, возвращающую более одного значения?

Все еще пытаюсь войти в логику R … что такое «лучший» способ распаковать (на LHS) результаты от функции, возвращающей несколько значений?

Я не могу этого сделать, по-видимому:

R> functionReturningTwoValues  functionReturningTwoValues() [1] 1 2 R> a, b  c(a, b) <- functionReturningTwoValues() Error in c(a, b) <- functionReturningTwoValues() : object 'a' not found 

Должен ли я действительно сделать следующее?

 R> r  a <- r[1]; b <- r[2] 

или программист R написал бы что-то более похожее на это:

 R> functionReturningTwoValues  r  r$first [1] 1 R> r$second [1] 2 

— отредактирован, чтобы ответить на вопросы Шейна —

Мне действительно не нужны имена для частей результата. Я применяю одну агрегированную функцию к первому компоненту, а другой ко второму компоненту ( min И max ., Если это была одна и та же функция для обоих компонентов, мне не нужно было бы их разделять).

    (1) list […] <- Я опубликовал это более десяти лет назад на r-help . С тех пор он был добавлен в пакет gsubfn. Он не требует специального оператора, но требует, чтобы левая сторона была написана с использованием list[...] следующим образом:

     library(gsubfn) # need 0.7-0 or later list[a, b] <- functionReturningTwoValues() 

    Если вам нужен только первый или второй компонент, все они тоже работают:

     list[a] <- functionReturningTwoValues() list[a, ] <- functionReturningTwoValues() list[, b] <- functionReturningTwoValues() 

    (Конечно, если вам нужно только одно значение, то functionReturningTwoValues()[[1]] или functionReturningTwoValues()[[2]] будет достаточной.)

    Более подробную информацию см. В цитированной строке r-help.

    (2) с Если намерение состоит только в том, чтобы объединить несколько значений впоследствии и имена возвращаемых значений, то простой альтернативой является использование with :

     myfun <- function() list(a = 1, b = 2) list[a, b] <- myfun() a + b # same with(myfun(), a + b) 

    (3) Прикрепить Другой вариант:

     attach(myfun()) a + b 

    ДОБАВЛЕНО: with

    Я как-то наткнулся на этот умный хак в Интернете … Я не уверен, что это противно или красиво, но это позволяет создать «волшебный» оператор, который позволяет вам распаковать несколько возвращаемых значений в свою собственную переменную. Здесь определена функция := и приведена ниже для потомков:

     ':=' <- function(lhs, rhs) { frame <- parent.frame() lhs <- as.list(substitute(lhs)) if (length(lhs) > 1) lhs <- lhs[-1] if (length(lhs) == 1) { do.call(`=`, list(lhs[[1]], rhs), envir=frame) return(invisible(NULL)) } if (is.function(rhs) || is(rhs, 'formula')) rhs <- list(rhs) if (length(lhs) > length(rhs)) rhs <- c(rhs, rep(list(NULL), length(lhs) - length(rhs))) for (i in 1:length(lhs)) do.call(`=`, list(lhs[[i]], rhs[[i]]), envir=frame) return(invisible(NULL)) } 

    С этим в руке вы можете делать то, что вам нужно:

     functionReturningTwoValues <- function() { return(list(1, matrix(0, 2, 2))) } c(a, b) := functionReturningTwoValues() a #[1] 1 b # [,1] [,2] # [1,] 0 0 # [2,] 0 0 

    Я не знаю, как я к этому отношусь. Возможно, вы найдете его полезным в своем интерактивном рабочем пространстве. Использование его для сборки (перезаписываемых) библиотек (для массового потребления) может быть не лучшей идеей, но я думаю, это зависит от вас.

    ... вы знаете, что они говорят об ответственности и власти ...

    Обычно я переношу вывод в список, который является очень гибким (вы можете иметь любую комбинацию чисел, строк, векторов, матриц, массивов, списков, объектов, которые он выводит)

    так что:

     func2<-function(input) { a<-input+1 b<-input+2 output<-list(a,b) return(output) } output<-func2(5) for (i in output) { print(i) } [1] 6 [1] 7 
     functionReturningTwoValues <- function() { results <- list() results$first <- 1 results$second <-2 return(results) } a <- functionReturningTwoValues() 

    Я думаю, что это работает.

    На этот вопрос нет правильного ответа. Я действительно зависит от того, что вы делаете с данными. В простом примере выше я бы настоятельно предложил:

    1. Держите все как можно проще.
    2. Там, где это возможно, лучше всего использовать ваши векторизированные функции. Это обеспечивает максимальную гибкость и скорость в долгосрочной перспективе.

    Важно ли иметь значения 1 и 2 выше? Другими словами, почему в этом примере важно, чтобы 1 и 2 были названы a и b, а не только r [1] и r [2]? Одна важная вещь, которая должна понять в этом контексте, состоит в том, что a и b также являются векторами длины 1. Таким образом, вы ничего не меняете в процессе создания этого назначения, кроме наличия двух новых векторов, которым не нужны индексы для ссылаться:

     > r <- c(1,2) > a <- r[1] > b <- r[2] > class(r) [1] "numeric" > class(a) [1] "numeric" > a [1] 1 > a[1] [1] 1 

    Вы также можете назначить имена исходному вектору, если вы скорее ссылаетесь на букву, чем на индекс:

     > names(r) <- c("a","b") > names(r) [1] "a" "b" > r["a"] a 1 

    [Edit] Учитывая, что вы будете применять min и max для каждого вектора отдельно, я бы предложил либо использовать матрицу (если a и b будут иметь одинаковую длину и один и тот же тип данных) или кадр данных (если a и b будут одна и та же длина, но могут быть разными типами данных), либо используйте список, как в последнем примере (если они могут иметь разную длину и типы данных).

     > r <- data.frame(a=1:4, b=5:8) > r ab 1 1 5 2 2 6 3 3 7 4 4 8 > min(r$a) [1] 1 > max(r$b) [1] 8 

    Списки кажутся идеальными для этой цели. Например, в рамках функции, которую вы имели бы

     x = desired_return_value_1 # (vector, matrix, etc) y = desired_return_value_2 # (vector, matrix, etc) returnlist = list(x,y...) } # end of function 

    основная программа

     x = returnlist[[1]] y = returnlist[[2]] 

    Я собрал рэллот R для решения этой проблемы. zeallot включает в себя оператор присваивания или распаковки, %<-% . LHS оператора - любое количество переменных для назначения, построенных с использованием вызовов c() . RHS оператора представляет собой вектор, список, кадр данных, объект даты или любой пользовательский объект с внедренным методом destructure (см. « ?zeallot::destructure ).

    Вот несколько примеров, основанных на исходном посте,

     library(zeallot) functionReturningTwoValues <- function() { return(c(1, 2)) } c(a, b) %<-% functionReturningTwoValues() a # 1 b # 2 functionReturningListOfValues <- function() { return(list(1, 2, 3)) } c(d, e, f) %<-% functionReturningListOfValues() d # 1 e # 2 f # 3 functionReturningNestedList <- function() { return(list(1, list(2, 3))) } c(f, c(g, h)) %<-% functionReturningNestedList() f # 1 g # 2 h # 3 functionReturningTooManyValues <- function() { return(as.list(1:20)) } c(i, j, ...rest) %<-% functionReturningTooManyValues() i # 1 j # 2 rest # list(3, 4, 5, ..) 

    Ознакомьтесь с виньеткой для получения дополнительной информации и примеров.

    Да на второй и третий вопросы – это то, что вам нужно сделать, поскольку вы не можете иметь несколько «lvalues» слева от задания.

    Как насчет использования назначения?

     functionReturningTwoValues <- function(a, b) { assign(a, 1, pos=1) assign(b, 2, pos=1) } 

    Вы можете передать имена переменной, которую хотите передать по ссылке.

     > functionReturningTwoValues('a', 'b') > a [1] 1 > b [1] 2 

    Если вам нужно получить доступ к существующим значениям, обратное assign - get .

    [A] Если каждый из foo и bar является единственным числом, то нет ничего плохого в c (foo, bar); и вы также можете назвать компоненты: c (Foo = foo, Bar = bar). Таким образом, вы можете получить доступ к компонентам результата «res» в качестве res [1], res [2]; или, в названном случае, в качестве res [“Foo”], res [“BAR”].

    [B] Если foo и bar являются векторами того же типа и длины, то снова нет ничего плохого в возвращении cbind (foo, bar) или rbind (foo, bar); также именный. В случае «cbind» вы получите доступ к foo и bar как res [, 1], res [, 2] или как res [, «Foo»], res [, «Bar»]. Возможно, вы также предпочтете вернуть матрицу данных, а не матрицу:

     data.frame(Foo=foo,Bar=bar) 

    и получить доступ к ним как res $ Foo, res $ Bar. Это также хорошо работает, если foo и bar имеют одинаковую длину, но не одного типа (например, foo – вектор чисел, bar – вектор символьных строк).

    [C] Если foo и bar достаточно разные, чтобы не сочетать удобно, как указано выше, то вы определенно вернете список.

    Например, ваша функция может соответствовать линейной модели, а также вычислять предсказанные значения, чтобы вы могли

     LM<-lm(....) ; foo<-summary(LM); bar<-LM$fit 

    и затем вы return list(Foo=foo,Bar=bar) а затем получите доступ к сводке как res $ Foo, outlookируемые значения в качестве res $ Bar

    источник: http://r.789695.n4.nabble.com/How-to-return-multiple-values-in-a-function-td858528.html

    Если вы хотите вернуть результат своей функции в глобальную среду, вы можете использовать list2env , как в этом примере:

     myfun <- function(x) { a <- 1:x b <- 5:x df <- data.frame(a=a, b=b) newList <- list("my_obj1" = a, "my_obj2" = b, "myDF"=df) list2env(newList ,.GlobalEnv) } myfun(3) 

    Эта функция создаст три объекта в вашей глобальной среде:

     > my_obj1 [1] 1 2 3 > my_obj2 [1] 5 4 3 > myDF ab 1 1 5 2 2 4 3 3 3 

    Чтобы получить несколько выходов из функции и сохранить их в нужном формате, вы можете сохранить выходы на свой жесткий диск (в рабочем каталоге) изнутри функции, а затем загрузить их извне функции:

     myfun <- function(x) { df1 <- ... df2 <- ... save(df1, file = "myfile1") save(df2, file = "myfile2") } load("myfile1") load("myfile2") 
    Давайте будем гением компьютера.