tidyデータ: 人間にも機械にも優しいデータの記述形式

データが手に入ってすぐにモデルに利用できることはごく稀で、前処理と呼ばれる作業が必要になります。この作業はデータをモデルに流し込む前段階として必ず必要です。それはデータが欠損値や外れ値といった、扱いに困る値を含んでいることもありますが、多くのデータは入力の形式のまま利用可能ではなく、プログラムが処理しやすい形式へ整形することが求められるためです。

前処理の作業には、このようなデータ整形だけでなく、欠損値の削除や外れ値の削除も含まれます。またモデリングのために余分な列を除外したり、スケーリングの処理を施すなども必要です。そのため前処理を通したデータの質が統計モデリングや機械学習の学習効果に影響を及ぼします。ただしこれらはどのようなデータ、問題に対しても有効なものではなく、利用範囲には幅があります。

ここでは前処理を行わない際の問題点について触れ、tidyデータと呼ばれる形式へのデータ整形方法と一般的な前処理について紹介します。

前処理の必要性

前処理の必要性とその範囲は適用するモデルのタイプによって異なります。

木ベースのモデル(決定木、ランダムフォレスト)では、特徴量を入力とする複数のステップ関数(閾値を超えた場合に1, そうでなければ0に変換する)の組み合わせによって構成されるため変数のスケールの影響を受けません。しかしロジスティック回帰や部分最小二乗法、リッジ回帰や距離を利用するk-means、主成分分析など、多くのモデルは入力のスケールに敏感で、変数間のスケールを揃える必要があります。

前処理の必要性1: 重回帰モデル

df_lp_kanto %>% 
  lm(posted_land_price ~ distance_from_station + acreage + night_population,
     data = .) %>% 
  tidy()
## # A tibble: 4 x 5
##   term                    estimate  std.error statistic  p.value
##   <chr>                      <dbl>      <dbl>     <dbl>    <dbl>
## 1 (Intercept)           620112.    37120.         16.7  1.15e-61
## 2 distance_from_station   -115.        9.85      -11.7  1.82e-31
## 3 acreage                    3.41      2.63        1.29 1.95e- 1
## 4 night_population          -0.103     0.0976     -1.05 2.93e- 1

distance_from_staion の影響が一番強く、acreagenight_populationが続きます。しかし distance_from_staion の値が大きすぎて他の変数の効果が小さいように見えます。これは元のスケールの影響を受けているためです。 distance_from_staionacreage の1は同じ単位ではありません。偏回帰係数を比較する場合、スケーリングを行っておく必要があります。

df_lp_kanto %>% 
  recipe(posted_land_price ~ distance_from_station + acreage + night_population) %>% 
  step_center(all_predictors()) %>% 
  step_scale(all_predictors()) %>% 
  prep() %>% 
  juice() %>% 
  lm(posted_land_price ~ .,
     data = .) %>% 
  tidy()
## Warning: All elements of `...` must be named.
## Did you want `data = c(type, role, source)`?

## Warning: All elements of `...` must be named.
## Did you want `data = c(type, role, source)`?

## Warning: All elements of `...` must be named.
## Did you want `data = c(type, role, source)`?

## # A tibble: 4 x 5
##   term                  estimate std.error statistic   p.value
##   <chr>                    <dbl>     <dbl>     <dbl>     <dbl>
## 1 (Intercept)            415432.    19034.     21.8  8.65e-103
## 2 distance_from_station -226650.    19344.    -11.7  1.82e- 31
## 3 acreage                 24678.    19058.      1.29 1.95e-  1
## 4 night_population       -20325.    19326.     -1.05 2.93e-  1

前処理の必要性2: 変数間のスケールが揃わない主成分分析

主成分分析では入力に用いる変数間のスケールが標準化されていることが前提です。それは特徴量空間におけるデータ間を距離を利用するためです。そのため、値の範囲が大きく異なる変数があることでその影響を強く受けます。まずは標準化を行わない場合の結果を見てみましょう。

pca_res <- 
  prcomp(~ distance_from_station + acreage + night_population, 
       data = df_lp_kanto,
       center = FALSE,
       scale. = FALSE)
# 主成分軸上のSDが大きく異なっていることに注意(単位の影響を強く受けている)
pca_res
## Standard deviations (1, .., p=3):
## [1] 326371.254   7270.361   2279.386
## 
## Rotation (n x k) = (3 x 3):
##                               PC1          PC2         PC3
## distance_from_station 0.003184224  0.027579010 -0.99961456
## acreage               0.001558732  0.999618272  0.02758408
## night_population      0.999993716 -0.001645965  0.00314002
# 第1主成分のみで累積寄与率が99%を超える
summary(pca_res)
## Importance of components:
##                              PC1      PC2       PC3
## Standard deviation     3.264e+05 7.27e+03 2.279e+03
## Proportion of Variance 9.995e-01 5.00e-04 5.000e-05
## Cumulative Proportion  9.995e-01 1.00e+00 1.000e+00

次にあらかじめ標準化したデータを元に主成分分析を行った結果を見ます。

pca_res <- 
  prcomp(~ distance_from_station + acreage + night_population, 
       data = df_lp_kanto,
       center = TRUE,
       scale. = TRUE)
# SDが小さくなる
pca_res
## Standard deviations (1, .., p=3):
## [1] 1.0884300 0.9949390 0.9085244
## 
## Rotation (n x k) = (3 x 3):
##                              PC1         PC2        PC3
## distance_from_station -0.6939753  0.09743571  0.7133755
## acreage               -0.2496712 -0.96188952 -0.1115026
## night_population       0.6753241 -0.25548939  0.6918544
# 第2主成分軸まで含めて72%を説明
summary(pca_res)
## Importance of components:
##                           PC1    PC2    PC3
## Standard deviation     1.0884 0.9949 0.9085
## Proportion of Variance 0.3949 0.3300 0.2751
## Cumulative Proportion  0.3949 0.7249 1.0000

前処理の必要性3: kNN

library(FNN)
set.seed(12)

split_hazard <- 
  df_hazard_kys %>%
  select(hazard, max_elevation, mean_slope_aspect) %>% 
  drop_na() %>% 
  rsample::initial_split(strata = "hazard")

df_train <- training(split_hazard)
df_test <- testing(split_hazard)

knn_res <- knn(df_train, df_test, df_train$hazard, k = 2, prob = FALSE)
df_test$predict <- knn_res
attr(knn_res, "nn.index") %>% as.data.frame() %>% as_tibble() %>% .[21, ]
## # A tibble: 1 x 2
##      V1    V2
##   <int> <int>
## 1   409   619
# get.knn(df_test %>% select_if(is.numeric), k = 2)["nn.index"] %>% as.data.frame() %>% .[397, ]

rec <- 
  df_train %>% 
  recipe(hazard ~ .) %>% 
  step_center(all_predictors()) %>% 
  step_scale(all_predictors())

df_train_baked <- 
  rec %>% 
  prep(training = df_train) %>% 
  bake(new_data = df_train)
## Warning: All elements of `...` must be named.
## Did you want `data = c(type, role, source)`?

## Warning: All elements of `...` must be named.
## Did you want `data = c(type, role, source)`?

## Warning: All elements of `...` must be named.
## Did you want `data = c(type, role, source)`?
df_test_baked <- 
  rec %>% 
  prep(training = df_train) %>% 
  bake(new_data = df_test)
## Warning: All elements of `...` must be named.
## Did you want `data = c(type, role, source)`?

## Warning: All elements of `...` must be named.
## Did you want `data = c(type, role, source)`?

## Warning: All elements of `...` must be named.
## Did you want `data = c(type, role, source)`?
knn(df_train_baked, df_test_baked, df_train_baked$hazard, k = 2) %>% 
  table()
## .
## FALSE  TRUE 
##   363    42
# p1 <-
#   ggplot(df_train, aes(max_elevation, mean_slope_aspect)) +
#   geom_point(aes(color = hazard))
# 
# # p1 + 
# #   ylim(range(df_train$max_elevation))
# # 
# # plot_grid(p1, p2)
p2 <-
  ggplot(df_test,
       aes(max_elevation, mean_slope_aspect)) +
  geom_point(aes(color = predict))

p3 <- 
  p2 + 
  scale_color_manual(values = c("gray", "gray")) +
  geom_point(data = df_test %>% 
               slice(c(397, 147, 271)), 
         aes(max_elevation, mean_slope_aspect),
      #   color = "transparent",
         size = 3)

# わかりにくいけど、スケールを揃えたら距離が離れる点が近隣として選ばれている
plot_grid(p3 +
  ylim(range(df_test$max_elevation)) +
    guides(color = FALSE), NULL,
  p2 + guides(color = FALSE), 
  p3 + guides(color = FALSE))

スケーリング処理

変数間のスケールを統一する処理には複数の方法があります。いずれもデータが取りうる値のスケールを変換し、一定の範囲に収める処理を行います。これらは個々の特徴量に対して適用され、後に述べるような対数変換のようにデータの分布を変化させない変換方法になります。

PLSなどで恩恵がありますが

一方でデータが持つ単位を失ってしまうことは、データの解釈を困難にさせます。

これらのスケーリング処理はデータの分布には影響しないことが特徴です。

Min-Maxスケーリング

特徴量の値を0~1の範囲に収める変換をMin-Maxスケーリングと呼びます。

[ \tilde{x} = \frac{x - min(x)}{max(x) - min(x)} ]

Min-Maxスケーリングを行う際は、数値の取り得る値があらかじめわかっているものが望ましいです。データとして存在しない値や外れ値が与えられることで、その値に引っ張られて相対的な差がなくなってしまう恐れがあるからです。

# 地価データの「最寄り駅までの距離」の範囲
lp_dist <- df_lp_kanto$distance_from_station
range(lp_dist)
## [1]     0 24000
lp_dist_minmax <- 
  scale(lp_dist, center = min(lp_dist), scale = (max(lp_dist) - min(lp_dist)))
range(lp_dist_minmax)

全体を考慮

p1 <- 
  ggplot(df_lp_kanto, aes(x = 0, y = distance_from_station)) +
  geom_violin(color = ds_col(1))

p2 <- 
  ggplot(data = NULL, aes(0, lp_dist_minmax[, 1])) +
  geom_violin(color = ds_col(5)) +
  ylab("min_max(lp_dist)")

plot_grid(p1, p2)
  • 外れ値が含まれていない場合に良い
  • 外れ値の情報を活用したい時には適さない

標準化

標準化 (standardization)… 平均0、分散(標準偏差)1になる。

[ \tilde{x} = \frac{x- mean(x)}{sqrt(var(x))} ]

  • 複数の変数に対して行うことで比較が可能になる (平均0, 標準偏差1)
  • 範囲スケーリング… 最大値、最小値を利用する
  • 共通の「単位」をもつように扱いたい場合に有効
    • 距離または内積を利用する –> KNN, SVMs
    • ペナルティを課すため –> lasso, ridge

を標準化 (standardization) と呼びます。具体的には変数の取りうる値を平均0、分散1に変換する処理です。

# スケーリングでは分布は変わらない
range(df_lp_kanto$posted_land_price)
p_base <- 
  df_lp_kanto %>% 
  ggplot(aes(x = posted_land_price)) +
  geom_histogram(bins = 30)
p_base
df_lp_kanto %>% 
  ggplot(aes(x = scale(posted_land_price, center = TRUE, scale = TRUE)[, 1])) +
  geom_histogram(bins = 30)

# hist(df_lp_kanto$posted_land_price)
# hist(scale(df_lp_kanto$posted_land_price)[, 1])

リッジ回帰、主成分分析では変数の標準化が前提となっている

正規化

正規化 (normalization)

取りうる値の範囲が狭くなります。これは外れ値に対して有効な処理と言えます。

体重や身長など、単位が異なる変数を比較する際に役立ちます。

正規化により、取りうる値の範囲が統一されるためです。

データの表現方法を変える

# 地価データにおける駅からの距離
# 平均と分散
df_lp_kanto %>% 
  summarise(mean = mean(distance_from_station),
            sd = sd(distance_from_station))
## # A tibble: 1 x 2
##    mean    sd
##   <dbl> <dbl>
## 1 1566. 1965.
# center, scale引数はいずれも既定でTRUEです
lp_dist_scaled <- 
  c(scale(df_lp_kanto$distance_from_station, center = TRUE, scale = TRUE))

mean(lp_dist_scaled) # 限りなく0に近くなる
## [1] 3.056636e-17
sd(lp_dist_scaled) # 分散は1
## [1] 1
diff(range(lp_dist))
## [1] 24000
median(lp_dist)
## [1] 1000
diff(range(c(scale(lp_dist))))
## [1] 12.215

標準地からの鉄道駅までの距離の中央値は900で、最小値は0 (近接の場合は0が与えられる)、最大値は24000です。この特徴量を標準化するとその差は14.02にまで縮まります。

距離の

これらの手法は、対数変換と異なりデータの分布には影響しないことが特徴です。

p1 <- 
  ggplot(df_lp_kanto, 
             aes(distance_from_station)) +
  geom_histogram(bins = 30, fill = ds_col(1))
p2 <-
  ggplot(df_lp_kanto,
             aes(c(scale(distance_from_station)))) +
  geom_histogram(bins = 30, fill = ds_col(5)) + 
  scale_fill_identity() + aes(fill = ds_col(1))

plot_grid(p1, p2, ncol = 2)

データ浄化

  1. 情報を含まない列の削除
    • 分散が0の説明変数 (constant cols)
    • 重複した説明変数を一つに
      • 多重共線性
    • 相関係数が1である説明変数の組 (perfectly correlated cols) をユニークに
      • データリーク

データセットに含まれる列のうち、attribute_change_forest_lawattribute_change_parks_law は論理型の変数でありながら1値しか持たない(分散が0)ものでした。

外れ値

欠損処理

欠損処理

不要な列の削除

特徴量選択の文脈においてはフィルタ法

… 情報量がない列

単一の変数についてと、変数間の関係をそれぞれ調べることが重要になります。

もう一度データを確認しておきましょう。

  • 変数内
    • 単一の値のみが含まれる変数 (分散0)
  • 変数間
    • 同一の値の組み合わせ
    • 相関係数が1になる
  • データセット全体
    • 完全に欠損する行・列
df_lp_kanto %>% skimr::skim_to_list()
## $character
## # A tibble: 14 x 8
##    variable               missing complete n     min   max   empty n_unique
##  * <chr>                  <chr>   <chr>    <chr> <chr> <chr> <chr> <chr>   
##  1 .prefecture            0       8476     8476  3     4     0     7       
##  2 administrative_area_c… 0       8476     8476  5     5     0     329     
##  3 building_structure     220     8256     8476  2     8     0     130     
##  4 configuration          7454    1022     8476  2     3     0     2       
##  5 current_use            0       8476     8476  2     14    0     70      
##  6 fire_area              4859    3617     8476  2     2     0     2       
##  7 forest_law             8450    26       8476  3     3     0     1       
##  8 name_of_nearest_stati… 0       8476     8476  1     14    0     1481    
##  9 parks_law              8465    11       8476  7     7     0     2       
## 10 proximity_with_transp… 8308    168      8476  2     6     0     3       
## 11 surrounding_present_u… 0       8476     8476  15    22    0     4397    
## 12 urban_planning_area    0       8476     8476  2     3     0     4       
## 13 usage_description      7552    924      8476  1     8     0     40      
## 14 use_district           515     7961     8476  2     3     0     12      
## 
## $integer
## # A tibble: 6 x 12
##   variable missing complete n     mean  sd    p0    p25   p50   p75   p100 
## * <chr>    <chr>   <chr>    <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
## 1 .row_id  0       8476     8476  " 18… "   … 13534 1640… " 18… " 20… " 23…
## 2 acreage  0       8476     8476  "   … "   … 46    "  1… "   … "   … "413…
## 3 distanc… 0       8476     8476  "  1… "   … 0     "  5… "  1… "  1… " 24…
## 4 number_… 0       8476     8476  "   … "   … 0     "   … "   … "   … "   …
## 5 number_… 0       8476     8476  "   … "   … 0     "   … "   … "   … "   …
## 6 posted_… 0       8476     8476  "415… 1766… 613   "599… "155… "308… "   …
## # … with 1 more variable: hist <chr>
## 
## $logical
## # A tibble: 17 x 6
##    variable                 missing complete n     mean    count           
##  * <chr>                    <chr>   <chr>    <chr> <chr>   <chr>           
##  1 attribute_change_acreage 0       8476     8476  "0.003… FAL: 8450, TRU:…
##  2 attribute_change_address 0       8476     8476  "0.007… FAL: 8411, TRU:…
##  3 attribute_change_buildi… 0       8476     8476  0.00024 FAL: 8474, TRU:…
##  4 attribute_change_buildi… 0       8476     8476  "0.008… FAL: 8406, TRU:…
##  5 attribute_change_curren… 0       8476     8476  "0.008… FAL: 8402, TRU:…
##  6 attribute_change_distan… 0       8476     8476  "0.005… FAL: 8431, TRU:…
##  7 attribute_change_fire_a… 0       8476     8476  "0.007… FAL: 8417, TRU:…
##  8 attribute_change_floor_… 0       8476     8476  0.00024 FAL: 8474, TRU:…
##  9 attribute_change_forest… 0       8476     8476  "0    … FAL: 8476, NA: 0
## 10 attribute_change_parks_… 0       8476     8476  "0    … FAL: 8476, NA: 0
## 11 attribute_change_suppli… 0       8476     8476  "0.005… FAL: 8429, TRU:…
## 12 attribute_change_urban_… 0       8476     8476  0.00012 FAL: 8475, TRU:…
## 13 attribute_change_use_di… 0       8476     8476  0.00012 FAL: 8475, TRU:…
## 14 common_surveyed_position 0       8476     8476  "0.071… FAL: 7870, TRU:…
## 15 gas_facility             0       8476     8476  "0.78 … TRU: 6616, FAL:…
## 16 sewage_facility          0       8476     8476  "0.92 … TRU: 7775, FAL:…
## 17 water_facility           0       8476     8476  "0.99 … TRU: 8427, FAL:…
## 
## $numeric
## # A tibble: 8 x 12
##   variable missing complete n     mean  sd    p0    p25   p50   p75   p100 
## * <chr>    <chr>   <chr>    <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
## 1 .latitu… 0       8476     8476  "   … "   … "  3… "   … "   … "   … "   …
## 2 .longit… 0       8476     8476  "   … "   … " 13… "   … "  1… "   … "  1…
## 3 attribu… 0       8476     8476  "   … "   … "   … "   … "   … "   … "   …
## 4 buildin… 0       8476     8476  "   … "   … "   … "   … "   … "   … "   …
## 5 depth_r… 0       8476     8476  "   … "   … "   … "   … "   … "   … "   …
## 6 floor_a… 0       8476     8476  "   … "  1… "   … "   … "  2… "   … " 13…
## 7 frontag… 0       8476     8476  "   … "   … "   … "   … "   … "   … "   …
## 8 night_p… 0       8476     8476  2594… "2e+… "321… "118… "2e+… "341… "9e+…
## # … with 1 more variable: hist <chr>

attribute_change_forest_lawattribute_change_parks_law の列は二値データを持つことができる変数ですが、いずれも一つの値しか記録されていません。変数内での分散は0となり、情報を持っていないのと同義です(標準化も適用できません)。そのためこれらの値はモデル構築の前にデータから削除しても問題ないと考えられます。

情報量がゼロ(分散ゼロ)

数値データでは稀ですが、論理値変数やカテゴリ変数に潜んでいる、こうした1種類の値しか取らないデータは除外しましょう。

# 分散ゼロ
scale(c(0, 0, 0), scale = TRUE)
##      [,1]
## [1,]  NaN
## [2,]  NaN
## [3,]  NaN
## attr(,"scaled:center")
## [1] 0
## attr(,"scaled:scale")
## [1] 0
df_lp_kanto_clean <- 
  df_lp_kanto %>% 
  verify(ncol(.) == 45) %>% 
  as.data.frame() %>% 
  remove_constant() %>% 
  as_tibble() %>% 
  verify(ncol(.) == 43)

dim(df_lp_kanto_clean)
## [1] 8476   43

このデータの場合、これ以外で単独の値しか持たない変数はありませんでした。

どうやら違うようです。

df_lp_kanto %>% 
  distinct(attribute_change_urban_planning_area, 
           attribute_change_use_district)
## # A tibble: 3 x 2
##   attribute_change_urban_planning_area attribute_change_use_district
##   <lgl>                                <lgl>                        
## 1 FALSE                                FALSE                        
## 2 FALSE                                TRUE                         
## 3 TRUE                                 FALSE

分散が0に近い変数を削除する

変数の数は減りますが、重要な変数を削除してしまう恐れがあります。この作業は慎重に

dim(df_lp_kanto_clean)
## [1] 8476   43
nzv_filter <- 
  df_lp_kanto_clean %>%
  recipe(~ .) %>% 
  step_nzv(all_predictors())

filtered_te <- 
  nzv_filter %>% 
  prep() %>% 
  juice() %>% 
  verify(ncol(.) == 28)
## Warning: All elements of `...` must be named.
## Did you want `data = c(type, role, source)`?

## Warning: All elements of `...` must be named.
## Did you want `data = c(type, role, source)`?
dim(filtered_te)
## [1] 8476   28
# 削除される列がどのような値を持っているか確認して
df_lp_kanto_clean %>% 
  select(names(df_lp_kanto_clean)[!names(df_lp_kanto_clean) %in% names(filtered_te)]) %>% 
  glimpse()
## Observations: 8,476
## Variables: 15
## $ usage_description                      <chr> "別荘", NA, NA, "別荘", NA, …
## $ attribute_change_supplied_facility     <lgl> FALSE, FALSE, FALSE, FAL…
## $ water_facility                         <lgl> TRUE, TRUE, TRUE, TRUE, …
## $ forest_law                             <chr> NA, NA, NA, NA, NA, NA, …
## $ attribute_change_floor_area_ratio      <lgl> FALSE, FALSE, FALSE, FAL…
## $ attribute_change_selected_land_status  <dbl> 1, 1, 1, 1, 1, 1, 1, 1, …
## $ attribute_change_address               <lgl> FALSE, FALSE, FALSE, FAL…
## $ attribute_change_acreage               <lgl> FALSE, FALSE, FALSE, FAL…
## $ attribute_change_current_use           <lgl> FALSE, FALSE, FALSE, FAL…
## $ attribute_change_building_structure    <lgl> FALSE, FALSE, FALSE, FAL…
## $ attribute_change_distance_from_station <lgl> FALSE, FALSE, FALSE, FAL…
## $ attribute_change_use_district          <lgl> FALSE, FALSE, FALSE, FAL…
## $ attribute_change_fire_area             <lgl> FALSE, FALSE, FALSE, FAL…
## $ attribute_change_urban_planning_area   <lgl> FALSE, FALSE, FALSE, FAL…
## $ attribute_change_building_coverage     <lgl> FALSE, FALSE, FALSE, FAL…
# 重要ではなさそうなので削除
df_lp_kanto_clean <- filtered_te
# 変わらん(正しい)
# df_hazard_kys %>% 
#   as.data.frame() %>% 
#   remove_empty("cols") %>% 
#   dim()

相関係数が高い変数を

多重共線性の問題を引き起こします。

いずれかの変数だけを利用するようにしましょう。

library(GGally) 
## Registered S3 method overwritten by 'GGally':
##   method from   
##   +.gg   ggplot2
df_lp_kanto_clean %>%
  select(-starts_with(".")) %>%
  select_if(is.numeric) %>% 
  corrr::correlate()
## 
## Correlation method: 'pearson'
## Missing treated using: 'pairwise.complete.obs'

## # A tibble: 10 x 11
##    rowname posted_land_pri… distance_from_s…  acreage depth_ratio
##    <chr>              <dbl>            <dbl>    <dbl>       <dbl>
##  1 posted…         NA                -0.126   0.00819     0.0375 
##  2 distan…         -0.126            NA       0.0468     -0.0646 
##  3 acreage          0.00819           0.0468 NA           0.00383
##  4 depth_…          0.0375           -0.0646  0.00383    NA      
##  5 number…          0.438            -0.228  -0.0135      0.102  
##  6 number…          0.543            -0.138   0.0164      0.0428 
##  7 buildi…          0.212            -0.206  -0.00184     0.294  
##  8 fronta…          0.0486           -0.0185  0.0330     -0.220  
##  9 floor_…          0.473            -0.228   0.00763     0.223  
## 10 night_…          0.0103           -0.172  -0.0202      0.0211 
## # … with 6 more variables: number_of_floors <dbl>,
## #   number_of_basement_floors <dbl>, building_coverage <dbl>,
## #   frontage_ratio <dbl>, floor_area_ratio <dbl>, night_population <dbl>
library(GGally)

df_lp_kanto_clean %>% 
  select(-starts_with(".")) %>% 
  select_if(is.numeric) %>% 
  corrr::correlate()

# 最大 0.79... building_coverage - floor_area_ratio
df_lp_kanto_clean %>% 
  select(-starts_with(".")) %>% 
  select_if(is.numeric) %>% 
  ggcorr(label = TRUE, label_round = 2)

# df_lp_kanto では不適切
df_lp_kanto_clean %>% 
  recipe(~ .) %>% 
  step_corr(all_numeric(), -starts_with("."), threshold = 0.9) %>% 
  prep() %>% 
  juice() %>% 
  verify(ncol(.) == 28L)
## Warning: All elements of `...` must be named.
## Did you want `data = c(type, role, source)`?

## Warning: All elements of `...` must be named.
## Did you want `data = c(type, role, source)`?

## # A tibble: 8,476 x 28
##    .row_id .prefecture administrative_… posted_land_pri… name_of_nearest…
##      <int> <fct>       <fct>                       <int> <fct>           
##  1   13534 群馬県      10425                        4150 万座・鹿沢口    
##  2   13604 群馬県      10426                       39000 長野原草津口    
##  3   13605 群馬県      10426                       56800 長野原草津口    
##  4   13613 群馬県      10424                        2700 羽根尾          
##  5   13620 群馬県      10424                       12100 羽根尾          
##  6   13621 群馬県      10426                       29800 長野原草津口    
##  7   13640 群馬県      10424                       16400 群馬大津        
##  8   13755 群馬県      10382                       17400 下仁田          
##  9   13758 群馬県      10382                       25500 下仁田          
## 10   13765 群馬県      10382                       23300 下仁田          
## # … with 8,466 more rows, and 23 more variables:
## #   distance_from_station <int>, acreage <int>, current_use <fct>,
## #   building_structure <fct>, gas_facility <lgl>, sewage_facility <lgl>,
## #   proximity_with_transportation_facility <fct>, depth_ratio <dbl>,
## #   number_of_floors <int>, number_of_basement_floors <int>,
## #   use_district <fct>, building_coverage <dbl>, configuration <fct>,
## #   surrounding_present_usage <fct>, fire_area <fct>,
## #   urban_planning_area <fct>, parks_law <fct>, frontage_ratio <dbl>,
## #   floor_area_ratio <dbl>, common_surveyed_position <lgl>,
## #   .longitude <dbl>, .latitude <dbl>, night_population <dbl>
# 閾値を低くする
df_lp_kanto_clean %>% 
  recipe(~ .) %>% 
  step_corr(all_numeric(), -starts_with("."), threshold = 0.75) %>% 
  prep() %>% 
  juice() %>% 
  verify(ncol(.) == 27L)
## Warning: All elements of `...` must be named.
## Did you want `data = c(type, role, source)`?

## Warning: All elements of `...` must be named.
## Did you want `data = c(type, role, source)`?

## # A tibble: 8,476 x 27
##    .row_id .prefecture administrative_… posted_land_pri… name_of_nearest…
##      <int> <fct>       <fct>                       <int> <fct>           
##  1   13534 群馬県      10425                        4150 万座・鹿沢口    
##  2   13604 群馬県      10426                       39000 長野原草津口    
##  3   13605 群馬県      10426                       56800 長野原草津口    
##  4   13613 群馬県      10424                        2700 羽根尾          
##  5   13620 群馬県      10424                       12100 羽根尾          
##  6   13621 群馬県      10426                       29800 長野原草津口    
##  7   13640 群馬県      10424                       16400 群馬大津        
##  8   13755 群馬県      10382                       17400 下仁田          
##  9   13758 群馬県      10382                       25500 下仁田          
## 10   13765 群馬県      10382                       23300 下仁田          
## # … with 8,466 more rows, and 22 more variables:
## #   distance_from_station <int>, acreage <int>, current_use <fct>,
## #   building_structure <fct>, gas_facility <lgl>, sewage_facility <lgl>,
## #   proximity_with_transportation_facility <fct>, depth_ratio <dbl>,
## #   number_of_floors <int>, number_of_basement_floors <int>,
## #   use_district <fct>, building_coverage <dbl>, configuration <fct>,
## #   surrounding_present_usage <fct>, fire_area <fct>,
## #   urban_planning_area <fct>, parks_law <fct>, frontage_ratio <dbl>,
## #   common_surveyed_position <lgl>, .longitude <dbl>, .latitude <dbl>,
## #   night_population <dbl>

テキストデータの前処理については別の章で解説します。

次元削減と特徴量抽出

元データの情報を可能な限り多く含んだ状態を維持しつつ、新たな特徴量を抽出するプロセスとして次元削減があります。主成分分析に代表されるように、これらの手法は高次元のデータのばらつきをより失わない方法で圧縮します。。特に主成分分析では高次元空間上に直交するベクトルを特徴量(成分)とすることから、入力に相関のある特徴量を与えても無相関の特徴量を得ることが可能です。

なお主成分分析は変数のスケールの影響を強く受けます。実行する前にはスケーリング処理を行うようにしましょう。主成分分析を含めた次元削減の実行については次元削減で解説します。

関連項目

参考文献