Как добавить строки в таблице данных R



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



Я инициализирую пустой 2-столбцовый фрейм данных, как показано ниже.



df = data.frame(x = numeric(), y = character())


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



for (i in 1:10) {
df$x = rbind(df$x, i)
df$y = rbind(df$y, toString(i))
}


Я также попытался функций c,append и merge без успеха. Пожалуйста, дайте мне знать, если у вас есть какие-либо предложения.

1561   5  

5 ответов:

обновление

не зная, что вы пытаетесь сделать, я поделюсь еще одним предложением: предварительно выделите векторы типа, который вы хотите для каждого столбца, вставьте значения в эти векторы, а затем, в конце концов, создайте свой data.frame.

продолжая с Джулиана f3 (заранее выделенном data.frame) как самый быстрый вариант до сих пор, определяется как:

# pre-allocate space
f3 <- function(n){
  df <- data.frame(x = numeric(n), y = character(n), stringsAsFactors = FALSE)
  for(i in 1:n){
    df$x[i] <- i
    df$y[i] <- toString(i)
  }
  df
}

вот аналогичный подход, но тот, где data.frame создается как последний шаг.

# Use preallocated vectors
f4 <- function(n) {
  x <- numeric(n)
  y <- character(n)
  for (i in 1:n) {
    x[i] <- i
    y[i] <- i
  }
  data.frame(x, y, stringsAsFactors=FALSE)
}

microbenchmark из пакета "microbenchmark" даст нам более полное представление, чем system.time:

library(microbenchmark)
microbenchmark(f1(1000), f3(1000), f4(1000), times = 5)
# Unit: milliseconds
#      expr         min          lq      median         uq         max neval
#  f1(1000) 1024.539618 1029.693877 1045.972666 1055.25931 1112.769176     5
#  f3(1000)  149.417636  150.529011  150.827393  151.02230  160.637845     5
#  f4(1000)    7.872647    7.892395    7.901151    7.95077    8.049581     5

f1() (подход ниже) невероятно неэффективен из-за того, как часто он вызывает data.frame и потому что растущие объекты таким образом, как правило, медленно в R. f3() значительно улучшен из-за предварительного выделения, но data.frame сама структура может быть частью узкого места здесь. f4() пытается обойти это узкое место без компрометируя подход, который вы хотите принять.


оригинальный ответ

это действительно не очень хорошая идея, но если вы хотели сделать это таким образом, я думаю, можно попробовать:

for (i in 1:10) {
  df <- rbind(df, data.frame(x = i, y = toString(i)))
}

обратите внимание, что в коде есть еще одна проблема:

  • вы должны использовать stringsAsFactors если вы хотите, чтобы символы не преобразуются в факторы. Использование:df = data.frame(x = numeric(), y = character(), stringsAsFactors = FALSE)

давайте сравним три предложенных решения:

# use rbind
f1 <- function(n){
  df <- data.frame(x = numeric(), y = character())
  for(i in 1:n){
    df <- rbind(df, data.frame(x = i, y = toString(i)))
  }
  df
}
# use list
f2 <- function(n){
  df <- data.frame(x = numeric(), y = character(), stringsAsFactors = FALSE)
  for(i in 1:n){
    df[i,] <- list(i, toString(i))
  }
  df
}
# pre-allocate space
f3 <- function(n){
  df <- data.frame(x = numeric(1000), y = character(1000), stringsAsFactors = FALSE)
  for(i in 1:n){
    df$x[i] <- i
    df$y[i] <- toString(i)
  }
  df
}
system.time(f1(1000))
#   user  system elapsed 
#   1.33    0.00    1.32 
system.time(f2(1000))
#   user  system elapsed 
#   0.19    0.00    0.19 
system.time(f3(1000))
#   user  system elapsed 
#   0.14    0.00    0.14

лучшим решением является предварительное выделение пространства (как это предусмотрено в R). Следующее лучшее решение-использовать list, и худшее решение (по крайней мере, на основе этих результатов синхронизации), кажется,rbind.

предположим, что вы просто не знаете размер данных.кадр заранее. Это может быть несколько строк или несколько миллионов. Вам нужно иметь какой-то контейнер, который растет динамически. Принимая во внимание мой опыт и все связанные с ним ответы, поэтому я пришел с 4 различными решениями:

  1. rbindlist для сведения.кадр

  2. использовать data.tableбыстро set операция и соедините ее с ручным удвоением стол, когда это необходимо.

  3. использовать RSQLite и добавить к таблице, хранящейся в памяти.

  4. data.frameсобственная способность расти и использовать пользовательскую среду (которая имеет ссылочную семантику) для хранения данных.кадр, поэтому он не будет скопирован по возвращении.

вот тест всех методов для малого и большого количества добавленных строк. Каждый метод имеет 3 функции связанные с ним:

  • create(first_element) это возвращает соответствующий объект поддержки с first_element положить в.

  • append(object, element) что добавляет element до конца таблицы (представлено object).

  • access(object) получает data.frame все элементы.

rbindlist для сведения.кадр

это довольно легко и прямо вперед:

create.1<-function(elems)
{
  return(as.data.table(elems))
}

append.1<-function(dt, elems)
{ 
  return(rbindlist(list(dt,  elems),use.names = TRUE))
}

access.1<-function(dt)
{
  return(dt)
}

data.table::set + ручное удвоение таблицы при необходимости.

я сохраню истинную длину таблицы в .

create.2<-function(elems)
{
  return(as.data.table(elems))
}

append.2<-function(dt, elems)
{
  n<-attr(dt, 'rowcount')
  if (is.null(n))
    n<-nrow(dt)
  if (n==nrow(dt))
  {
    tmp<-elems[1]
    tmp[[1]]<-rep(NA,n)
    dt<-rbindlist(list(dt, tmp), fill=TRUE, use.names=TRUE)
    setattr(dt,'rowcount', n)
  }
  pos<-as.integer(match(names(elems), colnames(dt)))
  for (j in seq_along(pos))
  {
    set(dt, i=as.integer(n+1), pos[[j]], elems[[j]])
  }
  setattr(dt,'rowcount',n+1)
  return(dt)
}

access.2<-function(elems)
{
  n<-attr(elems, 'rowcount')
  return(as.data.table(elems[1:n,]))
}

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

это в основном копирование и вставка Карстен В. ответ на подобную тему.

create.3<-function(elems)
{
  con <- RSQLite::dbConnect(RSQLite::SQLite(), ":memory:")
  RSQLite::dbWriteTable(con, 't', as.data.frame(elems))
  return(con)
}

append.3<-function(con, elems)
{ 
  RSQLite::dbWriteTable(con, 't', as.data.frame(elems), append=TRUE)
  return(con)
}

access.3<-function(con)
{
  return(RSQLite::dbReadTable(con, "t", row.names=NULL))
}

data.frameсобственная строка-добавление + пользовательский окружающая среда.

create.4<-function(elems)
{
  env<-new.env()
  env$dt<-as.data.frame(elems)
  return(env)
}

append.4<-function(env, elems)
{ 
  env$dt[nrow(env$dt)+1,]<-elems
  return(env)
}

access.4<-function(env)
{
  return(env$dt)
}

набор тестов:

для удобства я буду использовать одну тестовую функцию, чтобы покрыть их все с косвенным вызовом. (Я проверил: с помощью do.call вместо того, чтобы вызывать функции напрямую, не делает код более измеримым).

test<-function(id, n=1000)
{
  n<-n-1
  el<-list(a=1,b=2,c=3,d=4)
  o<-do.call(paste0('create.',id),list(el))
  s<-paste0('append.',id)
  for (i in 1:n)
  {
    o<-do.call(s,list(o,el))
  }
  return(do.call(paste0('access.', id), list(o)))
}

давайте посмотрим производительность для N=10 вставок.

я также добавил функции "плацебо" (с суффиксом 0), которые ничего не выполняют-просто для измерения накладных расходов тестовая установка.

r<-microbenchmark(test(0,n=10), test(1,n=10),test(2,n=10),test(3,n=10), test(4,n=10))
autoplot(r)

Timings for adding n=10 rows

Timings for n=100 rows Timings for n=1000 rows

для строк 1E5 (измерения выполнены на Intel(R) Core (TM) i7-4710HQ CPU @ 2.50 GHz):

nr  function      time
4   data.frame    228.251 
3   sqlite        133.716
2   data.table      3.059
1   rbindlist     169.998 
0   placebo         0.202

похоже, что SQLite-based sulution, хотя и восстанавливает некоторую скорость на больших данных, нигде не находится рядом с данными.таблица + ручной экспоненциальный рост. Разница составляет почти два порядка величину!

резюме

если вы знаете, что вы добавите довольно небольшое количество строк (n

для всего остального использовать data.table::set и растут данные.таблица экспоненциально (например, с помощью моего кода).

давайте возьмем вектор "точка", которая имеет номера от 1 до 5

point = c(1,2,3,4,5)

если мы хотим добавить номер 6 в любом месте внутри вектора, то ниже команда может пригодиться

i)векторы

new_var = append(point, 6 ,after = length(point))

ii)столбцы таблицы

new_var = append(point, 6 ,after = length(mtcars$mpg))

команда append принимает три аргумента:

  1. вектор / столбец, который будет модифицированный.
  2. значение, которое будет включено в измененный вектор.
  3. индекс, после которого значения должны быть добавлены.

простой...!! Извиняюсь в случае чего...!

более общее решение для Может быть следующим.

    extendDf <- function (df, n) {
    withFactors <- sum(sapply (df, function(X) (is.factor(X)) )) > 0
    nr          <- nrow (df)
    colNames    <- names(df)
    for (c in 1:length(colNames)) {
        if (is.factor(df[,c])) {
            col         <- vector (mode='character', length = nr+n) 
            col[1:nr]   <- as.character(df[,c])
            col[(nr+1):(n+nr)]<- rep(col[1], n)  # to avoid extra levels
            col         <- as.factor(col)
        } else {
            col         <- vector (mode=mode(df[1,c]), length = nr+n)
            class(col)  <- class (df[1,c])
            col[1:nr]   <- df[,c] 
        }
        if (c==1) {
            newDf       <- data.frame (col ,stringsAsFactors=withFactors)
        } else {
            newDf[,c]   <- col 
        }
    }
    names(newDf) <- colNames
    newDf
}

функция extendDf () расширяет фрейм данных с помощью n строк.

пример:

aDf <- data.frame (l=TRUE, i=1L, n=1, c='a', t=Sys.time(), stringsAsFactors = TRUE)
extendDf (aDf, 2)
#      l i n c                   t
# 1  TRUE 1 1 a 2016-07-06 17:12:30
# 2 FALSE 0 0 a 1970-01-01 01:00:00
# 3 FALSE 0 0 a 1970-01-01 01:00:00

system.time (eDf <- extendDf (aDf, 100000))
#    user  system elapsed 
#   0.009   0.002   0.010
system.time (eDf <- extendDf (eDf, 100000))
#    user  system elapsed 
#   0.068   0.002   0.070

Comments

    Ничего не найдено.