programing

R 데이터 프레임에 행을 추가하는 방법

topblog 2023. 7. 2. 18:56
반응형

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))
}

▁functions다니▁the▁attempted시▁i▁also 기능도 시도해 보았습니다.c,append,그리고.merge할 것이 .당신이 제안할 것이 있으면 저에게 알려주세요.

주석에서 업데이트:저는 R이 어떻게 사용되어야 하는지를 알지 못하지만, 모든 반복에서 인덱스를 업데이트하는 데 필요한 추가 코드 라인을 무시하고 싶었고, 궁극적으로 얼마나 많은 행이 필요할지 모르기 때문에 데이터 프레임의 크기를 쉽게 사전 할당할 수 없습니다.위의 내용은 단지 재현 가능한 장난감의 예에 불과하다는 것을 기억하십시오.어쨌든, 당신의 제안에 감사드립니다!

갱신하다

하려고 , 더 제안을 그 벡터에 의 당신무하엇모서하면르는지려고더저습니는공다제가을한안지원마대다벡그삽값유벡미할는하형을음당하에터고에리입한막지를터의이해열각유겠에하을▁your▁not▁of,,▁you▁create▁iion:▁pre▁end,▁into▁the▁onealloc▁insert그▁are▁knowing벡▁more마다값▁type삽당▁you▁column,할미신▁what▁want을▁to▁and▁suggestdata.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()할당으로 되었지만, 사전할로훨개지만었되선인씬해당으,▁is만지▁but▁the▁much사었되개▁due▁improved.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에서 의도한 대로 공간을 미리 할당하는 것이 가장 좋습니다.은 차의솔은다같습다니음과션루선다▁is같니습▁the를 사용하는 것입니다.list그리고 최악의 해결책은 (적어도 이러한 타이밍 결과에 기초하여) 다음과 같습니다.rbind.

단순히 data.frame의 크기를 미리 알지 못한다고 가정합니다.몇 줄이 될 수도 있고, 몇 백만 줄이 될 수도 있습니다.동적으로 성장하는 일종의 용기가 필요합니다.제 경험과 SO의 모든 관련 답변을 고려하여 다음과 같은 4가지 솔루션을 제공합니다.

  1. rbindlist 파일로

  2. 사용하다data.table의름빠set필요할 때 수동으로 테이블을 두 배로 늘립니다.

  3. 사용하다RSQLite그리고 기억 속에 있는 테이블에 추가합니다.

  4. data.frame사용자 지정 환경(참조 의미론 포함)을 사용하여 데이터.frame을 저장하여 반환 시 복사되지 않도록 할 수 있습니다.

다음은 작은 수의 행과 큰 수의 행 모두에 대한 모든 방법에 대한 검정입니다.각 방법에는 다음과 같은 세 가지 기능이 연결되어 있습니다.

  • create(first_element)를 사용하여 적절한 백업 개체를 반환합니다.first_element끼우다.

  • append(object, element)그것이 추가됩니다.element까지 ( (표시됨로)(됨지표ed▁by시▁to끝)로 표시됨)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필요할 때 수동으로 테이블을 두 배로 늘립니다.

를 테블의실길저장다니합이에 하겠습니다.rowcount기여하다.

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

이것은 기본적으로 유사한 스레드에 Karsten W. 답변의 복사&붙여넣기입니다.

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-4710에서 측정)HQ CPU @ 2.50GHz):

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 기반 솔루션은 대용량 데이터에서 어느 정도 속도를 얻지만 data.table + 수동 지수 증가와는 거리가 먼 것으로 보입니다.그 차이는 거의 두 자릿수입니다!

요약

행 수가 적은 경우(n<=100) 가장 간단한 솔루션을 사용하십시오. 괄호 표기법을 사용하여 행을 data.frame에 할당하고 data.frame이 미리 채워지지 않았다는 사실은 무시하십시오.

에는 다른모용사용로를 사용합니다.data.table::set기하급수적으로 : mycode ).data.table은 다음과 같습니다.

purr, tidyr 및 dplyr로 업데이트

질문이 이미 날짜가 지정되어 있기 때문에(6년) 답변에 새로운 패키지 tidyr 및 purr이 포함된 솔루션이 누락되었습니다.그래서 이 패키지로 작업하는 사람들을 위해, 저는 이전의 답변들에 해결책을 추가하고 싶습니다 - 특히 매우 흥미로운 것들입니다.

purrr과 tidyr의 가장 큰 장점은 더 나은 가독성 IMHO입니다.purrr은 lapply를 보다 유연한 map() 제품군로 대체하고, tidyr은 초정밀 방법 add_row를 제공합니다 - 그냥 하는 것입니다 :)

map_df(1:1000, function(x) { df %>% add_row(x = x, y = toString(x)) })

이 솔루션은 읽기에 짧고 직관적이며 비교적 빠릅니다.

system.time(
   map_df(1:1000, function(x) { df %>% add_row(x = x, y = toString(x)) })
)
   user  system elapsed 
   0.756   0.006   0.766

거의 선형적으로 확장되므로 1e5 행의 성능은 다음과 같습니다.

system.time(
  map_df(1:100000, function(x) { df %>% add_row(x = x, y = toString(x)) })
)
   user  system elapsed 
 76.035   0.259  76.489 

이는 @Adam Ryczkowski의 벤치마크에서 data.table 바로 다음으로 순위를 매길 것입니다(만약 당신이 위약을 무시한다면:

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

에 대한 보다 일반적인 솔루션은 다음과 같습니다.

    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

1에서 5까지의 숫자를 가진 벡터 '점'을 구하겠습니다.

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

벡터 안에 숫자 6을 추가하려면 아래 명령이 유용할 수 있습니다.

벡터

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

ii) 테이블의 열

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

append에서는 세 가지 인수를 사용합니다.

  1. 수정할 벡터/열.
  2. 수정된 벡터에 포함할 값입니다.
  3. 첨자 뒤에 값을 추가합니다.

단순...!!무슨 일이 있으면 사과드립니다!

나의 해결책은 원래 답과 거의 동일하지만 나에게는 효과가 없습니다.

열 이름을 지정하면 작동합니다.

painel <- rbind(painel, data.frame("col1" = xtweets$created_at,
                                   "col2" = xtweets$text))

언급URL : https://stackoverflow.com/questions/20689650/how-to-append-rows-to-an-r-data-frame

반응형