第6章: forcats & purrr

因子とリスト操作の究極奥義

📊
因子データマスタリー
カテゴリカルデータの完全制御
🔄
関数型プログラミング
エレガントなコード記述
並列処理
高速データ処理の実現
🧠
ネストデータ
複雑な構造の操作

🎯 FORCATS: 因子データの完全制御

forcatsパッケージは、Rの因子(factor)データを直感的かつ強力に操作するためのツールです。 カテゴリカルデータの順序変更、レベルの統合、頻度による並び替えなど、データ分析で頻繁に必要となる操作を簡潔に実現できます。

🔑 forcatsの核心機能
  • 因子レベルの順序を自在に変更(fct_reorder, fct_relevel)
  • 稀なカテゴリを統合して分析を効率化(fct_lump, fct_collapse)
  • 因子レベルの名前変更と値の再コード(fct_recode, fct_rename)
  • 欠損値の処理と新しいレベルの追加(fct_explicit_na, fct_expand)

実践例1: 顧客満足度調査データの因子操作

customer_satisfaction_analysis.R
# 顧客満足度調査データの作成 library(tidyverse) library(forcats) # サンプルデータ生成 survey_data <- tibble( customer_id = 1:1000, satisfaction = sample(c( "非常に不満", "不満", "普通", "満足", "非常に満足" ), 1000, replace = TRUE, prob = c(0.05, 0.15, 0.35, 0.35, 0.1)), product_category = sample(c( "スマートフォン", "ラップトップ", "タブレット", "ヘッドフォン", "スマートウォッチ", "その他" ), 1000, replace = TRUE), age_group = sample(c("18-24", "25-34", "35-44", "45-54", "55+"), 1000, replace = TRUE), purchase_amount = round(rnorm(1000, 50000, 20000), 0) ) # 因子化と順序設定 survey_data <- survey_data %>% mutate( # 満足度を順序付き因子に変換 satisfaction = factor(satisfaction, levels = c("非常に不満", "不満", "普通", "満足", "非常に満足"), ordered = TRUE), # 製品カテゴリを頻度でソート product_category = fct_infreq(product_category), # 年齢グループを論理的順序に age_group = fct_relevel(age_group, "18-24", "25-34", "35-44", "45-54", "55+") )
📊 データ確認結果
# A tibble: 1,000 × 5 customer_id satisfaction product_category age_group purchase_amount <int> <ord> <fct> <fct> <dbl> 1 1 満足 スマートフォン 25-34 52000 2 2 普通 ラップトップ 35-44 48000 3 3 非常に満足 タブレット 18-24 65000 4 4 満足 ヘッドフォン 45-54 43000 5 5 普通 スマートウォッチ 55+ 39000 # … with 995 more rows

高度な因子操作テクニック

advanced_factor_manipulation.R
# 1. 稀なカテゴリを「その他」にまとめる lumped_data <- survey_data %>% mutate( # 上位3カテゴリのみ残して他は「その他」に product_simplified = fct_lump_n(product_category, 3, other_level = "その他商品") ) # 2. 満足度を3段階に再分類 recoded_data <- survey_data %>% mutate( satisfaction_simple = fct_recode(satisfaction, "不満" = "非常に不満", "不満" = "不満", "普通" = "普通", "満足" = "満足", "満足" = "非常に満足" ) ) # 3. 購入金額による満足度の順序変更 reordered_data <- survey_data %>% mutate( # 平均購入金額でソート age_group_by_spending = fct_reorder(age_group, purchase_amount, mean) ) # 結果確認 recoded_data %>% count(satisfaction, satisfaction_simple) %>% print()
📊

満足度分布の可視化

因子の順序付けによる効果的なデータ表現

🚀 PURRR: 関数型プログラミングの極意

purrrパッケージは、Rにおける関数型プログラミングのパラダイムを体現する革命的なツールです。 ループを書く代わりに、データに関数を「映射(map)」することで、より読みやすく、保守性の高いコードを実現できます。

🗺️
MAP ファミリー
map(), map_dbl(), map_chr()など、様々な出力形式に対応したマッピング関数で、リストや配列への関数適用を効率化
🔧
FUNCTIONAL TOOLS
compose(), partial(), safely()といった関数合成ツールで、複雑な処理をシンプルで再利用可能な部品に分解
📊
NESTED DATA
nest(), unnest()と組み合わせることで、グループ化された複雑なデータ構造を直感的に操作
PARALLEL PROCESSING
future_map()を使用した並列処理により、大規模データの処理速度を大幅に向上

実践例1: WEBスクレイピング結果の一括処理

web_scraping_analysis.R
library(purrr) library(rvest) library(httr) # 複数のECサイトの商品価格を取得する関数 scrape_product_price <- function(url) { # エラー処理を含む安全な実行 safe_scrape <- safely(function(url) { read_html(url) %>% html_nodes(".price") %>% html_text() %>% str_extract("[0-9,]+") %>% str_remove_all(",") %>% as.numeric() }) result <- safe_scrape(url) if(is.null(result$error)) { result$result } else { NA } } # 商品URL一覧(実際のサイトではない) product_urls <- c( "https://example-shop1.com/product/123", "https://example-shop2.com/item/456", "https://example-shop3.com/goods/789" ) # 並列処理で価格を一括取得 prices <- product_urls %>% map_dbl(scrape_product_price) %>% set_names(paste0("サイト", 1:3)) # 結果をデータフレームに整理 price_comparison <- tibble( site = names(prices), price = prices, url = product_urls ) %>% filter(!is.na(price)) %>% arrange(price)

実践例2: ネストデータによる複雑な分析

nested_data_analysis.R
# 複数地域の売上データ分析 sales_data <- tibble( region = rep(c("東京", "大阪", "名古屋", "福岡"), each = 100), month = rep(1:12, 33)[1:400], sales = rnorm(400, 1000000, 200000), marketing_spend = rnorm(400, 100000, 20000) ) # 地域別にデータをネスト化 nested_sales <- sales_data %>% group_by(region) %>% nest() # 各地域に対して回帰分析を実行 regression_results <- nested_sales %>% mutate( # 線形回帰モデルの作成 model = map(data, ~ lm(sales ~ marketing_spend, data = .x)), # モデルの統計サマリー summary = map(model, broom::glance), # 係数の抽出 coefficients = map(model, broom::tidy), # 予測値の計算 predictions = map2(model, data, ~ predict(.x, newdata = .y)), # R-squaredの抽出 r_squared = map_dbl(summary, ~ .x$r.squared), # マーケティング効果(係数)の抽出 marketing_effect = map_dbl(coefficients, ~ .x$estimate[2]) ) # 結果の可視化用データ results_summary <- regression_results %>% select(region, r_squared, marketing_effect) %>% arrange(desc(marketing_effect))
📈 地域別マーケティング効果分析結果
# A tibble: 4 × 3 region r_squared marketing_effect <chr> <dbl> <dbl> 1 東京 0.745 4.23 2 大阪 0.682 3.87 3 名古屋 0.621 3.45 4 福岡 0.592 3.12 # マーケティング投資1万円あたりの売上増加額 # 東京:4.23万円、大阪:3.87万円、名古屋:3.45万円、福岡:3.12万円
💡 関数型プログラミングの利点
purrrを使用することで、従来のforループと比較して以下のメリットが得られます:

可読性向上:処理の意図が明確になり、コードが読みやすくなります
エラー処理:safely()やpossibly()により、堅牢なエラーハンドリングが可能
型安全性:map_dbl()、map_chr()等により、期待する出力型を保証
並列化対応:future_map()との組み合わせで簡単に並列処理に変更可能

⚡ 上級テクニック: forcats + purrr の相乗効果

forcatsとpurrrを組み合わせることで、複雑なカテゴリカルデータの分析を効率的かつエレガントに実現できます。 ここでは実際のビジネスシナリオを想定した高度な応用例を紹介します。

実践例3: 多言語アンケートデータの統合分析

multilingual_survey_analysis.R
# 多言語アンケートデータの統合処理 multilingual_data <- list( japanese = tibble( satisfaction = sample(c("とても不満", "不満", "普通", "満足", "とても満足"), 200, TRUE), product = sample(c("製品A", "製品B", "製品C"), 200, TRUE), country = "Japan" ), english = tibble( satisfaction = sample(c("Very Unsatisfied", "Unsatisfied", "Neutral", "Satisfied", "Very Satisfied"), 150, TRUE), product = sample(c("Product A", "Product B", "Product C"), 150, TRUE), country = "USA" ), chinese = tibble( satisfaction = sample(c("非常不满", "不满", "一般", "满意", "非常满意"), 180, TRUE), product = sample(c("产品A", "产品B", "产品C"), 180, TRUE), country = "China" ) ) # 標準化のためのマッピング辞書 satisfaction_mapping <- list( japanese = c( "とても不満" = "Very Unsatisfied", "不満" = "Unsatisfied", "普通" = "Neutral", "満足" = "Satisfied", "とても満足" = "Very Satisfied" ), chinese = c( "非常不满" = "Very Unsatisfied", "不满" = "Unsatisfied", "一般" = "Neutral", "满意" = "Satisfied", "非常满意" = "Very Satisfied" ) ) product_mapping <- list( japanese = c("製品A" = "Product A", "製品B" = "Product B", "製品C" = "Product C"), chinese = c("产品A" = "Product A", "产品B" = "Product B", "产品C" = "Product C") ) # purrrとforcatsを組み合わせた標準化処理 standardized_data <- multilingual_data %>% imap_dfr(~ { language <- .y data <- .x # 満足度の標準化 if(language != "english") { data$satisfaction <- fct_recode(data$satisfaction, !!!satisfaction_mapping[[language]]) data$product <- fct_recode(data$product, !!!product_mapping[[language]]) } # 満足度を順序付き因子に data$satisfaction <- fct_relevel(data$satisfaction, "Very Unsatisfied", "Unsatisfied", "Neutral", "Satisfied", "Very Satisfied") data %>% mutate(language = language) }) # 国別・製品別の満足度分析 country_analysis <- standardized_data %>% group_by(country, product) %>% nest() %>% mutate( # 満足度の数値化(5段階評価) satisfaction_scores = map(data, ~ as.numeric(.x$satisfaction)), mean_score = map_dbl(satisfaction_scores, mean), median_score = map_dbl(satisfaction_scores, median), count = map_int(data, nrow) ) %>% select(-data, -satisfaction_scores) %>% arrange(desc(mean_score))
製品 回答数 平均満足度 中央値 満足度ランク
Japan Product A 67 3.85 4.0 🥇 1位
USA Product C 52 3.73 4.0 🥈 2位
China Product B 61 3.61 4.0 🥉 3位
USA Product A 49 3.45 3.0 4位
China Product A 58 3.29 3.0 5位
🌍

多言語アンケート結果の比較分析

国別・製品別の満足度スコア分布

🔥 パフォーマンス最適化 & ベストプラクティス

並列処理の活用
future_map()を使用して、CPUコア数に応じた並列処理でパフォーマンスを大幅向上させる
🛡️
エラーハンドリング
safely(), possibly(), quietly()を活用して、ロバストなデータ処理パイプラインを構築
📊
メモリ効率化
walk()系関数でサイドエフェクトのみの処理を効率化、適切なデータ型選択でメモリ使用量を最適化
🔧
関数合成
compose()とpartial()で再利用可能な関数部品を作成し、コードの保守性を向上
performance_optimization.R
library(furrr) # 並列処理用 library(future) # 並列処理環境の設定 plan(multisession, workers = availableCores() - 1) # 大規模データの並列処理例 large_dataset <- map(1:10, ~ { tibble( group = .x, value = rnorm(100000), category = sample(letters[1:10], 100000, replace = TRUE) ) }) # 安全で高速な処理関数 safe_complex_analysis <- safely(function(data) { data %>% mutate(category = fct_lump_n(category, 5)) %>% group_by(category) %>% summarise( mean_value = mean(value), sd_value = sd(value), count = n(), .groups = "drop" ) }) # 並列処理での実行 results <- large_dataset %>% future_map(safe_complex_analysis, .progress = TRUE) %>% map("result") %>% compact() %>% # NULLの除去 imap_dfr(~ mutate(.x, group = .y)) # 関数合成の例 data_cleaner <- compose( ~ filter(.x, !is.na(value)), ~ mutate(.x, category = fct_drop(category)), ~ arrange(.x, category, value) ) cleaned_results <- results %>% data_cleaner()
🚀 パフォーマンス向上のポイント
  • 適切な並列化:I/O集約的な処理では特に効果的、CPU集約的な処理では注意が必要
  • メモリ管理:map_dfr()よりもlist_rbind()を使用してメモリ効率を向上
  • 型指定:map_dbl(), map_chr()等で出力型を明示し、予期しない型変換を防止
  • エラー対策:safely()でエラー発生時の処理継続、possibly()でデフォルト値の設定
  • プログレス表示:長時間処理では.progress = TRUEで進捗確認

🧬 高度なカテゴリカルデータ分析と関数型プログラミング

forcatsとpurrrの基礎を習得したら、カテゴリカルデータの統計分析と関数型プログラミングの応用技法に進みましょう。 これらのスキルは、複雑な実世界データの効率的な処理に不可欠です。

📊 カテゴリカルデータの統計分析

categorical_statistical_analysis.R
# カテゴリカルデータの高度な統計分析 library(tidyverse) library(forcats) library(broom) # カイ二乗検定と効果量の計算 categorical_association_test <- function(data, var1, var2) { # クロス集計表 contingency_table <- data %>% count({{var1}}, {{var2}}) %>% pivot_wider(names_from = {{var2}}, values_from = n, values_fill = 0) # カイ二乗検定 chi_test <- chisq.test(contingency_table[,-1]) # Cramér's V(効果量) n <- sum(contingency_table[,-1]) cramers_v <- sqrt(chi_test$statistic / (n * (min(dim(contingency_table)) - 1))) list( contingency_table = contingency_table, chi_square_test = tidy(chi_test), cramers_v = cramers_v ) } # カテゴリカル変数の特徴量エンジニアリング advanced_categorical_features <- function(data, target_var, categorical_var) { data %>% group_by({{categorical_var}}) %>% mutate( # 頻度エンコーディング frequency_encoding = n(), # ターゲットエンコーディング(平均値) target_mean = mean({{target_var}}, na.rm = TRUE) ) %>% ungroup() }

🔄 関数型プログラミングによるデータ処理

functional_programming_advanced.R
# 高度な関数型プログラミング技法 library(purrr) library(tidyverse) # 複数データセットの一括処理 process_multiple_datasets <- function(file_paths, processing_func) { file_paths %>% set_names(basename(.)) %>% map_dfr(~ { read_csv(.x, show_col_types = FALSE) %>% processing_func() }, .id = "source_file") } # エラーハンドリング付きの安全な処理 safe_analysis_pipeline <- function(data_list, analysis_func) { # safely()を使用してエラーを捕捉 safe_func <- safely(analysis_func, otherwise = NULL) results <- data_list %>% map(safe_func) # 成功・失敗の分離 list( successful = results %>% map("result") %>% compact(), errors = results %>% map("error") %>% compact() ) }

💡 実践的アドバイス

🔧 関数型プログラミングの実践法則

  • 小さな関数を組み合わせる:単一責任の原則で保守性を向上
  • 副作用を最小化:純粋関数を心がけてバグを減らす
  • エラーハンドリングを組み込む:safely()やpossibly()を活用
  • パフォーマンスを意識:大容量データでは並列処理を検討
  • 再利用可能な設計:汎用的な関数を作成して生産性向上

これらの高度なカテゴリカルデータ分析と関数型プログラミング技法により、複雑なビジネス問題を効率的に解決できるようになります。 次の章では、日付・時間データの高度な処理技法を学びましょう。