この記事ではdplyrパッケージの関数である、distinct()について解説します!
データフレームから重複を除去するdistinct()関数は、大規模なデータを取り扱うときに多用する関数です。
今回の記事作成にあたって公式のドキュメントを読み込んできたのですが、意外とオプション指定で使い方の幅が広がるように感じました。知ってる人も是非最後までみていってください😎
library(dplyr)
# マニュアル
distinct(データフレーム, ..., .keep_all = TRUE/FALSE)
# パイプを使ったモダンな方法
データフレーム %>%
distinct(...)
{tidyverse}に含まれる、{dplyr}の関数の一つです。データフレーム(またはtibble)の重複行を削除する関数です。
データフレームを使った テーブル変形では、同じ行データが重複して存在していると思わぬ結果を生む可能性があります。たとえば、グループごとの合計値を集計しようとしたときに、重複データがあると想定よりも合計値は大きくなってしまいます。
重複が発生するのは主に以下の二ケースだと思います。
前者の場合はdistinct()関数で修正するのが健全なやり方ですが、後者では付け焼刃的な修正になってしまうため、 重複を生む原因を探る方が先決です。
Snitch
パイプが10行くらいつながると意図せず重複行ができるあるある
おそらくこの用途が最も単純で、よく使う方法ではないかと思います。単純に重複する行を削除する目的で、以下のように使います。
data %>%
distinct()
以下のデータを例に、distinct()を使うメリットを見てみたいと思います。
pacman::p_load(tidyverse, magrittr)
house_price <-
read_tsv("./bengaluru_house_price.csv")
house_price
## # A tibble: 13,320 × 9
## area_type availability location size society total_sqft bath balcony price
## <chr> <chr> <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl>
## 1 Super bui… 19-Dec Electro… 2 BHK Coomee 1056 2 1 39.1
## 2 Plot Area Ready To Mo… Chikka … 4 Be… Theanmp 2600 5 3 120
## 3 Built-up … Ready To Mo… Uttarah… 3 BHK <NA> 1440 2 3 62
## 4 Super bui… Ready To Mo… Lingadh… 3 BHK Soiewre 1521 3 1 95
## 5 Super bui… Ready To Mo… Kothanur 2 BHK <NA> 1200 2 1 51
## 6 Super bui… Ready To Mo… Whitefi… 2 BHK DuenaTa 1170 2 1 38
## 7 Super bui… 18-May Old Air… 4 BHK Jaades 2732 4 NA 204
## 8 Super bui… Ready To Mo… Rajaji … 4 BHK Brway G 3300 4 NA 600
## 9 Super bui… Ready To Mo… Maratha… 3 BHK <NA> 1310 3 1 63.2
## 10 Plot Area Ready To Mo… Gandhi … 6 Be… <NA> 1020 6 NA 370
## # … with 13,310 more rows
このデータはベンガルの住宅価格に関するものです。 tibble情報を見ることで、行数が13,320行あることが分かります。
しかし、実はこのデータには全く同じデータの重複行があります😫
全ての列について重複した行を抽出したいケースっていうのもレアですが、やるとしたら以下のように 全ての列でgroup_by()を行った後、tally()で集計し、n >= 2でフィルターする方法が良いでしょう。
この操作の様に全列に関する重複行を取り出してみることで、データそのものに不具合があったのか・自分の操作がバグを生んだのか、といった分析をすることができます。
house_price %>%
group_by_all() %>%
tally() %>%
filter(n > 1)
# # A tibble: 381 × 10
# # Groups: area_type, availability, location, size, society, total_sqft, bath, balcony [332]
# area_type availability location size society total_sqft bath balcony price n
# <chr> <chr> <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl> <int>
# 1 Built-up Area 18-Aug Electronic City Phase II 2 BHK Sryalan 1000 2 1 28.9 2
# 2 Built-up Area 18-Dec Electronic City 2 BHK NA 1025 2 1 29.6 2
# 3 Built-up Area 18-Dec Electronic City 2 BHK NA 1065 2 1 30.8 2
# 4 Built-up Area 18-Dec Electronic City 2 BHK NA 1125 2 1 32.5 2
# 5 Built-up Area 18-Dec Electronic City 2 BHK NA 1130 2 1 32.6 2
# 6 Built-up Area 18-Dec Electronic City 2 BHK NA 1165 2 1 33.6 2
# 7 Built-up Area 18-Dec Electronic City 2 BHK NA 1200 2 1 34.6 2
# 8 Built-up Area 18-Dec Electronic City 3 BHK NA 1320 2 1 38.1 2
# 9 Built-up Area 18-Dec Electronic City 3 BHK NA 1400 2 1 40.4 2
# 10 Built-up Area Ready To Move 5th Phase JP Nagar 2 BHK NA 1256 2 1 62.8 2
# # … with 371 more rows
どうやら381個の重複があったようですね。
group_by()とtally()を組み合わせる方法とは別に、count()を使う方法もあります。
Snitch
最近tidyverse再入門してみたら group_by() %>% tally()
は count()
で置き換えられることを知りました
count()関数を使う場合は、“全列をグループ変数にする”ためにacross()関数を使う必要があります。この辺はちょっとテクいので、group_by()とtally()の方が直感的には分かりやすいかも。
では今回のdistinct()関数を使うとどうなるか見てみます。
house_price <- read_tsv("./bengaluru_house_price.csv")
house_price %>%
distinct()
# # A tibble: 12,790 × 9
# area_type availability location size society total_sqft bath balcony price
# <chr> <chr> <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl>
# 1 Super built-up Area 19-Dec Electronic City Phase II 2 BHK Coomee 1056 2 1 39.1
# 2 Plot Area Ready To Move Chikka Tirupathi 4 Bedroom Theanmp 2600 5 3 120
# 3 Built-up Area Ready To Move Uttarahalli 3 BHK NA 1440 2 3 62
# 4 Super built-up Area Ready To Move Lingadheeranahalli 3 BHK Soiewre 1521 3 1 95
# 5 Super built-up Area Ready To Move Kothanur 2 BHK NA 1200 2 1 51
# 6 Super built-up Area Ready To Move Whitefield 2 BHK DuenaTa 1170 2 1 38
# 7 Super built-up Area 18-May Old Airport Road 4 BHK Jaades 2732 4 NA 204
# 8 Super built-up Area Ready To Move Rajaji Nagar 4 BHK Brway G 3300 4 NA 600
# 9 Super built-up Area Ready To Move Marathahalli 3 BHK NA 1310 3 1 63.2
# 10 Plot Area Ready To Move Gandhi Bazar 6 Bedroom NA 1020 6 NA 370
# # … with 12,780 more rows
データが12790行に減っていますので、重複が削除できたであろうことが分かります。
念のためgroup_by_all()とtally()を使って確認してみます。
house_price %>%
distinct() %>%
group_by_all() %>%
tally() %>%
filter(n > 1)
# A tibble: 0 × 10
distinct()関数がきちんと重複を削除してくれたことを確認できました。
今回のデータは13320行もありますので、手動で重複を探すなんてとてもじゃないけど無理です。
このような操作をたった一行で実現できるので、distinct()関数は重宝されます。
データフレームの特定の列だけに注目していて、組み合わせが何パターンあるかだけを知りたい場合には列名を指定することで最小パターンを取得することができます。
先ほどのベンガル住宅価格データを使ってみてみましょう。
area_typeやsocietyは連続値ではなく、いくつかの固定された値から選択されているように見えます。
これらについて、どんな値が含まれているのか、distinct()関数に列名を引数として与えることで確認してみましょう。
house_price %>%
distinct(area_type, society)
# # A tibble: 3,184 × 2
# area_type society
# <chr> <chr>
# 1 Super built-up Area Coomee
# 2 Plot Area Theanmp
# 3 Built-up Area NA
# 4 Super built-up Area Soiewre
# 5 Super built-up Area NA
# 6 Super built-up Area DuenaTa
# 7 Super built-up Area Jaades
# 8 Super built-up Area Brway G
# 9 Plot Area NA
# 10 Plot Area Prrry M
# # … with 3,174 more rows
このように、3174パターンが絞り込めました。
単純に一列に含まれる値をリストアップするのにも使えますね。
house_price %>%
distinct(area_type)
# # A tibble: 4 × 1
# area_type
# <chr>
# 1 Super built-up Area
# 2 Plot Area
# 3 Built-up Area
# 4 Carpet Area
実はこの記事を書くにあたってちゃんと調べるまで、私もこの使い方を使ったことがありませんでした。
ちなみに、distinct()のオプションを知らなかった時によく行っていたのは以下の例です。
#真似してはいけない例
house_price %>%
.$area_type %>%
unique()
# [1] "Super built-up Area" "Plot Area" "Built-up Area" "Carpet Area"
先ほどのように特定の列に注目したdistinct()を使いつつも、残りの列情報も知りたい場合に.keep_all()関数を使うこともできます。
しかし、.keep_all()は全ての列を保持しますが、distinct()関数に与えた列以外は全て代表値が
採用されていることに注意です。
他の列を使用したデータフレーム変形がこの後に控えている場合を考えると、.keep_all()を
使う出番は少なそうです。
house_price %>%
distinct(area_type, .keep_all = TRUE)
# # A tibble: 4 × 9
# area_type availability location size society total_sqft bath balcony price
# <chr> <chr> <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl>
# 1 Super built-up Area 19-Dec Electronic City Phase II 2 BHK Coomee 1056 2 1 39.1
# 2 Plot Area Ready To Move Chikka Tirupathi 4 Bedroom Theanmp 2600 5 3 120
# 3 Built-up Area Ready To Move Uttarahalli 3 BHK NA 1440 2 3 62
# 4 Carpet Area Ready To Move Maruthi Sevanagar 2 BHK SMikaay 950 2 2 47
以上、distinct()の使い方でした!
オプションを活用する方法は見落とされがちですが、「特定の列に含まれる要素の取り出し」はイディオム的に覚える価値がある応用技だと思います。是非分からなくなったら辞書代わりにこの記事を参考にしてください~