2列をチェーンしてR data.tableの行順序を設定する


8

2列のチェーンに基づいてRデータテーブルを注文する方法を理解しようとしています。

これが私のサンプルのdata.tableです。

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

col1 = 0で始まる行の順序が必要です。行2のcol1の値は、前の行のcol2の値と等しくなければなりません。

さらに、通常、行の順序をつなぐ値が常に一致している必要があります。しかし、そうでない場合は、最も近い値を選択する必要があります(以下の行4および5を参照)。

私が探している結果を以下に示します。

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

私はこれを行うためにクレイジーな関数を書くことができると思います。しかし、エレガントなdata.tableソリューションがあるかどうか疑問に思っています。

編集
私はテーブルを更新して、重複する行と一意のソース列を持つ追加のIDを含めました:

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

ID内に一致する値が存在する可能性があります。上記のB、行7および9を参照してください。ただし、このデータの取得元である行ごとに固有のソースがあります。

望ましい出力は次のようになります。

    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

出力では、一致する行8と9は任意の順序にすることができます。

ありがとう!


col2IDでの重複を持っていますか?あなたの例はそのまま動作しますが、それ以上の行がある場合、col21625または一致しないものになります。
コール

はい。思ったよりも。データセットの詳細については、編集された投稿を参照してください。
AlexP

回答:


3

次に、別のアプローチを示します。

  1. 最初に0値を配置するデータを並べ替えます。
  2. 残りの値をループして、col2一致する場所のインデックスを返します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

グループ化を使用して実行している場合は、ループをa内にラップしdt[, .I[{...}, by = id]$V1てインデックスを返すことができます。または、見栄えをよくするために、関数を作成できます。

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

これでうまくいきます!私はまだこれを少し良く理解する必要がありますが、良い結果です。「id」列にさらに値がある場合、何をしなければなりませんか?IDがそれぞれの値を持つ「b」と「c」を持っているとしましょう。
AlexP

@AlexP編集を参照してください。これは、修正された質問の予想される出力と一致します。
Cole

7

これはigraphwith を使用するオプション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]

出力:

    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

データ:

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

うーん.. lapplyを実行するとエラーが発生します:all_simple_paths(g、x [1L]、x [2L])のエラー:paths.c:77で:無効な開始頂点、無効な値
AlexP

チェーンの出力は、ルートリーフ1:0 7907 2:0 8004 3:7915 7907 4:7915 8004
AlexP

@AlexP、グラフの頂点は文字型でなければなりません。したがって、それがas.characterを使用する理由ですcol*
chinsoon12

ああ、わかった!列を文字クラスに変更するのを逃しました。できます!本当にありがとう!
AlexP

1
@AlexP IDを処理するコードを追加しました
chinsoon12
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.