如何在R中按列名拆分数据框?

8

我花了24个小时的时间搜索我认为是一个微不足道的问题(对于像我这样的R新手来说并非如此),但仍未有结果。所以请帮帮我。我有一个单独的数据框,我想将其拆分成两个。以下是数据的样式;

d1 d2 d3 d4 p1 p2 p3 p4
30 40 20 60 1  3  2  5  
20 50 40 30 3  4  1  5 
40 20 50 30 2  3  1  4 

以下是我想要的效果:

$d
d1 d2 d3 d4
30 40 20 60
20 50 40 30
40 20 50 30 

$p
p1 p2 p3 p4
1  3  2  5 
3  4  1  5
2  3  1  4

我尝试过大部分在网上找到的命令和示例,但它们似乎都是沿着行切分数据的,例如:

split(1:3, 1:2)

我该如何使用索引来表示我想要从前4列中分离出后4列?

6个回答

14

使用sapplystartsWith

sapply(c("d", "p"),
       function(x) df[startsWith(names(df),x)],
       simplify = FALSE)

# $d
# d1 d2 d3 d4
# 1 30 40 20 60
# 2 20 50 40 30
# 3 40 20 50 30
# 
# $p
# p1 p2 p3 p4
# 1  1  3  2  5
# 2  3  4  1  5
# 3  2  3  1  4

一个 tidyverse 的翻译:

library(tidyverse)
map(set_names(c("d", "p")),~select(df,starts_with(.x)))
# $d
# d1 d2 d3 d4
# 1 30 40 20 60
# 2 20 50 40 30
# 3 40 20 50 30
# 
# $p
# p1 p2 p3 p4
# 1  1  3  2  5
# 2  3  4  1  5
# 3  2  3  1  4

你知道如何在管道中传递 df 吗?df %>% map(set_names(c("d", "p")),~select(.,starts_with(.x))) 的效果不如预期。我认为这个可能行得通:df %>% map(~set_names(c("d", "p")) %>% select(., starts_with(.x))),但实际上并不行。你有什么建议吗?谢谢。 - user63230
1
也许 df %>% {map(set_names(c("d", "p")), function(.x) select(.,starts_with(.x)))} 这样点操作符就清楚地表示了 df,而不是像公式符号那样表示 .x - moodymudskipper

11

这里是使用 base R 中的 split 方法的一个选项


split.default(df1, sub('\\d+', '', names(df1)))
#$d
#  d1 d2 d3 d4
#1 30 40 20 60
#2 20 50 40 30
#3 40 20 50 30

#$p
#  p1 p2 p3 p4
#1  1  3  2  5
#2  3  4  1  5
#3  2  3  1  4

数据

df1 <- structure(list(d1 = c(30L, 20L, 40L), d2 = c(40L, 50L, 20L), 
    d3 = c(20L, 40L, 50L), d4 = c(60L, 30L, 30L), p1 = c(1L, 
    3L, 2L), p2 = c(3L, 4L, 3L), p3 = c(2L, 1L, 1L), p4 = c(5L, 
    5L, 4L)), class = "data.frame", row.names = c(NA, -3L))

1
有趣的是,在数据框上,split.default() 垂直分割 df,但 split.data.frame() 水平分割? - s_baldur
4
它的目的是用于列表和向量,因此它将data.frame视为列表,这将导致水平拆分,聪明极了,我今天学到了一招 :) - moodymudskipper

3

在基础R中,您可以使用grep函数。

ss <- c("d", "p")
lapply(setNames(ss, ss), function(x) df[, grep(x, colnames(df))])
#$d
#  d1 d2 d3 d4
#1 30 40 20 60
#2 20 50 40 30
#3 40 20 50 30
#
#$p
#  p1 p2 p3 p4
#1  1  3  2  5
#2  3  4  1  5
#3  2  3  1  4

示例数据

df <- read.table(text =
    "d1 d2 d3 d4 p1 p2 p3 p4
30 40 20 60 1  3  2  5
20 50 40 30 3  4  1  5
40 20 50 30 2  3  1  4", header = T)

1
你其实不需要 setNames,但它也不会有什么坏处。 - Bertil Baron
有些人更喜欢使用names()而不是colnames()——因为它更短,稍微快一点,因为colnames()最终会调用names()。 - s_baldur
很好,@BertilBaron;但是如果没有使用setNames函数,结果中的list将是未命名的;OP期望输出的list是带有名称的。 - Maurits Evers

2
这里介绍一种使用tidyverse的方法。
library(tidyverse)
df %>% gather(ind, values) %>%
  split(., gsub("[0-9]", "", df_td$ind)) %>%
  map(function(x) {
    x %>% 
      group_by(ind) %>% 
      mutate(id = row_number()) %>% 
      spread(ind, values) %>% 
      select(-1)})

# $d
# # A tibble: 3 x 4
#      d1    d2    d3    d4
#   <int> <int> <int> <int>
# 1    30    40    20    60
# 2    20    50    40    30
# 3    40    20    50    30

# $p
# # A tibble: 3 x 4
#      p1    p2    p3    p4
#   <int> <int> <int> <int>
# 1     1     3     2     5
# 2     3     4     1     5
# 3     2     3     1     4

数据

df <- structure(list(d1 = c(30L, 20L, 40L), d2 = c(40L, 50L, 20L), 
    d3 = c(20L, 40L, 50L), d4 = c(60L, 30L, 30L), p1 = c(1L, 
    3L, 2L), p2 = c(3L, 4L, 3L), p3 = c(2L, 1L, 1L), p4 = c(5L, 
    5L, 4L)), class = "data.frame", row.names = c(NA, -3L))

0

使用索引,这应该可以做到:

d = df[,c(1:4)]
p = df[,c(5:8)]

用名称,扩展相同的概念:

dindices = grep("^d", colnames(df))
pindices = grep("^p", colnames(df))
d = df[,dindices]
p = df[,pindices]

0

你可以使用dplyr库中的select函数从源数据框中创建两个新的数据框:

d<-select(dfsource, d1, d2, d3, d4)
p<-select(dfsource, p1, p2, p3, p4)

希望这能帮到你!对我来说没问题!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接