通过数据帧迭代地追加或绑定列

时间:2018-06-16 14:44:41

标签: r dataframe dplyr tidyr

我有一个包含900列的数据框。我想使用tidyverse以三(或另一个数字)的倍数附加/绑定列。例如,将列2:3追加到1;列5:6到4,列8:9到7,依此类推整个数据帧。因此,最后我将有300列,同时保留主列的名称(其他列已被追加到其中)。

我该怎么做?非常感谢你:))

3 个答案:

答案 0 :(得分:2)

tidyverse方法:

library(tidyverse)
# data
df = data.frame(matrix(1:27, ncol=9))
names(df) <- paste('Int', rep(1:3, each=3), 'A', rep(1:3, 3), sep='_')

n = 3
df %>% 
    # split the data frame into three data frames 
    split.default(rep(1:n, ncol(df) / n)) %>% 
    # rename and row bind the three data frames together
    map_df(
        ~ set_names(.x, names(df)[c(T, rep(F, n - 1))]) %>%
            tibble::rownames_to_column('gene')
    )

#  gene Int_1_A_1 Int_2_A_1 Int_3_A_1
#1    1         1        10        19
#2    2         2        11        20
#3    3         3        12        21
#4    1         4        13        22
#5    2         5        14        23
#6    3         6        15        24
#7    1         7        16        25
#8    2         8        17        26
#9    3         9        18        27

关于set_names 的更多说明:c(T, rep(F, n - 1))首先创建一个向量为c(T, F, F, ...),因此names(df)[c(T, rep(F, n - 1))]每n个元素都会获取一个名称到R Cycling规则。

或者,如果您从矩阵开始,您可以使用array函数和所需的形状对其进行整形:

m = matrix(1:27, ncol=9)
m
#     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
#[1,]    1    4    7   10   13   16   19   22   25
#[2,]    2    5    8   11   14   17   20   23   26
#[3,]    3    6    9   12   15   18   21   24   27

array(m, c(nrow(m) * 3, ncol(m) / 3))
#      [,1] [,2] [,3]
# [1,]    1   10   19
# [2,]    2   11   20
# [3,]    3   12   21
# [4,]    4   13   22
# [5,]    5   14   23
# [6,]    6   15   24
# [7,]    7   16   25
# [8,]    8   17   26
# [9,]    9   18   27

要保留姓名,您可以使用data.table::melt

library(data.table)

示例数据

df = data.frame(matrix(1:27, ncol=9))
names(df) <- paste('Int', rep(1:3, each=3), 'A', rep(1:3, 3), sep='_')
df
#  Int_1_A_1 Int_1_A_2 Int_1_A_3 Int_2_A_1 Int_2_A_2 Int_2_A_3 Int_3_A_1 Int_3_A_2 Int_3_A_3
#1         1         4         7        10        13        16        19        22        25
#2         2         5         8        11        14        17        20        23        26
#3         3         6         9        12        15        18        21        24        27

# create the patterns that group data frames    
cols <- paste0('Int_', seq_len(ncol(df) / 3), '_A')

# melt the data.table based on the column patterns and here you also get an id column telling
# you where the data comes from the 1st, 2nd or 3rd ..
setNames(melt(setDT(df), measure=patterns(cols)), c('id', cols))

#   id Int_1_A Int_2_A Int_3_A
#1:  1       1      10      19
#2:  1       2      11      20
#3:  1       3      12      21
#4:  2       4      13      22
#5:  2       5      14      23
#6:  2       6      15      24
#7:  3       7      16      25
#8:  3       8      17      26
#9:  3       9      18      27

答案 1 :(得分:2)

使用tidyr::unitetidyr::separate_rows可以实现解决方案。方法是首先将3个组中的列合并,然后使用tidyr::separate_rows函数展开行中的列。

我在他的回答中采用了@Psidom创建的数据。另外,我应该提到基于data.table::melt的问题最适合。但是可以使用不同的方法探索不同的想法。

library(tidyverse)
# data
df = data.frame(matrix(1:27, ncol=9))
names(df) <- paste('Int', rep(1:3, each=3), 'A', rep(1:3, 3), sep='_')

lapply(split(names(df),cut(1:ncol(df),3, labels = seq_len(ncol(df) / 3))),
       function(x){unite_(df[,x], paste(x[1],x[3], sep = ":"), x, sep = ",",
                               remove = TRUE)}) %>%
  bind_cols() %>%
  separate_rows(., seq_len(ncol(.)), sep = ",")

#   Int_1_A_1:Int_1_A_3 Int_2_A_1:Int_2_A_3 Int_3_A_1:Int_3_A_3
# 1                   1                  10                  19
# 2                   4                  13                  22
# 3                   7                  16                  25
# 4                   2                  11                  20
# 5                   5                  14                  23
# 6                   8                  17                  26
# 7                   3                  12                  21
# 8                   6                  15                  24
# 9                   9                  18                  27

答案 2 :(得分:0)

基础R解决方案:

df <- head(mtcars)[-1:-2] # 9 cols

df[(seq(df)-1) %% 3 == 0] <-
  lapply(split(seq(df), (seq(df)-1) %/% 3),
         function(x) apply(df[x], 1, paste, collapse="_"))
df <- df[(seq(df)-1) %% 3 == 0]
df
#                           disp            wt    am
# Mazda RX4          160_110_3.9  2.62_16.46_0 1_4_4
# Mazda RX4 Wag      160_110_3.9 2.875_17.02_0 1_4_4
# Datsun 710         108_93_3.85  2.32_18.61_1 1_4_1
# Hornet 4 Drive    258_110_3.08 3.215_19.44_1 0_3_1
# Hornet Sportabout 360_175_3.15  3.44_17.02_0 0_3_2
# Valiant           225_105_2.76  3.46_20.22_1 0_3_1