2017-09-25 73 views
2

我有以下數據集:計數過渡

data <- data.frame(id = 1:7, 
        t1 = c("AV1", "AV1", "AZ", "AV1", "AV1","AV1","AV2"), 
        t2 = c("AV2", NA, "AV3", "AV2", "AV2",NA, "AV3"), 
        t3 = c("AZ", "AV2", "AV4", "AZ", "AZ","AV4","AV4")) 

的每一行代表一個單獨的「ID」,與狀態(值)在多個不同的時間步長(列「 T1「 - 」 T3" ):

id t1 t2 t3 
1 AV1 AV2 AZ 
2 AV1 NA AV2 
3 AZ AV3 AV4 
4 AV1 AV2 AZ 
5 AV1 AV2 AZ 
6 AV1 NA AV4 
7 AV2 AV3 AV4 

我想算不同的轉變,‘從’的值在一個時間步,‘要’在隨後的時間步長值,總結了整個數據集:

from   to     count 
    AV1   AV2     4    
    AV2   AZ     3    
    AZ    AV3     1    
    AV3   AV4     2   
    AV1   AV4     1 
    AV2   AV3     1 

因此,「count」表示特定轉換髮生的次數。例如,AV1到AV2發生4次,AV2到AZ 3次。排除NA

非常感謝!

回答

3

爲了避免列的硬編碼,可以重塑你的數據,以長格式(melt)。使用headtail,以及每個「id」(by = id),以連續的時間步長對齊值。計數每個唯一過渡(by = .(from, to)

library(data.table) 
setDT(data) 
d <- melt(data ,id.vars = "id", na.rm=TRUE) 
d[ , .(from = head(value, -1), to = tail(value, -1)), by = id][ , .N, by = .(from, to)] 
# from to N 
# 1: AV1 AV2 4 
# 2: AV2 AZ 3 
# 3: AZ AV3 1 
# 4: AV3 AV4 2 
# 5: AV1 AV4 1 
# 6: AV2 AV3 1 

base替代在此類似,雖然具有的​​過渡串接的行(.N)的數目:

d <- na.omit(reshape(data, varying = list(2:4), direction = "long")) 
as.data.frame(table(unlist(by(d, d$id, function(dat) paste(head(dat$t1, -1), tail(dat$t1, -1), sep = " - "))))) 

#  Var1 Freq 
# 1 AV1 - AV2 4 
# 2 AV1 - AV4 1 
# 3 AV2 - AV3 1 
# 4 AV2 - AZ 3 
# 5 AV3 - AV4 2 
# 6 AZ - AV3 1 
0

其中一個辦法可能是

library(dplyr) 

d1 <- data %>% group_by(t1, t2) %>% filter(!is.na(t1) & !is.na(t2)) %>% summarise(n()) %>% `colnames<-`(c("from", "to", "weight")) %>% as.data.frame() 
d2 <- data %>% group_by(t2, t3) %>% filter(!is.na(t2) & !is.na(t3)) %>% summarise(n()) %>% `colnames<-`(c("from", "to", "weight")) %>% as.data.frame() 
d3 <- data %>% group_by(t1, t3) %>% filter(!is.na(t1) & !is.na(t3)) %>% summarise(n()) %>% `colnames<-`(c("from", "to", "weight")) %>% as.data.frame() 
#final data 
df <- rbind(d1, d2, d3) %>% group_by(from, to) %>% summarise(weight=sum(weight)) %>% as.data.frame() 
+0

@Pr喜時間,感謝您的回覆。但是,我注意到結果中存在更多的關係,例如,AV1> AZ和AV2> AV4不直接存在,而是間接存在。 –

+1

@MohammadZahrawy'df'擁有你期待的所有關係。 BTW Sotos的答案很棒,可以在任意數量的列上覆制。 – Prem

0

編輯
一個tidyverse的做法,避免了硬編碼列可以按照類似的方法來@ Henrik的優良接受的答案。在這種情況下,我使用了lead函數來合併相鄰的值,然後得到結果的count

library(tidyverse) 
data %>% 
    gather(key, value, -id) %>% filter(!is.na(value)) %>% group_by(id) %>% 
    transmute(from = value, to = lead(value)) %>% filter(!is.na(to)) %>% ungroup() %>% 
    count(from, to) 

#> # A tibble: 6 x 3 
#> from to  n 
#> <chr> <chr> <int> 
#> 1 AV1 AV2  4 
#> 2 AV1 AV4  1 
#> 3 AV2 AV3  1 
#> 4 AV2 AZ  3 
#> 5 AV3 AV4  2 
#> 6 AZ AV3  1 

原液
怎麼這樣呢?這不是很優雅,但我認爲它會完成工作。

library(dplyr) 
data <- tibble(id = 1:7, 
       t1 = c("AV1", "AV1", "AZ", "AV1", "AV1", "AV1", "AV2"), 
       t2 = c("AV2", NA, "AV3", "AV2", "AV2", NA, "AV3"), 
       t3 = c("AZ", "AV2", "AV4", "AZ", "AZ", "AV4", "AV4")) 


cnt1 <- data %>% filter(!is.na(t2)) %>% count(t1, t2) %>% rename(from = t1, to = t2) 
cnt2 <- data %>% filter(!is.na(t2)) %>% count(t2, t3) %>% rename(from = t2, to = t3) 
cnt3 <- data %>% filter(is.na(t2)) %>% count(t1, t3) %>% rename(from = t1, to = t3) 

cnt1 %>% 
    bind_rows(cnt2) %>% 
    bind_rows(cnt3) %>% 
    group_by(from, to) %>% 
    summarise(weight = sum(n)) 
#> # A tibble: 6 x 3 
#> # Groups: from [?] 
#> from to weight 
#> <chr> <chr> <int> 
#> 1 AV1 AV2  4 
#> 2 AV1 AV4  1 
#> 3 AV2 AV3  1 
#> 4 AV2 AZ  3 
#> 5 AV3 AV4  2 
#> 6 AZ AV3  1 
+0

謝謝。它工作,但這是一個樣本,我有53個序列(53列)哈哈。無論如何,你的代碼可以做到這一點嗎? –

+0

我認爲這可能是@MohammadZahrawy!必須有更好的方法來做到這一點。希望別人能提供更好的解決方案! – markdly

+0

@MohammadZahrawy,我添加了一個編輯來包含一個'tidyverse'選項,它應該適用於任意數量的列。 – markdly

2

這裏是一個通用的方法這將適用於任意數量的列。我們找到所有列的組合(索引方式)。我們使用它們來索引原始df中的列,並將它們放入列表中。粘貼元素,做一些清理(trimws(gsub('NA', '', do.call(paste, a[i1[,x]]))),然後使用table函數,我們得到您的預期結果。將它包裝在as.data.frame中給出您的預期輸出結構。

i1 <- combn(seq_along(a[-1])+1, 2) 

final_d <- as.data.frame(table(unlist(lapply(seq(ncol(i1)), function(x) { 
       v1 <- trimws(gsub('NA', '', do.call(paste, a[i1[,x]]))); 
       grep('\\s', v1, value = TRUE) 
       })))) 

賦予,

 Var1 Freq 
1 AV1 AV2 4 
2 AV1 AV4 1 
3 AV1 AZ 3 
4 AV2 AV3 1 
5 AV2 AV4 1 
6 AV2 AZ 3 
7 AV3 AV4 2 
8 AZ AV3 1 
9 AZ AV4 1 

或者完全一樣得到它,

setNames(data.frame(do.call('rbind', strsplit(as.character(final_d$Var1),' ',fixed=TRUE)), 
        final_d$Freq), 
        c('from', 'to', 'freq.')) 
from to freq. 
1 AV1 AV2  4 
2 AV1 AV4  1 
3 AV1 AZ  3 
4 AV2 AV3  1 
5 AV2 AV4  1 
6 AV2 AZ  3 
7 AV3 AV4  2 
8 AZ AV3  1 
9 AZ AV4  1