3

ddplyを使用してデータをクリーンアップしようとしていますが、130万行で実行速度が非常に遅くなっています。

サンプルコード:

#Create Sample Data Frame
num_rows <- 10000
df <- data.frame(id=sample(1:20, num_rows, replace=T), 
                Consumption=sample(-20:20, num_rows, replace=T), 
                StartDate=as.Date(sample(15000:15020, num_rows, replace=T), origin = "1970-01-01"))
df$EndDate <- df$StartDate + 90
#df <- df[order(df$id, df$StartDate, df$Consumption),]
#Are values negative? 
# Needed for subsetting in ddply rows with same positive and negative values
df$Neg <- ifelse(df$Consumption < 0, -1, 1)
df$Consumption <- abs(df$Consumption)

ある行の消費値が同じであるが、別の行の消費値とは負である行を削除する関数を作成しました(同じIDの場合)。

#Remove rows from a data frame where there is an equal but opposite consumption value
#Should ensure only one negative value is removed for each positive one. 
clean_negatives <- function(x3){
  copies <- abs(sum(x3$Neg))
  sgn <- ifelse(sum(x3$Neg) <0, -1, 1) 
  x3 <- x3[0:copies,]
  x3$Consumption <- sgn*x3$Consumption
  x3$Neg <- NULL
  x3}

次に、ddplyを使用してその関数を適用し、データ内のこれらの誤った行を削除します

ptm <- proc.time()
df_cleaned <- ddply(df, .(id,StartDate, EndDate, Consumption),
                    function(x){clean_negatives(x)})
proc.time() - ptm

data.tableを使用してこれを高速化できることを望んでいましたが、data.tableを使用して支援する方法を理解できませんでした。

130万行の場合、これまでのところ、デスクトップの計算に1日かかりますが、まだ完了していません。

4

1 に答える 1

6

あなたの質問はdata.table実装について尋ねます。それで、ここにそれを示しました。関数も大幅に簡略化できます。最初にsign合計してを取得しNeg、次にテーブルをフィルタリングしてから、を乗算Consumptionsignます(以下を参照)。

require(data.table)
# get the data.table in dt
dt <- data.table(df, key = c("id", "StartDate", "EndDate", "Consumption"))
# first obtain the sign directly
dt <- dt[, sign := sign(sum(Neg)), by = c("id", "StartDate", "EndDate", "Consumption")]
# then filter by abs(sum(Neg))
dt.fil <- dt[, .SD[seq_len(abs(sum(Neg)))], by = c("id", "StartDate", "EndDate", "Consumption")]
# modifying for final output (line commented after Statquant's comment
# dt.fil$Consumption <- dt.fil$Consumption * dt.fil$sign
dt.fil[, Consumption := (Consumption*sign)]
dt.fil <- subset(dt.fil, select=-c(Neg, sign))

ベンチマーク

  • 百万行のデータ:

    #Create Sample Data Frame
    num_rows <- 1e6
    df <- data.frame(id=sample(1:20, num_rows, replace=T), 
                    Consumption=sample(-20:20, num_rows, replace=T), 
                    StartDate=as.Date(sample(15000:15020, num_rows, replace=T), origin = "1970-01-01"))
    df$EndDate <- df$StartDate + 90
    df$Neg <- ifelse(df$Consumption < 0, -1, 1)
    df$Consumption <- abs(df$Consumption)
    
  • data.table機能:

    FUN.DT <- function() {
        require(data.table)
        dt <- data.table(df, key=c("id", "StartDate", "EndDate", "Consumption"))
        dt <- dt[, sign := sign(sum(Neg)), 
                   by = c("id", "StartDate", "EndDate", "Consumption")]
        dt.fil <- dt[, .SD[seq_len(abs(sum(Neg)))], 
                   by=c("id", "StartDate", "EndDate", "Consumption")]
        dt.fil[, Consumption := (Consumption*sign)]
        dt.fil <- subset(dt.fil, select=-c(Neg, sign))
    }
    
  • あなたの機能ddply

    FUN.PLYR <- function() {
        require(plyr)
        clean_negatives <- function(x3) {
            copies <- abs(sum(x3$Neg))
            sgn <- ifelse(sum(x3$Neg) <0, -1, 1) 
            x3 <- x3[0:copies,]
            x3$Consumption <- sgn*x3$Consumption
            x3$Neg <- NULL
            x3
        }
        df_cleaned <- ddply(df, .(id, StartDate, EndDate, Consumption), 
                               function(x) clean_negatives(x))
    }
    
  • ベンチマークrbenchmark(1回の実行のみ)

    require(rbenchmark)
    benchmark(FUN.DT(), FUN.PLYR(), replications = 1, order = "elapsed")
    
            test replications elapsed relative user.self sys.self user.child sys.child
    1   FUN.DT()            1   6.137    1.000     5.926    0.211          0         0
    2 FUN.PLYR()            1 242.268   39.477   152.855    82.881         0         0
    

私のdata.table実装は現在の実装よりも約39倍高速ですplyr(関数が異なるため、私の実装をあなたの実装と比較します)。

Note:結果を取得するための完全な時間を取得するために、関数内にパッケージをロードしました。また、同じ理由で、ベンチマーク関数内のキーを使用してに変換data.frameしました。data.tableしたがって、これが最小のスピードアップです。

于 2013-01-22T08:52:28.600 に答える