8

値の発生に基づいて、データ フレームのサブセットを取得しようとしています。これは、以下に示す例で最もよく説明されています。この質問は、R のデータ フレーム内の列の一意の値ごとに上位の有限数の行を選択することと高い関係があります 。ただし、head() コマンドで選択される項目の数を変更したいです。

#Sample data
input <- matrix( c(1000001,1000001,1000001,1000001,1000001,1000001,1000002,1000002,1000002,1000003,1000003,1000003,100001,100002,100003,100004,100005,100006,100002,100003,100007,100002,100003,100008,"2011-01-01","2011-01-02","2011-01-01","2011-01-04","2011-01-01","2011-01-02","2011-01-01","2011-01-04","2011-01-01","2011-01-02","2011-01-01","2011-01-04"), ncol=3)
colnames(input) <- c( "Product" , "Something" ,"Date")
input <- as.data.frame(input)
input$Date <- as.Date(input[,"Date"], "%Y-%m-%d")

#Sort based on date, I want to leave out the entries with the oldest dates.
input <- input[ with( input, order(Date)), ]

#Create number of items I want to select
table_input <- as.data.frame(table(input$Product))
table_input$twentyfive <- ceiling( table_input$Freq*0.25  )

#This next part is a very time consuming method (Have 2 mln rows, 90k different products)

first <- TRUE

for( i in table_input$Var1 ) {
  data_selected <- input[input$Product == i,]
  number <- table_input[table_input$Var1 == i ,]$twentyfive

  head <- head( data_selected, number)        

  if( first == FALSE) {
    output <- rbind(output, head)
  } else {
    output <- head
  }
  first <- FALSE
}

誰かがより良い、より効率的な方法を知っていることを願っています。ここで答えから分割関数を使用しようとしました: R のデータ フレーム内の列の一意の値ごとに上位の有限数の行を選択して、製品を分割し、それらを反復して head() を選択しようとしました。ただし、分割機能は常にメモリ不足になります(割り当てられません..)

input_split <- split(input, input$Product) #Works here, but not i my problem.

結局のところ、私の問題は、それぞれのユニークな製品の異なる量を選択したいということです. ここでは、1000001 からの 2 つのアイテムと、1000002 および 1000003 からの 1 つのアイテムです。

4

2 に答える 2

10

2 つの解決策が思い浮かびます。plyr::ddplyはあなたのニーズに合わせて設計されていますが、を使用するdata.tableと、非常に高速になります。

チャンクにdata.frame分割し、日付でソートされた各チャンクの下位 25% の行をすべて削除し、data.frame. これは、1 つの単純な行で実行できます...

require( plyr )
ddply( input , .(Product) , function(x) x[ - c( 1 : ceiling( nrow(x) * 0.25 ) ) , ] )
#  Product Something       Date
#1 1000001    100005 2011-01-01
#2 1000001    100002 2011-01-02
#3 1000001    100006 2011-01-02
#4 1000001    100004 2011-01-04
#5 1000002    100007 2011-01-01
#6 1000002    100003 2011-01-04
#7 1000003    100002 2011-01-02
#8 1000003    100008 2011-01-04

data.table解決

data.tableからの最新の開発バージョンが必要になります( data.tableの CRAN バージョンにはまだr-forge負の添字が実装されていないため)。install.package呼び出しに従って最新バージョンを入手してください...

install.packages( "data.table" , repos="http://r-forge.r-project.org" )
require( data.table )
DT <- data.table( input )

#  Sort by Product then Date very quickly
setkeyv( DT , c( "Product" , "Date" ) )

#  Return the bottom 75% of rows (i.e. not the earliest)
DT[ ,  tail( .SD , -ceiling( nrow(.SD) * .25 ) )  , by = Product ] 
#   Product Something       Date
#1: 1000001    100005 2011-01-01
#2: 1000001    100002 2011-01-02
#3: 1000001    100006 2011-01-02
#4: 1000001    100004 2011-01-04
#5: 1000002    100007 2011-01-01
#6: 1000002    100003 2011-01-04
#7: 1000003    100002 2011-01-02
#8: 1000003    100008 2011-01-04

より良い使い方data.table

これをもっと簡単に行うことができます(したがって、の開発バージョンは必要ありませんdata.table)...

DT[ ,  .SD[ -c( 1:ceiling( .25 * .N ) ) ] , by = Product ] 

lapplyまた、引数で使用することもできjます (の使用について心配していました)。これは、90,000 個の製品 (グループ) を含む 2e6 行.SDで ~ 14 秒で実行されます...data.table

set.seed(1)
Product <- sample( 1:9e5 , 2e6 , repl = TRUE )
dates <- sample( 1:20 , 2e6 , repl = TRUE )
Date <- as.Date( Sys.Date() + dates )
DT <- data.table( Product = Product , Date = Date )

system.time( { setkeyv( DT , c( "Product" , "Date" ) ); DT[ , lapply( .SD , `[` ,  -c( 1:ceiling( .25 * .N ) ) ) , by = Product ] } )
#   user  system elapsed 
#  14.65    0.03   14.74 

更新:を使用する最良の方法data.table!

したがって、 @Arun (現在はパッケージの作成者) のおかげで、これdata.tableを使用する最善の方法が得られました。これは、すべての行インデックスの整数ベクトルdata.tableを使用することです。次に、これらの行インデックスを使用してサブセットを実行し、最終的なテーブルを取得します。これは、上記の方法を使用するよりも 4 ~ 5 倍高速です。素晴らしいもの!.I[-(1:ceiling(.N*.25)).SD

system.time( DT[ DT[, .I[-(1:ceiling(.N*.25))] , by = Product]$V1] )
   user  system elapsed 
   3.02    0.00    3.03
于 2013-10-17T11:49:40.357 に答える
2

mapplyinputを使用する方法は次のtable_inputとおりです。

    #your code
    #input <- matrix( c(1000001,1000001,1000001,1000001,1000001,1000001,1000002,1000002,1000002,1000003,1000003,1000003,100001,100002,100003,100004,100005,100006,100002,100003,100007,100002,100003,100008,"2011-01-01","2011-01-02","2011-01-01","2011-01-04","2011-01-01","2011-01-02","2011-01-01","2011-01-04","2011-01-01","2011-01-02","2011-01-01","2011-01-04"), ncol=3)
    #colnames(input) <- c( "Product" , "Something" ,"Date")
    #input <- as.data.frame(input)
    #input$Date <- as.Date(input[,"Date"], "%Y-%m-%d")

    #Sort based on date, I want to leave out the entries with the oldest dates.
    #input <- input[ with( input, order(Date)), ]

    #Create number of items I want to select
    #table_input <- as.data.frame(table(input$Product))
    #table_input$twentyfive <- ceiling( table_input$Freq*0.25  )

    #function to "mapply" on "table_input"
    fun = function(p, d) { grep(p, input$Product)[1:d] }

    #subset "input"
    input[unlist(mapply(fun, table_input$Var1, table_input$twentyfive)),]

       Product Something       Date
    1  1000001    100001 2011-01-01
    3  1000001    100003 2011-01-01
    7  1000002    100002 2011-01-01
    11 1000003    100003 2011-01-01

また、SimonO101の回答の速度と代替案を比較するために、system.timeとを呼び出しました。replicatemapply

    #SimonO101's code
    #require( plyr )
    #ddply( input , .(Product) , function(x) x[ - c( 1 : ceiling( nrow(x) * 0.25 ) ) , ] )
    #install.packages( "data.table" , repos="http://r-forge.r-project.org" )
    #require( data.table )
    #DT <- data.table( input )
    #setkeyv( DT , c( "Product" , "Date" ) )
    #DT[ ,  tail( .SD , -ceiling( nrow(.SD) * .25 ) )  , by = Product ]

    > system.time(replicate(10000, input[unlist(mapply(fun, table_input$Var1, table_input$twentyfive)),]))
       user  system elapsed 
       5.29    0.00    5.29 
    > system.time(replicate(10000, ddply( input , .(Product) , function(x) x[ - c( 1 : ceiling( nrow(x) * 0.25 ) ) , ] )))
      user  system elapsed 
      43.48    0.03   44.04 
    > system.time(replicate(10000,  DT[ ,  tail( .SD , -ceiling( nrow(.SD) * .25 ) )  , by = Product ] ))                        
      user  system elapsed 
      34.30    0.01   34.50 

BUT : SimonO101 の代替案は、あなたが投稿したものを使用したため、 と同じものを生成しません。これが比較で何らかの役割を果たすかどうかはわかりません。また、比較は私がばかげて設定した可能性があります。あなたが指摘した速度の問題のために、私はそれをしました。私がナンセンスな話をしている場合に備えて、@ SimonO101にこれを見てもらいたいです。 mapplymapplytable_input

于 2013-10-17T14:17:49.627 に答える