Как создать фрейм данных в R, который автоматически обновляет столбцы, когда один из других столбцов был изменен? - PullRequest
0 голосов
/ 02 июля 2018

Это мой вход

    > a<-data.frame(p=c(1:3),q=c(3:5))
    > a
      p q
    1 1 3
    2 2 4
    3 3 5
    > a<-a%>%mutate(r=p*q)
    > a
      p q  r
    1 1 3  3
    2 2 4  8
    3 3 5 15

Изменение одной ячейки

   > a$p[1]<-2
   > a
     p q  r
   1 2 3  3
   2 2 4  8
   3 3 5 15     

a $ r [1] не обновился до 2 * 3 = 6

Я хочу, чтобы вывод был:

    > a
      p q  r
    1 1 3  6
    2 2 4  8
    3 3 5 15

То есть я хочу, чтобы значение r автоматически обновлялось при изменении одного из значений p или q.

Ответы [ 2 ]

0 голосов
/ 02 июля 2018

Вы пытаетесь повторить опыт пользователя Excel, верно? Редактирование одной ячейки должно обновить все зависимые ячейки.

Под прикрытием в Excel - и что в конечном итоге будет решением в R - производится перерасчет всех возможных логически нисходящих значений. Что-то должно будет вызвать это. В Excel триггер изменяет ячейку или выполняет команду пересчета. В расширении R Shiny это могут делать различные движения мыши и щелчки.

Да, вы могли бы запрограммировать метод, чтобы увидеть, изменился ли какой-либо элемент a$p или a$q, но что-то тоже должно было бы вызвать этот скрипт. R не предлагает средства, которые работают постоянно, например, часть вашего ПК, которая ожидает ввода с клавиатуры или движения мыши. Для большинства целей R не требует много времени для выполнения всех возможных вычислений, чем просто вычисление a[1,3] на основе нового значения a[a,1].

Вы можете создать функцию, которая выполняет все рутинные вычисления. Вы можете запустить его, набрав recalc() в консоли. Никаких параметров не требуется. Назначьте ему сочетание клавиш и запустите его после любого изменения объекта данных.

    recalc <- function() {
      a$r <<- a$p * a$q
      ...
      ...
      TRUE
      }

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

0 голосов
/ 02 июля 2018

Возможно, это не то, что вы ищете, но вот простой способ обновить одно значение и рассчитать столбцы соответственно. Я буду использовать пакет data.table, поскольку он позволяет обновлять столбцы по ссылке, по строке, поэтому вам не нужно пересчитывать весь столбец (столбцы) при каждом обновлении.

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

a <- data.frame(p = c(1:3),q = c(3:5))
a <- a %>% mutate(r = p*q, s = p/q - 4)

library(data.table)
setDT(a)
attr(a, 'refresh.cols') <- c(r = 'p*q', s = 'p/q - 4')

df.update <- function(df, row, ...){
  l <- list(...)
  Map(function(x, nm) df[row, eval(parse(text = paste(nm, ':=', x)))]
      , l, names(l))
  df[row, names(attr(a, "refresh.cols")) := 
       lapply(attr(a, "refresh.cols"), function(x) eval(parse(text = x)))]
}
a
#    p q  r         s
# 1: 1 3  3 -3.666667
# 2: 2 4  8 -3.500000
# 3: 3 5 15 -3.400000

a %>% df.update(row = 1, p = 2)
a
#    p q  r         s
# 1: 2 3  6 -3.333333
# 2: 2 4  8 -3.500000
# 3: 3 5 15 -3.400000


a %>% df.update(row = 3, p = 6, q = 7)
a
#    p q  r         s
# 1: 2 3  6 -3.333333
# 2: 2 4  8 -3.500000
# 3: 6 7 42 -3.142857

a %>% df.update(row = 2, p = 9, q = 'p/3 + 1')
a
#    p q  r         s
# 1: 2 3  6 -3.333333
# 2: 9 4 36 -1.750000
# 3: 6 7 42 -3.142857
...