{ "nbformat": 4, "nbformat_minor": 2, "metadata": { "colab": { "name": "lesson_2-R.ipynb", "provenance": [], "collapsed_sections": [], "toc_visible": true }, "kernelspec": { "name": "ir", "display_name": "R" }, "language_info": { "name": "R" }, "coopTranslator": { "original_hash": "f3c335f9940cfd76528b3ef918b9b342", "translation_date": "2025-09-04T01:39:34+00:00", "source_file": "2-Regression/2-Data/solution/R/lesson_2-R.ipynb", "language_code": "ja" } }, "cells": [ { "cell_type": "markdown", "source": [ "# 回帰モデルの構築: データの準備と可視化\n", "\n", "## **カボチャの線形回帰 - レッスン2**\n", "#### はじめに\n", "\n", "TidymodelsとTidyverseを使って機械学習モデルの構築を始める準備が整った今、データに対して質問を投げかける準備ができました。データを扱い、機械学習のソリューションを適用する際には、適切な質問をすることでデータセットの可能性を最大限に引き出すことが非常に重要です。\n", "\n", "このレッスンでは以下を学びます:\n", "\n", "- モデル構築のためのデータ準備方法。\n", "\n", "- `ggplot2`を使ったデータの可視化方法。\n", "\n", "解きたい質問によって使用する機械学習アルゴリズムの種類が決まります。そして、得られる答えの質はデータの性質に大きく依存します。\n", "\n", "これを実際の演習を通じて確認してみましょう。\n", "\n", "\n", "

\n", " \n", "

イラスト: @allison_horst
\n", "\n", "\n", "\n" ], "metadata": { "id": "Pg5aexcOPqAZ" } }, { "cell_type": "markdown", "source": [ "## 1. かぼちゃデータのインポートとTidyverseの召喚\n", "\n", "このレッスンを効率よく進めるために、以下のパッケージが必要です:\n", "\n", "- `tidyverse`: [tidyverse](https://www.tidyverse.org/) は、データサイエンスをより速く、簡単に、そして楽しくするために設計された[Rパッケージのコレクション](https://www.tidyverse.org/packages)です!\n", "\n", "以下のコマンドでインストールできます:\n", "\n", "`install.packages(c(\"tidyverse\"))`\n", "\n", "以下のスクリプトは、このモジュールを完了するために必要なパッケージがインストールされているか確認し、足りない場合は自動的にインストールします。\n" ], "metadata": { "id": "dc5WhyVdXAjR" } }, { "cell_type": "code", "execution_count": null, "source": [ "suppressWarnings(if(!require(\"pacman\")) install.packages(\"pacman\"))\n", "pacman::p_load(tidyverse)" ], "outputs": [], "metadata": { "id": "GqPYUZgfXOBt" } }, { "cell_type": "markdown", "source": [ "では、このレッスン用に提供された[データ](https://github.com/microsoft/ML-For-Beginners/blob/main/2-Regression/data/US-pumpkins.csv)を読み込み、いくつかのパッケージを起動しましょう!\n" ], "metadata": { "id": "kvjDTPDSXRr2" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Load the core Tidyverse packages\n", "library(tidyverse)\n", "\n", "# Import the pumpkins data\n", "pumpkins <- read_csv(file = \"https://raw.githubusercontent.com/microsoft/ML-For-Beginners/main/2-Regression/data/US-pumpkins.csv\")\n", "\n", "\n", "# Get a glimpse and dimensions of the data\n", "glimpse(pumpkins)\n", "\n", "\n", "# Print the first 50 rows of the data set\n", "pumpkins %>% \n", " slice_head(n =50)" ], "outputs": [], "metadata": { "id": "VMri-t2zXqgD" } }, { "cell_type": "markdown", "source": [ "`glimpse()` を使うと、空白があり、文字列(`chr`)と数値データ(`dbl`)が混在していることがすぐにわかります。`Date` は文字型で、さらに `Package` という奇妙な列があり、データが `sacks` や `bins`、その他の値が混在しています。実際、このデータは少し混乱していますね 😤。\n", "\n", "実際のところ、完全に整ったデータセットをそのまま使って機械学習モデルを作成できることはあまりありません。でも心配しないでください。このレッスンでは、標準的なRライブラリを使って生のデータセットを準備する方法を学びます 🧑‍🔧。さらに、データを視覚化するためのさまざまなテクニックも学びます。📈📊\n", "
\n", "\n", "> おさらい: パイプ演算子(`%>%`)は、オブジェクトを関数や呼び出し式に順次渡すことで、論理的な順序で操作を実行します。コード内でパイプ演算子は「そして次に」と言っているようなものだと考えることができます。\n" ], "metadata": { "id": "REWcIv9yX29v" } }, { "cell_type": "markdown", "source": [ "## 2. データの欠損を確認する\n", "\n", "データサイエンティストがよく直面する問題の一つに、不完全または欠損しているデータがあります。Rでは、欠損値や不明な値を特別なセントネル値 `NA` (Not Available) で表します。\n", "\n", "では、データフレームに欠損値が含まれているかどうかをどうやって確認するのでしょうか?\n", "
\n", "- 一つの簡単な方法は、ベースRの関数 `anyNA` を使用することです。この関数は論理オブジェクト `TRUE` または `FALSE` を返します。\n" ], "metadata": { "id": "Zxfb3AM5YbUe" } }, { "cell_type": "code", "execution_count": null, "source": [ "pumpkins %>% \n", " anyNA()" ], "outputs": [], "metadata": { "id": "G--DQutAYltj" } }, { "cell_type": "markdown", "source": [ "素晴らしいですね、いくつかのデータが欠けているようです!そこから始めるのが良いでしょう。\n", "\n", "- 別の方法として、`is.na()`関数を使用することができます。この関数は、各列の要素が欠けているかどうかを論理値`TRUE`で示します。\n" ], "metadata": { "id": "mU-7-SB6YokF" } }, { "cell_type": "code", "execution_count": null, "source": [ "pumpkins %>% \n", " is.na() %>% \n", " head(n = 7)" ], "outputs": [], "metadata": { "id": "W-DxDOR4YxSW" } }, { "cell_type": "markdown", "source": [ "大きなデータフレームの場合、このようにすべての行と列を個別に確認するのは非効率的で、実質的に不可能です😴。\n", "\n", "- より直感的な方法としては、各列の欠損値の合計を計算することです:\n" ], "metadata": { "id": "xUWxipKYY0o7" } }, { "cell_type": "code", "execution_count": null, "source": [ "pumpkins %>% \n", " is.na() %>% \n", " colSums()" ], "outputs": [], "metadata": { "id": "ZRBWV6P9ZArL" } }, { "cell_type": "markdown", "source": [ "データが欠けている部分はありますが、今回の作業には影響がないかもしれません。さらに分析を進めてみましょう。\n", "\n", "> 素晴らしいパッケージや関数のセットに加えて、Rには非常に優れたドキュメントがあります。例えば、`help(colSums)` または `?colSums` を使用して、その関数について詳しく調べることができます。\n" ], "metadata": { "id": "9gv-crB6ZD1Y" } }, { "cell_type": "markdown", "source": [ "## 3. Dplyr: データ操作の文法\n", "\n", "

\n", " \n", "

@allison_horstによるイラスト
\n", "\n", "\n", "\n" ], "metadata": { "id": "o4jLY5-VZO2C" } }, { "cell_type": "markdown", "source": [ "[`dplyr`](https://dplyr.tidyverse.org/)は、Tidyverseのパッケージであり、データ操作の文法を提供します。これにより、最も一般的なデータ操作の課題を解決するための一貫した動詞のセットが利用できます。このセクションでは、dplyrの動詞について詳しく見ていきましょう! \n" ], "metadata": { "id": "i5o33MQBZWWw" } }, { "cell_type": "markdown", "source": [ "#### dplyr::select()\n", "\n", "`select()`は、`dplyr`パッケージに含まれる関数で、保持したい列や除外したい列を選択するのに役立ちます。\n", "\n", "データフレームを扱いやすくするために、`select()`を使って必要な列だけを残し、いくつかの列を削除しましょう。\n", "\n", "例えば、この演習では、分析に必要な列として`Package`、`Low Price`、`High Price`、`Date`を使用します。これらの列を選択してみましょう。\n" ], "metadata": { "id": "x3VGMAGBZiUr" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Select desired columns\n", "pumpkins <- pumpkins %>% \n", " select(Package, `Low Price`, `High Price`, Date)\n", "\n", "\n", "# Print data set\n", "pumpkins %>% \n", " slice_head(n = 5)" ], "outputs": [], "metadata": { "id": "F_FgxQnVZnM0" } }, { "cell_type": "markdown", "source": [ "#### dplyr::mutate()\n", "\n", "`mutate()`は、`dplyr`パッケージの関数で、既存の列を保持しながら、新しい列を作成したり、既存の列を変更したりするのに役立ちます。\n", "\n", "`mutate`の一般的な構文は以下の通りです:\n", "\n", "`data %>% mutate(new_column_name = what_it_contains)`\n", "\n", "`Date`列を使って`mutate`を試してみましょう。以下の操作を行います:\n", "\n", "1. 日付(現在は文字型)を月の形式に変換します(これらはアメリカの日付なので、形式は`MM/DD/YYYY`です)。\n", "\n", "2. 日付から月を抽出し、新しい列に保存します。\n", "\n", "Rでは、[lubridate](https://lubridate.tidyverse.org/)パッケージを使うと、日付データや日時データの操作が簡単になります。そこで、`dplyr::mutate()`、`lubridate::mdy()`、`lubridate::month()`を使って、上記の目的を達成してみましょう。なお、以降の操作で`Date`列は不要になるため、削除しても問題ありません。\n" ], "metadata": { "id": "2KKo0Ed9Z1VB" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Load lubridate\n", "library(lubridate)\n", "\n", "pumpkins <- pumpkins %>% \n", " # Convert the Date column to a date object\n", " mutate(Date = mdy(Date)) %>% \n", " # Extract month from Date\n", " mutate(Month = month(Date)) %>% \n", " # Drop Date column\n", " select(-Date)\n", "\n", "# View the first few rows\n", "pumpkins %>% \n", " slice_head(n = 7)" ], "outputs": [], "metadata": { "id": "5joszIVSZ6xe" } }, { "cell_type": "markdown", "source": [ "やったね!🤩\n", "\n", "次に、新しい列 `Price` を作成しましょう。この列はカボチャの平均価格を表します。では、`Low Price` 列と `High Price` 列の平均を取り、新しい `Price` 列に値を埋めていきましょう。\n", "
\n" ], "metadata": { "id": "nIgLjNMCZ-6Y" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Create a new column Price\n", "pumpkins <- pumpkins %>% \n", " mutate(Price = (`Low Price` + `High Price`)/2)\n", "\n", "# View the first few rows of the data\n", "pumpkins %>% \n", " slice_head(n = 5)" ], "outputs": [], "metadata": { "id": "Zo0BsqqtaJw2" } }, { "cell_type": "markdown", "source": [ "イエス!💪\n", "\n", "「でも待って!」と、`View(pumpkins)`でデータセット全体をざっと見た後に言うかもしれません。「ここ、何かおかしくない?」🤔\n", "\n", "`Package`列を見てみると、カボチャはさまざまな形態で販売されていることがわかります。一部は`1 1/9 bushel`の単位で、一部は`1/2 bushel`の単位で、一部はカボチャ1個ごと、一部はポンドごと、さらに幅の異なる大きな箱で販売されているものもあります。\n", "\n", "これを確認してみましょう:\n" ], "metadata": { "id": "p77WZr-9aQAR" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Verify the distinct observations in Package column\n", "pumpkins %>% \n", " distinct(Package)" ], "outputs": [], "metadata": { "id": "XISGfh0IaUy6" } }, { "cell_type": "markdown", "source": [ "素晴らしいですね!👏\n", "\n", "カボチャは一貫して重さを測るのが非常に難しいようですので、`Package`列に*bushel*という文字列が含まれるカボチャだけを選択し、新しいデータフレーム`new_pumpkins`に入れましょう。\n" ], "metadata": { "id": "7sMjiVujaZxY" } }, { "cell_type": "markdown", "source": [ "#### dplyr::filter() と stringr::str_detect()\n", "\n", "[`dplyr::filter()`](https://dplyr.tidyverse.org/reference/filter.html): 条件を満たす**行**のみを含むデータのサブセットを作成します。この場合、`Package`列に文字列 *bushel* を含むかどうかでカボチャをフィルタリングします。\n", "\n", "[stringr::str_detect()](https://stringr.tidyverse.org/reference/str_detect.html): 文字列内で特定のパターンが存在するかどうかを検出します。\n", "\n", "[`stringr`](https://github.com/tidyverse/stringr) パッケージは、一般的な文字列操作を簡単に行うための関数を提供します。\n" ], "metadata": { "id": "L8Qfcs92ageF" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Retain only pumpkins with \"bushel\"\n", "new_pumpkins <- pumpkins %>% \n", " filter(str_detect(Package, \"bushel\"))\n", "\n", "# Get the dimensions of the new data\n", "dim(new_pumpkins)\n", "\n", "# View a few rows of the new data\n", "new_pumpkins %>% \n", " slice_head(n = 5)" ], "outputs": [], "metadata": { "id": "hy_SGYREampd" } }, { "cell_type": "markdown", "source": [ "私たちは、バスケット単位でカボチャを含む約415行のデータに絞り込んだことがわかります。🤩 \n", "
\n" ], "metadata": { "id": "VrDwF031avlR" } }, { "cell_type": "markdown", "source": [ "#### dplyr::case_when()\n", "\n", "**でも、もう一つやるべきことがあります**\n", "\n", "バスケットの量が行ごとに異なることに気づきましたか?価格を1 1/9や1/2バスケットではなく、1バスケットあたりの価格として正規化する必要があります。標準化するために少し計算をしましょう。\n", "\n", "[`case_when()`](https://dplyr.tidyverse.org/reference/case_when.html) 関数を使って、条件に応じてPrice列を*変換*します。`case_when`を使うと、複数の`if_else()`ステートメントをベクトル化することができます。\n" ], "metadata": { "id": "mLpw2jH4a0tx" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Convert the price if the Package contains fractional bushel values\n", "new_pumpkins <- new_pumpkins %>% \n", " mutate(Price = case_when(\n", " str_detect(Package, \"1 1/9\") ~ Price/(1 + 1/9),\n", " str_detect(Package, \"1/2\") ~ Price/(1/2),\n", " TRUE ~ Price))\n", "\n", "# View the first few rows of the data\n", "new_pumpkins %>% \n", " slice_head(n = 30)" ], "outputs": [], "metadata": { "id": "P68kLVQmbM6I" } }, { "cell_type": "markdown", "source": [ "さて、私たちはバスケット単位での価格分析を行うことができます。しかし、このカボチャのバスケットに関する研究は、「データの性質を理解すること」がいかに`重要`であるかを示しています!\n", "\n", "> ✅ [The Spruce Eats](https://www.thespruceeats.com/how-much-is-a-bushel-1389308)によると、バスケットの重さはその中身の種類によって異なります。これは体積の測定方法だからです。「例えば、トマトのバスケットは56ポンドの重さがあるとされています... 葉物や緑の野菜はスペースを多く取り、重さが少ないため、ほうれん草のバスケットはたった20ポンドです。」かなり複雑ですね!バスケットからポンドへの換算はやめて、バスケット単位で価格を設定しましょう。このカボチャのバスケットに関する研究は、データの性質を理解することがいかに重要であるかを示しています!\n", "\n", "> ✅ 半バスケット単位で販売されているカボチャが非常に高価であることに気づきましたか?その理由を考えられますか?ヒント:小さなカボチャは大きなカボチャよりもずっと高価です。おそらく、大きな空洞のあるパイ用カボチャが1つ占めるスペースに比べて、小さなカボチャがバスケットにたくさん詰め込まれるためでしょう。\n" ], "metadata": { "id": "pS2GNPagbSdb" } }, { "cell_type": "markdown", "source": [ "さて、最後に、ちょっとした冒険のために 💁‍♀️、Month列を最初の位置、つまり`Package`列の「前」に移動させましょう。\n", "\n", "列の位置を変更するには、`dplyr::relocate()`を使用します。\n" ], "metadata": { "id": "qql1SowfbdnP" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Create a new data frame new_pumpkins\n", "new_pumpkins <- new_pumpkins %>% \n", " relocate(Month, .before = Package)\n", "\n", "new_pumpkins %>% \n", " slice_head(n = 7)" ], "outputs": [], "metadata": { "id": "JJ1x6kw8bixF" } }, { "cell_type": "markdown", "source": [ "よくできました!👌 これで、新しい回帰モデルを構築するためのクリーンで整然としたデータセットが手に入りましたね! \n", "
\n" ], "metadata": { "id": "y8TJ0Za_bn5Y" } }, { "cell_type": "markdown", "source": [ "## 4. ggplot2を使ったデータ可視化\n", "\n", "

\n", " \n", "

ダサニ・マディパリによるインフォグラフィック
\n", "\n", "\n", "\n", "\n", "こんな*賢い*言葉があります:\n", "\n", "> 「シンプルなグラフは、他のどんな手段よりも多くの情報をデータアナリストの頭に届けてくれる。」 --- ジョン・テューキー\n", "\n", "データサイエンティストの役割の一部は、扱っているデータの質や性質を示すことです。そのために、彼らはしばしば興味深い可視化、つまりプロットやグラフ、チャートを作成し、データのさまざまな側面を示します。このようにして、視覚的に関係性やギャップを示すことができ、通常では見つけにくいものを明らかにすることができます。\n", "\n", "可視化は、データに最も適した機械学習手法を決定するのにも役立ちます。例えば、散布図が直線に沿っているように見える場合、そのデータは線形回帰の演習に適していることを示しています。\n", "\n", "Rにはグラフを作成するためのいくつかのシステムがありますが、[`ggplot2`](https://ggplot2.tidyverse.org/index.html)はその中でも最もエレガントで汎用性の高いものの一つです。`ggplot2`を使うと、**独立したコンポーネントを組み合わせる**ことでグラフを作成することができます。\n", "\n", "まずは、Price列とMonth列を使ったシンプルな散布図から始めてみましょう。\n", "\n", "この場合、まず[`ggplot()`](https://ggplot2.tidyverse.org/reference/ggplot.html)を使い、データセットと美的マッピング([`aes()`](https://ggplot2.tidyverse.org/reference/aes.html)を使用)を指定し、その後、散布図用のレイヤー(例えば[`geom_point()`](https://ggplot2.tidyverse.org/reference/geom_point.html))を追加します。\n" ], "metadata": { "id": "mYSH6-EtbvNa" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Set a theme for the plots\n", "theme_set(theme_light())\n", "\n", "# Create a scatter plot\n", "p <- ggplot(data = new_pumpkins, aes(x = Price, y = Month))\n", "p + geom_point()" ], "outputs": [], "metadata": { "id": "g2YjnGeOcLo4" } }, { "cell_type": "markdown", "source": [ "このプロットは役に立つと思いますか 🤷?何か驚く点はありますか?\n", "\n", "特に役に立つわけではありません。単にデータを特定の月に点として散らばらせて表示しているだけです。\n", "
\n" ], "metadata": { "id": "Ml7SDCLQcPvE" } }, { "cell_type": "markdown", "source": [ "### **どうすれば役立つものにできるのか?**\n", "\n", "チャートに有益なデータを表示させるには、通常データを何らかの方法でグループ化する必要があります。例えば、私たちの場合、各月ごとのカボチャの平均価格を見つけることで、データの基礎的なパターンについてより深い洞察を得ることができます。このことから、もう一つの**dplyr**の便利な機能にたどり着きます:\n", "\n", "#### `dplyr::group_by() %>% summarize()`\n", "\n", "Rでのグループ化集計は以下の方法で簡単に計算できます:\n", "\n", "`dplyr::group_by() %>% summarize()`\n", "\n", "- `dplyr::group_by()`は、分析単位をデータセット全体から月ごとなどの個別のグループに変更します。\n", "\n", "- `dplyr::summarize()`は、グループ化変数ごとに1つの列、指定した集計統計ごとに1つの列を持つ新しいデータフレームを作成します。\n", "\n", "例えば、`dplyr::group_by() %>% summarize()`を使用して、**Month**列に基づいてカボチャをグループ化し、各月の**平均価格**を求めることができます。\n" ], "metadata": { "id": "jMakvJZIcVkh" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Find the average price of pumpkins per month\r\n", "new_pumpkins %>%\r\n", " group_by(Month) %>% \r\n", " summarise(mean_price = mean(Price))" ], "outputs": [], "metadata": { "id": "6kVSUa2Bcilf" } }, { "cell_type": "markdown", "source": [ "簡潔に!✨\n", "\n", "月のようなカテゴリカルな特徴は、棒グラフ📊で表現するのが適しています。棒グラフを作成するためのレイヤーは `geom_bar()` と `geom_col()` です。詳細を知りたい場合は `?geom_bar` を参照してください。\n", "\n", "さあ、作ってみましょう!\n" ], "metadata": { "id": "Kds48GUBcj3W" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Find the average price of pumpkins per month then plot a bar chart\r\n", "new_pumpkins %>%\r\n", " group_by(Month) %>% \r\n", " summarise(mean_price = mean(Price)) %>% \r\n", " ggplot(aes(x = Month, y = mean_price)) +\r\n", " geom_col(fill = \"midnightblue\", alpha = 0.7) +\r\n", " ylab(\"Pumpkin Price\")" ], "outputs": [], "metadata": { "id": "VNbU1S3BcrxO" } }, { "cell_type": "markdown", "source": [ "🤩🤩これは、より役立つデータビジュアライゼーションですね!9月と10月にカボチャの価格が最も高くなることを示しているようです。これはあなたの予想に合っていますか?なぜそう思いますか、またはなぜそうではないですか?\n", "\n", "第2レッスンの完了、おめでとうございます👏!データをモデル構築のために準備し、その後、ビジュアライゼーションを使ってさらに多くの洞察を得ることができましたね!\n" ], "metadata": { "id": "zDm0VOzzcuzR" } }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n---\n\n**免責事項**: \nこの文書はAI翻訳サービス[Co-op Translator](https://github.com/Azure/co-op-translator)を使用して翻訳されています。正確性を追求しておりますが、自動翻訳には誤りや不正確な部分が含まれる可能性があります。元の言語で記載された文書が正式な情報源とみなされるべきです。重要な情報については、専門の人間による翻訳を推奨します。この翻訳の使用に起因する誤解や誤解釈について、当社は責任を負いません。\n" ] } ] }