Как 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

Чтобы избавиться от NAs, вывод должен быть списком.

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
Другие вопросы по тегам