7

私は、要素の構造、サイズ、または順序が均一ではない(両方が存在する可能性があり、同一として扱われる必要がある)深くネストされたリストを含むリスト列を持つ大きな(数百万行)データテーブルを操作していlist(x=1,y=2)ますlist(y=2,x=1)。データテーブルの一部の列とリスト列のデータのサブセットを含む任意のグループ化を繰り返し実行する必要があります。すべての行にサブセットと一致する値があるわけではありません。

私が思いついたアプローチは非常に複雑に感じます。重要なポイントは次のとおりです。

  • ネストされたリスト構造の値を識別します。私のアプローチは、を使用することです。これはul <- unlist(list_col)、ネストされたデータ構造を「フラット化」し、各要素に直接アクセスするための階層名を作成しますaddress.country.code

  • リストされていない同じデータの順列がグループ化の観点から等しいと見なされるようにする。私のアプローチは、リストされていないベクトルをそれらの値の名前で並べ替え、ul[order(names(ul))]その結果を参照によって新しい文字ベクトル列として割り当てることです。

  • フラット化された値のサブセットでグループ化を実行します。by=値がリストまたはベクトルである列を操作することはできませんでした。したがって、一意の文字ベクトルを単純な値にマップする方法を見つける必要がありました。私はこれをで行いましdigestた。

2つの主力機能は次のとおりです。

# Flatten list column in a data.table
flatten_list_col <- function(dt, col_name, flattened_col_name='props') {

  flatten_props <- function(d) {
    if (length(d) > 0) {
      ul <- unlist(d)
      names <- names(ul)
      if (length(names) > 0) {
        ul[order(names)]          
      } else {
        NA
      }
    } else {
      NA
    }
  }

  flattened <- lapply(dt[[col_name]], flatten_props)
  dt[, as.character(flattened_col_name) := list(flattened), with=F]
}

# Group by properties in a flattened list column
group_props <- function(prop_group, prop_col_name='props') {
  substitute({
    l <- lapply(eval(as.name(prop_col_name)), function(x) x[names(x) %in% prop_group])
    as.character(lapply(l, digest))
  }, list(prop_group=prop_group, prop_col_name=prop_col_name))
}

再現可能な例を次に示します。

library(data.table)

dt <- data.table(
  id=c(1,1,1,2,2,2), 
  count=c(1,1,2,2,3,3), 
  d=list(
    list(x=1, y=2), 
    list(y=2, x=1), 
    list(x=1, y=2, z=3),
    list(y=5, abc=list(a=1, b=2, c=3)),
    NA,
    NULL    
    )
)

flatten_list_col(dt, 'd')
dt[, list(total=sum(count)), by=list(id, eval(group_props(c('x', 'y'))))]

出力は次のとおりです。

> flatten_list_col(dt, 'd')
   id count      d   props
1:  1     1 <list>     1,2
2:  1     1 <list>     1,2
3:  1     2 <list>   1,2,3
4:  2     2 <list> 1,2,3,5
5:  2     3     NA      NA
6:  2     3             NA

> dt[, list(total=sum(count)), by=list(id, eval(group_props(c('x', 'y'))))]
   id                      group_props total
1:  1 325c6bbb2c33456d0301cf3909dd1572     4
2:  2 7aa1e567cd0d6920848d331d3e49fb7e     2
3:  2 ee7aa3b9ffe6bffdee83b6ecda90faac     6

このアプローチは機能しますが、リストをフラット化して順序付けする必要があり、ダイジェストを計算する必要があるため、かなり非効率的です。私は次のことについて疑問に思っています:

  1. これは、リスト列から直接値を取得することで、フラット化された列を作成せずに実行できますか?これには、単純な名前ではなく、選択したプロパティを式として指定する必要があります。

  2. の必要性を回避する方法はありdigestますか?

4

1 に答える 1

4

ここには多くの問題があります。最も重要なこと(そして他の人のためにまだ来ていないこと)は、参照によって割り当てているが、参照によってそうするためのスペースよりも多くの値に置き換えようとしていることです。

この非常に単純な例を見てください

DT <- data.table(x=1, y = list(1:5))
DT[,new := unlist(y)]
Warning message:
In `[.data.table`(DT, , `:=`(new, unlist(y))) :
  Supplied 5 items to be assigned to 1 items of column 'new' (4 unused)

nrow(DT)新しく作成されたリストの最初のアイテムを除くすべてが失われます。それらはdata.tableの行に対応しません

したがって、これらのリスト変数を分解するdata.tableのに十分な大きさの新しい変数を作成する必要があります。これは参照では不可能です。

 newby <- dt[,list(x, props = as.character(unlist(data))), by = list(newby = seq_len(nrow(dt)))][,newby:=NULL]
newby


   x props
 1: 1     1
 2: 1     2
 3: 1     2
 4: 1     1
 5: 1    10
 6: 2     1
 7: 2     2
 8: 2     3
 9: 2     5
10: 2     1
11: 2     2
12: 2     3
13: 3    NA
14: 3    NA

すべての値が同じタイプであり、変換でデータが失われないタイプであることを確認するには、as.characterが必要であることに注意してください。momemntでは、NA数値/整数データのリストの中に論理値があります。


すべてのコンポーネントを強制的に文字にするための別の編集(NAも含む)。propsは、各行に1文字のベクトルを持つリストになりました。

flatten_props <-function(data){if(is.list(data)){ul <-unlist(data)if(length(ul)> 1){ul <-ul [order(names(ul))]} as .character(ul)} else {as.character(unlist(data))}}

dt[, props := lapply(data, flatten_props)]
dt
   x   data   props
1: 1 <list>     1,2
2: 1 <list>  10,1,2
3: 2 <list>   1,2,3
4: 2 <list> 1,2,3,5
5: 3     NA      NA
6: 3   

dt[,lapply(props,class)]
          V1        V2        V3        V4        V5        V6
1: character character character character character character
于 2012-12-21T03:56:17.773 に答える