Відповіді:
rbind.fill
з пакета plyr
може бути те, що ви шукаєте.
Більш свіже рішення використовувати dplyr
«s bind_rows
функцію , яку я припускаю , є більш ефективним , ніж smartbind
.
df1 <- data.frame(a = c(1:5), b = c(6:10))
df2 <- data.frame(a = c(11:15), b = c(16:20), c = LETTERS[1:5])
dplyr::bind_rows(df1, df2)
a b c
1 1 6 <NA>
2 2 7 <NA>
3 3 8 <NA>
4 4 9 <NA>
5 5 10 <NA>
6 11 16 A
7 12 17 B
8 13 18 C
9 14 19 D
10 15 20 E
ABC
не може бути перетворений з символу в числовий. Чи є спочатку перетворення стовпців?
Ви можете використовувати smartbind
з gtools
пакета.
Приклад:
library(gtools)
df1 <- data.frame(a = c(1:5), b = c(6:10))
df2 <- data.frame(a = c(11:15), b = c(16:20), c = LETTERS[1:5])
smartbind(df1, df2)
# result
a b c
1.1 1 6 <NA>
1.2 2 7 <NA>
1.3 3 8 <NA>
1.4 4 9 <NA>
1.5 5 10 <NA>
2.1 11 16 A
2.2 12 17 B
2.3 13 18 C
2.4 14 19 D
2.5 15 20 E
smartbind
два великих кадри даних (загалом приблизно 3 * 10 ^ 6 рядків) і перервав це через 10 хвилин.
Якщо стовпці в df1 - це підмножина колонок у df2 (за назвами стовпців):
df3 <- rbind(df1, df2[, names(df1)])
Альтернатива з data.table
:
library(data.table)
df1 = data.frame(a = c(1:5), b = c(6:10))
df2 = data.frame(a = c(11:15), b = c(16:20), c = LETTERS[1:5])
rbindlist(list(df1, df2), fill = TRUE)
rbind
також працюватиме до data.table
тих пір, поки об'єкти будуть перетворені на data.table
об'єкти, так
rbind(setDT(df1), setDT(df2), fill=TRUE)
також буде працювати в цій ситуації. Це може бути кращим, коли у вас є пара даних.tables і не хочете складати список.
intersect
підхід, працюють лише для 2-х фреймів даних і не можуть легко узагальнити.
Більшість базових відповідей R стосуються ситуації, коли лише один data.frame має додаткові стовпці або що отриманий data.frame мав би перетин стовпців. Оскільки в ОП пише, що я сподіваюся зберегти стовпці, які не збігаються після прив’язки , відповідь, використовуючи базові методи R для вирішення цього питання, ймовірно, варто опублікувати.
Нижче я представляю два базових методу R: один, який змінює вихідні кадри data.frames, і той, який не робить. Крім того, я пропоную метод, який узагальнює неруйнівний метод для більш ніж двох фреймів data.frames.
Спочатку давайте отримаємо кілька прикладних даних.
# sample data, variable c is in df1, variable d is in df2
df1 = data.frame(a=1:5, b=6:10, d=month.name[1:5])
df2 = data.frame(a=6:10, b=16:20, c = letters[8:12])
Два data.frames, змінити оригінали
Для того, щоб зберегти всі стовпці з обох data.frames у rbind
(і дозволити роботі функції, не призводячи до помилки), ви додаєте стовпчики NA до кожного data.frame з відповідними відсутніми іменами, заповненими використовуючи setdiff
.
# fill in non-overlapping columns with NAs
df1[setdiff(names(df2), names(df1))] <- NA
df2[setdiff(names(df1), names(df2))] <- NA
Тепер, rbind
-ем
rbind(df1, df2)
a b d c
1 1 6 January <NA>
2 2 7 February <NA>
3 3 8 March <NA>
4 4 9 April <NA>
5 5 10 May <NA>
6 6 16 <NA> h
7 7 17 <NA> i
8 8 18 <NA> j
9 9 19 <NA> k
10 10 20 <NA> l
Зауважте, що перші два рядки змінюють вихідні data.frames, df1 та df2, додаючи повний набір стовпців до обох.
Два фрейми data.frames, не змінюють оригінали
Щоб залишити вихідні data.frames недоторканими, спочатку проведіть цикл через різні імена, поверніть названий вектор NA, які об'єднані в список із використанням data.frame c
. Потім data.frame
перетворює результат у відповідний data.frame для rbind
.
rbind(
data.frame(c(df1, sapply(setdiff(names(df2), names(df1)), function(x) NA))),
data.frame(c(df2, sapply(setdiff(names(df1), names(df2)), function(x) NA)))
)
Багато data.frames, не змінюють оригінали.
Якщо ви маєте більше двох data.frames, ви можете зробити наступне.
# put data.frames into list (dfs named df1, df2, df3, etc)
mydflist <- mget(ls(pattern="df\\d+"))
# get all variable names
allNms <- unique(unlist(lapply(mydflist, names)))
# put em all together
do.call(rbind,
lapply(mydflist,
function(x) data.frame(c(x, sapply(setdiff(allNms, names(x)),
function(y) NA)))))
Можливо, трохи приємніше не бачити назви рядків оригінальних data.frames? Потім зробіть це.
do.call(rbind,
c(lapply(mydflist,
function(x) data.frame(c(x, sapply(setdiff(allNms, names(x)),
function(y) NA)))),
make.row.names=FALSE))
mydflist <- list(as, dr, kr, hyt, ed1, of)
. Слід створити об'єкт списку, який не збільшує розмір вашого оточення, а лише вказує на кожен елемент списку (доки ви не змінюєте жодного змісту згодом). Після закінчення операції видаліть об'єкт списку, щоб бути безпечним.
Ви також можете просто витягнути загальні назви стовпців.
> cols <- intersect(colnames(df1), colnames(df2))
> rbind(df1[,cols], df2[,cols])
Я написав функцію, щоб це зробити, тому що мені подобається, що мій код підказує мені, якщо щось не так. Ця функція буде чітко вказати, які назви стовпців не відповідають, і якщо у вас невідповідність типу. Тоді вона зробить усе можливе, щоб все-таки поєднати data.frames. Обмеження полягає в тому, що ви можете комбінувати одночасно два кадри data.frames.
### combines data frames (like rbind) but by matching column names
# columns without matches in the other data frame are still combined
# but with NA in the rows corresponding to the data frame without
# the variable
# A warning is issued if there is a type mismatch between columns of
# the same name and an attempt is made to combine the columns
combineByName <- function(A,B) {
a.names <- names(A)
b.names <- names(B)
all.names <- union(a.names,b.names)
print(paste("Number of columns:",length(all.names)))
a.type <- NULL
for (i in 1:ncol(A)) {
a.type[i] <- typeof(A[,i])
}
b.type <- NULL
for (i in 1:ncol(B)) {
b.type[i] <- typeof(B[,i])
}
a_b.names <- names(A)[!names(A)%in%names(B)]
b_a.names <- names(B)[!names(B)%in%names(A)]
if (length(a_b.names)>0 | length(b_a.names)>0){
print("Columns in data frame A but not in data frame B:")
print(a_b.names)
print("Columns in data frame B but not in data frame A:")
print(b_a.names)
} else if(a.names==b.names & a.type==b.type){
C <- rbind(A,B)
return(C)
}
C <- list()
for(i in 1:length(all.names)) {
l.a <- all.names[i]%in%a.names
pos.a <- match(all.names[i],a.names)
typ.a <- a.type[pos.a]
l.b <- all.names[i]%in%b.names
pos.b <- match(all.names[i],b.names)
typ.b <- b.type[pos.b]
if(l.a & l.b) {
if(typ.a==typ.b) {
vec <- c(A[,pos.a],B[,pos.b])
} else {
warning(c("Type mismatch in variable named: ",all.names[i],"\n"))
vec <- try(c(A[,pos.a],B[,pos.b]))
}
} else if (l.a) {
vec <- c(A[,pos.a],rep(NA,nrow(B)))
} else {
vec <- c(rep(NA,nrow(A)),B[,pos.b])
}
C[[i]] <- vec
}
names(C) <- all.names
C <- as.data.frame(C)
return(C)
}
Можливо, я повністю неправильно прочитав ваше запитання, але "я сподіваюся зберегти стовпці, які не відповідають після прив'язки", змушує мене думати, що ви шукаєте left join
або right join
схожий на запит SQL. R має merge
функцію, яка дозволяє задавати ліве, праве або внутрішнє приєднання, подібне до об'єднання таблиць у SQL.
Тут вже є велике запитання та відповідь на цю тему: Як з'єднати (об'єднати) кадри даних (внутрішні, зовнішні, ліві, праві)?
gtools / smartbind не любив працювати з датами, ймовірно, тому, що це було as.vectoring. Тож ось моє рішення ...
sbind = function(x, y, fill=NA) {
sbind.fill = function(d, cols){
for(c in cols)
d[[c]] = fill
d
}
x = sbind.fill(x, setdiff(names(y),names(x)))
y = sbind.fill(y, setdiff(names(x),names(y)))
rbind(x, y)
}
Просто для документації. Ви можете спробувати Stack
бібліотеку та її функцію Stack
в наступному вигляді:
Stack(df_1, df_2)
У мене також складається враження, що це швидше, ніж інші методи для великих наборів даних.
Ви також можете використовувати sjmisc::add_rows()
, що використовує dplyr::bind_rows()
, але, на відміну від цього bind_rows()
, add_rows()
зберігає атрибути і, отже, корисно для мічених даних .
Дивіться наступний приклад з міченим набором даних. Функція frq()
друкує таблиці частот із позначеннями значень, якщо дані позначені міткою.
library(sjmisc)
library(dplyr)
data(efc)
# select two subsets, with some identical and else different columns
x1 <- efc %>% select(1:5) %>% slice(1:10)
x2 <- efc %>% select(3:7) %>% slice(11:20)
str(x1)
#> 'data.frame': 10 obs. of 5 variables:
#> $ c12hour : num 16 148 70 168 168 16 161 110 28 40
#> ..- attr(*, "label")= chr "average number of hours of care per week"
#> $ e15relat: num 2 2 1 1 2 2 1 4 2 2
#> ..- attr(*, "label")= chr "relationship to elder"
#> ..- attr(*, "labels")= Named num 1 2 3 4 5 6 7 8
#> .. ..- attr(*, "names")= chr "spouse/partner" "child" "sibling" "daughter or son -in-law" ...
#> $ e16sex : num 2 2 2 2 2 2 1 2 2 2
#> ..- attr(*, "label")= chr "elder's gender"
#> ..- attr(*, "labels")= Named num 1 2
#> .. ..- attr(*, "names")= chr "male" "female"
#> $ e17age : num 83 88 82 67 84 85 74 87 79 83
#> ..- attr(*, "label")= chr "elder' age"
#> $ e42dep : num 3 3 3 4 4 4 4 4 4 4
#> ..- attr(*, "label")= chr "elder's dependency"
#> ..- attr(*, "labels")= Named num 1 2 3 4
#> .. ..- attr(*, "names")= chr "independent" "slightly dependent" "moderately dependent" "severely dependent"
bind_rows(x1, x1) %>% frq(e42dep)
#>
#> # e42dep <numeric>
#> # total N=20 valid N=20 mean=3.70 sd=0.47
#>
#> val frq raw.prc valid.prc cum.prc
#> 3 6 30 30 30
#> 4 14 70 70 100
#> <NA> 0 0 NA NA
add_rows(x1, x1) %>% frq(e42dep)
#>
#> # elder's dependency (e42dep) <numeric>
#> # total N=20 valid N=20 mean=3.70 sd=0.47
#>
#> val label frq raw.prc valid.prc cum.prc
#> 1 independent 0 0 0 0
#> 2 slightly dependent 0 0 0 0
#> 3 moderately dependent 6 30 30 30
#> 4 severely dependent 14 70 70 100
#> NA NA 0 0 NA NA
rbind.ordered=function(x,y){
diffCol = setdiff(colnames(x),colnames(y))
if (length(diffCol)>0){
cols=colnames(y)
for (i in 1:length(diffCol)) y=cbind(y,NA)
colnames(y)=c(cols,diffCol)
}
diffCol = setdiff(colnames(y),colnames(x))
if (length(diffCol)>0){
cols=colnames(x)
for (i in 1:length(diffCol)) x=cbind(x,NA)
colnames(x)=c(cols,diffCol)
}
return(rbind(x, y[, colnames(x)]))
}
rbind.fill
іbind_rows()
обидва мовчки скидають назви рядків.