我有一个数据框如下
Identifier V1 Location V2
1 12 A 21
1 12 B 24
2 20 B 15
2 20 C 18
2 20 B 23
3 43 A 10
3 43 B 17
3 43 A 18
3 43 B 20
3 43 C 25
3 43 A 30
我想为每个标识符重新构建一行,并为当前位置列中的每个值重新构建一列。我不关心V1中的数据,但我需要V2中的数据,这些数据将成为新列中的值。
请注意,对于Location列,标识符2和3有重复的值。
我认为第一项任务是使位置列中的值唯一。
我使用了以下内容(数据框称为“测试”)
L<-length(Test$Identifier)
for (i in 1:L)
{
temp<-Test$Location[Test$Identifier==i]
temp1<-make.unique(as.character(temp), sep="-")
levels(Test$Location)=c(levels(Test$Location),temp1)
Test$Location[Test$Identifier==i]=temp1
}
这会产生
Identifier V1 Location V2
1 12 A 21
1 12 B 24
2 20 B 15
2 20 C 18
2 20 B-1 23
3 43 A 10
3 43 B 17
3 43 A-1 18
3 43 B-1 20
3 43 C 25
3 50 A-2 30
然后使用
cast(Test, Identifier ~ Location)
给出
Identifier A B C B-1 A-1 A-2
1 21 24 NA NA NA NA
2 NA 15 18 23 NA NA
3 10 17 25 20 18 30
这或多或少都是我想要的。
我的问题是
这是解决问题的正确方法吗?
我知道R-people不使用“for”结构,所以是否有更优雅(更优雅)的方式来做到这一点?我应该提到真实数据集有超过160,000行,并在Location向量中以超过50个唯一值开始,该函数运行只需一个多小时。任何事情都会更快。我还应该提到,尽管增加了内存限制,但是必须一次在20-30k行的输出上运行强制转换函数。然后合并所有演员输出
有没有办法对输出中的列进行排序,以便(这里)它们是A,A-1,A-2,B,B-1,C
请回复你的回复!
答案 0 :(得分:1)
通常您的原始格式比您想要的结果要好得多。但是,您可以使用split-apply-combine方法轻松完成此操作,例如,使用package plyr:
DF <- read.table(text="Identifier V1 Location V2
1 12 A 21
1 12 B 24
2 20 B 15
2 20 C 18
2 20 B 23
3 43 A 10
3 43 B 17
3 43 A 18
3 43 B 20
3 43 C 25
3 43 A 30", header=TRUE, stringsAsFactors=FALSE)
#note that I make sure that there are only characters and not factors
#use as.character if you have factors
library(plyr)
DF <- ddply(DF, .(Identifier), transform, Loc2 = make.unique(Location, sep="-"))
library(reshape2)
DFwide <- dcast(DF, Identifier ~Loc2, value.var="V2")
# Identifier A B B-1 C A-1 A-2
#1 1 21 24 NA NA NA NA
#2 2 NA 15 23 18 NA NA
#3 3 10 17 20 25 18 30
如果列顺序对您很重要(通常不是):
DFwide[, c(1, order(names(DFwide)[-1])+1)]
# Identifier A A-1 A-2 B B-1 C
#1 1 21 NA NA 24 NA NA
#2 2 NA NA NA 15 23 18
#3 3 10 18 30 17 20 25
答案 1 :(得分:1)
作为参考,这里相当于@Roland在基础R中的答案。
使用ave
创建唯一的“位置”列....
DF$Location <- with(DF, ave(Location, Identifier,
FUN = function(x) make.unique(x, sep = "-")))
...和reshape
更改数据结构。
## If you want both V1 and V2 in your "wide" dataset
## "dcast" can't directly do this--you'll need `recast` if you
## wanted both columns, which first `melt`s and then `dcast`s....
reshape(DF, direction = "wide", idvar = "Identifier", timevar = "Location")
## If you only want V2, as you indicate in your question
reshape(DF, direction = "wide", idvar = "Identifier",
timevar = "Location", drop = "V1")
# Identifier V2.A V2.B V2.C V2.B-1 V2.A-1 V2.A-2
# 1 1 21 24 NA NA NA NA
# 3 2 NA 15 18 23 NA NA
# 6 3 10 17 25 20 18 30
重新排序列可以像@Roland建议的那样进行。