2011-06-08 119 views
2

车削数据我有这样R:在数据帧的列

Tag Date (DD/MM/YYYY) 
AA 1/1/2010 
AB 2/1/2010 
AC 3/1/2010 
AA 4/1/2010 
AB 5/1/2010 
AA 6/1/2010 
AB 7/1/2010 
AC 8/1/2010 

一个数据帧现在,有限量的不同标记,(小于10的平均)。我需要的是以更舒适的方式处理数据。我已经分析了标签序列数据以找出更频繁的重复模式,在这种情况下它将是(AA,AB,AC)。

现在,我想要的是将数据转换成这样的东西,所以我可以使用它。

AA  AB  AC 
1/1/2010 2/1/2010 3/1/2010 
4/1/2010 5/1/2010 NA 
6/1/2010 7/1/2010 8/1/2010 

我看到这个问题,Turning field values into column names in an R data frame,它非常接近我所需要的。这样做

>libray(reshape2) 
>df<-sqldf("SELECT Tag, Date FROM validData") 
>head(dcast(df,Date~Tag)) 

产生

Using Date as value column: use value_var to override. 
Aggregation function missing: defaulting to length 

       Date AF687A AVISOO B32D76 B3DC39 B52C72 DF7EAD DF8E83 DFA521 DFA91A 
1 2010-12-23 09:18:50  0  0  0  0  1  0  0  0  0 
2 2010-12-23 09:18:52  1  0  0  0  0  0  0  0  0 
3 2010-12-23 09:18:54  0  0  0  0  1  0  0  0  0 
4 2010-12-23 09:18:57  1  0  0  0  0  0  0  0  0 
5 2010-12-23 09:18:58  0  0  0  0  1  0  0  0  0 
6 2010-12-23 09:19:00  0  0  0  1  0  0  0  0  0 

我觉得我很近,但我想不通的最后一个步骤,如在什么上面描述我压缩表。任何线索?

+1

你做没有明确提及这一点,但你假设了一个关系(例如:当第六次观察没有标记AC时,在所需结果中使用NA)。如果您满意:标签为'AA'的所有日期,以及标签为'AB'(等)的所有日期,即使这些日期的长度不相同,任务也会简单得多。你能确认你想要哪一个? – 2011-06-08 15:51:54

+0

理想情况下,我想要第一个选择,当模式与列表不同时创建一个新行。我知道使用命令式语言或PLSQL之类的语言比使用R语言更容易。但至少第二选择将是我猜测的开始。 – 2011-06-08 15:59:03

回答

6

我会计算你想从Tag列的模式中放入Date的行和列,然后只填写一个新的矩阵。

首先设置你想匹配每一行的模式;我将使用unique的结果。如果第一组缺少一个值(不是最后一个值),这将无法正常工作。

pat <- unique(df$Tag) 

然后通过将标签与模式匹配来计算列,并通过注意何时开始新模式来计算该列。

col <- match(df$Tag, pat) 
row <- cumsum(c(0,diff(col))<=0) 

然后创建矩阵并填充它!

out <- matrix(nrow=max(row), ncol=max(col)) 
colnames(out) <- pat 
out[cbind(row, col)] <- df$Date 

结果是

> out 
    AA   AB   AC   
[1,] "1/1/2010" "2/1/2010" "3/1/2010" 
[2,] "4/1/2010" "5/1/2010" NA   
[3,] "6/1/2010" "7/1/2010" "8/1/2010" 
+0

真的很好的解决方案,但是,在'diff(k)'中有什么'k',你意思是'col'? – 2011-06-08 17:48:02

+0

是的,谢谢。固定。 – Aaron 2011-06-08 18:48:48

+0

你解决这个问题的方式确实令人印象深刻,尤其是'cumsum(c(0,diff(col))<= 0)',因此'out [cbind(row,col)] < - df $ Date'。我希望你能获得更多积分。 – Henrik 2011-06-08 20:34:22

1

尽管你在你的问题中描述了一张表格,但在我看来,你确实想要列出一个表格。为此,您可以使用拆分功能:

split(df, df$Tag) 

$AA 
    Tag  Date 
1 AA 1/1/2010 
4 AA 4/1/2010 
6 AA 6/1/2010 

$AB 
    Tag  Date 
2 AB 2/1/2010 
5 AB 5/1/2010 
7 AB 7/1/2010 

$AC 
    Tag  Date 
3 AC 3/1/2010 
8 AC 8/1/2010 

为了摆脱在每个列表中的标签栏,你可以结合使用lapplysplit

lapply(split(df, df$Tag), function(x)x$Date[drop=TRUE]) 

$AA 
[1] 1/1/2010 4/1/2010 6/1/2010 
Levels: 1/1/2010 4/1/2010 6/1/2010 

$AB 
[1] 2/1/2010 5/1/2010 7/1/2010 
Levels: 2/1/2010 5/1/2010 7/1/2010 

$AC 
[1] 3/1/2010 8/1/2010 
Levels: 3/1/2010 8/1/2010 
+0

我了解您的解决方案,但表格会突出显示NA值,如果它恰好显示。现在,这是一个很好的起点,我一定会考虑一下。 – 2011-06-08 16:08:22

1

我的回答使用了大量的讨厌编码(即两个嵌套的循环),以获得所需的解决方案,但它给你你想要什么:

df <- structure(list(Tag = c("AA", "AB", "AC", "AA", "AB", "AA", "AB", 
"AC"), Date = c("1/1/2010", "2/1/2010", "3/1/2010", "4/1/2010", 
"5/1/2010", "6/1/2010", "7/1/2010", "8/1/2010")), .Names = c("Tag", 
"Date"), class = "data.frame", row.names = c(NA, -8L)) 

l <- nrow(df) 
counter <- 1 
cols <- c("AA", "AB", "AC") 

fin <- data.frame(AA = NULL, AB = NULL, AC = NULL) 
tmp <- data.frame(AA = NA, AB = NA, AC = NA) 

while(counter < l) { 
    tmp <- data.frame(AA = NA, AB = NA, AC = NA) 
    for (col in 1:3) { 
     if (df[counter,1] == cols[col]) { 
      tmp[1,col] <- df[counter,2] 
      counter <- counter + 1 
     } 
    } 
    fin <- rbind(fin, tmp) 
} 

fin 

为您提供:

 AA  AB  AC 
1 1/1/2010 2/1/2010 3/1/2010 
2 4/1/2010 5/1/2010  <NA> 
3 6/1/2010 7/1/2010 8/1/2010 

请注意,您可以用cols <- unique(sort(df[,1]))争取更通用的解决方案(for (col in 1:3)fintmp建立将需要作相应改变)。

此外,这种解决方案根本不符合内存效率或任何事情。如果您预先分配等等(在更大的数据框架上),您将获得巨大的改进,但是如果要快速且肮脏的方式,它会起作用。

+0

非常好!我发现这是做这件事的方法,只是认为在R中可能有一个隐藏的包,它用一些神秘的参数来投射它会按我想要的那样吐出来。但是,谢谢! (我会等待已接受的解决方案,看看是否有其他人提出了更好的R-ness方法) – 2011-06-08 16:32:05

1

@Andrie是相当接近的解决方案

# here assumed length 3 
# but you can calculate it as max 
do.call(cbind,lapply(split(mdf$Date,mdf$Tag),"[",seq(3))) 


    AA   AB   AC   
[1,] "1/1/2010" "2/1/2010" "3/1/2010" 
[2,] "4/1/2010" "5/1/2010" "8/1/2010" 
[3,] "6/1/2010" "7/1/2010" NA   

EDIT(第一个解决方案没有考虑到模式

mdf$grp <- cumsum(1*c(TRUE,diff(as.numeric(factor(mdf$Tag)))<=0)) 
reshape(mdf,direction="wide",idvar="grp",timevar="Tag") 

    grp Date.AA Date.AB Date.AC 
1 1 1/1/2010 2/1/2010 3/1/2010 
4 2 4/1/2010 5/1/2010  <NA> 
6 3 6/1/2010 7/1/2010 8/1/2010