data.frameの各行を列で指定された回数繰り返します


150
df <- data.frame(var1 = c('a', 'b', 'c'), var2 = c('d', 'e', 'f'),
                 freq = 1:3)

上記のdata.frameの最初の2列を各行に展開して、各行が列 'freq'で指定された回数繰り返されるようにする最も簡単な方法は何ですか?

言い換えれば、これから行きます:

df
  var1 var2 freq
1    a    d    1
2    b    e    2
3    c    f    3

これに:

df.expanded
  var1 var2
1    a    d
2    b    e
3    b    e
4    c    f
5    c    f
6    c    f

回答:


169

これが1つの解決策です。

df.expanded <- df[rep(row.names(df), df$freq), 1:2]

結果:

    var1 var2
1      a    d
2      b    e
2.1    b    e
3      c    f
3.1    c    f
3.2    c    f

すごい!角括弧をそのように使用できることはいつも忘れます。サブセット化または並べ替えのためだけにインデックスを作成することを考えています。私ははるかにエレガントではなく、間違いなく効率が悪い別のソリューションを持っていました。とにかく投稿して他の人が比較できるようにします。
wkmor1

22
大型のためdata.frame、より効率的に交換することであるrow.names(df)seq.int(1,nrow(df))seq_len(nrow(df))
Marek

これは、ビッグデータフレーム(150万行、5列)で非常に効果的に機能しました。ありがとう!
gabe

4
1:2はこの例のソリューションをハードコーディングし、1:ncol(df)は任意のデータフレームに対して機能します。
vladiim 2018

71

古い質問、tidyverseの新しい動詞:

library(tidyr) # version >= 0.8.0
df <- data.frame(var1=c('a', 'b', 'c'), var2=c('d', 'e', 'f'), freq=1:3)
df %>% 
  uncount(freq)

    var1 var2
1      a    d
2      b    e
2.1    b    e
3      c    f
3.1    c    f
3.2    c    f

2
整頓された解決策をありがとう。このようなソリューションは通常、「シンプル」で読みやすいという基準を満たしています。
D.ウッズ

45

パッケージexpandRows()から使用splitstackshape

library(splitstackshape)
expandRows(df, "freq")

シンプルな構文は非常に高速で、data.frameまたはで動作しdata.tableます。

結果:

    var1 var2
1      a    d
2      b    e
2.1    b    e
3      c    f
3.1    c    f
3.2    c    f

23

@neilfwsのソリューションはdata.framesには適しdata.tableていますが、row.namesプロパティが不足しているためsには適していません。このアプローチは次の両方で機能します。

df.expanded <- df[rep(seq(nrow(df)), df$freq), 1:2]

のコードdata.tableは少しクリーナーです:

# convert to data.table by reference
setDT(df)
df.expanded <- df[rep(seq(.N), freq), !"freq"]

4
別の選択肢:df[rep(seq(.N), freq)][, freq := NULL]
Jaap

別の代替案df[rep(1:.N, freq)][, freq:=NULL]
デールキューブ

4

非常に大きなdata.framesでこの操作を実行する必要がある場合は、data.tableに変換し、以下を使用することをお勧めします。

library(data.table)
dt <- data.table(df)
dt.expanded <- dt[ ,list(freq=rep(1,freq)),by=c("var1","var2")]
dt.expanded[ ,freq := NULL]
dt.expanded

このソリューションがどれだけ速いかを見てください:

df <- data.frame(var1=1:2e3, var2=1:2e3, freq=1:2e3)
system.time(df.exp <- df[rep(row.names(df), df$freq), 1:2])
##    user  system elapsed 
##    4.57    0.00    4.56
dt <- data.table(df)
system.time(dt.expanded <- dt[ ,list(freq=rep(1,freq)),by=c("var1","var2")])
##    user  system elapsed 
##    0.05    0.01    0.06

エラーが発生します:Error in rep(1, freq) : invalid 'times' argument。また、この質問に対するdata.tableの回答がすでにあることを考えると、アプローチがどのように異なるか、または現在のdata.tableの回答よりも優れている場合について説明することもできます。または、大きな違いがない場合は、既存の回答にコメントとして追加することもできます。
Sam Firke、2015

@SamFirke:コメントありがとうございます。奇妙なことに、私はもう一度試したところ、そのようなエラーは発生しませんでした。dfOPの質問のオリジナルを使用しますか?他の回答は構文を使用したdata.tableパッケージの誤用の一種であるため、私の回答の方が優れていdata.frameますdata.table。「一般に、名前ではなく番号で列を参照することは悪い習慣です」のFAQ を参照してください。
vonjd

1
説明ありがとう。あなたのコードdfはOPによって投稿されたサンプルで私のために機能しますが、より大きなdata.frameでこれをベンチマークしようとすると、そのエラーが発生しました。私が使用したdata.frameは次のとおりです。set.seed(1) dfbig <- data.frame(var1=sample(letters, 1000, replace = TRUE), var2=sample(LETTERS, 1000, replace = TRUE), freq=sample(1:10, 1000, replace = TRUE)) 小さなdata.frameでは、基本的な回答がベンチマークでうまく機能しますが、大きなdata.frameにうまくスケーリングできません。他の3つの回答は、このより大きなdata.frameで正常に実行されました。
Sam Firke、2015

@SamFirke:これは確かに奇妙です。そこでも機能するはずですが、なぜ機能しないのかわかりません。それから質問を作成しますか、それとも私がしますか?
vonjd

良いアイデア。あなたはできる?私はdata.table構文がわからないので、答えを判断するのは私ではありません。
Sam Firke、2015

4

各行を数回繰り返す別のdplyr方法slicefreq

library(dplyr)

df %>%  
  slice(rep(seq_len(n()), freq)) %>% 
  select(-freq)

#  var1 var2
#1    a    d
#2    b    e
#3    b    e
#4    c    f
#5    c    f
#6    c    f

seq_len(n()) 部品は、次のいずれかに置き換えることができます。

df %>% slice(rep(1:nrow(df), freq)) %>% select(-freq)
#Or
df %>% slice(rep(row_number(), freq)) %>% select(-freq)
#Or
df %>% slice(rep(seq_len(nrow(.)), freq)) %>% select(-freq)

2

別の可能性は使用していtidyr::expandます:

library(dplyr)
library(tidyr)

df %>% group_by_at(vars(-freq)) %>% expand(temp = 1:freq) %>% select(-temp)
#> # A tibble: 6 x 2
#> # Groups:   var1, var2 [3]
#>   var1  var2 
#>   <fct> <fct>
#> 1 a     d    
#> 2 b     e    
#> 3 b     e    
#> 4 c     f    
#> 5 c     f    
#> 6 c     f

vonjdの回答のワンライナーバージョン:

library(data.table)

setDT(df)[ ,list(freq=rep(1,freq)),by=c("var1","var2")][ ,freq := NULL][]
#>    var1 var2
#> 1:    a    d
#> 2:    b    e
#> 3:    b    e
#> 4:    c    f
#> 5:    c    f
#> 6:    c    f

reprexパッケージ(v0.2.1)によって2019-05-21に作成されました


1

私はこれが事実ではないことを知っていますが、元のfreqカラムを維持する必要がある場合は、別のtidyverseアプローチを一緒に使用できますrep

library(purrr)

df <- data.frame(var1 = c('a', 'b', 'c'), var2 = c('d', 'e', 'f'), freq = 1:3)

df %>% 
  map_df(., rep, .$freq)
#> # A tibble: 6 x 3
#>   var1  var2   freq
#>   <fct> <fct> <int>
#> 1 a     d         1
#> 2 b     e         2
#> 3 b     e         2
#> 4 c     f         3
#> 5 c     f         3
#> 6 c     f         3

reprexパッケージ(v0.3.0)によって2019-12-21に作成されました


それとも、使用.remove = FALSEuncount()
アダム
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.