2015-01-26 90 views
1

我將非結構化數據轉換爲長格式並需要創建一個ID(分組)變量。我想根據另一個變量中包含的值集來分配ID變量。更具體地說,考慮以下數據集。帶有索引的for循環的替代方法 - R

set.seed(1234); x.1 <- rep(letters[1:5], 10) 
x.2 <- sample(c(0:10), 50, replace=TRUE) 
x.3 <- rep(NA, 50); df <- data.frame(x.1, x.2, x.3) 
df <- df[-c(2, 19),] 

獨特情況下,可以從X.1變量被識別 - 它以a並用e結束。情況總是如此。 x.3將保存ID(分組)變量。

> head(df, 9) 
x.1 x.2 x.3 
a 1 NA 
c 6 NA 
d 6 NA 
e 9 NA 
a 7 NA 
b 0 NA 
c 2 NA 
d 7 NA 
e 5 NA 

ae之間記錄對於給定的情況下的數量可以顯着變化(在實際數據文件)。因此,我不能通過簡單地將變量除以固定數量的記錄來分配唯一的ID。我想通了如何使用for循環,使正確分配:

START <- which(df$x.1== "a") 
END <- which(df$x.1 == "e") 
for(i in 1:length(START)){df$x.3[START[i]:END[i]] <- i} 

head(df, 9) 
x.1 x.2 x.3 
a 1 1 
c 6 1 
d 6 1 
e 9 1 
a 7 2 
b 0 2 
c 2 2 
d 7 2 
e 5 2 

這種方法的明顯的問題是,它實在太慢了超過一百萬條記錄的數據集。看起來lapply可能是一個替代方案,但我似乎無法弄清楚如何指定案件何時結束以及新案件是否在數據文件中遍歷時開始。而且,如果存在的話,隨時指出我現有的答案 - 我沒有罰款!

在此先感謝。

回答

7

如果有羣體之間沒有縫隙,每一個「E」後,即遵循「一」爲一個組,您可以使用cumsum容易:

df$x.3 <- cumsum(df$x.1 == "a") 
df 
# x.1 x.2 x.3 
#1 a 1 1 
#3 c 6 1 
#4 d 6 1 
#5 e 9 1 
#6 a 7 2 
#7 b 0 2 
#8 c 2 2 
#9 d 7 2 
#10 e 5 2 
#11 a 7 3 
#12 b 5 3 
#13 c 3 3 
#... 

如果你的數據是極其大的你可以使用data.table通過引用來更新數據:

library(data.table) 
setDT(df)[, x.3 := cumsum(x.1 == "a")] 

作爲正確地評價@nicola指出的那樣,這是假定a唯一出現在beginngsÔ f組,不在它們中間。根據樣本數據,這似乎是一個有效的假設。


工作原理:

讓我們的欄目 「X.1」 的一個子集:

x <- df$x.1[1:15] 
x 
# [1] a c d e a b c d e a b c d e a 
#Levels: a b c d e 

現在,您可以檢查,如果x等於 「A」,這將創造一個邏輯矢量:

x == "a" 
# [1] TRUE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE 

現在,cumsum的作用:它增加了累計個所有的真值(這是本質上1秒):

cumsum(x == "a") 
# [1] 1 1 1 1 2 2 2 2 2 3 3 3 3 3 4 

所以,你可以像使用數值向量邏輯的載體和做數學計算與他們喜歡的1和0的向量。

+0

這是優雅的。你能描述一下cumsum是如何做到的嗎?這是完全正確的,但我不完全理解這個邏輯。 – 2015-01-26 21:04:28

+1

+1。但是,如果可以重複「a」,則可能會失敗。在這種情況下,更通用的解決方案可能是'cumsum(c(TRUE,df $ x.1 [1:(nrow(df)-1)] =「e」&df $ x.1 [2:nrow(df)] ==「a」))',唯一的條件是一個情況以「e」結尾並以「a」開頭。 – nicola 2015-01-26 21:07:23

+1

@BrianP,我添加了一個解釋 – 2015-01-26 21:13:18