Stellen Sie die Zeilenreihenfolge für R data.table ein, indem Sie 2 Spalten verketten

8

Ich versuche herauszufinden, wie man eine R-Datentabelle basierend auf der Verkettung von 2 Spalten bestellt.

Hier ist meine Beispieldatentabelle.

dt <- data.table(id = c('A', 'A', 'A', 'A', 'A')
         , col1 = c(7521, 0, 7915, 5222, 5703)
         , col2 = c(7907, 5703, 8004, 7521, 5222))

   id col1 col2
1:  A 7521 7907
2:  A    0 5703
3:  A 7915 8004
4:  A 5222 7521
5:  A 5703 5222

Ich brauche die Zeilenreihenfolge, um mit col1 = 0 zu beginnen. Der Wert für col1 in Zeile 2 sollte gleich dem Wert von col2 in der vorhergehenden Zeile sein und so weiter.

Außerdem sollte es im Allgemeinen immer einen übereinstimmenden Wert geben, der die Zeilenreihenfolge verkettet. Wenn nicht, sollte der nächstgelegene Wert ausgewählt werden (siehe Zeilen 4 und 5 unten).

Das gesuchte Ergebnis ist unten dargestellt:

   id col1 col2
1:  A    0 5703
2:  A 5703 5222
3:  A 5222 7521
4:  A 7521 7907
5:  A 7915 8004

Ich denke, ich kann eine verrückte Funktion schreiben, um dies zu tun. Aber ich frage mich, ob es eine elegante data.table-Lösung gibt.

BEARBEITEN
Ich habe die Tabelle aktualisiert, um eine zusätzliche ID mit doppelten Zeilen und eine eindeutige Quellenspalte aufzunehmen:

dt <- data.table(id = c('A', 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'B')
               , col1 = c(7521, 0, 7915, 5222, 5703, 1644, 1625, 0, 1625, 1625)
               , col2 = c(7907, 5703, 8004, 7521, 5222, 1625, 1625, 1644, 1625, 1505)
               , source = c('c', 'b', 'a', 'e', 'd', 'y', 'z', 'x', 'w', 'v'))

    id col1 col2 source
 1:  A 7521 7907      c
 2:  A    0 5703      b
 3:  A 7915 8004      a
 4:  A 5222 7521      e
 5:  A 5703 5222      d
 6:  B 1644 1625      y
 7:  B 1625 1625      z
 8:  B    0 1644      x
 9:  B 1625 1625      w
10:  B 1625 1505      v

Innerhalb einer ID können übereinstimmende Werte vorhanden sein. Siehe B, Zeilen 7 und 9 oben. Es gibt jedoch eine eindeutige Quelle für jede Zeile, aus der diese Daten stammen.

Die gewünschte Ausgabe wäre:

    id col1 col2 source
 1:  A    0 5703      b
 2:  A 5703 5222      d
 3:  A 5222 7521      e
 4:  A 7521 7907      c
 5:  A 7915 8004      a
 6:  B    0 1644      x
 7:  B 1644 1625      y
 8:  B 1625 1625      w
 9:  B 1625 1625      z
10:  B 1625 1625      v

In der Ausgabe können die übereinstimmenden Zeilen 8 und 9 in beliebiger Reihenfolge sein.

Vielen Dank!

AlexP
quelle
Hätte col2Duplikate in einer ID? Ihr Beispiel würde so funktionieren, wie es ist, aber wenn es weitere Zeilen gibt, col2wäre dies entweder 1625 oder eine Nichtübereinstimmung.
Cole
Ja. Daran habe ich nicht gedacht. Weitere Informationen zu Datensätzen finden Sie im bearbeiteten Beitrag.
AlexP

Antworten:

3

Hier ist ein anderer Ansatz, der:

  1. Ordnet die Daten neu, bei denen der Wert 0 an erster Stelle steht.
  2. Durchläuft den Rest der Werte, um den Index der col2Übereinstimmungen zurückzugeben col1.
setorder(dt, col1)

neworder = seq_len(nrow(dt))
init = 1L
col1 = dt[['col1']]; col2 = dt[['col2']]

for (i in seq_along(neworder)[-1L]) {
  ind = match(col2[init], col1)
  if (is.na(ind)) break
  neworder[i] = init = ind
}

dt[neworder]

##       id  col1  col2
##   <char> <num> <num>
##1:      A     0  5703
##2:      A  5703  5222
##3:      A  5222  7521
##4:      A  7521  7907
##5:      A  7915  8004

Wenn Sie dies mit Gruppierung tun, können Sie die Schleife in a einschließen dt[, .I[{...}, by = id]$V1, um die Indizes zurückzugeben. Oder damit es besser aussieht, können wir eine Funktion erstellen.

recursive_order = function (x, y) {
  neworder = seq_len(length(x))
  init = 1L

  for (i in neworder[-1L]) {
    ind = match(y[init], x)
    if (is.na(ind)) break

    # Multiple matches which means all the maining matches are the same number
    if (ind == init) { 
      inds = which(x %in% y[init])
      l = length(inds)
      neworder[i:(i + l - 2L)] = inds[-1L]
      break
    }
    neworder[i] = init = ind
  }
  return(neworder)
}

dt <- data.table(id = c('A', 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'B')
                 , col1 = c(7521, 0, 7915, 5222, 5703, 1644, 1625, 0, 1625, 1625)
                 , col2 = c(7907, 5703, 8004, 7521, 5222, 1625, 1625, 1644, 1625, 1505)
                 , source = c('c', 'b', 'a', 'e', 'd', 'y', 'z', 'x', 'w', 'v'))

setorder(dt, col1)
dt[dt[, .I[recursive_order(col1, col2)], by = id]$V1]

       id  col1  col2 source
    <char> <num> <num> <char>
 1:      A     0  5703      b
 2:      A  5703  5222      d
 3:      A  5222  7521      e
 4:      A  7521  7907      c
 5:      A  7915  8004      a
 6:      B     0  1644      x
 7:      B  1644  1625      y
 8:      B  1625  1625      z
 9:      B  1625  1625      w
10:      B  1625  1505      v
Cole
quelle
Das funktioniert! Ich muss das noch ein bisschen besser verstehen, aber gute Ergebnisse. Was müssten Sie tun, wenn die Spalte 'id' mehr Werte enthält? Nehmen wir an, es hat die IDs 'b' und 'c' mit ihren jeweiligen Werten.
AlexP
@AlexP siehe bearbeiten. Dies entspricht Ihrer erwarteten Ausgabe Ihrer überarbeiteten Frage.
Cole
7

Hier ist eine Option igraphmit data.table:

#add id in front of cols to distinguishes them as vertices
cols <- paste0("col", 1L:2L)
dt[, (cols) := lapply(.SD, function(x) paste0(id, x)), .SDcols=cols]

#permutations of root nodes and leaf nodes
chains <- dt[, CJ(root=setdiff(col1, col2), leaf=setdiff(col2, col1)), id]

#find all paths from root nodes to leaf nodes
#note that igraph requires vertices to be of character type
library(igraph)
g <- graph_from_data_frame(dt[, .(col1, col2)])
l <- lapply(unlist(
  apply(chains, 1L, function(x) all_simple_paths(g, x[["root"]], x[["leaf"]])), 
  recursive=FALSE), names)
links <- data.table(g=rep(seq_along(l), lengths(l)), col1=unlist(l))

#look up edges
dt[links, on=.(col1), nomatch=0L]

Ausgabe:

    id  col1  col2 source g
 1:  A    A0 A5703      b 1
 2:  A A5703 A5222      d 1
 3:  A A5222 A7521      e 1
 4:  A A7521 A7907      c 1
 5:  A A7915 A8004      a 2
 6:  B    B0 B1644      x 3
 7:  B B1644 B1625      y 3
 8:  B B1625 B1625      z 3
 9:  B B1625 B1625      w 3
10:  B B1625 B1505      v 3

Daten:

library(data.table)
dt <- data.table(id = c('A', 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'B')
  , col1 = c(7521, 0, 7915, 5222, 5703, 1644, 1625, 0, 1625, 1625)
  , col2 = c(7907, 5703, 8004, 7521, 5222, 1625, 1625, 1644, 1625, 1505)
  , source = c('c', 'b', 'a', 'e', 'd', 'y', 'z', 'x', 'w', 'v'))
chinsoon12
quelle
Hmmm .. Ich erhalte eine Fehlermeldung, wenn ich das Lapply mache: Fehler in all_simple_paths (g, x [1L], x [2L]): Bei paths.c: 77: Ungültiger Startscheitelpunkt, Ungültiger Wert
AlexP
Die Ausgabe für Ketten ist Wurzelblatt 1: 0 7907 2: 0 8004 3: 7915 7907 4: 7915 8004
AlexP
@AlexP, der Scheitelpunkt des Diagramms muss vom Zeichentyp sein. Deshalb benutze ich as.character oncol*
chinsoon12
Ahhhh ok! Ich habe die Änderung der Spalten in eine Zeichenklasse verpasst. Es klappt! Vielen Dank!
AlexP
1
@AlexP Ich habe Code hinzugefügt, um id
chinsoon12