在数据帧中按因子分割列

时间:2014-06-29 00:55:30

标签: r split dataframe plyr

假设我有一个这样的数据框:

v1   v2   v3
a    1    a
a    2    b
a    6    c
b    3    a
b    4    b
b    5    c

其中v1是一个因子,v3是一个字符。我想将一些函数应用于数据框,这样v2将在v1中拆分,然后包含在数据框中:

v1   v2   v3   v4   v5
a    1    a    1    NA
a    2    b    2    NA
a    6    c    6    NA
b    3    a    NA   3
b    4    b    NA   4
b    5    c    NA   5

我能够解决的解决方案非常复杂。这样做有一种优雅的方式吗?

(注意:v3的存在是因为任何解决方案都需要能够处理数据帧中应该忽略的其他非数字向量的存在。)

5 个答案:

答案 0 :(得分:6)

1)transform / ifelse 如果v1中有少量已知值,则一种简单的方法是手动生成每个新列:

transform(DF, a = ifelse(v1 == "a", v2, NA), 
              b = ifelse(v1 == "b", v2, NA))

2)tapply 更通用的方法是:

cbind(DF, tapply(DF$v2, list(1:nrow(DF), DF$v1), identity))

上述解决方案不需要任何插件包。

3)data.table 。此解决方案假定v1是一个因素,并且DF的行是唯一的(如问题中的情况):

# devtools::install_github("Rdatatable/datatable")  # 1.9.3

library(data.table)
DT <- data.table(DF)

DT[, split(v2, v1), by = DT]

如果DT的行可能不是唯一的那么(根据与Arun的讨论),这将有效:

DT[, c(.SD, split(v2, v1)), by = 1:nrow(DT)][, -1, with = FALSE]

更新一些改进。

答案 1 :(得分:3)

使用dplyr 如果dd是数据集

# install.packages("devtools")
devtools::install_github("hadley/tidyr")
library(dplyr)
library(tidyr)
dd1 <- dd %>% 
  mutate(n = seq_len(n())) %>%
  spread(v1,v2) %>% 
  arrange(n) %>% 
  select(-n,-v3)

cbind(dd, dd1)
#    v1 v2 v3  a  b
#1  a  1  a  1 NA
#2  a  2  b  2 NA
#3  a  6  c  6 NA
#4  b  3  a NA  3
#5  b  4  b NA  4
#6  b  5  c NA  5

答案 2 :(得分:2)

您可以在此处使用reshape2包。首先,您的测试数据

dd<-data.frame(
    v1 = factor(c("a", "a", "a", "b", "b", "b")),
    v2 = c(1, 2, 6, 3, 4, 5), 
    v3 = c("a", "b", "c", "a", "b", "c"),
    stringsAsFactors=F
)

现在创建新列

library(reshape2)
nc<-dcast(dd, 1:nrow(dd)~v1, value.var="v2")[-1]

现在将它们合并到

dd<-cbind(dd, nc)
dd

获取

  v1 v2 v3  a  b
1  a  1  a  1 NA
2  a  2  b  2 NA
3  a  6  c  6 NA
4  b  3  a NA  3
5  b  4  b NA  4
6  b  5  c NA  5

答案 3 :(得分:1)

Gabor的data.table答案很好,但每行split,这对较大的表来说会变慢。以下是使用rbindlist的另一种方式,来自1.9.3

tmp = DT[, list(V = list(
             setattr(list(v2), 'names', v1)
           )), by = list(v1 = as.character(v1))]$V
## 1.9.3
tmp = rbindlist(tmp, fill=TRUE)
#     a  b
# 1:  1 NA
# 2:  2 NA
# 3:  6 NA
# 4: NA  3
# 5: NA  4
# 6: NA  5

DT[, c(names(tmp)) := tmp]
#    v1 v2 v3  a  b
# 1:  a  1  a  1 NA
# 2:  a  2  b  2 NA
# 3:  a  6  c  6 NA
# 4:  b  3  a NA  3
# 5:  b  4  b NA  4
# 6:  b  5  c NA  5

PS:此解决方案假定DT已在列v1上排序。


这是另一个不依赖v1排序的版本:

for (j in unique(DT$v1)) {
    ix = which(DT$v1 == j)
    set(DT, i=ix, j=j, value=DT$v2[ix])
}

答案 4 :(得分:0)

这是一种基础方法,可直接完成工作:

n <- ncol(x)
for (i in 1:length(levels(x$v1))) 
{
    s <- as.numeric(x$v1)==i
    x[s,n+i] <- x$v2[s]
}

> x
  v1 v2 v3 V4 V5
1  a  1  a  1 NA
2  a  2  b  2 NA
3  a  6  c  6 NA
4  b  3  a NA  3
5  b  4  b NA  4
6  b  5  c NA  5

这是取因子x$v1的数值,并将其用作从v2复制数据的位置。由于它正在添加列,因此可能存在的其他内容并不重要。