7

皆さん、

私は次の課題に困惑しています。次のようなデータセットがあります。

BuyerID    Fruit.1     Fruit.2    Fruit.3    Amount.1    Amount.2    Amount.3
879        Banana      Apple                 4           3
765        Strawberry  Apple      Orange     1           2           4
123        Orange      Banana                1           1           1
 11        Strawberry                        3
773        Kiwi        Banana                1           2

私がやりたいことは、データを単純化し(可能であれば)、「果物」変数と「量」変数を折りたたむことです

BuyerID    Fruit                             Amount      Total    Count
879        "Banana" "Apple"                  4  3            7        2
765        "Strawberry" "Apple" "Orange"     1  2  4         7        3
123        "Orange" "Banana"                 1  1  1         3        2
 11        "Strawberry"                      3               3        1
773        "Kiwi" "Banana"                   1  2            3        2

c() と rbind() を使用してみましたが、必要な結果が得られません - ここでヒントを試しました: data.frame rows to a listも同様ですが、これが最善の方法であるかどうかはわかりません私のデータを簡素化するために。

これは、たとえば、特定のアイテムの発生をカウントするために、より少ない変数を処理する方が簡単になるためです (たとえば、60% のバイヤーがバナナを購入します)。

これが実行可能であることを願っています-また、どんな提案も受け付けています。どんな解決策でも大歓迎です!

ありがとうございました。

4

5 に答える 5

11

データの複製を試み、data.table を使用する

DT  <- data.frame(
  BuyerID = c(879,765,123,11,773), 
  Fruit.1 = c('Banana','Strawberry','Orange','Strawberry','Kiwi'),
  Fruit.2 = c('Apple','Apple','Banana',NA,'Banana'),
  Fruit.3 = c( NA, 'Orange',NA,NA,NA),
  Amount.1 = c(4,1,1,3,1), Amount.2 = c(3,2,1,NA,2), Amount.3 = c(NA,4,1,NA,NA),
  Total = c(7,7,3,3,3), 
  Count = c(2,3,2,1,2), 
  stringsAsFactors = FALSE)

# reshaping to long form and data.table

library(data.table)
DTlong <- data.table(reshape(DT, varying = list(Fruit = 2:4, Amount = 5:7), 
  direction = 'long'))

# create lists (without NA values)
# also adding count and total columns 
# by using <- to save Fruit and Amount for later use

DTlist <- DTlong[, list(Fruit <- list(as.vector(na.omit(Fruit.1))), 
                        Amount <- list(as.vector(na.omit(Amount.1))), 
                        Count  = length(unlist(Fruit)),
                        Total = sum(unlist(Amount))), 
                 by = BuyerID]

  BuyerID                      V1    V2 Count Total
1:     879            Banana,Apple   4,3     2     7
2:     765 Strawberry,Apple,Orange 1,2,4     3     7
3:     123           Orange,Banana 1,1,1     2     3
4:      11              Strawberry     3     1     3
5:     773             Kiwi,Banana   1,2     2     3

@RicardoSaporta 編集:

必要に応じて、list(list(c(....)))
これを使用すると、実行時間をかなり節約できます (欠点は、NA空白ではなく s が追加されることです)。ただし、@Marius が指摘するように、DTlong上記の方がおそらく作業が簡単です。

DT <- data.table(DT)
DT[,   Fruit := list(list(c(  Fruit.1,   Fruit.2,   Fruit.3))), by=BuyerID]
DT[, Ammount := list(list(c(Amount.1, Amount.2, Amount.3))), by=BuyerID]

# Or as a single line
DT[,   list(  Fruit = list(c( Fruit.1,  Fruit.2,  Fruit.3)), 
            Ammount = list(c(Amount.1, Amount.2, Amount.3)), 
            Total, Count),  # other columns used
            by = BuyerID]
于 2013-03-06T02:54:18.777 に答える
6

ここに、基本パッケージを使用したソリューションがあります。これはタイラーソリューションに似ていますが、1回適用するだけです。

res <- apply(DT,1,function(x){
  data.frame(Fruit= paste(na.omit(x[2:4]),collapse=' '),
             Amount = paste(na.omit(x[5:7]),collapse =','),
             Total = sum(as.numeric(na.omit(x[5:7]))),
             Count = length(na.omit(x[2:4])))
})
do.call(rbind,res)
                    Fruit  Amount Total Count
1            Banana Apple    4, 3     7     2
2 Strawberry Apple Orange 1, 2, 4     7     3
3           Orange Banana 1, 1, 1     3     2
4              Strawberry       3     3     1
5             Kiwi Banana    1, 2     3     2

また、grepでインデックス番号を変更します。

 Fruit  = gregexpr('Fruit[.][0-9]', colnames(dat)) > 0  
 Amount = gregexpr('Amount[.][0-9]', colnames(dat)) > 0 

 x[2:4] replace by x[which(Fruit)]....

編集いくつかのベンチマークを追加します。

library(microbenchmark)
library(data.table)
microbenchmark(ag(),mn(), am(), tr())
Unit: milliseconds
  expr       min        lq    median        uq       max
1 ag() 11.584522 12.268140 12.671484 13.317934 109.13419
2 am()  9.776206 10.515576 10.798504 11.437938 137.44867
3 mn()  6.470190  6.805646  6.974797  7.290722  48.68571
4 tr()  1.759771  1.929870  2.026960  2.142066   7.06032

小さなdata.frameの場合、TylerRinker勝者です!! 私がこれをどのように説明するか(単なる推測)

  1. data:tableソリューションは、形状変更の使用に悩まされており、通常、data.tableはビッグデータの方が高速です。
  2. Agスタディソリューションは、各行のサブセット化のために遅くなります。これは、使用する前にサブセット化するTylerソリューションとは異なります。
  3. reshapeとmergeを使用しているため、ソリューションは低速です。
于 2013-03-06T03:39:19.930 に答える
4

すでに存在する素晴らしい答えに加えて、ここに別のものがあります(ベースRに固執します):

with(DT, {
  # Convert to long format
  DTlong <- reshape(DT, direction = "long", 
                    idvar = "BuyerID", varying = 2:ncol(DT))
  # aggregate your fruit columns 
  # You need the `do.call(data.frame, ...)` to convert
  #   the resulting matrix-as-a-column into separate columns
  Agg1 <- do.call(data.frame, 
                  aggregate(Fruit ~ BuyerID, DTlong,
                            function(x) c(Fruit = paste0(x, collapse = " "),
                                          Count = length(x))))
  # aggregate the amount columns
  Agg2 <- aggregate(Amount ~ BuyerID, DTlong, sum)
  # merge the results
  merge(Agg1, Agg2)
})
#   BuyerID             Fruit.Fruit Fruit.Count Amount
# 1      11              Strawberry           1      3
# 2     123           Orange Banana           2      3
# 3     765 Strawberry Apple Orange           3      7
# 4     773             Kiwi Banana           2      3
# 5     879            Banana Apple           2      7

基本的なコンセプトは次のとおりです。

  1. データを長い形式で取得するために使用reshapeします (これは、実際には停止する必要があると思います)
  2. 2 つの異なるaggregateコマンドを使用します。1 つは果物の列を集計し、もう 1 つは金額の列を集計します。の数式アプローチはaggregateの削除を処理しますが、引数NAを使用して目的の動作を指定できます。na.action
  3. merge2 つを組み合わせて使用​​します。
于 2013-03-06T05:36:46.187 に答える
0

質問がされたときは存在しませんでしたが、これにはtidyrうまくいきます。

@mnelの回答からのデータを再利用して、

library(tidyr)
separator <- ' '
DT %>%
  unite(Fruit, grep("Fruit", names(.)), sep = separator) %>%
  unite(Amount, grep("Amount", names(.)), sep = separator)

#   BuyerID                   Fruit  Amount Total Count
# 1     879         Banana Apple NA  4 3 NA     7     2
# 2     765 Strawberry Apple Orange   1 2 4     7     3
# 3     123        Orange Banana NA   1 1 1     3     2
# 4      11        Strawberry NA NA 3 NA NA     3     1
# 5     773          Kiwi Banana NA  1 2 NA     3     2
于 2016-04-13T17:39:19.743 に答える