Как cbind или rbind векторов различной длины, не повторяя элементы более коротких векторов?
cbind(1:2, 1:10)
[,1] [,2]
[1,] 1 1
[2,] 2 2
[3,] 1 3
[4,] 2 4
[5,] 1 5
[6,] 2 6
[7,] 1 7
[8,] 2 8
[9,] 1 9
[10,] 2 10
Я хочу вывод, как показано ниже
[,1] [,2]
[1,] 1 1
[2,] 2 2
[3,] 3
[4,] 4
[5,] 5
[6,] 6
[7,] 7
[8,] 8
[9,] 9
[10,] 10
7 ответов
Хитрость заключается в том, чтобы сделать все ваши входные данные одинаковой длины.
x <- 1:2
y <- 1:10
n <- max(length(x), length(y))
length(x) <- n
length(y) <- n
Если вы хотите, чтобы ваш вывод был массивом, то cbind
работает, но вы получаете дополнительные NA
значения для заполнения прямоугольника.
cbind(x, y)
x y
[1,] 1 1
[2,] 2 2
[3,] NA 3
[4,] NA 4
[5,] NA 5
[6,] NA 6
[7,] NA 7
[8,] NA 8
[9,] NA 9
[10,] NA 10
Чтобы избавиться от NA
s, вывод должен быть списком.
Map(function(...)
{
ans <- c(...)
ans[!is.na(ans)]
}, as.list(x), as.list(y)
)
[[1]]
[1] 1 1
[[2]]
[1] 2 2
[[3]]
[1] 3
[[4]]
[1] 4
[[5]]
[1] 5
[[6]]
[1] 6
[[7]]
[1] 7
[[8]]
[1] 8
[[9]]
[1] 9
[[10]]
[1] 10
РЕДАКТИРОВАТЬ: я поменялся mapply(..., SIMPLIFY = FALSE)
за Map
,
Я столкнулся с подобной проблемой и хотел бы предложить дополнительное решение, которое, я надеюсь, может оказаться полезным для некоторых. Решение довольно простое и использует qpcR
пакет и предоставленный cbind.na
функция.
пример
x <- 1:2
y <- 1:10
dta <- qpcR:::cbind.na(x, y)
Результаты
> head(dta)
x y
[1,] 1 1
[2,] 2 2
[3,] NA 3
[4,] NA 4
[5,] NA 5
[6,] NA 6
Дополнительные комментарии
Следуя исходному примеру Chares, имена столбцов могут быть легко удалены:
colnames(dta) <- NULL
операция выдаст желаемый результат в полном объеме:
> head(dta)
[,1] [,2]
[1,] 1 1
[2,] 2 2
[3,] NA 3
[4,] NA 4
[5,] NA 5
[6,] NA 6
Я хотел бы предложить альтернативное решение, которое использует пакет rowr и их функцию cbind.fill.
> rowr::cbind.fill(1:2,1:10, fill = NA);
object object
1 1 1
2 2 2
3 NA 3
4 NA 4
5 NA 5
6 NA 6
7 NA 7
8 NA 8
9 NA 9
10 NA 10
Или, альтернативно, чтобы соответствовать желаемому результату OP:
> rowr::cbind.fill(1:2,1:10, fill = '');
object object
1 1 1
2 2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 10
Учитывая, что некоторые из вышеперечисленных решений полагаются на пакеты, которые больше не доступны, здесь вспомогательная функция, которая использует только
dplyr.
bind_cols_fill <- function(df_list) {
max_rows <- map_int(df_list, nrow) %>% max()
map(df_list, function(df) {
if(nrow(df) == max_rows) return(df)
first <- names(df)[1] %>% sym()
df %>% add_row(!!first := rep(NA, max_rows - nrow(df)))
}) %>% bind_cols()
}
Обратите внимание, что для этого требуется список фреймов данных, так что это немного громоздко, если нужно объединить только два вектора:
x <- 1:2
y <- 1:10
bind_cols_fill(list(tibble(x), tibble(y))
Вспомогательная функция...
bind.pad <- function(l, side="r", len=max(sapply(l,length)))
{
if (side %in% c("b", "r")) {
out <- sapply(l, 'length<-', value=len)
} else {
out <- sapply(sapply(sapply(l, rev), 'length<-', value=len, simplify=F), rev)}
if (side %in% c("r", "l")) out <- t(out)
out
}
Примеры:
> l <- lapply(c(3,2,1,2,3),seq)
> lapply(c("t","l","b","r"), bind.pad, l=l, len=4)
[[1]]
[,1] [,2] [,3] [,4] [,5]
[1,] NA NA NA NA NA
[2,] 1 NA NA NA 1
[3,] 2 1 NA 1 2
[4,] 3 2 1 2 3
[[2]]
[,1] [,2] [,3] [,4]
[1,] NA 1 2 3
[2,] NA NA 1 2
[3,] NA NA NA 1
[4,] NA NA 1 2
[5,] NA 1 2 3
[[3]]
[,1] [,2] [,3] [,4] [,5]
[1,] 1 1 1 1 1
[2,] 2 2 NA 2 2
[3,] 3 NA NA NA 3
[4,] NA NA NA NA NA
[[4]]
[,1] [,2] [,3] [,4]
[1,] 1 2 3 NA
[2,] 1 2 NA NA
[3,] 1 NA NA NA
[4,] 1 2 NA NA
[5,] 1 2 3 NA
Другое решение без зависимостей:
my_bind <- function(x, y){
if(length(x = x) > length(x = y)){
len_diff <- length(x) - length(y)
y <- c(y, rep(NA, len_diff))
}else if(length(x = x) < length(x = y)){
len_diff <- length(y) - length(x)
x <- c(x, rep(NA, len_diff))
}
cbind(x, y)
}
my_bind(x = letters[1:4], y = letters[1:2])
Используя здесь и там идеи других людей, ниже приведен мой собственный файл cbind.fill, который:
- выводит кадр данных
- одинаково работает с векторами, фреймами данных и матрицами
- хранит классы переменных фреймов данных
- использует только базовые функции
- дает вам возможность давать собственные имена кадрам выходных данных
- заставляет меня гордиться
cbind.fill = function(...,names=NA) {
xlist = list(...)
y= Reduce(
function(a,b) {
if(is.vector(a)) na = length(a)
if(is.data.frame(a)|is.matrix(a)) na = nrow(a)
if(is.vector(b)) nb = length(b)
if(is.data.frame(b)|is.matrix(b)) nb = nrow(b)
subset(
merge(
cbind(cbindfill.id = 1:na, a),
cbind(cbindfill.id = 1:nb, b),
all = TRUE,by="cbindfill.id"
),
select = -cbindfill.id
)}
,xlist)
if(!is.na(names[1])) colnames(y) <- names
return(y)
}
Короче говоря, он создает NA с помощью функции и обходитmerge
ограничение функции двумя элементами с помощьюReduce
функция.
Вот пример для проверки:
x <- 1:2
y <- 1:5
z <- data.frame(my=letters[1:4],your=as.integer(5:8),his=as.factor(12:15))
> cbind.fill(x,y,z)
a b my your his
1 1 1 a 5 12
2 2 2 b 6 13
3 NA 3 c 7 14
4 NA 4 d 8 15
5 NA 5 <NA> NA <NA>
> str(cbind.fill(x,y,z))
'data.frame': 5 obs. of 5 variables:
$ a : int 1 2 NA NA NA
$ b : int 1 2 3 4 5
$ my : chr "a" "b" "c" "d" ...
$ your: int 5 6 7 8 NA
$ his : Factor w/ 4 levels "12","13","14",..: 1 2 3 4 NA