【问题标题】:append or bind columns iteratively through a dataframe通过数据框迭代地追加或绑定列
【发布时间】:2018-11-26 02:03:23
【问题描述】:

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

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

【问题讨论】:

  • 你想在 1 中连接 3 列的字符吗?

标签: r dataframe dplyr tidyr


【解决方案1】:

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))] 根据 R 循环规则每 n 个元素取一个名称。

或者如果你从一个矩阵开始,你可以用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

【讨论】:

  • 非常感谢。非常优雅且易于遵循的解决方案。我们可以保留行名吗?此外,如果“顶部”列的列名可以保持原样,那将是很好的。谢谢。
  • 您的数据框最初是如何命名的?如果您想保留原始名称中的信息,您可能需要reshape / melt / dcast
  • 列名是这样的: 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, etc... 所以,在 rowbind 之后,我将有 Int_1_A_1, IntA_1, Int_A_1, Int_A_1,等谢谢
  • 再次感谢您。我正在遵循您的 tidyverse 方法。代码完美地保留了我想要的列名,但仍然没有得到行名。我的行名实际上是基因名,我想保留它们。谢谢
  • 另外,你能解释一下map_df(~ set_names(.x, names(df)[c(T, rep(F, n - 1))]))
【解决方案2】:

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

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

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

【讨论】:

    【解决方案3】:

    基础 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
    

    【讨论】:

    • 谢谢。这将连接列而不是行绑定它们!这不是我想要的。不幸的是,@Psidom 的解决方案几乎完美,只是它删除了行名。
    • 当我面前有电脑时我会编辑,这很容易解决
    猜你喜欢
    • 2021-01-19
    • 1970-01-01
    • 2022-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-24
    • 1970-01-01
    • 2015-09-06
    相关资源
    最近更新 更多