根据一组值将单个列拆分为多个列

时间:2016-11-09 14:36:18

标签: r dataframe

编辑: 好的,所以我使用G.Grothendieck的建议解决了我最初的问题,再次感谢,正是我所追求的干净方式。最初的帖子如下。 现在的事实是我的文件比这个模板更微妙。

实际上看起来像这样:

A1
100
200
txt 
A2
STRING
300
400
txt txt
txt
txt txt txt
A3
STRING
STRING
150
250
A2
.
.
.
在某事物之后就知道的STRING,有时它不会发生,有时只发生一次或几次。我一开始并没有注意到几次出现,所以在考虑它只发生一次时,我做了一个循环来处理这个问题:

for (i in 1:nrow(raw_data)){
  if (is.na(raw_data[i,2])) {
    raw_data <- raw_data[-c(i)]
  } else if (raw_data[i,2] == "STRING") {
    raw_data[i,2] = raw_data[i,3]
    raw_data[i,3] = raw_data[i,4]
    raw_data[i,4] = raw_data[i,5]
    raw_data[i,5] = raw_data[i,6]
    raw_data[i,6] = raw_data[i,7]
    raw_data[i,7] = raw_data[i,8]
    raw_data[i,8] = raw_data[i,9]
    raw_data[i,9] = raw_data[i,10]
    raw_data[i,10] = raw_data[i,12]
    raw_data[i,11] = "Yes"
    if (is.na(raw_data[i,13])){
      raw_data[i,12] = NA
    } else raw_data[i,12] = raw_data[i,13]

基本上我在第11栏中指定“是”来说明找到了字符串。我显然应该在这里陈述出现而不是是/否(默认为0,1或2或......)。所有其他列值都向左移动,以便它们返回到预期的列。

如果可能的话,我怎样才能适应这样的事实:实际上,我可能会发生几次STRING。我可能要改变我的方法吗?

现在对于那些喜欢挑战的人,我真的开始评估我的处理是否真的对这个文件有效...如何处理原始文件的每一行,因为我们知道像A1这样的东西A2等应该进入col1等...?

无论如何,感谢那些会对此进行调查的人并尝试:)

首发帖子: 我在R中有一个数据集,它由一个包含我想要在多列中的变量的列组成。结构如下:

A1
100
200
txt 
A2
300
400
txt txt
txt
txt txt txt
A3
150
250
A2
.
.
.

理想情况下,这是我追逐的结果:

A1 | 100 | 200 | txt  
A2 | 300 | 400 | txt txt | txt | txt txt
A3 | 150 | 250
A2 |  .  |  .  |  .

集合{A1; A2; A3}是已知的。我现在遇到的主要困难是列数是未知的。

我已经开始转置我的数据,并且正在考虑在单行上进行循环,每次我看到我的集合{A1; A2; A3}中的一个值时,我会以此值开始一个新行在第1列中,以便第1列仅包含{A1; A2; A3}值。

我确信有更清洁的方式来完成这项任务。

提前感谢您对此的帮助!

3 个答案:

答案 0 :(得分:5)

创建分组变量g并使用tapply将数据从长格式转换为列表v。最后,将v的每个组件转换为"ts"个对象,将cbind"ts"对象转换为一起(因为"ts"个对象可以绑定在一起并自动填充NAs )将结果转换为矩阵m。将m转换为data.frame并将type.convert应用于每列以修复列类型。如果矩阵##足以作为答案,则可以省略标记为m的两行。

没有使用任何包裹。

g <- cumsum(DF[[1]] %in% c("A1", "A2", "A3"))
v <- tapply(DF[[1]], g, c, simplify = FALSE)
m <- t(do.call(cbind, lapply(v, ts)))
DFout<- as.data.frame(m, stringsAsFactors = FALSE)    ##
DFout[] <- lapply(DFout, type.convert, as.is = TRUE)  ##

,并提供:

> DFout
  V1  V2  V3      V4   V5          V6
1 A1 100 200    txt  <NA>        <NA>
2 A2 300 400 txt txt  txt txt txt txt
3 A3 150 250    <NA> <NA>        <NA>
4 A2  NA  NA    <NA> <NA>        <NA>

注意:可重复形式的输入是:

DF <- structure(list(V1 = c("A1", "100", "200", "txt ", "A2", "300", 
"400", "txt txt", "txt", "txt txt txt", "A3", "150", "250", "A2"
)), .Names = "V1", row.names = c(NA, -14L), class = "data.frame")

答案 1 :(得分:2)

另一个想法:

library(dplyr)
library(splitstackshape)

df %>%
  group_by(id = cumsum(V1 %in% c("A1", "A2", "A3"))) %>%
  summarise(col = toString(V1)) %>%
  cSplit('col')

给出了:

#   id col_1 col_2 col_3   col_4 col_5       col_6
#1:  1    A1   100   200     txt    NA          NA
#2:  2    A2   300   400 txt txt   txt txt txt txt
#3:  3    A3   150   250      NA    NA          NA
#4:  4    A2    NA    NA      NA    NA          NA

答案 2 :(得分:2)

OP在其他答案发布后编辑了他的问题。因此,这些答案并未意识到"STRING"偶尔出现的附加复杂性。

以下解决方案解决了此问题,并计算了"STRING"在删除之前的出现次数。

library(data.table)
setDT(DF)[, rn := cumsum(V1 %like% "^A\\d+")][
  , occurrences := sum(V1 == "STRING"), by = rn][
    V1 != "STRING", 
    dcast(.SD, rn + occurrences ~ rowid(rn, prefix = "V"), value.var = "V1")][
      , lapply(.SD, function(x) if (is.character(x)) type.convert(x, as.is = TRUE) else x)]
   rn occurrences V1  V2  V3      V4  V5          V6
1:  1           0 A1 100 200     txt  NA          NA
2:  2           1 A2 300 400 txt txt txt txt txt txt
3:  3           2 A3 150 250      NA  NA          NA
4:  4           0 A2  NA  NA      NA  NA          NA

解释

  1. setDF(DF)强制执行data.table 到位,即无需复制。
  2. 标识以A开头,后跟一个或多个数字的行。每个行和所有后续行,直到下一个Axx获得唯一的组ID。遇到下一个Axx行时,组ID将提前1.
  3. 计算每组行中"STRING"的出现次数。
  4. 删除包含"STRING"的行后,其余行将使用dcast()进行重新整形。公式rn + occurrences ~ rowid(rn, prefix = "V")确定新表的布局。 rnoccurrences位于每一行的前面,而每组的行形成列。由于事先不知道每个组中的行数,rowid()函数用于对每个组中的行进行编号,从而创建新的列名。
  5. 最后,所有字符列都会转换为适当的类型。参数as.is = TRUE可以防止角色的因素变为因素。
  6. 数据

    DF <- structure(list(V1 = c("A1", "100", "200", "txt", "A2", "STRING", 
    "300", "400", "txt txt", "txt", "txt txt txt", "A3", "STRING", 
    "STRING", "150", "250", "A2")), .Names = "V1", row.names = c(NA, 
    -17L), class = "data.frame")