Учитывая вектор v и fn f, как мне сгенерировать матрицу {v, f(v), f(f(v)),... f^k(v)}?

У меня есть вектор x и функция f. Мне нужно сгенерировать матрицу, у которой первый столбец — v, второй столбец — f(v) и так далее до последнего столбца f^k(v). На функциональном языке я мог бы использовать операцию развертывания. Я посмотрел шпаргалку purrr, но аналога не нашел.

Если вам нужен конкретный пример, возьмите v = c(1:100) и f = function(x){return (2*x)} — но, пожалуйста, не злоупотребляйте тем фактом, что в этом случае f^k имеет красивую закрытую форму.

возможно, есть короткое рекурсивное решение, если вы определите свою собственную рекурсию

qwr 15.08.2024 22:27

@qwr У меня нет хорошей модели того, что происходит под капотом в R, и я беспокоюсь об эффективности. Например. Я не хочу в конечном итоге выделять k матриц и копировать O(k^2).

Mohan 15.08.2024 22:33

Это обобщение матриц Вандермонда?

qwr 15.08.2024 22:44

что не так с циклом for? Я обнаружил, что функциональный код в R может работать намного медленнее, если он не оптимизирован для конкретной задачи. особенно муррр.

qwr 15.08.2024 22:46

@qwr Я читал с момента публикации вопроса и пришел к такому же выводу. Я думаю, дело в том, что я новичок в R и никогда не видел цикла for в коде R! (Кроме того, не Вандермонд. Что-то вроде моделирования цепи Маркова.)

Mohan 15.08.2024 22:52

Большинство людей используют R для управления фреймами данных (аналогично реляционной модели данных). Общие матричные вещи — сильная сторона numpy.

qwr 16.08.2024 00:16
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
6
60
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Вы ищете что-то вроде:

v = c(1:100)  
f = function(x){return (2*x)}

df <- data.frame(v)

for( i in 1:5) {
   df[,(i+1)] <- f(df[,i])
}

Возможно. Я не понимаю там модель памяти. Что происходит внутри, когда вы продолжаете добавлять новые столбцы в v?

Mohan 15.08.2024 22:53

Я действительно не знаю. Может не вызывать беспокойства, если только v не очень длинное или k не велико. Если вас беспокоит создание и удаление нескольких матриц, просто заранее выделите окончательный размер.

Dave2e 15.08.2024 22:56

Я бы написал это как предварительное выделение памяти способом C/FOTRAN, потому что нет смысла динамически увеличивать объект, окончательный размер которого вам известен.

qwr 16.08.2024 00:17

Другой подход, выращивание формулы функции вместо объекта памяти (см. R-Inferno стр. 12):

  • эта функция сначала генерирует строку, содержащую k вызовов функции f, связанных оператором канала |>, затем анализирует строку и оценивает ее по x:

    raise_f <- \(f, k, x) {
      parse(text = paste('x |> ',
                         sprintf('(%s)()',  paste(deparse(f), collapse = ' ')) |> 
                rep(k) |> paste(collapse = ' |> ')
      )
      ) |> eval()
    }

  • попробуйте функцию на векторе длиной 5, поднимите f от 1 до 3, cbind полученные векторы в матрицу:

    f <- \(x) 2 * x
    k <- 3
    init <- 1:5
    
    do.call(cbind, Map(1:3, f = \(k) raise_f(f, k, 1:5)))

выход:


    ##      [,1] [,2] [,3]
    ## [1,]    2    4    8
    ## [2,]    4    8   16
    ## [3,]    6   12   24
    ## [4,]    8   16   32
    ## [5,]   10   20   40

Сравнение производительности с циклом матрицы:


    library(microbenchmark)
    
    microbenchmark(
      loop_a_matrix = for( i in 1:5) {df[,(i+1)] <- f(df[,i])},
      grow_a_formula = do.call(cbind, Map(1:5, f = \(k) raise_f(f, k, 1:100)))
    )
    
    ## Unit: microseconds
    ##            expr    min      lq     mean  median     uq     max neval cld
    ##   loop_a_matrix 5481.6 6350.55 7254.535 7012.95 7999.2 12083.5   100  a 
    ##  grow_a_formula  919.1 1044.00 1335.874 1219.80 1419.6  3817.9   100   b

Это приносит очки за сообразительность, но «Если ответ parse(), вам обычно следует переосмыслить вопрос» (Томас Ламли 2005, fortunes::fortune("If the answer is parse"))

Ben Bolker 15.08.2024 23:53

Однажды мне придется перечитать (и полностью понять) Advanced R :-)

I_O 16.08.2024 00:02

У нас есть функциональное программирование, поэтому нам не нужно вот так проделывать строковые функции!

qwr 16.08.2024 00:14

@qwr: есть какие-нибудь подсказки по построению рекурсивного выражения типа f(f(x)) из f (не делая f рекурсивной функцией)?

I_O 16.08.2024 08:19

Вы можете использовать Reduce, как показано ниже.

v <- 1:10
k <- 5
f <- \(x) 2 * x
do.call(cbind, Reduce(\(x, y) f(x), rep(list(v), 5), accumulate = TRUE))

что дает результат

      [,1] [,2] [,3] [,4] [,5]
 [1,]    1    2    4    8   16
 [2,]    2    4    8   16   32
 [3,]    3    6   12   24   48
 [4,]    4    8   16   32   64
 [5,]    5   10   20   40   80
 [6,]    6   12   24   48   96
 [7,]    7   14   28   56  112
 [8,]    8   16   32   64  128
 [9,]    9   18   36   72  144
[10,]   10   20   40   80  160

@BenBolker ага, ты прав! Я забыл cbind их

ThomasIsCoding 16.08.2024 00:09

Это также самый быстрый (на данный момент) FWIW

Ben Bolker 16.08.2024 00:25

@BenBolker вау, это превзошло мои ожидания, рад видеть такую ​​скорость 😀

ThomasIsCoding 16.08.2024 06:56

Предварительное выделение матрицы и ее заполнение происходит примерно в 10 раз быстрее, хотя более медленной версии по-прежнему требуется всего около 0,1 секунды для построения матрицы 1000x100... использование Reduce() увеличивает скорость в два раза. Попытка использовать метод построения строк приводит к ошибке «слишком глубоко вложенная оценка».

f <- function(x) {return (2*x)} 
f1 <- function(n1 = 1000, n2 = 1000) {
    df <- data.frame(seq.int(n1))
    for (i in 1:(n2-1)) {
        df[,(i+1)] <- f(df[,i])
    }
    df <- as.matrix(df)
    dimnames(df) <- NULL
    df
}

f2 <- function(n1= 1000, n2 = 1000) {
    df <- matrix(nrow=n1, ncol = n2)
    df[,1] <- seq.int(n1)
    for (i in 1:(n2-1)) {
        df[,(i+1)] <- f(df[,i])
    }
    df
}

f3 <- function(n1 = 1000, n2 = 1000) { 
    v <- seq.int(n1)
   do.call(cbind, Reduce(\(x, y) f(x), rep(list(v), n2), 
                         accumulate = TRUE)) 
}
bench:mark(f1(), f2(), f3() )  
expression      min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc
  <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>
1 f1()       126.75ms 126.81ms      7.89        NA     3.94     2     1
2 f2()         8.14ms   8.93ms    107.          NA    19.5     33     6
3 f3()         3.67ms   3.98ms    221.          NA    21.2     73     7

(Я думаю, что медленный метод был бы намного медленнее, если бы вы увеличивали фрейм данных по строкам, а не по столбцам...)

Другие вопросы по теме