2016-02-05 91 views
4

我有一系列按順序標記的批處理記錄。有時批次重疊。在向量中查找唯一一組字符串,其中向量元素可以是多個字符串

x <- c("1","1","1/2","2","3","4","5/4","5") 
> data.frame(x) 
    x 
1 1 
2 1 
3 1/2 
4 2 
5 3 
6 4 
7 5/4 
8 5 

我想查找不重疊的批次集並標記這些時間段。批次「1/2」包括「1」和「2」,因此它不是唯一的。當批次=「3」時,它不包含在任何以前的批次中,因此它開始一個新的時期。我很難處理合並的批次,否則這將很簡單。這樣做的結果是:

x period 
1 1  1 
2 1  1 
3 1/2  1 
4 2  1 
5 3  2 
6 4  3 
7 5/4  3 
8 5  3 

我的經驗是在更多的功能性編程範例,所以我知道我做這個的方式是非R。我正在尋找在R中乾淨而簡單的方法。任何幫助表示讚賞。

這是我的un-R代碼,但超級笨重,不可擴展。

x <- c("1","1","1/2","2","3","4","5/4","5") 

p <- 1 #period number 
temp <- NULL #temp variable for storing cases of x (batches) 
temp[1] <- x[1] 
period <- NULL 
rl <- 0 #length to repeat period 

for (i in 1:length(x)){ 

    #check for "/", split and add to temp 
    if (grepl("/", x[i])){ 
     z <- strsplit(x[i], "/") #split character 
     z <- unlist(z) #convert to vector 
     temp <- c(temp, z, x[i]) #add to temp vector for comparison 
    } 

    #check if x in temp 
    if(x[i] %in% temp){ 
     temp <- append(temp, x[i]) #add to search vector 
     rl <- rl + 1 #increase length 
    } else { 
     period <- append(period, rep(p, rl)) #add to period vector 
     p <- p + 1 #increase period count 
     temp <- NULL #reset 
     rl <- 1 #reset 
    } 
} 

#add last batch 

rl <- length(x) - length(period) 
period <- append(period, rep(p,rl)) 

df <- data.frame(x,period) 

> df 
    x period 
1 1  1 
2 1  1 
3 1/2  1 
4 2  1 
5 3  2 
6 4  3 
7 5/4  3 
8 5  3 
+0

因此,因爲批1/2包含1&2,2不再是唯一批次嗎?類似於爲什麼5不被認爲是一個獨特的批次? – MikeJewski

+0

是的,確切地說。 1/2包含1和2的部分。對於5/4也是如此。 –

+0

是否有批次包含兩個部分但以前沒有出現過的情況? – mtoto

回答

1

一點點短:

x <- c("1","1","1/2","2","3","4","5/4","5") 
x<-data.frame(x=x, period=-1, stringsAsFactors = F) 
period=0 
prevBatch=-1 
for (i in 1:nrow(x)) 
{ 
    spl=unlist(strsplit(x$x[i], "/")) 
    currentBatch=min(spl) 
    if (currentBatch<prevBatch) { stop("Error in sequence") } 
    if (currentBatch>prevBatch) 
     period=period+1; 

    x$period[i]=period; 

    prevBatch=max(spl) 
} 
x 
2

R具有功能性的範式的影響,所以你可以MapReduce解決這個問題。請注意,此解決方案遵循您將工具看作值的方法。如果您認爲批號是連續的,則可以使用更簡單的方法,因爲它們在您的示例中。

x <- c("1","1","1/2","2","3","4","5/4","5") 
s<-strsplit(x,"/") 
r<-Reduce(union,s,init=list(),acc=TRUE) 
p<-cumsum(Map(function(x,y) length(intersect(x,y))==0,s,r[-length(r)])) 

data.frame(x,period=p) 
 
    x period 
1 1  1 
2 1  1 
3 1/2  1 
4 2  1 
5 3  2 
6 4  3 
7 5/4  3 
8 5  3 

這樣做是先計算見過值的累積工會。然後,它映射到這個位置,以確定之前沒有看到當前值的位置。 (或者,第二步可以包含在減少內,但是這將是無需解構的支持的話)。累積和提供了基於交點已經空的次數的「週期」數字。

如果你做這樣的假設批號是連續的,那麼你可以做以下代替

x <- c("1","1","1/2","2","3","4","5/4","5") 
s<-strsplit(x,"/") 
n<-mapply(function(x) range(as.numeric(x)),s) 
p<-cumsum(c(1,n[1,-1]>n[2,-ncol(n)])) 

data.frame(x,period=p) 

出於同樣的結果(這裏不重複)。

+0

謝謝!我仍然試圖理解這個解決方案(儘管我通過運行它來了解它的工作原理)。 'acc = TRUE'在Reduce中做什麼?使用'union'減少似乎創建了一個獨特的分組列表。在那種情況下,爲什麼1/2會分裂?這讓我困惑,因爲那麼地圖步驟不會尋找原始'x'中不存在的分組? –

0

這裏的原始扭曲使用tidyr將數據分割成兩列,以便更容易使用:

# sample data 
x <- c("1","1","1/2","2","3","4","5/4","5") 
df <- data.frame(x) 

library(tidyr) 
# separate x into two columns, with second NA if only one number 
df <- separate(df, x, c('x1', 'x2'), sep = '/', remove = FALSE, convert = TRUE) 

現在df樣子:

> df 
    x x1 x2 
1 1 1 NA 
2 1 1 NA 
3 1/2 1 2 
4 2 2 NA 
5 3 3 NA 
6 4 4 NA 
7 5/4 5 4 
8 5 5 NA 

現在環路可能很簡單:

period <- 1 
for(i in 1:nrow(df)){ 
    period <- c(period, 
       # test if either x1 or x2 of row i are in any x1 or x2 above it 
       ifelse(any(df[i, 2:3] %in% unlist(df[1:(i-1),2:3])), 
        period[i],  # if so, repeat the terminal value 
        period[i] + 1)) # else append the terminal value + 1 
} 

# rebuild df with x and period, which loses its extra initializing value here 
df <- data.frame(x = df$x, period = period[2:length(period)]) 

由此產生的df

> df 
    x period 
1 1  1 
2 1  1 
3 1/2  1 
4 2  1 
5 3  2 
6 4  3 
7 5/4  3 
8 5  3 
相關問題