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
성공하지. 제안 사항이 있으면 알려주십시오.
답변
최신 정보
무엇을 하려는지 모르기 때문에 한 가지 더 제안을 공유하겠습니다. 각 열에 대해 원하는 유형의 벡터를 미리 할당하고 해당 벡터에 값을 삽입 한 다음 마지막에 data.frame
.
지금까지 가장 빠른 옵션 으로 Julian ‘s 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
.
답변
단순히 data.frame의 크기를 미리 모른다고 가정합니다. 몇 개의 행 또는 수백만 개가 될 수 있습니다. 동적으로 커지는 일종의 컨테이너가 필요합니다. 내 경험과 모든 관련 답변을 고려하여 다음과 같은 4 가지 솔루션을 제공합니다.
-
rbindlist
data.frame에 -
사용
data.table
의 빠른set
작동 및 필요시 수동으로 테이블을 두 배로 부부를. -
RSQLite
메모리에 보관 된 테이블을 사용 하고 추가합니다. -
data.frame
데이터 프레임을 저장하기 위해 사용자 정의 환경 (참조 의미 체계가 있음)을 확장하고 사용하는 자체 기능을 사용하여 반환시 복사되지 않습니다.
다음은 추가 된 행의 수가 많거나 적을 때 모든 방법에 대한 테스트입니다. 각 메서드에는 관련된 3 가지 기능이 있습니다.
-
create(first_element)
적절한 백업 개체를 반환합니다first_element
. -
append(object, element)
element
테이블 끝에 를 추가 합니다 (로object
표시됨). -
access(object)
data.frame
삽입 된 모든 요소를 가져옵니다 .
rbindlist
data.frame에
이것은 매우 쉽고 간단합니다.
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)
1E5 행의 경우 (Intel (R) Core (TM) i7-4710HQ 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
에는 data.table을 기하 급수적으로 사용 하고 확장합니다 (예 : 내 코드 사용).
답변
purrr, tidyr 및 dplyr로 업데이트
질문이 이미 날짜가 지정 되었기 때문에 (6 년), 답변에는 최신 패키지가 깔끔하고 깔끔한 솔루션이 없습니다. 따라서 이러한 패키지로 작업하는 사람들을 위해 이전 답변에 대한 솔루션을 추가하고 싶습니다. 특히.
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 (위약을 무시하는 경우) 바로 다음으로 2 위를 차지합니다.
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
답변
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
은 세 가지 인수를 사용합니다.
- 수정할 벡터 / 열.
- 수정 된 벡터에 포함될 값입니다.
- 값이 추가되는 아래 첨자.
단순한…!! 어떤 경우에 사과 …!
답변
보다 일반적인 솔루션은 다음과 같습니다.
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
답변
내 솔루션은 원래 답변과 거의 동일하지만 저에게 효과가 없습니다.
그래서 열에 이름을 지정했고 작동합니다.
painel <- rbind(painel, data.frame("col1" = xtweets$created_at,
"col2" = xtweets$text))