Help us understand the problem. What is going on with this article?

kaggleのkernels学習ノート~Mercari EDA~

More than 1 year has passed since last update.

Mercari EDA

題名の通りkaggleのkernelsを和訳したものです。自分の勉強の備忘録をまとめていきます。そして、データ分析に携わる誰かのお役に立てれば幸いです。和訳とか得意じゃないので間違っていたらごめんなさい。
今回はTroy WaltersさんのMercari EDAを和訳していきます。

Introduction

このコンペでは、商品説明、カテゴリー、ブランド名、使用状況に関するデータをもとに販売された商品価格を予測することが目的になります。非常に限られた数の特徴量しかないので、このコンペでは、非構造化テキストのモデリングにつながる可能性があります。

Libraries

必要なライブラリをロードすることから始めましょう。

R
library(data.table)
library(magrittr)
library(ggplot2)
library(scales)
library(stringr)
library(quanteda)
library(gridExtra)

Data Overview

R
dtrain <- fread('../input/train.tsv', showProgress = FALSE)

dim(dtrain)
## [1] 1482535       8
print(object.size(dtrain), units = 'Mb')
## 417.3 Mb
summary(dtrain)
##     train_id           name           item_condition_id category_name     
##  Min.   :      0   Length:1482535     Min.   :1.000     Length:1482535    
##  1st Qu.: 370634   Class :character   1st Qu.:1.000     Class :character  
##  Median : 741267   Mode  :character   Median :2.000     Mode  :character  
##  Mean   : 741267                      Mean   :1.907                       
##  3rd Qu.:1111900                      3rd Qu.:3.000                       
##  Max.   :1482534                      Max.   :5.000                       
##   brand_name            price            shipping      item_description  
##  Length:1482535     Min.   :   0.00   Min.   :0.0000   Length:1482535    
##  Class :character   1st Qu.:  10.00   1st Qu.:0.0000   Class :character  
##  Mode  :character   Median :  17.00   Median :0.0000   Mode  :character  
##                     Mean   :  26.74   Mean   :0.4473                     
##                     3rd Qu.:  29.00   3rd Qu.:1.0000                     
##                     Max.   :2009.00   Max.   :1.0000

Target Variable (Price)

ターゲットの分析から始めましょう。まずは、価格の範囲を確認します。

R
range(dtrain$price)
## [1]    0 2009

商品価格は$0~$2009です。価格のヒストグラムを見てみましょう。価格には歪みがあり、0がいくつかあるため、価格+1の対数をプロットします。

R
ggplot(data = dtrain, aes(x = log(price+1))) + 
    geom_histogram(fill = 'orangered2') +
    labs(title = 'Histogram of log item price + 1')

unnamed-chunk-4-1.png
log(price)+1は、約3が中心で、左側に0を境に、右により長い裾があります。

Item Condition

商品のコンディションを見ていきましょう。

R
table(dtrain$item_condition_id)
## 
##      1      2      3      4      5 
## 640549 375479 432161  31962   2384
dtrain[, .N, by = item_condition_id] %>%
    ggplot(aes(x = as.factor(item_condition_id), y = N/1000)) +
    geom_bar(stat = 'identity', fill = 'cyan2') + 
    labs(x = 'Item condition', y = 'Number of items (000s)', title = 'Number of items by condition category')

unnamed-chunk-6-1.png

コンディションの範囲は1〜5です。コンディション1は他よりも多くあります。コンディション4と5は比較的まれです。データの説明から、コンディションの順序が何であるかは明らかではありません。私の考えでは、、コンディション4と5は非常にまれであるため、これらはより良いコンディションの商品である可能性が高いと思われます。私たちはこれを検証することができます。4や5がコンディションが良い場合は、価格と正の相関があります。そうかどうかを見てみましょう。

R
dtrain[, .(.N, median_price = median(price)), by = item_condition_id][order(item_condition_id)]
##    item_condition_id      N median_price
## 1:                 1 640549           18
## 2:                 2 375479           17
## 3:                 3 432161           16
## 4:                 4  31962           15
## 5:                 5   2384           19
ggplot(data = dtrain, aes(x = as.factor(item_condition_id), y = log(price + 1))) + 
    geom_boxplot(fill = 'cyan2', color = 'darkgrey')

unnamed-chunk-7-1.png
コンディションごとに平均価格を見ると、望んでいたほど良い関係が示されてません。コンディション5は明らかに高い価格ですが、コンディション1は次に高い価格、コンディション2、3、4です。kaggler@Jurajが指摘しているように、コンディション1が最高、コンディション5が最悪です。コンディション5は、最も高い価格を持つという点で少し異常です。しかし、それはまた、商品数が最も少ないので、見積りも不確実です。

Shipping

Shippingは、商品の出荷料金が売り手によって支払われるか(1)、支払われないか(0)を示すダミー変数です。

R
table(dtrain$shipping)
## 
##      0      1 
## 819435 663100

最初の発送は、配送料が売り手によって支払われる商品が高価になるということです。しかし、いくつかの相反する要因があります。これは、特定の製品カテゴリおよび商品条件内では当てはまる可能性がありますが、集計上のアイテムを比較する場合には当てはまりません。

R
dtrain %>%
    ggplot(aes(x = log(price+1), fill = factor(shipping))) + 
    geom_density(adjust = 2, alpha = 0.6) + 
    labs(x = 'Log price', y = '', title = 'Distribution of price by shipping')

unnamed-chunk-9-1.png
売り手が送料を支払う商品は、平均価格が低くなります。

Brand

R
dtrain[, .(median_price = median(price)), by = brand_name] %>%
    head(25) %>%
    ggplot(aes(x = reorder(brand_name, median_price), y = median_price)) + 
    geom_point(color = 'cyan2') + 
    scale_y_continuous(labels = scales::dollar) + 
    coord_flip() +
    labs(x = '', y = 'Median price', title = 'Top 25 most expensive brands') 

unnamed-chunk-10-1.png
エア・ジョーダンとアカシア・スウェットは、最も高価なブランドで、中央値の価格はそれぞれ$80と$60です。

Item Categories

カテゴリを見てみましょう。 まず、商品カテゴリはいくつありますか?

R
length(unique(dtrain$category_name))
## [1] 1288
sort(table(dtrain$category_name), decreasing = TRUE)[1:10]
## 
##              Women/Athletic Apparel/Pants, Tights, Leggings 
##                                                       60177 
##                               Women/Tops & Blouses/T-Shirts 
##                                                       46380 
##                                          Beauty/Makeup/Face 
##                                                       34335 
##                                          Beauty/Makeup/Lips 
##                                                       29910 
##                    Electronics/Video Games & Consoles/Games 
##                                                       26557 
##                                          Beauty/Makeup/Eyes 
##                                                       25215 
## Electronics/Cell Phones & Accessories/Cases, Covers & Skins 
##                                                       24676 
##                                        Women/Underwear/Bras 
##                                                       21274 
##                                 Women/Tops & Blouses/Blouse 
##                                                       20284 
##                             Women/Tops & Blouses/Tank, Cami 
##                                                       20284

最も人気のある10個のカテゴリを見ると、Mercariには女性のアパレルがかなり人気があります。次に上位10カテゴリーのうち5は女性用アパレルです。 Makeupは、Electronicsと同様に上位にリストされたカテゴリーです。

さて、カテゴリ別に価格を調べましょう。 高い販売価格を持つ商品カテゴリは何ですか?

R
dtrain[, .(median = median(price)), by = category_name][order(median, decreasing = TRUE)][1:30] %>%
    ggplot(aes(x = reorder(category_name, median), y = median)) + 
    geom_point(color = 'orangered2') + 
    coord_flip() + 
    labs(x = '', y = 'Median price', title = 'Median price by item category (Top 30)') + 
    scale_y_continuous(labels = scales::dollar)

unnamed-chunk-13-1.png
Vintage and Collectibles/Antique/Furnitureは、一番高い価格の中央値を持ち、次にKids/Strollers/Standardが続きます。

これらのカテゴリは非常に限定的です。より大きいカテゴリーに集約し、さらに探求することは興味深いでしょう。サブカテゴリは '/'で区切られているので、data.tabletstrsplit()を使って各アイテムのレベル1とレベル2のカテゴリを簡単に取得できます。

R
# split the item category_name by '/' and get the first two category levels as separate columns
dtrain[, c("level_1_cat", "level_2_cat") := tstrsplit(dtrain$category_name, split = "/", keep = c(1,2))]

# peek at the first few rows to make sure this worked correctly. 
head(dtrain[, c("level_1_cat", "level_2_cat")])
##    level_1_cat         level_2_cat
## 1:         Men                Tops
## 2: Electronics Computers & Tablets
## 3:       Women      Tops & Blouses
## 4:        Home          Home Décor
## 5:       Women             Jewelry
## 6:       Women               Other

レベル1のカテゴリはいくつありますか?

R
table(dtrain$level_1_cat)
## 
##                 Beauty            Electronics               Handmade 
##                 207828                 122690                  30842 
##                   Home                   Kids                    Men 
##                  67871                 171689                  93680 
##                  Other      Sports & Outdoors Vintage & Collectibles 
##                  45351                  25342                  46530 
##                  Women 
##                 664385

レベル1のカテゴリは10種類あります。販売価格はどのように、これらのカテゴリ間で異るのでしょうか?

R
dtrain %>%
    ggplot(aes(x = level_1_cat, y = log(price+1))) + 
    geom_boxplot(fill = 'cyan2', color = 'darkgrey') + 
    coord_flip() + 
    labs(x = '', y = 'Log price + 1', title = 'Boxplot of price by top-level category')

unnamed-chunk-16-1.png

興味深いことに、男性のカテゴリは高い中央値を持つように見えます。レベル2のカテゴリについても同じことをしましょう。

R
# get number of unique level 2 categories
length(unique(dtrain$level_2_cat))
## [1] 114

レベル2にはさらに多くのカテゴリがあります。

R
dtrain %>%
    ggplot(aes(x = level_2_cat, y = log(price+1))) + 
    geom_boxplot(fill = 'cyan2', color = 'darkgrey') + 
    coord_flip() + 
    labs(x = '', y = 'Log price + 1', title = 'Boxplot of price by second-level category')

unnamed-chunk-18-1.png

Feature Interactions

商品数がレベル1のカテゴリとコンディションは、どのように分布しているかを調べます。

R
p1 <-
    dtrain[, .N, by = c('level_1_cat', 'item_condition_id')] %>%
    ggplot(aes(x = item_condition_id, y = level_1_cat, fill = N/1000)) +
    geom_tile() +
    scale_fill_gradient(low = 'lightblue', high = 'cyan4') +
    labs(x = 'Condition', y = '', fill = 'Number of items (000s)', title = 'Item count by category and condition') +
    theme_bw() + 
    theme(legend.position = 'bottom')

p2 <-
    dtrain[, .(median_price = median(price)), by = c('level_1_cat', 'item_condition_id')] %>%
    ggplot(aes(x = item_condition_id, y = level_1_cat, fill = median_price)) +
    geom_tile() +
    scale_fill_gradient(low = 'lightblue', high = 'cyan4', labels = dollar) +
    labs(x = 'Condition', y = '', fill = 'Median price', title = 'Item price by category and condition') + 
    theme_bw() + 
    theme(legend.position = 'bottom', axis.text.y = element_blank())

grid.arrange(p1, p2, ncol = 2)

unnamed-chunk-19-1.png
コンディション1、2、3の女性の品目が最も多いです。これに美容製品が続きます。

Item Description

この時点で、すでにかなりの量のデータの探索を行ってきましたが、このコンペでの大部分である説明文からはまだ何も得られません。構造化されていないテキストデータを探索するために、テキスト処理と正規化を行う必要があります。

説明文の長さと価格との間には関係がありますか?

R
# calculate description length
dtrain[, desc_length := nchar(item_description)]

# set desc_length to NA where no description exists
dtrain[item_description == 'No description yet', desc_length := NA]

cor(dtrain$desc_length, dtrain$price, use = 'complete.obs')
## [1] 0.04417687

説明文長さと価格には相関はないようです。

Set up

テキスト解析を始めましょう。これを行うには、quantedaパッケージを使用します。 まず、説明欄から「説明がありません」をすべて削除します。次に、説明列をcorpusオブジェクトに変換します。

R
train[item_description == 'No description yet', item_description := NA]

# create the corpus object from the item_description column
dcorpus <- corpus(dtrain$item_description)

# check first few lines of summary frame
summary(dcorpus)[1:5, ]
## Corpus consisting of 1482535 documents, showing 100 documents:
## 
##   Text Types Tokens Sentences
##  text1     1      1         0
##  text2    32     39         3
##  text3    26     32         2
##  text4    34     41         8
##  text5     5      5         1
## 
## Source:  /kaggle/working/* on x86_64 by root
## Created: Tue Dec  5 17:27:46 2017
## Notes:

kwic()関数を使用して、特定のキーワードが現れるコンテキストを調べることができます。 ここでは、キーワード「Urban Outfitters」の最初の10件の結果を表示します。

R
options(width = 200)
kwic(dcorpus, phrase("Kate Spade"), valuetype = "fixed") %>%
    head()
##                                                                                                        
##    [text151, 6:7] Your ever-faithful companion, the | Kate Spade | New York® Cobble Hill               
##    [text216, 1:2]                                   | KATE SPADE | Glitter Bug Wristlet Rose Gold      
##  [text216, 17:18]              7 Case Purse Bag NWT | Kate Spade | New York wristlet in Rose           
##    [text599, 5:6]               Black and Polka Dot | Kate Spade | Wallet Authentic Small crack in     
##  [text781, 21:22]               to just set around. | Kate Spade | Wellesley Small Rachelle Satchel Bag
##   [text792, 9:10]                 And Gift Box new! | Kate spade | mug Waterford crystal wine stopper

説明文の文書行列を作成します。これを行うには、dfm()を使用し、コーパスオブジェクトを渡します。個々の単語から始め、英語のストップワードや句読点を削除し、言葉を削除します。

R
dfm1 <- dfm(
    dcorpus, 
    ngrams = 1, 
    remove = c("rm", stopwords("english")),
    remove_punct = TRUE,
    remove_numbers = TRUE,
    stem = TRUE)

N-grams

R
# get 25 most common words
tf <- topfeatures(dfm1, n = 25)

# convert to df and plot
data.frame(term = names(tf), freq = unname(tf)) %>%
    ggplot(aes(x = reorder(term, freq), y = freq/1000)) + 
    geom_bar(stat = 'identity', fill = 'orangered2') + 
    labs(x = '', y = 'Frequency (000s)', title = '25 most common description words') + 
    coord_flip() 

unnamed-chunk-24-1.png

私たちはdfmオブジェクトを使ってワードクラウドを作ることもできます。ここでは、データセットに少なくとも3万回出現する言葉でワードクラウドを作っています。

R
set.seed(100)
textplot_wordcloud(dfm1, min.freq = 3e4, random.order = FALSE,
                   rot.per = .25, 
                   colors = RColorBrewer::brewer.pal(8,"Dark2"))

unnamed-chunk-25-1.png

ngramsを見たいと思ったら、同様の方法で、dfm()のngrams引数を2に設定することができます。これにより、非常に長いドキュメント用語行列が生成されます。また、非常に時間がかかるため、sample_corpus()を使用してランダム コーパス内の文書の15%のサンプルを使用します。

R
dfm2 <- dcorpus %>%
    corpus_sample(size = floor(ndoc(dcorpus) * 0.15)) %>%
    dfm(
        ngrams = 2,
        ignoredFeatures = c("rm", stopwords("english")),
        remove_punct = TRUE,
        remove_numbers = TRUE,
        concatenator = " "
    )
# get 25 most common bigrams
tf <- topfeatures(dfm2, n = 25)

# convert to df and plot
data.frame(term = names(tf), freq = unname(tf)) %>%
    ggplot(aes(x = reorder(term, freq), y = freq/1000)) + 
    geom_bar(stat = 'identity', fill = 'orangered2') + 
    labs(x = '', y = 'Frequency (000s)', title = '25 most common description bigrams') + 
    coord_flip() 

unnamed-chunk-27-1.png

「Brand new」についで、「free shipping」は、発生しやすいようです。バイグラムのワードクラウドを作ることもできます。

R
set.seed(100)
textplot_wordcloud(dfm2, min.freq = 2000, random.order = FALSE,
                   rot.per = .25, 
                   colors = RColorBrewer::brewer.pal(8,"Dark2"))

unnamed-chunk-28-1.png
このプロセスをもう一度やり直してみましょう。今回は3-gramsです。

R
dfm3 <- dcorpus %>%
    corpus_sample(size = floor(ndoc(dcorpus) * 0.15)) %>%
    dfm(
        ngrams = 3,
        ignoredFeatures = c("rm", stopwords("english")),
        remove_punct = TRUE,
        remove_numbers = TRUE,
        concatenator = " "
    )
# get 25 most common trigrams
tf <- topfeatures(dfm3, n = 25)

# convert to df and plot
data.frame(term = names(tf), freq = unname(tf)) %>%
    ggplot(aes(x = reorder(term, freq), y = freq/1000)) + 
    geom_bar(stat = 'identity', fill = 'orangered2') + 
    labs(x = '', y = 'Frequency (000s)', title = '25 most common description 3-grams') + 
    coord_flip() 

unnamed-chunk-30-1.png

「price is firm」と「ew with tags」が最も一般的な3グラムであることがわかります。 「Brand new never」、「check out my」、「smoke free home」などが人気のトライグラムです。

R
set.seed(100)
textplot_wordcloud(dfm3, min.freq = 100, random.order = FALSE,
                   rot.per = .25, 
                   colors = RColorBrewer::brewer.pal(8,"Dark2"))

unnamed-chunk-31-1.png
さらなる分析を行うために、トレーニングデータの他の特徴量をコーパスに追加してみましょう。これを行うには、quantedaパッケージのdocvars()を使用します。

R
# Add other features as docvars
docvars(dcorpus, "price") <- dtrain$price
docvars(dcorpus, "brand_name") <- dtrain$brand_name
docvars(dcorpus, "item_condition_id") <- dtrain$item_condition_id
docvars(dcorpus, "level_1_cat") <- dtrain$level_1_cat
docvars(dcorpus, "level_2_cat") <- dtrain$level_2_cat

dcorpusのサマリーを使用して、トップレベルのビューから始めます。ここでは、各トップレベルカテゴリのトークン数の分布をプロットします。

R
p1 <- summary(dcorpus) %>%
    ggplot(aes(x = level_1_cat, y = Tokens)) +
    geom_boxplot(aes(fill = level_1_cat), color = 'grey') +
    coord_flip() +
    theme(legend.position = 'bottom') + 
    labs(x = '', y = 'Number of tokens in description')

p2 <- summary(dcorpus) %>%
    ggplot(aes(x = Tokens)) +
    geom_density(aes(fill = level_1_cat), color = 'grey') + 
    facet_wrap(~level_1_cat) + 
    theme(legend.position = "none") + 
    labs(x = 'Number of tokens in description')

grid.arrange(p1, p2, ncol = 2)

unnamed-chunk-33-1.png

最も興味深いのは、Menカテゴリの説明文の平均トークン数が最も少ないことです。それだけでなく、平均周りに密な分布を持っています。情報がないため、このカテゴリの価格を正確に予測することが難しくなる可能性があります。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away