2017-09-25 3 views
2

Ich habe folgenden Datenbestand:Count Übergänge zwischen verschiedenen Zuständen in mehrere Zeitschritte (Spalten)

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")) 

Jede Zeile stellt einen individuellen „id“, mit den Zuständen (Werten) bei mehreren verschiedenen Zeitschritten (Spalten " 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 

I, die verschiedenen Übergänge zählen möge, ‚von‘ ein Wert in einem Zeitschritt ‚auf‘ ein Wert in einem nachfolgenden Zeitschritt für den gesamten Datensatz zusammengefasst :

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

"count" gibt also an, wie oft ein bestimmter Übergang auftritt. Zum Beispiel tritt AV1 bis AV2 viermal, AV2 bis AZ dreimal auf. NA s sind ausgeschlossen.

Vielen Dank!

Antwort

3

Hartcodierung der Spalten zu vermeiden, können Sie Ihre Daten in Langformat (melt) umformen. Verwenden Sie head und tail mit jeder "ID" (by = id), um Werte in aufeinanderfolgenden Zeitschritten auszurichten. Zählnummer Reihen (.N) jeder eindeutigen Übergang (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 

A base alternative in ähnlicher Weise, wenn auch mit den Übergängen verketteten:

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

Einer der Ansatz könnte

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

Hallo @Pr em, danke für deine Antwort. Mir ist jedoch aufgefallen, dass es in den Ergebnissen mehr Beziehungen gibt, zum Beispiel AV1> AZ und AV2> AV4, die nicht direkt, sondern indirekt existieren. –

+1

@MohammadZahrawy 'df' hat alle Beziehungen, die Sie erwarten. Die Antwort von BTW Sotos ist großartig und kann auf beliebig vielen Spalten repliziert werden. – Prem

0

bearbeiten
A tidyverse Weg beschritten, der Spalten harte Kodierung vermeidet @ Henriks ausgezeichnete akzeptierte Antwort einen ähnlichen Ansatz folgen konnte. In diesem Fall habe ich die Funktion lead verwendet, um benachbarte Werte zu kombinieren, bevor ein Ergebnis count genommen wird.

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 

Original-Lösung
Wie wäre es so etwas? Es ist nicht sehr elegant, aber ich denke, es wird den Job erledigen.

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

Danke. es funktioniert, aber das ist eine Probe, ich habe 53 Sequenzen (53 Spalten) haha. Gibt es das überhaupt, um das mit deinem Code möglich zu machen? –

+0

Ich dachte, das könnte der Fall @MohammadZahrawy sein! Es muss einen besseren Weg geben, dies zu tun. Hoffentlich kann jemand anderes eine bessere Lösung bieten! – markdly

+0

@MohammadZahrawy, Ich habe eine Bearbeitung hinzugefügt, um eine 'tidyverse'-Option einzuschließen, die für eine beliebige Anzahl von Spalten funktionieren sollte. – markdly

2

Hier ist ein gattungsgemäßes Verfahren das wird für eine beliebige Anzahl von Spalten funktionieren. Wir finden alle Paarkombinationen der Spalten (indexweise). Wir verwenden sie, um die Spalten aus dem ursprünglichen df zu indizieren und sie in die Liste aufzunehmen. Einfügen der Elemente, einige Reinigung (trimws(gsub('NA', '', do.call(paste, a[i1[,x]]))) und dann table Funktion verwenden, erhalten wir Ihre erwarteten Ergebnisse. Wrapping in as.data.frame gibt Ihre erwartete Ausgabestruktur.

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) 
       })))) 

die gibt,

 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 

Oder es genau das gleiche zu bekommen,

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 
Verwandte Themen